...

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		"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