libgo: update to Go 1.11
[official-gcc.git] / libgo / go / net / http / fcgi / fcgi.go
blobfb822f8a6d5d8db7028a437e5221d590da490042
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.
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
13 // This file defines the raw protocol and some utilities used by the child and
14 // the host.
16 import (
17 "bufio"
18 "bytes"
19 "encoding/binary"
20 "errors"
21 "io"
22 "sync"
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
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
43 // keep the connection between web-server and responder open after request
44 const flagKeepConn = 1
46 const (
47 maxWrite = 65535 // maximum record body
48 maxPad = 255
51 const (
52 roleResponder = iota + 1 // only Responders are implemented.
53 roleAuthorizer
54 roleFilter
57 const (
58 statusRequestComplete = iota
59 statusCantMultiplex
60 statusOverloaded
61 statusUnknownRole
64 type header struct {
65 Version uint8
66 Type recType
67 Id uint16
68 ContentLength uint16
69 PaddingLength uint8
70 Reserved uint8
73 type beginRequest struct {
74 role uint16
75 flags uint8
76 reserved [5]uint8
79 func (br *beginRequest) read(content []byte) error {
80 if len(content) != 8 {
81 return errors.New("fcgi: invalid begin request record")
83 br.role = binary.BigEndian.Uint16(content)
84 br.flags = content[2]
85 return nil
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
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)
100 // conn sends records over rwc
101 type conn struct {
102 mutex sync.Mutex
103 rwc io.ReadWriteCloser
105 // to avoid allocations
106 buf bytes.Buffer
107 h header
110 func newConn(rwc io.ReadWriteCloser) *conn {
111 return &conn{rwc: rwc}
114 func (c *conn) Close() error {
115 c.mutex.Lock()
116 defer c.mutex.Unlock()
117 return c.rwc.Close()
120 type record struct {
121 h header
122 buf [maxWrite + maxPad]byte
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
129 if rec.h.Version != 1 {
130 return errors.New("fcgi: invalid header version")
132 n := int(rec.h.ContentLength) + int(rec.h.PaddingLength)
133 if _, err = io.ReadFull(r, rec.buf[:n]); err != nil {
134 return err
136 return nil
139 func (r *record) content() []byte {
140 return r.buf[:r.h.ContentLength]
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
152 if _, err := c.buf.Write(b); err != nil {
153 return err
155 if _, err := c.buf.Write(pad[:c.h.PaddingLength]); err != nil {
156 return err
158 _, err := c.rwc.Write(c.buf.Bytes())
159 return err
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)
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
178 if _, err := w.WriteString(k); err != nil {
179 return err
181 if _, err := w.WriteString(v); err != nil {
182 return err
185 w.Close()
186 return nil
189 func readSize(s []byte) (uint32, int) {
190 if len(s) == 0 {
191 return 0, 0
193 size, n := uint32(s[0]), 1
194 if size&(1<<7) != 0 {
195 if len(s) < 4 {
196 return 0, 0
198 n = 4
199 size = binary.BigEndian.Uint32(s)
200 size &^= 1 << 31
202 return size, n
205 func readString(s []byte, size uint32) string {
206 if size > uint32(len(s)) {
207 return ""
209 return string(s[:size])
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
218 b[0] = byte(size)
219 return 1
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
229 func (w *bufWriter) Close() error {
230 if err := w.Writer.Flush(); err != nil {
231 w.closer.Close()
232 return err
234 return w.closer.Close()
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}
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
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
258 if err := w.c.writeRecord(w.recType, w.reqId, p[:n]); err != nil {
259 return nn, err
261 nn += n
262 p = p[n:]
264 return nn, nil
267 func (w *streamWriter) Close() error {
268 // send empty record to close the stream
269 return w.c.writeRecord(w.recType, w.reqId, nil)