141 lines
2.6 KiB
Go
141 lines
2.6 KiB
Go
package fastcgi
|
|
|
|
import (
|
|
"bufio"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
)
|
|
|
|
type FCGIRequestType uint8
|
|
|
|
const FCGI_LISTENSOCK_FILENO uint8 = 0
|
|
const FCGI_HEADER_LEN uint8 = 8
|
|
const VERSION_1 uint8 = 1
|
|
const FCGI_NULL_REQUEST_ID uint8 = 0
|
|
const FCGI_KEEP_CONN uint8 = 1
|
|
const doubleCRLF = "\r\n\r\n"
|
|
|
|
const (
|
|
FCGI_BEGIN_REQUEST FCGIRequestType = iota + 1
|
|
FCGI_ABORT_REQUEST
|
|
FCGI_END_REQUEST
|
|
FCGI_PARAMS
|
|
FCGI_STDIN
|
|
FCGI_STDOUT
|
|
FCGI_STDERR
|
|
FCGI_DATA
|
|
FCGI_GET_VALUES
|
|
FCGI_GET_VALUES_RESULT
|
|
FCGI_UNKNOWN_TYPE
|
|
FCGI_MAXTYPE = FCGI_UNKNOWN_TYPE
|
|
)
|
|
|
|
const (
|
|
FCGI_RESPONDER uint8 = iota + 1
|
|
FCGI_AUTHORIZER
|
|
FCGI_FILTER
|
|
)
|
|
|
|
const (
|
|
FCGI_REQUEST_COMPLETE uint8 = iota
|
|
FCGI_CANT_MPX_CONN
|
|
FCGI_OVERLOADED
|
|
FCGI_UNKNOWN_ROLE
|
|
)
|
|
|
|
const (
|
|
FCGI_MAX_CONNS string = "MAX_CONNS"
|
|
FCGI_MAX_REQS string = "MAX_REQS"
|
|
FCGI_MPXS_CONNS string = "MPXS_CONNS"
|
|
)
|
|
|
|
const (
|
|
maxWrite = 65500 // 65530 may work, but for compatibility
|
|
maxPad = 255
|
|
)
|
|
|
|
// Connects to the fcgi responder at the specified network address.
|
|
// See func net.Dial for a description of the network and address parameters.
|
|
func Dial(network, address string) (fcgi *FCGIClient, err error) {
|
|
var conn net.Conn
|
|
|
|
conn, err = net.Dial(network, address)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
fcgi = &FCGIClient{
|
|
rwc: conn,
|
|
keepAlive: false,
|
|
reqId: 1,
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func readSize(s []byte) (uint32, int) {
|
|
if len(s) == 0 {
|
|
return 0, 0
|
|
}
|
|
size, n := uint32(s[0]), 1
|
|
if size&(1<<7) != 0 {
|
|
if len(s) < 4 {
|
|
return 0, 0
|
|
}
|
|
n = 4
|
|
size = binary.BigEndian.Uint32(s)
|
|
size &^= 1 << 31
|
|
}
|
|
return size, n
|
|
}
|
|
|
|
func readString(s []byte, size uint32) string {
|
|
if size > uint32(len(s)) {
|
|
return ""
|
|
}
|
|
return string(s[:size])
|
|
}
|
|
|
|
func encodeSize(b []byte, size uint32) int {
|
|
if size > 127 {
|
|
size |= 1 << 31
|
|
binary.BigEndian.PutUint32(b, size)
|
|
return 4
|
|
}
|
|
b[0] = byte(size)
|
|
return 1
|
|
}
|
|
|
|
// bufWriter encapsulates bufio.Writer but also closes the underlying stream when
|
|
// Closed.
|
|
type bufWriter struct {
|
|
closer io.Closer
|
|
*bufio.Writer
|
|
}
|
|
|
|
func (w *bufWriter) Close() error {
|
|
if err := w.Writer.Flush(); err != nil {
|
|
w.closer.Close()
|
|
return err
|
|
}
|
|
return w.closer.Close()
|
|
}
|
|
|
|
func newWriter(c *FCGIClient, recType FCGIRequestType) *bufWriter {
|
|
s := &streamWriter{c: c, recType: recType}
|
|
w := bufio.NewWriterSize(s, maxWrite)
|
|
return &bufWriter{s, w}
|
|
}
|
|
|
|
type badStringError struct {
|
|
what string
|
|
str string
|
|
}
|
|
|
|
func (e *badStringError) Error() string { return fmt.Sprintf("%s %q", e.what, e.str) }
|
|
|
|
// Checks whether chunked is part of the encodings stack
|
|
func chunked(te []string) bool { return len(te) > 0 && te[0] == "chunked" }
|