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 // http://www.fastcgi.com/devkit/doc/fcgi-spec.html#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 } 272