gitee.com/ks-custle/core-gm@v0.0.0-20230922171213-b83bdd97b62c/gmhttp/fcgi/child.go (about)

     1  // Copyright 2011 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package fcgi
     6  
     7  // This file implements FastCGI from the perspective of a child process.
     8  
     9  import (
    10  	"context"
    11  	"errors"
    12  	"fmt"
    13  	"io"
    14  	"net"
    15  	"os"
    16  	"strings"
    17  	"time"
    18  
    19  	http "gitee.com/ks-custle/core-gm/gmhttp"
    20  	"gitee.com/ks-custle/core-gm/gmhttp/cgi"
    21  )
    22  
    23  // request holds the state for an in-progress request. As soon as it's complete,
    24  // it's converted to an http.Request.
    25  type request struct {
    26  	pw        *io.PipeWriter
    27  	reqId     uint16
    28  	params    map[string]string
    29  	buf       [1024]byte
    30  	rawParams []byte
    31  	keepConn  bool
    32  }
    33  
    34  // envVarsContextKey uniquely identifies a mapping of CGI
    35  // environment variables to their values in a request context
    36  type envVarsContextKey struct{}
    37  
    38  func newRequest(reqId uint16, flags uint8) *request {
    39  	r := &request{
    40  		reqId:    reqId,
    41  		params:   map[string]string{},
    42  		keepConn: flags&flagKeepConn != 0,
    43  	}
    44  	r.rawParams = r.buf[:0]
    45  	return r
    46  }
    47  
    48  // parseParams reads an encoded []byte into Params.
    49  func (r *request) parseParams() {
    50  	text := r.rawParams
    51  	r.rawParams = nil
    52  	for len(text) > 0 {
    53  		keyLen, n := readSize(text)
    54  		if n == 0 {
    55  			return
    56  		}
    57  		text = text[n:]
    58  		valLen, n := readSize(text)
    59  		if n == 0 {
    60  			return
    61  		}
    62  		text = text[n:]
    63  		if int(keyLen)+int(valLen) > len(text) {
    64  			return
    65  		}
    66  		key := readString(text, keyLen)
    67  		text = text[keyLen:]
    68  		val := readString(text, valLen)
    69  		text = text[valLen:]
    70  		r.params[key] = val
    71  	}
    72  }
    73  
    74  // response implements http.ResponseWriter.
    75  type response struct {
    76  	req            *request
    77  	header         http.Header
    78  	code           int
    79  	wroteHeader    bool
    80  	wroteCGIHeader bool
    81  	w              *bufWriter
    82  }
    83  
    84  func newResponse(c *child, req *request) *response {
    85  	return &response{
    86  		req:    req,
    87  		header: http.Header{},
    88  		w:      newWriter(c.conn, typeStdout, req.reqId),
    89  	}
    90  }
    91  
    92  func (r *response) Header() http.Header {
    93  	return r.header
    94  }
    95  
    96  func (r *response) Write(p []byte) (n int, err error) {
    97  	if !r.wroteHeader {
    98  		r.WriteHeader(http.StatusOK)
    99  	}
   100  	if !r.wroteCGIHeader {
   101  		r.writeCGIHeader(p)
   102  	}
   103  	return r.w.Write(p)
   104  }
   105  
   106  func (r *response) WriteHeader(code int) {
   107  	if r.wroteHeader {
   108  		return
   109  	}
   110  	r.wroteHeader = true
   111  	r.code = code
   112  	if code == http.StatusNotModified {
   113  		// Must not have body.
   114  		r.header.Del("Content-Type")
   115  		r.header.Del("Content-Length")
   116  		r.header.Del("Transfer-Encoding")
   117  	}
   118  	if r.header.Get("Date") == "" {
   119  		r.header.Set("Date", time.Now().UTC().Format(http.TimeFormat))
   120  	}
   121  }
   122  
   123  // writeCGIHeader finalizes the header sent to the client and writes it to the output.
   124  // p is not written by writeHeader, but is the first chunk of the body
   125  // that will be written. It is sniffed for a Content-Type if none is
   126  // set explicitly.
   127  func (r *response) writeCGIHeader(p []byte) {
   128  	if r.wroteCGIHeader {
   129  		return
   130  	}
   131  	r.wroteCGIHeader = true
   132  	_, _ = fmt.Fprintf(r.w, "Status: %d %s\r\n", r.code, http.StatusText(r.code))
   133  	if _, hasType := r.header["Content-Type"]; r.code != http.StatusNotModified && !hasType {
   134  		r.header.Set("Content-Type", http.DetectContentType(p))
   135  	}
   136  	_ = r.header.Write(r.w)
   137  	_, _ = r.w.WriteString("\r\n")
   138  	_ = r.w.Flush()
   139  }
   140  
   141  func (r *response) Flush() {
   142  	if !r.wroteHeader {
   143  		r.WriteHeader(http.StatusOK)
   144  	}
   145  	_ = r.w.Flush()
   146  }
   147  
   148  func (r *response) Close() error {
   149  	r.Flush()
   150  	return r.w.Close()
   151  }
   152  
   153  type child struct {
   154  	conn    *conn
   155  	handler http.Handler
   156  
   157  	requests map[uint16]*request // keyed by request ID
   158  }
   159  
   160  func newChild(rwc io.ReadWriteCloser, handler http.Handler) *child {
   161  	return &child{
   162  		conn:     newConn(rwc),
   163  		handler:  handler,
   164  		requests: make(map[uint16]*request),
   165  	}
   166  }
   167  
   168  func (c *child) serve() {
   169  	defer func(conn *conn) {
   170  		_ = conn.Close()
   171  	}(c.conn)
   172  	defer c.cleanUp()
   173  	var rec record
   174  	for {
   175  		if err := rec.read(c.conn.rwc); err != nil {
   176  			return
   177  		}
   178  		if err := c.handleRecord(&rec); err != nil {
   179  			return
   180  		}
   181  	}
   182  }
   183  
   184  var errCloseConn = errors.New("fcgi: connection should be closed")
   185  
   186  var emptyBody = io.NopCloser(strings.NewReader(""))
   187  
   188  // ErrRequestAborted is returned by Read when a handler attempts to read the
   189  // body of a request that has been aborted by the web server.
   190  var ErrRequestAborted = errors.New("fcgi: request aborted by web server")
   191  
   192  // ErrConnClosed is returned by Read when a handler attempts to read the body of
   193  // a request after the connection to the web server has been closed.
   194  var ErrConnClosed = errors.New("fcgi: connection to web server closed")
   195  
   196  func (c *child) handleRecord(rec *record) error {
   197  	req, ok := c.requests[rec.h.Id]
   198  	if !ok && rec.h.Type != typeBeginRequest && rec.h.Type != typeGetValues {
   199  		// The spec says to ignore unknown request IDs.
   200  		return nil
   201  	}
   202  
   203  	switch rec.h.Type {
   204  	case typeBeginRequest:
   205  		if req != nil {
   206  			// The server is trying to begin a request with the same ID
   207  			// as an in-progress request. This is an error.
   208  			return errors.New("fcgi: received ID that is already in-flight")
   209  		}
   210  
   211  		var br beginRequest
   212  		if err := br.read(rec.content()); err != nil {
   213  			return err
   214  		}
   215  		if br.role != roleResponder {
   216  			_ = c.conn.writeEndRequest(rec.h.Id, 0, statusUnknownRole)
   217  			return nil
   218  		}
   219  		req = newRequest(rec.h.Id, br.flags)
   220  		c.requests[rec.h.Id] = req
   221  		return nil
   222  	case typeParams:
   223  		// NOTE(eds): Technically a key-value pair can straddle the boundary
   224  		// between two packets. We buffer until we've received all parameters.
   225  		if len(rec.content()) > 0 {
   226  			req.rawParams = append(req.rawParams, rec.content()...)
   227  			return nil
   228  		}
   229  		req.parseParams()
   230  		return nil
   231  	case typeStdin:
   232  		content := rec.content()
   233  		if req.pw == nil {
   234  			var body io.ReadCloser
   235  			if len(content) > 0 {
   236  				// body could be an io.LimitReader, but it shouldn't matter
   237  				// as long as both sides are behaving.
   238  				body, req.pw = io.Pipe()
   239  			} else {
   240  				body = emptyBody
   241  			}
   242  			go c.serveRequest(req, body)
   243  		}
   244  		if len(content) > 0 {
   245  			// TODO(eds): This blocks until the handler reads from the pipe.
   246  			// If the handler takes a long time, it might be a problem.
   247  			_, _ = req.pw.Write(content)
   248  		} else {
   249  			delete(c.requests, req.reqId)
   250  			if req.pw != nil {
   251  				_ = req.pw.Close()
   252  			}
   253  		}
   254  		return nil
   255  	case typeGetValues:
   256  		values := map[string]string{"FCGI_MPXS_CONNS": "1"}
   257  		_ = c.conn.writePairs(typeGetValuesResult, 0, values)
   258  		return nil
   259  	case typeData:
   260  		// If the filter role is implemented, read the data stream here.
   261  		return nil
   262  	case typeAbortRequest:
   263  		delete(c.requests, rec.h.Id)
   264  		_ = c.conn.writeEndRequest(rec.h.Id, 0, statusRequestComplete)
   265  		if req.pw != nil {
   266  			_ = req.pw.CloseWithError(ErrRequestAborted)
   267  		}
   268  		if !req.keepConn {
   269  			// connection will close upon return
   270  			return errCloseConn
   271  		}
   272  		return nil
   273  	default:
   274  		b := make([]byte, 8)
   275  		b[0] = byte(rec.h.Type)
   276  		_ = c.conn.writeRecord(typeUnknownType, 0, b)
   277  		return nil
   278  	}
   279  }
   280  
   281  // filterOutUsedEnvVars returns a new map of env vars without the
   282  // variables in the given envVars map that are read for creating each http.Request
   283  func filterOutUsedEnvVars(envVars map[string]string) map[string]string {
   284  	withoutUsedEnvVars := make(map[string]string)
   285  	for k, v := range envVars {
   286  		if addFastCGIEnvToContext(k) {
   287  			withoutUsedEnvVars[k] = v
   288  		}
   289  	}
   290  	return withoutUsedEnvVars
   291  }
   292  
   293  func (c *child) serveRequest(req *request, body io.ReadCloser) {
   294  	r := newResponse(c, req)
   295  	httpReq, err := cgi.RequestFromMap(req.params)
   296  	if err != nil {
   297  		// there was an error reading the request
   298  		r.WriteHeader(http.StatusInternalServerError)
   299  		_ = c.conn.writeRecord(typeStderr, req.reqId, []byte(err.Error()))
   300  	} else {
   301  		httpReq.Body = body
   302  		withoutUsedEnvVars := filterOutUsedEnvVars(req.params)
   303  		envVarCtx := context.WithValue(httpReq.Context(), envVarsContextKey{}, withoutUsedEnvVars)
   304  		httpReq = httpReq.WithContext(envVarCtx)
   305  		c.handler.ServeHTTP(r, httpReq)
   306  	}
   307  	// Make sure we serve something even if nothing was written to r
   308  	_, _ = r.Write(nil)
   309  	_ = r.Close()
   310  	_ = c.conn.writeEndRequest(req.reqId, 0, statusRequestComplete)
   311  
   312  	// Consume the entire body, so the host isn't still writing to
   313  	// us when we close the socket below in the !keepConn case,
   314  	// otherwise we'd send a RST. (golang.org/issue/4183)
   315  	// TODO(bradfitz): also bound this copy in time. Or send
   316  	// some sort of abort request to the host, so the host
   317  	// can properly cut off the client sending all the data.
   318  	// For now just bound it a little and
   319  	_, _ = io.CopyN(io.Discard, body, 100<<20)
   320  	_ = body.Close()
   321  
   322  	if !req.keepConn {
   323  		_ = c.conn.Close()
   324  	}
   325  }
   326  
   327  func (c *child) cleanUp() {
   328  	for _, req := range c.requests {
   329  		if req.pw != nil {
   330  			// race with call to Close in c.serveRequest doesn't matter because
   331  			// Pipe(Reader|Writer).Close are idempotent
   332  			_ = req.pw.CloseWithError(ErrConnClosed)
   333  		}
   334  	}
   335  }
   336  
   337  // Serve accepts incoming FastCGI connections on the listener l, creating a new
   338  // goroutine for each. The goroutine reads requests and then calls handler
   339  // to reply to them.
   340  // If l is nil, Serve accepts connections from os.Stdin.
   341  // If handler is nil, http.DefaultServeMux is used.
   342  //
   343  //goland:noinspection GoUnusedExportedFunction
   344  func Serve(l net.Listener, handler http.Handler) error {
   345  	if l == nil {
   346  		var err error
   347  		l, err = net.FileListener(os.Stdin)
   348  		if err != nil {
   349  			return err
   350  		}
   351  		defer func(l net.Listener) {
   352  			_ = l.Close()
   353  		}(l)
   354  	}
   355  	if handler == nil {
   356  		handler = http.DefaultServeMux
   357  	}
   358  	for {
   359  		rw, err := l.Accept()
   360  		if err != nil {
   361  			return err
   362  		}
   363  		c := newChild(rw, handler)
   364  		go c.serve()
   365  	}
   366  }
   367  
   368  // ProcessEnv returns FastCGI environment variables associated with the request r
   369  // for which no effort was made to be included in the request itself - the data
   370  // is hidden in the request's context. As an example, if REMOTE_USER is set for a
   371  // request, it will not be found anywhere in r, but it will be included in
   372  // ProcessEnv's response (via r's context).
   373  func ProcessEnv(r *http.Request) map[string]string {
   374  	env, _ := r.Context().Value(envVarsContextKey{}).(map[string]string)
   375  	return env
   376  }
   377  
   378  // addFastCGIEnvToContext reports whether to include the FastCGI environment variable s
   379  // in the http.Request.Context, accessible via ProcessEnv.
   380  func addFastCGIEnvToContext(s string) bool {
   381  	// Exclude things supported by net/http natively:
   382  	switch s {
   383  	case "CONTENT_LENGTH", "CONTENT_TYPE", "HTTPS",
   384  		"PATH_INFO", "QUERY_STRING", "REMOTE_ADDR",
   385  		"REMOTE_HOST", "REMOTE_PORT", "REQUEST_METHOD",
   386  		"REQUEST_URI", "SCRIPT_NAME", "SERVER_PROTOCOL":
   387  		return false
   388  	}
   389  	if strings.HasPrefix(s, "HTTP_") {
   390  		return false
   391  	}
   392  	// Explicitly include FastCGI-specific things.
   393  	// This list is redundant with the default "return true" below.
   394  	// Consider this documentation of the sorts of things we expect
   395  	// to maybe see.
   396  	switch s {
   397  	case "REMOTE_USER":
   398  		return true
   399  	}
   400  	// Unknown, so include it to be safe.
   401  	return true
   402  }