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