github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/gmhttp/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 // 7 // See https://fast-cgi.github.io/ for an unofficial mirror of the 8 // original documentation. 9 // 10 // Currently only the responder role is supported. 11 package fcgi 12 13 // This file defines the raw protocol and some utilities used by the child and 14 // the host. 15 16 import ( 17 "bufio" 18 "bytes" 19 "encoding/binary" 20 "errors" 21 "io" 22 "sync" 23 ) 24 25 // recType is a record type, as defined by 26 // https://web.archive.org/web/20150420080736/http://www.fastcgi.com/drupal/node/6?q=node/22#S8 27 type recType uint8 28 29 const ( 30 typeBeginRequest recType = 1 31 typeAbortRequest recType = 2 32 typeEndRequest recType = 3 33 typeParams recType = 4 34 typeStdin recType = 5 35 typeStdout recType = 6 36 typeStderr recType = 7 37 typeData recType = 8 38 typeGetValues recType = 9 39 typeGetValuesResult recType = 10 40 typeUnknownType recType = 11 41 ) 42 43 // keep the connection between web-server and responder open after request 44 const flagKeepConn = 1 45 46 const ( 47 maxWrite = 65535 // maximum record body 48 maxPad = 255 49 ) 50 51 const ( 52 roleResponder = iota + 1 // only Responders are implemented. 53 roleAuthorizer 54 roleFilter 55 ) 56 57 const ( 58 statusRequestComplete = iota 59 statusCantMultiplex 60 statusOverloaded 61 statusUnknownRole 62 ) 63 64 type header struct { 65 Version uint8 66 Type recType 67 Id uint16 68 ContentLength uint16 69 PaddingLength uint8 70 Reserved uint8 71 } 72 73 type beginRequest struct { 74 role uint16 75 flags uint8 76 reserved [5]uint8 77 } 78 79 func (br *beginRequest) read(content []byte) error { 80 if len(content) != 8 { 81 return errors.New("fcgi: invalid begin request record") 82 } 83 br.role = binary.BigEndian.Uint16(content) 84 br.flags = content[2] 85 return nil 86 } 87 88 // for padding so we don't have to allocate all the time 89 // not synchronized because we don't care what the contents are 90 var pad [maxPad]byte 91 92 func (h *header) init(recType recType, reqId uint16, contentLength int) { 93 h.Version = 1 94 h.Type = recType 95 h.Id = reqId 96 h.ContentLength = uint16(contentLength) 97 h.PaddingLength = uint8(-contentLength & 7) 98 } 99 100 // conn sends records over rwc 101 type conn struct { 102 mutex sync.Mutex 103 rwc io.ReadWriteCloser 104 105 // to avoid allocations 106 buf bytes.Buffer 107 h header 108 } 109 110 func newConn(rwc io.ReadWriteCloser) *conn { 111 return &conn{rwc: rwc} 112 } 113 114 func (c *conn) Close() error { 115 c.mutex.Lock() 116 defer c.mutex.Unlock() 117 return c.rwc.Close() 118 } 119 120 type record struct { 121 h header 122 buf [maxWrite + maxPad]byte 123 } 124 125 func (rec *record) read(r io.Reader) (err error) { 126 if err = binary.Read(r, binary.BigEndian, &rec.h); err != nil { 127 return err 128 } 129 if rec.h.Version != 1 { 130 return errors.New("fcgi: invalid header version") 131 } 132 n := int(rec.h.ContentLength) + int(rec.h.PaddingLength) 133 if _, err = io.ReadFull(r, rec.buf[:n]); err != nil { 134 return err 135 } 136 return nil 137 } 138 139 func (r *record) content() []byte { 140 return r.buf[:r.h.ContentLength] 141 } 142 143 // writeRecord writes and sends a single record. 144 func (c *conn) writeRecord(recType recType, reqId uint16, b []byte) error { 145 c.mutex.Lock() 146 defer c.mutex.Unlock() 147 c.buf.Reset() 148 c.h.init(recType, reqId, len(b)) 149 if err := binary.Write(&c.buf, binary.BigEndian, c.h); err != nil { 150 return err 151 } 152 if _, err := c.buf.Write(b); err != nil { 153 return err 154 } 155 if _, err := c.buf.Write(pad[:c.h.PaddingLength]); err != nil { 156 return err 157 } 158 _, err := c.rwc.Write(c.buf.Bytes()) 159 return err 160 } 161 162 func (c *conn) writeEndRequest(reqId uint16, appStatus int, protocolStatus uint8) error { 163 b := make([]byte, 8) 164 binary.BigEndian.PutUint32(b, uint32(appStatus)) 165 b[4] = protocolStatus 166 return c.writeRecord(typeEndRequest, reqId, b) 167 } 168 169 func (c *conn) writePairs(recType recType, reqId uint16, pairs map[string]string) error { 170 w := newWriter(c, recType, reqId) 171 b := make([]byte, 8) 172 for k, v := range pairs { 173 n := encodeSize(b, uint32(len(k))) 174 n += encodeSize(b[n:], uint32(len(v))) 175 if _, err := w.Write(b[:n]); err != nil { 176 return err 177 } 178 if _, err := w.WriteString(k); err != nil { 179 return err 180 } 181 if _, err := w.WriteString(v); err != nil { 182 return err 183 } 184 } 185 w.Close() 186 return nil 187 } 188 189 func readSize(s []byte) (uint32, int) { 190 if len(s) == 0 { 191 return 0, 0 192 } 193 size, n := uint32(s[0]), 1 194 if size&(1<<7) != 0 { 195 if len(s) < 4 { 196 return 0, 0 197 } 198 n = 4 199 size = binary.BigEndian.Uint32(s) 200 size &^= 1 << 31 201 } 202 return size, n 203 } 204 205 func readString(s []byte, size uint32) string { 206 if size > uint32(len(s)) { 207 return "" 208 } 209 return string(s[:size]) 210 } 211 212 func encodeSize(b []byte, size uint32) int { 213 if size > 127 { 214 size |= 1 << 31 215 binary.BigEndian.PutUint32(b, size) 216 return 4 217 } 218 b[0] = byte(size) 219 return 1 220 } 221 222 // bufWriter encapsulates bufio.Writer but also closes the underlying stream when 223 // Closed. 224 type bufWriter struct { 225 closer io.Closer 226 *bufio.Writer 227 } 228 229 func (w *bufWriter) Close() error { 230 if err := w.Writer.Flush(); err != nil { 231 w.closer.Close() 232 return err 233 } 234 return w.closer.Close() 235 } 236 237 func newWriter(c *conn, recType recType, reqId uint16) *bufWriter { 238 s := &streamWriter{c: c, recType: recType, reqId: reqId} 239 w := bufio.NewWriterSize(s, maxWrite) 240 return &bufWriter{s, w} 241 } 242 243 // streamWriter abstracts out the separation of a stream into discrete records. 244 // It only writes maxWrite bytes at a time. 245 type streamWriter struct { 246 c *conn 247 recType recType 248 reqId uint16 249 } 250 251 func (w *streamWriter) Write(p []byte) (int, error) { 252 nn := 0 253 for len(p) > 0 { 254 n := len(p) 255 if n > maxWrite { 256 n = maxWrite 257 } 258 if err := w.c.writeRecord(w.recType, w.reqId, p[:n]); err != nil { 259 return nn, err 260 } 261 nn += n 262 p = p[n:] 263 } 264 return nn, nil 265 } 266 267 func (w *streamWriter) Close() error { 268 // send empty record to close the stream 269 return w.c.writeRecord(w.recType, w.reqId, nil) 270 }