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