Source file src/net/http/fcgi/child.go

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

View as plain text