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