1
2
3
4
5
6
7
8
9
10
11
12 package fcgi
13
14
15
16
17 import (
18 "bufio"
19 "bytes"
20 "encoding/binary"
21 "errors"
22 "io"
23 "sync"
24 )
25
26
27
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
45 const flagKeepConn = 1
46
47 const (
48 maxWrite = 65535
49 maxPad = 255
50 )
51
52 const (
53 roleResponder = iota + 1
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
90
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
102 type conn struct {
103 mutex sync.Mutex
104 rwc io.ReadWriteCloser
105
106
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
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
224
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
245
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
270 return w.c.writeRecord(w.recType, w.reqId, nil)
271 }
272
View as plain text