diff --git a/fastcgi/client.go b/fastcgi/client.go index 128235f..1108d85 100644 --- a/fastcgi/client.go +++ b/fastcgi/client.go @@ -4,6 +4,7 @@ import ( "bufio" "bytes" "encoding/binary" + "fmt" "io" "net/http" "net/http/httputil" @@ -53,41 +54,83 @@ func (client *FCGIClient) writeEndRequest(appStatus int, protocolStatus uint8) e // Value size // Name // Value -// The high bit of name size and value size is used for signaling -// how many bytes are used to store the length/size. -// If the size is > 127, we can just use one byte, -// and the high bit will be 0, otherwise, we use -// four bytes and the high bit will be 1 +type NameValuePair struct { + // Making the length values 32 bit for ease. + // However, when encoding, the rules for + // how many bytes are used will apply. + NameLength uint32 + ValueLength uint32 + // Data + NameData string + ValueData string +} + func (client *FCGIClient) writePairs(recType FCGIRequestType, pairs map[string]string) error { - w := newWriter(client, recType) - b := make([]byte, 8) - nn := 0 + // Get ourselves a nice slice to work with + nvpairs := []NameValuePair{} for k, v := range pairs { - m := 8 + len(k) + len(v) - if m > maxWrite { - // param data size exceed 65535 bytes" - vl := maxWrite - 8 - len(k) - v = v[:vl] - } - n := encodeSize(b, uint32(len(k))) - n += encodeSize(b[n:], uint32(len(v))) - m = n + len(k) + len(v) - if (nn + m) > maxWrite { - w.Flush() - nn = 0 - } - nn += m - if _, err := w.Write(b[:n]); err != nil { - return err - } - if _, err := w.WriteString(k); err != nil { - return err - } - if _, err := w.WriteString(v); err != nil { - return err - } + nvpairs = append(nvpairs, NameValuePair{ + NameLength: uint32(len(k)), + ValueLength: uint32(len(v)), + NameData: k, + ValueData: v, + }) } - w.Close() + + // We'll use this to put together + // the packet + var buf bytes.Buffer + + for _, p := range nvpairs { + // Let's see how many bytes we have in total. + // Since we have to leave 8 bytes for encoding + // the sizes, we'll add it to the calculation. + // If the value is larger than what we can + // handle, we'll truncate it + if (8 + p.NameLength + p.ValueLength) > maxWrite { + fmt.Println("We should not have hit this") + p.ValueLength = maxWrite - 8 - p.NameLength + p.ValueData = p.ValueData[:p.ValueLength] + } + + // The high bit of name size and value size is used for signaling + // how many bytes are used to store the length/size. + // If the size is > 127, we can just use one byte, + // and the high bit will be 0, otherwise, we use + // four bytes and the high bit will be 1 + // So if length is encoded in 4 bytes it would look + // something like: + // 10000000000000000000010000100000 + if p.NameLength > 127 { + p.NameLength |= 1 << 31 + b := make([]byte, 4) + binary.BigEndian.PutUint32(b, p.NameLength) + buf.Write(b) + } else { + buf.Write([]byte{byte(p.NameLength)}) + } + + if p.ValueLength > 127 { + p.ValueLength |= 1 << 31 + b := make([]byte, 4) + binary.BigEndian.PutUint32(b, p.ValueLength) + buf.Write(b) + } else { + buf.Write([]byte{byte(p.ValueLength)}) + } + + // Now we just write our values to the buffer + buf.WriteString(p.NameData) + buf.WriteString(p.ValueData) + } + + w := newWriter(client, recType) + defer w.Close() + + // Send the data + w.Write(buf.Bytes()) + w.Flush() + return nil } diff --git a/fastcgi/fastcgi.go b/fastcgi/fastcgi.go index a0371c0..841029b 100644 --- a/fastcgi/fastcgi.go +++ b/fastcgi/fastcgi.go @@ -98,16 +98,6 @@ func readString(s []byte, size uint32) string { 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 {