...
Run Format

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

Documentation: net/http/fcgi

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

View as plain text