github.com/ader1990/go@v0.0.0-20140630135419-8c24447fa791/src/pkg/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 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 }