github.com/miolini/go@v0.0.0-20160405192216-fca68c8cb408/src/net/http/fcgi/fcgi.go (about) 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 type header struct { 62 Version uint8 63 Type recType 64 Id uint16 65 ContentLength uint16 66 PaddingLength uint8 67 Reserved uint8 68 } 69 70 type beginRequest struct { 71 role uint16 72 flags uint8 73 reserved [5]uint8 74 } 75 76 func (br *beginRequest) read(content []byte) error { 77 if len(content) != 8 { 78 return errors.New("fcgi: invalid begin request record") 79 } 80 br.role = binary.BigEndian.Uint16(content) 81 br.flags = content[2] 82 return nil 83 } 84 85 // for padding so we don't have to allocate all the time 86 // not synchronized because we don't care what the contents are 87 var pad [maxPad]byte 88 89 func (h *header) init(recType recType, reqId uint16, contentLength int) { 90 h.Version = 1 91 h.Type = recType 92 h.Id = reqId 93 h.ContentLength = uint16(contentLength) 94 h.PaddingLength = uint8(-contentLength & 7) 95 } 96 97 // conn sends records over rwc 98 type conn struct { 99 mutex sync.Mutex 100 rwc io.ReadWriteCloser 101 102 // to avoid allocations 103 buf bytes.Buffer 104 h header 105 } 106 107 func newConn(rwc io.ReadWriteCloser) *conn { 108 return &conn{rwc: rwc} 109 } 110 111 func (c *conn) Close() error { 112 c.mutex.Lock() 113 defer c.mutex.Unlock() 114 return c.rwc.Close() 115 } 116 117 type record struct { 118 h header 119 buf [maxWrite + maxPad]byte 120 } 121 122 func (rec *record) read(r io.Reader) (err error) { 123 if err = binary.Read(r, binary.BigEndian, &rec.h); err != nil { 124 return err 125 } 126 if rec.h.Version != 1 { 127 return errors.New("fcgi: invalid header version") 128 } 129 n := int(rec.h.ContentLength) + int(rec.h.PaddingLength) 130 if _, err = io.ReadFull(r, rec.buf[:n]); err != nil { 131 return err 132 } 133 return nil 134 } 135 136 func (r *record) content() []byte { 137 return r.buf[:r.h.ContentLength] 138 } 139 140 // writeRecord writes and sends a single record. 141 func (c *conn) writeRecord(recType recType, reqId uint16, b []byte) error { 142 c.mutex.Lock() 143 defer c.mutex.Unlock() 144 c.buf.Reset() 145 c.h.init(recType, reqId, len(b)) 146 if err := binary.Write(&c.buf, binary.BigEndian, c.h); err != nil { 147 return err 148 } 149 if _, err := c.buf.Write(b); err != nil { 150 return err 151 } 152 if _, err := c.buf.Write(pad[:c.h.PaddingLength]); err != nil { 153 return err 154 } 155 _, err := c.rwc.Write(c.buf.Bytes()) 156 return err 157 } 158 159 func (c *conn) writeEndRequest(reqId uint16, appStatus int, protocolStatus uint8) error { 160 b := make([]byte, 8) 161 binary.BigEndian.PutUint32(b, uint32(appStatus)) 162 b[4] = protocolStatus 163 return c.writeRecord(typeEndRequest, reqId, b) 164 } 165 166 func (c *conn) writePairs(recType recType, reqId uint16, pairs map[string]string) error { 167 w := newWriter(c, recType, reqId) 168 b := make([]byte, 8) 169 for k, v := range pairs { 170 n := encodeSize(b, uint32(len(k))) 171 n += encodeSize(b[n:], uint32(len(v))) 172 if _, err := w.Write(b[:n]); err != nil { 173 return err 174 } 175 if _, err := w.WriteString(k); err != nil { 176 return err 177 } 178 if _, err := w.WriteString(v); err != nil { 179 return err 180 } 181 } 182 w.Close() 183 return nil 184 } 185 186 func readSize(s []byte) (uint32, int) { 187 if len(s) == 0 { 188 return 0, 0 189 } 190 size, n := uint32(s[0]), 1 191 if size&(1<<7) != 0 { 192 if len(s) < 4 { 193 return 0, 0 194 } 195 n = 4 196 size = binary.BigEndian.Uint32(s) 197 size &^= 1 << 31 198 } 199 return size, n 200 } 201 202 func readString(s []byte, size uint32) string { 203 if size > uint32(len(s)) { 204 return "" 205 } 206 return string(s[:size]) 207 } 208 209 func encodeSize(b []byte, size uint32) int { 210 if size > 127 { 211 size |= 1 << 31 212 binary.BigEndian.PutUint32(b, size) 213 return 4 214 } 215 b[0] = byte(size) 216 return 1 217 } 218 219 // bufWriter encapsulates bufio.Writer but also closes the underlying stream when 220 // Closed. 221 type bufWriter struct { 222 closer io.Closer 223 *bufio.Writer 224 } 225 226 func (w *bufWriter) Close() error { 227 if err := w.Writer.Flush(); err != nil { 228 w.closer.Close() 229 return err 230 } 231 return w.closer.Close() 232 } 233 234 func newWriter(c *conn, recType recType, reqId uint16) *bufWriter { 235 s := &streamWriter{c: c, recType: recType, reqId: reqId} 236 w := bufio.NewWriterSize(s, maxWrite) 237 return &bufWriter{s, w} 238 } 239 240 // streamWriter abstracts out the separation of a stream into discrete records. 241 // It only writes maxWrite bytes at a time. 242 type streamWriter struct { 243 c *conn 244 recType recType 245 reqId uint16 246 } 247 248 func (w *streamWriter) Write(p []byte) (int, error) { 249 nn := 0 250 for len(p) > 0 { 251 n := len(p) 252 if n > maxWrite { 253 n = maxWrite 254 } 255 if err := w.c.writeRecord(w.recType, w.reqId, p[:n]); err != nil { 256 return nn, err 257 } 258 nn += n 259 p = p[n:] 260 } 261 return nn, nil 262 } 263 264 func (w *streamWriter) Close() error { 265 // send empty record to close the stream 266 return w.c.writeRecord(w.recType, w.reqId, nil) 267 }