github.com/slayercat/go@v0.0.0-20170428012452-c51559813f61/src/net/http/fcgi/fcgi.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 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  }