Home | History | Annotate | Download | only in 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 	"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 }
    332