...

Source file src/pkg/net/http/fcgi/fcgi.go

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

View as plain text