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 }