src/pkg/net/http/fcgi/fcgi.go - The Go Programming Language

Golang

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