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 implements the FastCGI protocol.
      6 //
      7 // The protocol is not an official standard and the original
      8 // documentation is no longer online. See the Internet Archive's
      9 // mirror at: https://web.archive.org/web/20150420080736/http://www.fastcgi.com/drupal/node/6?q=node/22
     10 //
     11 // Currently only the responder role is supported.
     12 package fcgi
     13 
     14 // This file defines the raw protocol and some utilities used by the child and
     15 // the host.
     16 
     17 import (
     18 	"bufio"
     19 	"bytes"
     20 	"encoding/binary"
     21 	"errors"
     22 	"io"
     23 	"sync"
     24 )
     25 
     26 // recType is a record type, as defined by
     27 // https://web.archive.org/web/20150420080736/http://www.fastcgi.com/drupal/node/6?q=node/22#S8
     28 type recType uint8
     29 
     30 const (
     31 	typeBeginRequest    recType = 1
     32 	typeAbortRequest    recType = 2
     33 	typeEndRequest      recType = 3
     34 	typeParams          recType = 4
     35 	typeStdin           recType = 5
     36 	typeStdout          recType = 6
     37 	typeStderr          recType = 7
     38 	typeData            recType = 8
     39 	typeGetValues       recType = 9
     40 	typeGetValuesResult recType = 10
     41 	typeUnknownType     recType = 11
     42 )
     43 
     44 // keep the connection between web-server and responder open after request
     45 const flagKeepConn = 1
     46 
     47 const (
     48 	maxWrite = 65535 // maximum record body
     49 	maxPad   = 255
     50 )
     51 
     52 const (
     53 	roleResponder = iota + 1 // only Responders are implemented.
     54 	roleAuthorizer
     55 	roleFilter
     56 )
     57 
     58 const (
     59 	statusRequestComplete = iota
     60 	statusCantMultiplex
     61 	statusOverloaded
     62 	statusUnknownRole
     63 )
     64 
     65 type header struct {
     66 	Version       uint8
     67 	Type          recType
     68 	Id            uint16
     69 	ContentLength uint16
     70 	PaddingLength uint8
     71 	Reserved      uint8
     72 }
     73 
     74 type beginRequest struct {
     75 	role     uint16
     76 	flags    uint8
     77 	reserved [5]uint8
     78 }
     79 
     80 func (br *beginRequest) read(content []byte) error {
     81 	if len(content) != 8 {
     82 		return errors.New("fcgi: invalid begin request record")
     83 	}
     84 	br.role = binary.BigEndian.Uint16(content)
     85 	br.flags = content[2]
     86 	return nil
     87 }
     88 
     89 // for padding so we don't have to allocate all the time
     90 // not synchronized because we don't care what the contents are
     91 var pad [maxPad]byte
     92 
     93 func (h *header) init(recType recType, reqId uint16, contentLength int) {
     94 	h.Version = 1
     95 	h.Type = recType
     96 	h.Id = reqId
     97 	h.ContentLength = uint16(contentLength)
     98 	h.PaddingLength = uint8(-contentLength & 7)
     99 }
    100 
    101 // conn sends records over rwc
    102 type conn struct {
    103 	mutex sync.Mutex
    104 	rwc   io.ReadWriteCloser
    105 
    106 	// to avoid allocations
    107 	buf bytes.Buffer
    108 	h   header
    109 }
    110 
    111 func newConn(rwc io.ReadWriteCloser) *conn {
    112 	return &conn{rwc: rwc}
    113 }
    114 
    115 func (c *conn) Close() error {
    116 	c.mutex.Lock()
    117 	defer c.mutex.Unlock()
    118 	return c.rwc.Close()
    119 }
    120 
    121 type record struct {
    122 	h   header
    123 	buf [maxWrite + maxPad]byte
    124 }
    125 
    126 func (rec *record) read(r io.Reader) (err error) {
    127 	if err = binary.Read(r, binary.BigEndian, &rec.h); err != nil {
    128 		return err
    129 	}
    130 	if rec.h.Version != 1 {
    131 		return errors.New("fcgi: invalid header version")
    132 	}
    133 	n := int(rec.h.ContentLength) + int(rec.h.PaddingLength)
    134 	if _, err = io.ReadFull(r, rec.buf[:n]); err != nil {
    135 		return err
    136 	}
    137 	return nil
    138 }
    139 
    140 func (r *record) content() []byte {
    141 	return r.buf[:r.h.ContentLength]
    142 }
    143 
    144 // writeRecord writes and sends a single record.
    145 func (c *conn) writeRecord(recType recType, reqId uint16, b []byte) error {
    146 	c.mutex.Lock()
    147 	defer c.mutex.Unlock()
    148 	c.buf.Reset()
    149 	c.h.init(recType, reqId, len(b))
    150 	if err := binary.Write(&c.buf, binary.BigEndian, c.h); err != nil {
    151 		return err
    152 	}
    153 	if _, err := c.buf.Write(b); err != nil {
    154 		return err
    155 	}
    156 	if _, err := c.buf.Write(pad[:c.h.PaddingLength]); err != nil {
    157 		return err
    158 	}
    159 	_, err := c.rwc.Write(c.buf.Bytes())
    160 	return err
    161 }
    162 
    163 func (c *conn) writeEndRequest(reqId uint16, appStatus int, protocolStatus uint8) error {
    164 	b := make([]byte, 8)
    165 	binary.BigEndian.PutUint32(b, uint32(appStatus))
    166 	b[4] = protocolStatus
    167 	return c.writeRecord(typeEndRequest, reqId, b)
    168 }
    169 
    170 func (c *conn) writePairs(recType recType, reqId uint16, pairs map[string]string) error {
    171 	w := newWriter(c, recType, reqId)
    172 	b := make([]byte, 8)
    173 	for k, v := range pairs {
    174 		n := encodeSize(b, uint32(len(k)))
    175 		n += encodeSize(b[n:], uint32(len(v)))
    176 		if _, err := w.Write(b[:n]); err != nil {
    177 			return err
    178 		}
    179 		if _, err := w.WriteString(k); err != nil {
    180 			return err
    181 		}
    182 		if _, err := w.WriteString(v); err != nil {
    183 			return err
    184 		}
    185 	}
    186 	w.Close()
    187 	return nil
    188 }
    189 
    190 func readSize(s []byte) (uint32, int) {
    191 	if len(s) == 0 {
    192 		return 0, 0
    193 	}
    194 	size, n := uint32(s[0]), 1
    195 	if size&(1<<7) != 0 {
    196 		if len(s) < 4 {
    197 			return 0, 0
    198 		}
    199 		n = 4
    200 		size = binary.BigEndian.Uint32(s)
    201 		size &^= 1 << 31
    202 	}
    203 	return size, n
    204 }
    205 
    206 func readString(s []byte, size uint32) string {
    207 	if size > uint32(len(s)) {
    208 		return ""
    209 	}
    210 	return string(s[:size])
    211 }
    212 
    213 func encodeSize(b []byte, size uint32) int {
    214 	if size > 127 {
    215 		size |= 1 << 31
    216 		binary.BigEndian.PutUint32(b, size)
    217 		return 4
    218 	}
    219 	b[0] = byte(size)
    220 	return 1
    221 }
    222 
    223 // bufWriter encapsulates bufio.Writer but also closes the underlying stream when
    224 // Closed.
    225 type bufWriter struct {
    226 	closer io.Closer
    227 	*bufio.Writer
    228 }
    229 
    230 func (w *bufWriter) Close() error {
    231 	if err := w.Writer.Flush(); err != nil {
    232 		w.closer.Close()
    233 		return err
    234 	}
    235 	return w.closer.Close()
    236 }
    237 
    238 func newWriter(c *conn, recType recType, reqId uint16) *bufWriter {
    239 	s := &streamWriter{c: c, recType: recType, reqId: reqId}
    240 	w := bufio.NewWriterSize(s, maxWrite)
    241 	return &bufWriter{s, w}
    242 }
    243 
    244 // streamWriter abstracts out the separation of a stream into discrete records.
    245 // It only writes maxWrite bytes at a time.
    246 type streamWriter struct {
    247 	c       *conn
    248 	recType recType
    249 	reqId   uint16
    250 }
    251 
    252 func (w *streamWriter) Write(p []byte) (int, error) {
    253 	nn := 0
    254 	for len(p) > 0 {
    255 		n := len(p)
    256 		if n > maxWrite {
    257 			n = maxWrite
    258 		}
    259 		if err := w.c.writeRecord(w.recType, w.reqId, p[:n]); err != nil {
    260 			return nn, err
    261 		}
    262 		nn += n
    263 		p = p[n:]
    264 	}
    265 	return nn, nil
    266 }
    267 
    268 func (w *streamWriter) Close() error {
    269 	// send empty record to close the stream
    270 	return w.c.writeRecord(w.recType, w.reqId, nil)
    271 }
    272