github.com/rakyll/go@v0.0.0-20170216000551-64c02460d703/src/net/http/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  	"errors"
    11  	"fmt"
    12  	"io"
    13  	"io/ioutil"
    14  	"net"
    15  	"net/http"
    16  	"net/http/cgi"
    17  	"os"
    18  	"strings"
    19  	"sync"
    20  	"time"
    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  func newRequest(reqId uint16, flags uint8) *request {
    35  	r := &request{
    36  		reqId:    reqId,
    37  		params:   map[string]string{},
    38  		keepConn: flags&flagKeepConn != 0,
    39  	}
    40  	r.rawParams = r.buf[:0]
    41  	return r
    42  }
    43  
    44  // parseParams reads an encoded []byte into Params.
    45  func (r *request) parseParams() {
    46  	text := r.rawParams
    47  	r.rawParams = nil
    48  	for len(text) > 0 {
    49  		keyLen, n := readSize(text)
    50  		if n == 0 {
    51  			return
    52  		}
    53  		text = text[n:]
    54  		valLen, n := readSize(text)
    55  		if n == 0 {
    56  			return
    57  		}
    58  		text = text[n:]
    59  		if int(keyLen)+int(valLen) > len(text) {
    60  			return
    61  		}
    62  		key := readString(text, keyLen)
    63  		text = text[keyLen:]
    64  		val := readString(text, valLen)
    65  		text = text[valLen:]
    66  		r.params[key] = val
    67  	}
    68  }
    69  
    70  // response implements http.ResponseWriter.
    71  type response struct {
    72  	req         *request
    73  	header      http.Header
    74  	w           *bufWriter
    75  	wroteHeader bool
    76  }
    77  
    78  func newResponse(c *child, req *request) *response {
    79  	return &response{
    80  		req:    req,
    81  		header: http.Header{},
    82  		w:      newWriter(c.conn, typeStdout, req.reqId),
    83  	}
    84  }
    85  
    86  func (r *response) Header() http.Header {
    87  	return r.header
    88  }
    89  
    90  func (r *response) Write(data []byte) (int, error) {
    91  	if !r.wroteHeader {
    92  		r.WriteHeader(http.StatusOK)
    93  	}
    94  	return r.w.Write(data)
    95  }
    96  
    97  func (r *response) WriteHeader(code int) {
    98  	if r.wroteHeader {
    99  		return
   100  	}
   101  	r.wroteHeader = true
   102  	if code == http.StatusNotModified {
   103  		// Must not have body.
   104  		r.header.Del("Content-Type")
   105  		r.header.Del("Content-Length")
   106  		r.header.Del("Transfer-Encoding")
   107  	} else if r.header.Get("Content-Type") == "" {
   108  		r.header.Set("Content-Type", "text/html; charset=utf-8")
   109  	}
   110  
   111  	if r.header.Get("Date") == "" {
   112  		r.header.Set("Date", time.Now().UTC().Format(http.TimeFormat))
   113  	}
   114  
   115  	fmt.Fprintf(r.w, "Status: %d %s\r\n", code, http.StatusText(code))
   116  	r.header.Write(r.w)
   117  	r.w.WriteString("\r\n")
   118  }
   119  
   120  func (r *response) Flush() {
   121  	if !r.wroteHeader {
   122  		r.WriteHeader(http.StatusOK)
   123  	}
   124  	r.w.Flush()
   125  }
   126  
   127  func (r *response) Close() error {
   128  	r.Flush()
   129  	return r.w.Close()
   130  }
   131  
   132  type child struct {
   133  	conn    *conn
   134  	handler http.Handler
   135  
   136  	mu       sync.Mutex          // protects requests:
   137  	requests map[uint16]*request // keyed by request ID
   138  }
   139  
   140  func newChild(rwc io.ReadWriteCloser, handler http.Handler) *child {
   141  	return &child{
   142  		conn:     newConn(rwc),
   143  		handler:  handler,
   144  		requests: make(map[uint16]*request),
   145  	}
   146  }
   147  
   148  func (c *child) serve() {
   149  	defer c.conn.Close()
   150  	defer c.cleanUp()
   151  	var rec record
   152  	for {
   153  		if err := rec.read(c.conn.rwc); err != nil {
   154  			return
   155  		}
   156  		if err := c.handleRecord(&rec); err != nil {
   157  			return
   158  		}
   159  	}
   160  }
   161  
   162  var errCloseConn = errors.New("fcgi: connection should be closed")
   163  
   164  var emptyBody = ioutil.NopCloser(strings.NewReader(""))
   165  
   166  // ErrRequestAborted is returned by Read when a handler attempts to read the
   167  // body of a request that has been aborted by the web server.
   168  var ErrRequestAborted = errors.New("fcgi: request aborted by web server")
   169  
   170  // ErrConnClosed is returned by Read when a handler attempts to read the body of
   171  // a request after the connection to the web server has been closed.
   172  var ErrConnClosed = errors.New("fcgi: connection to web server closed")
   173  
   174  func (c *child) handleRecord(rec *record) error {
   175  	c.mu.Lock()
   176  	req, ok := c.requests[rec.h.Id]
   177  	c.mu.Unlock()
   178  	if !ok && rec.h.Type != typeBeginRequest && rec.h.Type != typeGetValues {
   179  		// The spec says to ignore unknown request IDs.
   180  		return nil
   181  	}
   182  
   183  	switch rec.h.Type {
   184  	case typeBeginRequest:
   185  		if req != nil {
   186  			// The server is trying to begin a request with the same ID
   187  			// as an in-progress request. This is an error.
   188  			return errors.New("fcgi: received ID that is already in-flight")
   189  		}
   190  
   191  		var br beginRequest
   192  		if err := br.read(rec.content()); err != nil {
   193  			return err
   194  		}
   195  		if br.role != roleResponder {
   196  			c.conn.writeEndRequest(rec.h.Id, 0, statusUnknownRole)
   197  			return nil
   198  		}
   199  		req = newRequest(rec.h.Id, br.flags)
   200  		c.mu.Lock()
   201  		c.requests[rec.h.Id] = req
   202  		c.mu.Unlock()
   203  		return nil
   204  	case typeParams:
   205  		// NOTE(eds): Technically a key-value pair can straddle the boundary
   206  		// between two packets. We buffer until we've received all parameters.
   207  		if len(rec.content()) > 0 {
   208  			req.rawParams = append(req.rawParams, rec.content()...)
   209  			return nil
   210  		}
   211  		req.parseParams()
   212  		return nil
   213  	case typeStdin:
   214  		content := rec.content()
   215  		if req.pw == nil {
   216  			var body io.ReadCloser
   217  			if len(content) > 0 {
   218  				// body could be an io.LimitReader, but it shouldn't matter
   219  				// as long as both sides are behaving.
   220  				body, req.pw = io.Pipe()
   221  			} else {
   222  				body = emptyBody
   223  			}
   224  			go c.serveRequest(req, body)
   225  		}
   226  		if len(content) > 0 {
   227  			// TODO(eds): This blocks until the handler reads from the pipe.
   228  			// If the handler takes a long time, it might be a problem.
   229  			req.pw.Write(content)
   230  		} else if req.pw != nil {
   231  			req.pw.Close()
   232  		}
   233  		return nil
   234  	case typeGetValues:
   235  		values := map[string]string{"FCGI_MPXS_CONNS": "1"}
   236  		c.conn.writePairs(typeGetValuesResult, 0, values)
   237  		return nil
   238  	case typeData:
   239  		// If the filter role is implemented, read the data stream here.
   240  		return nil
   241  	case typeAbortRequest:
   242  		c.mu.Lock()
   243  		delete(c.requests, rec.h.Id)
   244  		c.mu.Unlock()
   245  		c.conn.writeEndRequest(rec.h.Id, 0, statusRequestComplete)
   246  		if req.pw != nil {
   247  			req.pw.CloseWithError(ErrRequestAborted)
   248  		}
   249  		if !req.keepConn {
   250  			// connection will close upon return
   251  			return errCloseConn
   252  		}
   253  		return nil
   254  	default:
   255  		b := make([]byte, 8)
   256  		b[0] = byte(rec.h.Type)
   257  		c.conn.writeRecord(typeUnknownType, 0, b)
   258  		return nil
   259  	}
   260  }
   261  
   262  func (c *child) serveRequest(req *request, body io.ReadCloser) {
   263  	r := newResponse(c, req)
   264  	httpReq, err := cgi.RequestFromMap(req.params)
   265  	if err != nil {
   266  		// there was an error reading the request
   267  		r.WriteHeader(http.StatusInternalServerError)
   268  		c.conn.writeRecord(typeStderr, req.reqId, []byte(err.Error()))
   269  	} else {
   270  		httpReq.Body = body
   271  		c.handler.ServeHTTP(r, httpReq)
   272  	}
   273  	r.Close()
   274  	c.mu.Lock()
   275  	delete(c.requests, req.reqId)
   276  	c.mu.Unlock()
   277  	c.conn.writeEndRequest(req.reqId, 0, statusRequestComplete)
   278  
   279  	// Consume the entire body, so the host isn't still writing to
   280  	// us when we close the socket below in the !keepConn case,
   281  	// otherwise we'd send a RST. (golang.org/issue/4183)
   282  	// TODO(bradfitz): also bound this copy in time. Or send
   283  	// some sort of abort request to the host, so the host
   284  	// can properly cut off the client sending all the data.
   285  	// For now just bound it a little and
   286  	io.CopyN(ioutil.Discard, body, 100<<20)
   287  	body.Close()
   288  
   289  	if !req.keepConn {
   290  		c.conn.Close()
   291  	}
   292  }
   293  
   294  func (c *child) cleanUp() {
   295  	c.mu.Lock()
   296  	defer c.mu.Unlock()
   297  	for _, req := range c.requests {
   298  		if req.pw != nil {
   299  			// race with call to Close in c.serveRequest doesn't matter because
   300  			// Pipe(Reader|Writer).Close are idempotent
   301  			req.pw.CloseWithError(ErrConnClosed)
   302  		}
   303  	}
   304  }
   305  
   306  // Serve accepts incoming FastCGI connections on the listener l, creating a new
   307  // goroutine for each. The goroutine reads requests and then calls handler
   308  // to reply to them.
   309  // If l is nil, Serve accepts connections from os.Stdin.
   310  // If handler is nil, http.DefaultServeMux is used.
   311  func Serve(l net.Listener, handler http.Handler) error {
   312  	if l == nil {
   313  		var err error
   314  		l, err = net.FileListener(os.Stdin)
   315  		if err != nil {
   316  			return err
   317  		}
   318  		defer l.Close()
   319  	}
   320  	if handler == nil {
   321  		handler = http.DefaultServeMux
   322  	}
   323  	for {
   324  		rw, err := l.Accept()
   325  		if err != nil {
   326  			return err
   327  		}
   328  		c := newChild(rw, handler)
   329  		go c.serve()
   330  	}
   331  }