Add trailer support (#1165)

* Add trailer support

* fix issue and add documentation

* remove redundant code

* add error return for add/set trailer method

* fix lint error

* fix bad trailer error return issue and update bad content-length error

* update errNonNumericChars

* update errNonNumericChars

* fix issue about error and fix typo
This commit is contained in:
ichx
2021-12-05 21:11:51 +08:00
committed by GitHub
parent 017f0aa09d
commit da7ff7a208
10 changed files with 1418 additions and 255 deletions
+7
View File
@@ -361,6 +361,13 @@ func visitArgs(args []argsKV, f func(k, v []byte)) {
}
}
func visitArgsKey(args []argsKV, f func(k []byte)) {
for i, n := 0, len(args); i < n; i++ {
kv := &args[i]
f(kv.key)
}
}
func copyArgs(dst, src []argsKV) []argsKV {
if cap(dst) < len(src) {
tmp := make([]argsKV, len(src))
+633 -9
View File
@@ -42,8 +42,9 @@ type ResponseHeader struct {
contentType []byte
server []byte
h []argsKV
bufKV argsKV
h []argsKV
trailer []argsKV
bufKV argsKV
cookies []argsKV
}
@@ -77,8 +78,9 @@ type RequestHeader struct {
contentType []byte
userAgent []byte
h []argsKV
bufKV argsKV
h []argsKV
trailer []argsKV
bufKV argsKV
cookies []argsKV
@@ -382,6 +384,117 @@ func (h *RequestHeader) SetMultipartFormBoundaryBytes(boundary []byte) {
h.SetContentTypeBytes(h.bufKV.value)
}
// SetTrailer sets header Trailer value for chunked response
// to indicate which headers will be sent after the body.
//
// Use Set to set the trailer header later.
//
// Trailers are only supported with chunked transfer.
// Trailers allow the sender to include additional headers at the end of chunked messages.
//
// The following trailers are forbidden:
// 1. necessary for message framing (e.g., Transfer-Encoding and Content-Length),
// 2. routing (e.g., Host),
// 3. request modifiers (e.g., controls and conditionals in Section 5 of [RFC7231]),
// 4. authentication (e.g., see [RFC7235] and [RFC6265]),
// 5. response control data (e.g., see Section 7.1 of [RFC7231]),
// 6. determining how to process the payload (e.g., Content-Encoding, Content-Type, Content-Range, and Trailer)
//
// Return ErrBadTrailer if contain any forbidden trailers.
func (h *ResponseHeader) SetTrailer(trailer string) error {
return h.SetTrailerBytes(s2b(trailer))
}
// SetTrailerBytes sets Trailer header value for chunked response
// to indicate which headers will be sent after the body.
//
// Use Set to set the trailer header later.
//
// Trailers are only supported with chunked transfer.
// Trailers allow the sender to include additional headers at the end of chunked messages.
//
// The following trailers are forbidden:
// 1. necessary for message framing (e.g., Transfer-Encoding and Content-Length),
// 2. routing (e.g., Host),
// 3. request modifiers (e.g., controls and conditionals in Section 5 of [RFC7231]),
// 4. authentication (e.g., see [RFC7235] and [RFC6265]),
// 5. response control data (e.g., see Section 7.1 of [RFC7231]),
// 6. determining how to process the payload (e.g., Content-Encoding, Content-Type, Content-Range, and Trailer)
//
// Return ErrBadTrailer if contain any forbidden trailers.
func (h *ResponseHeader) SetTrailerBytes(trailer []byte) error {
h.trailer = h.trailer[:0]
return h.AddTrailerBytes(trailer)
}
// AddTrailer add Trailer header value for chunked response
// to indicate which headers will be sent after the body.
//
// Use Set to set the trailer header later.
//
// Trailers are only supported with chunked transfer.
// Trailers allow the sender to include additional headers at the end of chunked messages.
//
// The following trailers are forbidden:
// 1. necessary for message framing (e.g., Transfer-Encoding and Content-Length),
// 2. routing (e.g., Host),
// 3. request modifiers (e.g., controls and conditionals in Section 5 of [RFC7231]),
// 4. authentication (e.g., see [RFC7235] and [RFC6265]),
// 5. response control data (e.g., see Section 7.1 of [RFC7231]),
// 6. determining how to process the payload (e.g., Content-Encoding, Content-Type, Content-Range, and Trailer)
//
// Return ErrBadTrailer if contain any forbidden trailers.
func (h *ResponseHeader) AddTrailer(trailer string) error {
return h.AddTrailerBytes(s2b(trailer))
}
var ErrBadTrailer = errors.New("contain forbidden trailer")
// AddTrailerBytes add Trailer header value for chunked response
// to indicate which headers will be sent after the body.
//
// Use Set to set the trailer header later.
//
// Trailers are only supported with chunked transfer.
// Trailers allow the sender to include additional headers at the end of chunked messages.
//
// The following trailers are forbidden:
// 1. necessary for message framing (e.g., Transfer-Encoding and Content-Length),
// 2. routing (e.g., Host),
// 3. request modifiers (e.g., controls and conditionals in Section 5 of [RFC7231]),
// 4. authentication (e.g., see [RFC7235] and [RFC6265]),
// 5. response control data (e.g., see Section 7.1 of [RFC7231]),
// 6. determining how to process the payload (e.g., Content-Encoding, Content-Type, Content-Range, and Trailer)
//
// Return ErrBadTrailer if contain any forbidden trailers.
func (h *ResponseHeader) AddTrailerBytes(trailer []byte) error {
var err error
for i := -1; i+1 < len(trailer); {
trailer = trailer[i+1:]
i = bytes.IndexByte(trailer, ',')
if i < 0 {
i = len(trailer)
}
key := trailer[:i]
for len(key) > 0 && key[0] == ' ' {
key = key[1:]
}
for len(key) > 0 && key[len(key)-1] == ' ' {
key = key[:len(key)-1]
}
// Forbidden by RFC 7230, section 4.1.2
if isBadTrailer(key) {
err = ErrBadTrailer
continue
}
h.bufKV.key = append(h.bufKV.key[:0], key...)
normalizeHeaderKey(h.bufKV.key, h.disableNormalizing)
h.trailer = appendArgBytes(h.trailer, h.bufKV.key, nil, argsNoValue)
}
return err
}
// MultipartFormBoundary returns boundary part
// from 'multipart/form-data; boundary=...' Content-Type.
func (h *RequestHeader) MultipartFormBoundary() []byte {
@@ -530,6 +643,115 @@ func (h *RequestHeader) SetRequestURIBytes(requestURI []byte) {
h.requestURI = append(h.requestURI[:0], requestURI...)
}
// SetTrailer sets Trailer header value for chunked request
// to indicate which headers will be sent after the body.
//
// Use Set to set the trailer header later.
//
// Trailers are only supported with chunked transfer.
// Trailers allow the sender to include additional headers at the end of chunked messages.
//
// The following trailers are forbidden:
// 1. necessary for message framing (e.g., Transfer-Encoding and Content-Length),
// 2. routing (e.g., Host),
// 3. request modifiers (e.g., controls and conditionals in Section 5 of [RFC7231]),
// 4. authentication (e.g., see [RFC7235] and [RFC6265]),
// 5. response control data (e.g., see Section 7.1 of [RFC7231]),
// 6. determining how to process the payload (e.g., Content-Encoding, Content-Type, Content-Range, and Trailer)
//
// Return ErrBadTrailer if contain any forbidden trailers.
func (h *RequestHeader) SetTrailer(trailer string) error {
return h.SetTrailerBytes(s2b(trailer))
}
// SetTrailerBytes sets Trailer header value for chunked request
// to indicate which headers will be sent after the body.
//
// Use Set to set the trailer header later.
//
// Trailers are only supported with chunked transfer.
// Trailers allow the sender to include additional headers at the end of chunked messages.
//
// The following trailers are forbidden:
// 1. necessary for message framing (e.g., Transfer-Encoding and Content-Length),
// 2. routing (e.g., Host),
// 3. request modifiers (e.g., controls and conditionals in Section 5 of [RFC7231]),
// 4. authentication (e.g., see [RFC7235] and [RFC6265]),
// 5. response control data (e.g., see Section 7.1 of [RFC7231]),
// 6. determining how to process the payload (e.g., Content-Encoding, Content-Type, Content-Range, and Trailer)
//
// Return ErrBadTrailer if contain any forbidden trailers.
func (h *RequestHeader) SetTrailerBytes(trailer []byte) error {
h.trailer = h.trailer[:0]
return h.AddTrailerBytes(trailer)
}
// AddTrailer add Trailer header value for chunked request
// to indicate which headers will be sent after the body.
//
// Use Set to set the trailer header later.
//
// Trailers are only supported with chunked transfer.
// Trailers allow the sender to include additional headers at the end of chunked messages.
//
// The following trailers are forbidden:
// 1. necessary for message framing (e.g., Transfer-Encoding and Content-Length),
// 2. routing (e.g., Host),
// 3. request modifiers (e.g., controls and conditionals in Section 5 of [RFC7231]),
// 4. authentication (e.g., see [RFC7235] and [RFC6265]),
// 5. response control data (e.g., see Section 7.1 of [RFC7231]),
// 6. determining how to process the payload (e.g., Content-Encoding, Content-Type, Content-Range, and Trailer)
//
// Return ErrBadTrailer if contain any forbidden trailers.
func (h *RequestHeader) AddTrailer(trailer string) error {
return h.AddTrailerBytes(s2b(trailer))
}
// AddTrailerBytes add Trailer header value for chunked request
// to indicate which headers will be sent after the body.
//
// Use Set to set the trailer header later.
//
// Trailers are only supported with chunked transfer.
// Trailers allow the sender to include additional headers at the end of chunked messages.
//
// The following trailers are forbidden:
// 1. necessary for message framing (e.g., Transfer-Encoding and Content-Length),
// 2. routing (e.g., Host),
// 3. request modifiers (e.g., controls and conditionals in Section 5 of [RFC7231]),
// 4. authentication (e.g., see [RFC7235] and [RFC6265]),
// 5. response control data (e.g., see Section 7.1 of [RFC7231]),
// 6. determining how to process the payload (e.g., Content-Encoding, Content-Type, Content-Range, and Trailer)
//
// Return ErrBadTrailer if contain any forbidden trailers.
func (h *RequestHeader) AddTrailerBytes(trailer []byte) error {
var err error
for i := -1; i+1 < len(trailer); {
trailer = trailer[i+1:]
i = bytes.IndexByte(trailer, ',')
if i < 0 {
i = len(trailer)
}
key := trailer[:i]
for len(key) > 0 && key[0] == ' ' {
key = key[1:]
}
for len(key) > 0 && key[len(key)-1] == ' ' {
key = key[:len(key)-1]
}
// Forbidden by RFC 7230, section 4.1.2
if isBadTrailer(key) {
err = ErrBadTrailer
continue
}
h.bufKV.key = append(h.bufKV.key[:0], key...)
normalizeHeaderKey(h.bufKV.key, h.disableNormalizing)
h.trailer = appendArgBytes(h.trailer, h.bufKV.key, nil, argsNoValue)
}
return err
}
// IsGet returns true if request method is GET.
func (h *RequestHeader) IsGet() bool {
return string(h.Method()) == MethodGet
@@ -718,6 +940,7 @@ func (h *ResponseHeader) resetSkipNormalize() {
h.h = h.h[:0]
h.cookies = h.cookies[:0]
h.trailer = h.trailer[:0]
}
// Reset clears request header.
@@ -739,6 +962,7 @@ func (h *RequestHeader) resetSkipNormalize() {
h.host = h.host[:0]
h.contentType = h.contentType[:0]
h.userAgent = h.userAgent[:0]
h.trailer = h.trailer[:0]
h.h = h.h[:0]
h.cookies = h.cookies[:0]
@@ -766,6 +990,7 @@ func (h *ResponseHeader) CopyTo(dst *ResponseHeader) {
dst.server = append(dst.server, h.server...)
dst.h = copyArgs(dst.h, h.h)
dst.cookies = copyArgs(dst.cookies, h.cookies)
dst.trailer = copyArgs(dst.trailer, h.trailer)
}
// CopyTo copies all the headers to dst.
@@ -784,6 +1009,7 @@ func (h *RequestHeader) CopyTo(dst *RequestHeader) {
dst.host = append(dst.host, h.host...)
dst.contentType = append(dst.contentType, h.contentType...)
dst.userAgent = append(dst.userAgent, h.userAgent...)
dst.trailer = append(dst.trailer, h.trailer...)
dst.h = copyArgs(dst.h, h.h)
dst.cookies = copyArgs(dst.cookies, h.cookies)
dst.cookiesCollected = h.cookiesCollected
@@ -811,12 +1037,29 @@ func (h *ResponseHeader) VisitAll(f func(key, value []byte)) {
f(strSetCookie, v)
})
}
if len(h.trailer) > 0 {
f(strTrailer, appendArgsKeyBytes(nil, h.trailer, strCommaSpace))
}
visitArgs(h.h, f)
if h.ConnectionClose() {
f(strConnection, strClose)
}
}
// VisitAllTrailer calls f for each response Trailer.
//
// f must not retain references to value after returning.
func (h *ResponseHeader) VisitAllTrailer(f func(value []byte)) {
visitArgsKey(h.trailer, f)
}
// VisitAllTrailer calls f for each request Trailer.
//
// f must not retain references to value after returning.
func (h *RequestHeader) VisitAllTrailer(f func(value []byte)) {
visitArgsKey(h.trailer, f)
}
// VisitAllCookie calls f for each response cookie.
//
// Cookie name is passed in key and the whole Set-Cookie header value
@@ -858,6 +1101,9 @@ func (h *RequestHeader) VisitAll(f func(key, value []byte)) {
if len(userAgent) > 0 {
f(strUserAgent, userAgent)
}
if len(h.trailer) > 0 {
f(strTrailer, appendArgsKeyBytes(nil, h.trailer, strCommaSpace))
}
h.collectCookies()
if len(h.cookies) > 0 {
@@ -914,6 +1160,8 @@ func (h *ResponseHeader) del(key []byte) {
h.contentLengthBytes = h.contentLengthBytes[:0]
case HeaderConnection:
h.connectionClose = false
case HeaderTrailer:
h.trailer = h.trailer[:0]
}
h.h = delAllArgsBytes(h.h, key)
}
@@ -946,6 +1194,8 @@ func (h *RequestHeader) del(key []byte) {
h.contentLengthBytes = h.contentLengthBytes[:0]
case HeaderConnection:
h.connectionClose = false
case HeaderTrailer:
h.trailer = h.trailer[:0]
}
h.h = delAllArgsBytes(h.h, key)
}
@@ -991,6 +1241,9 @@ func (h *ResponseHeader) setSpecialHeader(key, value []byte) bool {
if caseInsensitiveCompare(strTransferEncoding, key) {
// Transfer-Encoding is managed automatically.
return true
} else if caseInsensitiveCompare(strTrailer, key) {
_ = h.SetTrailerBytes(value)
return true
}
case 'd':
if caseInsensitiveCompare(strDate, key) {
@@ -1036,6 +1289,9 @@ func (h *RequestHeader) setSpecialHeader(key, value []byte) bool {
if caseInsensitiveCompare(strTransferEncoding, key) {
// Transfer-Encoding is managed automatically.
return true
} else if caseInsensitiveCompare(strTrailer, key) {
_ = h.SetTrailerBytes(value)
return true
}
case 'h':
if caseInsensitiveCompare(strHost, key) {
@@ -1060,6 +1316,9 @@ func (h *RequestHeader) setSpecialHeader(key, value []byte) bool {
// the Content-Type, Content-Length, Connection, Server, Set-Cookie,
// Transfer-Encoding and Date headers can only be set once and will
// overwrite the previous value.
//
// If the header is set as a Trailer (forbidden trailers will not be set, see AddTrailer for more details),
// it will be sent after the chunked response body.
func (h *ResponseHeader) Add(key, value string) {
h.AddBytesKV(s2b(key), s2b(value))
}
@@ -1072,6 +1331,9 @@ func (h *ResponseHeader) Add(key, value string) {
// the Content-Type, Content-Length, Connection, Server, Set-Cookie,
// Transfer-Encoding and Date headers can only be set once and will
// overwrite the previous value.
//
// If the header is set as a Trailer (forbidden trailers will not be set, see AddTrailer for more details),
// it will be sent after the chunked response body.
func (h *ResponseHeader) AddBytesK(key []byte, value string) {
h.AddBytesKV(key, s2b(value))
}
@@ -1084,6 +1346,9 @@ func (h *ResponseHeader) AddBytesK(key []byte, value string) {
// the Content-Type, Content-Length, Connection, Server, Set-Cookie,
// Transfer-Encoding and Date headers can only be set once and will
// overwrite the previous value.
//
// If the header is set as a Trailer (forbidden trailers will not be set, see AddTrailer for more details),
// it will be sent after the chunked response body.
func (h *ResponseHeader) AddBytesV(key string, value []byte) {
h.AddBytesKV(s2b(key), value)
}
@@ -1096,6 +1361,9 @@ func (h *ResponseHeader) AddBytesV(key string, value []byte) {
// the Content-Type, Content-Length, Connection, Server, Set-Cookie,
// Transfer-Encoding and Date headers can only be set once and will
// overwrite the previous value.
//
// If the header is set as a Trailer (forbidden trailers will not be set, see AddTrailer for more details),
// it will be sent after the chunked response body.
func (h *ResponseHeader) AddBytesKV(key, value []byte) {
if h.setSpecialHeader(key, value) {
return
@@ -1107,6 +1375,9 @@ func (h *ResponseHeader) AddBytesKV(key, value []byte) {
// Set sets the given 'key: value' header.
//
// If the header is set as a Trailer (forbidden trailers will not be set, see SetTrailer for more details),
// it will be sent after the chunked response body.
//
// Use Add for setting multiple header values under the same key.
func (h *ResponseHeader) Set(key, value string) {
initHeaderKV(&h.bufKV, key, value, h.disableNormalizing)
@@ -1115,6 +1386,9 @@ func (h *ResponseHeader) Set(key, value string) {
// SetBytesK sets the given 'key: value' header.
//
// If the header is set as a Trailer (forbidden trailers will not be set, see SetTrailer for more details),
// it will be sent after the chunked response body.
//
// Use AddBytesK for setting multiple header values under the same key.
func (h *ResponseHeader) SetBytesK(key []byte, value string) {
h.bufKV.value = append(h.bufKV.value[:0], value...)
@@ -1123,6 +1397,9 @@ func (h *ResponseHeader) SetBytesK(key []byte, value string) {
// SetBytesV sets the given 'key: value' header.
//
// If the header is set as a Trailer (forbidden trailers will not be set, see SetTrailer for more details),
// it will be sent after the chunked response body.
//
// Use AddBytesV for setting multiple header values under the same key.
func (h *ResponseHeader) SetBytesV(key string, value []byte) {
k := getHeaderKeyBytes(&h.bufKV, key, h.disableNormalizing)
@@ -1131,6 +1408,9 @@ func (h *ResponseHeader) SetBytesV(key string, value []byte) {
// SetBytesKV sets the given 'key: value' header.
//
// If the header is set as a Trailer (forbidden trailers will not be set, see SetTrailer for more details),
// it will be sent after the chunked response body.
//
// Use AddBytesKV for setting multiple header values under the same key.
func (h *ResponseHeader) SetBytesKV(key, value []byte) {
h.bufKV.key = append(h.bufKV.key[:0], key...)
@@ -1140,6 +1420,9 @@ func (h *ResponseHeader) SetBytesKV(key, value []byte) {
// SetCanonical sets the given 'key: value' header assuming that
// key is in canonical form.
//
// If the header is set as a Trailer (forbidden trailers will not be set, see SetTrailer for more details),
// it will be sent after the chunked response body.
func (h *ResponseHeader) SetCanonical(key, value []byte) {
if h.setSpecialHeader(key, value) {
return
@@ -1253,6 +1536,9 @@ func (h *RequestHeader) DelAllCookies() {
//
// Multiple headers with the same key may be added with this function.
// Use Set for setting a single header for the given key.
//
// If the header is set as a Trailer (forbidden trailers will not be set, see AddTrailer for more details),
// it will be sent after the chunked request body.
func (h *RequestHeader) Add(key, value string) {
h.AddBytesKV(s2b(key), s2b(value))
}
@@ -1261,6 +1547,9 @@ func (h *RequestHeader) Add(key, value string) {
//
// Multiple headers with the same key may be added with this function.
// Use SetBytesK for setting a single header for the given key.
//
// If the header is set as a Trailer (forbidden trailers will not be set, see AddTrailer for more details),
// it will be sent after the chunked request body.
func (h *RequestHeader) AddBytesK(key []byte, value string) {
h.AddBytesKV(key, s2b(value))
}
@@ -1269,6 +1558,9 @@ func (h *RequestHeader) AddBytesK(key []byte, value string) {
//
// Multiple headers with the same key may be added with this function.
// Use SetBytesV for setting a single header for the given key.
//
// If the header is set as a Trailer (forbidden trailers will not be set, see AddTrailer for more details),
// it will be sent after the chunked request body.
func (h *RequestHeader) AddBytesV(key string, value []byte) {
h.AddBytesKV(s2b(key), value)
}
@@ -1281,6 +1573,9 @@ func (h *RequestHeader) AddBytesV(key string, value []byte) {
// the Content-Type, Content-Length, Connection, Cookie,
// Transfer-Encoding, Host and User-Agent headers can only be set once
// and will overwrite the previous value.
//
// If the header is set as a Trailer (forbidden trailers will not be set, see AddTrailer for more details),
// it will be sent after the chunked request body.
func (h *RequestHeader) AddBytesKV(key, value []byte) {
if h.setSpecialHeader(key, value) {
return
@@ -1292,6 +1587,9 @@ func (h *RequestHeader) AddBytesKV(key, value []byte) {
// Set sets the given 'key: value' header.
//
// If the header is set as a Trailer (forbidden trailers will not be set, see SetTrailer for more details),
// it will be sent after the chunked request body.
//
// Use Add for setting multiple header values under the same key.
func (h *RequestHeader) Set(key, value string) {
initHeaderKV(&h.bufKV, key, value, h.disableNormalizing)
@@ -1300,6 +1598,9 @@ func (h *RequestHeader) Set(key, value string) {
// SetBytesK sets the given 'key: value' header.
//
// If the header is set as a Trailer (forbidden trailers will not be set, see SetTrailer for more details),
// it will be sent after the chunked request body.
//
// Use AddBytesK for setting multiple header values under the same key.
func (h *RequestHeader) SetBytesK(key []byte, value string) {
h.bufKV.value = append(h.bufKV.value[:0], value...)
@@ -1308,6 +1609,9 @@ func (h *RequestHeader) SetBytesK(key []byte, value string) {
// SetBytesV sets the given 'key: value' header.
//
// If the header is set as a Trailer (forbidden trailers will not be set, see SetTrailer for more details),
// it will be sent after the chunked request body.
//
// Use AddBytesV for setting multiple header values under the same key.
func (h *RequestHeader) SetBytesV(key string, value []byte) {
k := getHeaderKeyBytes(&h.bufKV, key, h.disableNormalizing)
@@ -1316,6 +1620,9 @@ func (h *RequestHeader) SetBytesV(key string, value []byte) {
// SetBytesKV sets the given 'key: value' header.
//
// If the header is set as a Trailer (forbidden trailers will not be set, see SetTrailer for more details),
// it will be sent after the chunked request body.
//
// Use AddBytesKV for setting multiple header values under the same key.
func (h *RequestHeader) SetBytesKV(key, value []byte) {
h.bufKV.key = append(h.bufKV.key[:0], key...)
@@ -1325,6 +1632,9 @@ func (h *RequestHeader) SetBytesKV(key, value []byte) {
// SetCanonical sets the given 'key: value' header assuming that
// key is in canonical form.
//
// If the header is set as a Trailer (forbidden trailers will not be set, see SetTrailer for more details),
// it will be sent after the chunked request body.
func (h *RequestHeader) SetCanonical(key, value []byte) {
if h.setSpecialHeader(key, value) {
return
@@ -1390,6 +1700,8 @@ func (h *ResponseHeader) peek(key []byte) []byte {
return h.contentLengthBytes
case HeaderSetCookie:
return appendResponseCookieBytes(nil, h.cookies)
case HeaderTrailer:
return appendArgsKeyBytes(nil, h.trailer, strCommaSpace)
default:
return peekArgBytes(h.h, key)
}
@@ -1415,6 +1727,8 @@ func (h *RequestHeader) peek(key []byte) []byte {
return appendRequestCookieBytes(nil, h.cookies)
}
return peekArgBytes(h.h, key)
case HeaderTrailer:
return appendArgsKeyBytes(nil, h.trailer, strCommaSpace)
default:
return peekArgBytes(h.h, key)
}
@@ -1498,6 +1812,61 @@ func (h *ResponseHeader) tryRead(r *bufio.Reader, n int) error {
return nil
}
// ReadTrailer reads response trailer header from r.
//
// io.EOF is returned if r is closed before reading the first byte.
func (h *ResponseHeader) ReadTrailer(r *bufio.Reader) error {
n := 1
for {
err := h.tryReadTrailer(r, n)
if err == nil {
return nil
}
if err != errNeedMore {
return err
}
n = r.Buffered() + 1
}
}
func (h *ResponseHeader) tryReadTrailer(r *bufio.Reader, n int) error {
b, err := r.Peek(n)
if len(b) == 0 {
// Return ErrTimeout on any timeout.
if x, ok := err.(interface{ Timeout() bool }); ok && x.Timeout() {
return ErrTimeout
}
if n == 1 || err == io.EOF {
return io.EOF
}
// This is for go 1.6 bug. See https://github.com/golang/go/issues/14121 .
if err == bufio.ErrBufferFull {
if h.secureErrorLogMessage {
return &ErrSmallBuffer{
error: fmt.Errorf("error when reading response trailer"),
}
}
return &ErrSmallBuffer{
error: fmt.Errorf("error when reading response trailer: %s", errSmallBuffer),
}
}
return fmt.Errorf("error when reading response trailer: %s", err)
}
b = mustPeekBuffered(r)
headersLen, errParse := h.parseTrailer(b)
if errParse != nil {
if err == io.EOF {
return err
}
return headerError("response", err, errParse, b, h.secureErrorLogMessage)
}
mustDiscard(r, headersLen)
return nil
}
func headerError(typ string, err, errParse error, b []byte, secureErrorLogMessage bool) error {
if errParse != errNeedMore {
return headerErrorMsg(typ, errParse, b, secureErrorLogMessage)
@@ -1552,6 +1921,61 @@ func (h *RequestHeader) readLoop(r *bufio.Reader, waitForMore bool) error {
}
}
// ReadTrailer reads request trailer header from r.
//
// io.EOF is returned if r is closed before reading the first byte.
func (h *RequestHeader) ReadTrailer(r *bufio.Reader) error {
n := 1
for {
err := h.tryReadTrailer(r, n)
if err == nil {
return nil
}
if err != errNeedMore {
return err
}
n = r.Buffered() + 1
}
}
func (h *RequestHeader) tryReadTrailer(r *bufio.Reader, n int) error {
b, err := r.Peek(n)
if len(b) == 0 {
// Return ErrTimeout on any timeout.
if x, ok := err.(interface{ Timeout() bool }); ok && x.Timeout() {
return ErrTimeout
}
if n == 1 || err == io.EOF {
return io.EOF
}
// This is for go 1.6 bug. See https://github.com/golang/go/issues/14121 .
if err == bufio.ErrBufferFull {
if h.secureErrorLogMessage {
return &ErrSmallBuffer{
error: fmt.Errorf("error when reading request trailer"),
}
}
return &ErrSmallBuffer{
error: fmt.Errorf("error when reading request trailer: %s", errSmallBuffer),
}
}
return fmt.Errorf("error when reading request trailer: %s", err)
}
b = mustPeekBuffered(r)
headersLen, errParse := h.parseTrailer(b)
if errParse != nil {
if err == io.EOF {
return err
}
return headerError("request", err, errParse, b, h.secureErrorLogMessage)
}
mustDiscard(r, headersLen)
return nil
}
func (h *RequestHeader) tryRead(r *bufio.Reader, n int) error {
h.resetSkipNormalize()
b, err := r.Peek(n)
@@ -1648,6 +2072,8 @@ func (h *ResponseHeader) WriteTo(w io.Writer) (int64, error) {
// Header returns response header representation.
//
// Headers that set as Trailer will not represent. Use TrailerHeader for trailers.
//
// The returned value is valid until the request is released,
// either though ReleaseRequest or your request handler returning.
// Do not store references to returned value. Make copies instead.
@@ -1656,6 +2082,29 @@ func (h *ResponseHeader) Header() []byte {
return h.bufKV.value
}
// writeTrailer writes response trailer to w.
func (h *ResponseHeader) writeTrailer(w *bufio.Writer) error {
_, err := w.Write(h.TrailerHeader())
return err
}
// TrailerHeader returns response trailer header representation.
//
// Trailers will only be received with chunked transfer.
//
// The returned value is valid until the request is released,
// either though ReleaseRequest or your request handler returning.
// Do not store references to returned value. Make copies instead.
func (h *ResponseHeader) TrailerHeader() []byte {
h.bufKV.value = h.bufKV.value[:0]
for _, t := range h.trailer {
value := h.peek(t.key)
h.bufKV.value = appendHeaderLine(h.bufKV.value, t.key, value)
}
h.bufKV.value = append(h.bufKV.value, strCRLF...)
return h.bufKV.value
}
// String returns response header representation.
func (h *ResponseHeader) String() string {
return string(h.Header())
@@ -1702,11 +2151,24 @@ func (h *ResponseHeader) AppendBytes(dst []byte) []byte {
for i, n := 0, len(h.h); i < n; i++ {
kv := &h.h[i]
if h.noDefaultDate || !bytes.Equal(kv.key, strDate) {
// Exclude trailer from header
exclude := false
for _, t := range h.trailer {
if bytes.Equal(kv.key, t.key) {
exclude = true
break
}
}
if !exclude && (h.noDefaultDate || !bytes.Equal(kv.key, strDate)) {
dst = appendHeaderLine(dst, kv.key, kv.value)
}
}
if len(h.trailer) > 0 {
dst = appendHeaderLine(dst, strTrailer, appendArgsKeyBytes(nil, h.trailer, strCommaSpace))
}
n := len(h.cookies)
if n > 0 {
for i := 0; i < n; i++ {
@@ -1738,6 +2200,8 @@ func (h *RequestHeader) WriteTo(w io.Writer) (int64, error) {
// Header returns request header representation.
//
// Headers that set as Trailer will not represent. Use TrailerHeader for trailers.
//
// The returned value is valid until the request is released,
// either though ReleaseRequest or your request handler returning.
// Do not store references to returned value. Make copies instead.
@@ -1746,6 +2210,29 @@ func (h *RequestHeader) Header() []byte {
return h.bufKV.value
}
// writeTrailer writes request trailer to w.
func (h *RequestHeader) writeTrailer(w *bufio.Writer) error {
_, err := w.Write(h.TrailerHeader())
return err
}
// TrailerHeader returns request trailer header representation.
//
// Trailers will only be received with chunked transfer.
//
// The returned value is valid until the request is released,
// either though ReleaseRequest or your request handler returning.
// Do not store references to returned value. Make copies instead.
func (h *RequestHeader) TrailerHeader() []byte {
h.bufKV.value = h.bufKV.value[:0]
for _, t := range h.trailer {
value := h.peek(t.key)
h.bufKV.value = appendHeaderLine(h.bufKV.value, t.key, value)
}
h.bufKV.value = append(h.bufKV.value, strCRLF...)
return h.bufKV.value
}
// RawHeaders returns raw header key/value bytes.
//
// Depending on server configuration, header keys may be normalized to
@@ -1798,7 +2285,21 @@ func (h *RequestHeader) AppendBytes(dst []byte) []byte {
for i, n := 0, len(h.h); i < n; i++ {
kv := &h.h[i]
dst = appendHeaderLine(dst, kv.key, kv.value)
// Exclude trailer from header
exclude := false
for _, t := range h.trailer {
if bytes.Equal(kv.key, t.key) {
exclude = true
break
}
}
if !exclude {
dst = appendHeaderLine(dst, kv.key, kv.value)
}
}
if len(h.trailer) > 0 {
dst = appendHeaderLine(dst, strTrailer, appendArgsKeyBytes(nil, h.trailer, strCommaSpace))
}
// there is no need in h.collectCookies() here, since if cookies aren't collected yet,
@@ -1837,6 +2338,37 @@ func (h *ResponseHeader) parse(buf []byte) (int, error) {
return m + n, nil
}
func (h *ResponseHeader) parseTrailer(buf []byte) (int, error) {
if buf[0] == '0' {
buf = buf[len(strCRLF)+1:]
}
var s headerScanner
s.b = buf
s.disableNormalizing = h.disableNormalizing
var err error
for s.next() {
if len(s.key) > 0 {
if bytes.IndexByte(s.key, ' ') != -1 || bytes.IndexByte(s.key, '\t') != -1 {
err = fmt.Errorf("invalid trailer key %q", s.key)
continue
}
// Forbidden by RFC 7230, section 4.1.2
if isBadTrailer(s.key) {
err = fmt.Errorf("forbidden trailer key %q", s.key)
continue
}
}
h.h = appendArgBytes(h.h, s.key, s.value, argsHasValue)
}
if s.err != nil {
return 0, s.err
}
if err != nil {
return 0, err
}
return s.hLen, nil
}
func (h *RequestHeader) ignoreBody() bool {
return h.IsGet() || h.IsHead()
}
@@ -1859,6 +2391,77 @@ func (h *RequestHeader) parse(buf []byte) (int, error) {
return m + n, nil
}
func (h *RequestHeader) parseTrailer(buf []byte) (int, error) {
if buf[0] == '0' {
buf = buf[len(strCRLF)+1:]
}
var s headerScanner
s.b = buf
s.disableNormalizing = h.disableNormalizing
var err error
for s.next() {
if len(s.key) > 0 {
if bytes.IndexByte(s.key, ' ') != -1 || bytes.IndexByte(s.key, '\t') != -1 {
err = fmt.Errorf("invalid trailer key %q", s.key)
continue
}
// Forbidden by RFC 7230, section 4.1.2
if isBadTrailer(s.key) {
err = fmt.Errorf("forbidden trailer key %q", s.key)
continue
}
h.h = appendArgBytes(h.h, s.key, s.value, argsHasValue)
}
}
if s.err != nil {
return 0, s.err
}
if err != nil {
return 0, err
}
return s.hLen, nil
}
func isBadTrailer(key []byte) bool {
switch key[0] | 0x20 {
case 'a':
return caseInsensitiveCompare(key, strAuthorization)
case 'c':
if len(key) > len(HeaderContentType) && caseInsensitiveCompare(key[:8], strContentType[:8]) {
// skip compare prefix 'Content-'
return caseInsensitiveCompare(key[8:], strContentEncoding[8:]) ||
caseInsensitiveCompare(key[8:], strContentLength[8:]) ||
caseInsensitiveCompare(key[8:], strContentType[8:]) ||
caseInsensitiveCompare(key[8:], strContentRange[8:])
}
return caseInsensitiveCompare(key, strConnection)
case 'e':
return caseInsensitiveCompare(key, strExpect)
case 'h':
return caseInsensitiveCompare(key, strHost)
case 'k':
return caseInsensitiveCompare(key, strKeepAlive)
case 'm':
return caseInsensitiveCompare(key, strMaxForwards)
case 'p':
if len(key) > len(HeaderProxyConnection) && caseInsensitiveCompare(key[:6], strProxyConnection[:6]) {
// skip compare prefix 'Proxy-'
return caseInsensitiveCompare(key[6:], strProxyConnection[6:]) ||
caseInsensitiveCompare(key[6:], strProxyAuthenticate[6:]) ||
caseInsensitiveCompare(key[6:], strProxyAuthorization[6:])
}
case 'r':
return caseInsensitiveCompare(key, strRange)
case 't':
return caseInsensitiveCompare(key, strTE) ||
caseInsensitiveCompare(key, strTrailer) ||
caseInsensitiveCompare(key, strTransferEncoding)
case 'w':
return caseInsensitiveCompare(key, strWWWAuthenticate)
}
return false
}
func (h *ResponseHeader) parseFirstLine(buf []byte) (int, error) {
bNext := buf
var b []byte
@@ -2028,6 +2631,10 @@ func (h *ResponseHeader) parseHeaders(buf []byte) (int, error) {
}
continue
}
if caseInsensitiveCompare(s.key, strTrailer) {
err = h.SetTrailerBytes(s.value)
continue
}
}
h.h = appendArgBytes(h.h, s.key, s.value, argsHasValue)
}
@@ -2050,7 +2657,7 @@ func (h *ResponseHeader) parseHeaders(buf []byte) (int, error) {
h.connectionClose = !hasHeaderValue(v, strKeepAlive)
}
return len(buf) - len(s.b), nil
return len(buf) - len(s.b), err
}
func (h *RequestHeader) parseHeaders(buf []byte) (int, error) {
@@ -2116,6 +2723,10 @@ func (h *RequestHeader) parseHeaders(buf []byte) (int, error) {
}
continue
}
if caseInsensitiveCompare(s.key, strTrailer) {
err = h.SetTrailerBytes(s.value)
continue
}
}
}
h.h = appendArgBytes(h.h, s.key, s.value, argsHasValue)
@@ -2159,13 +2770,15 @@ func (h *RequestHeader) collectCookies() {
h.cookiesCollected = true
}
var errNonNumericChars = errors.New("non-numeric chars found")
func parseContentLength(b []byte) (int, error) {
v, n, err := parseUintBuf(b)
if err != nil {
return -1, err
return -1, fmt.Errorf("cannot parse Content-Length: %w", err)
}
if n != len(b) {
return -1, fmt.Errorf("non-numeric chars at the end of Content-Length")
return -1, fmt.Errorf("cannot parse Content-Length: %w", errNonNumericChars)
}
return v, nil
}
@@ -2499,6 +3112,17 @@ func AppendNormalizedHeaderKeyBytes(dst, key []byte) []byte {
return AppendNormalizedHeaderKey(dst, b2s(key))
}
func appendArgsKeyBytes(dst []byte, args []argsKV, sep []byte) []byte {
for i, n := 0, len(args); i < n; i++ {
kv := &args[i]
dst = append(dst, kv.key...)
if i+1 < n {
dst = append(dst, sep...)
}
}
return dst
}
var (
errNeedMore = errors.New("need more data: cannot find trailing lf")
errInvalidName = errors.New("invalid header name")
+223 -94
View File
@@ -4,9 +4,9 @@ import (
"bufio"
"bytes"
"encoding/base64"
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
"reflect"
"strings"
@@ -535,6 +535,7 @@ func TestRequestHeaderDel(t *testing.T) {
h.Set("User-Agent", "asdfas")
h.Set("Content-Length", "1123")
h.Set("Cookie", "foobar=baz")
h.Set(HeaderTrailer, "foo, bar")
h.Del("foo-bar")
h.Del("connection")
@@ -543,6 +544,7 @@ func TestRequestHeaderDel(t *testing.T) {
h.Del("user-agent")
h.Del("content-length")
h.Del("cookie")
h.Del("trailer")
hv := h.Peek("aaa")
if string(hv) != "bbb" {
@@ -576,6 +578,10 @@ func TestRequestHeaderDel(t *testing.T) {
if len(hv) > 0 {
t.Fatalf("non-zero value: %q", hv)
}
hv = h.Peek(HeaderTrailer)
if len(hv) > 0 {
t.Fatalf("non-zero value: %q", hv)
}
cv := h.Cookie("foobar")
if len(cv) > 0 {
@@ -596,6 +602,7 @@ func TestResponseHeaderDel(t *testing.T) {
h.Set(HeaderContentType, "aaa")
h.Set(HeaderServer, "aaabbb")
h.Set(HeaderContentLength, "1123")
h.Set(HeaderTrailer, "foo, bar")
var c Cookie
c.SetKey("foo")
@@ -608,6 +615,7 @@ func TestResponseHeaderDel(t *testing.T) {
h.Del(HeaderServer)
h.Del("content-length")
h.Del("set-cookie")
h.Del("trailer")
hv := h.Peek("aaa")
if string(hv) != "bbb" {
@@ -633,6 +641,10 @@ func TestResponseHeaderDel(t *testing.T) {
if len(hv) > 0 {
t.Fatalf("non-zero value: %q", hv)
}
hv = h.Peek(HeaderTrailer)
if len(hv) > 0 {
t.Fatalf("non-zero value: %q", hv)
}
if h.Cookie(&c) {
t.Fatalf("unexpected cookie obtianed: %q", &c)
@@ -642,6 +654,51 @@ func TestResponseHeaderDel(t *testing.T) {
}
}
func TestResponseHeaderSetTrailerGetBytes(t *testing.T) {
t.Parallel()
h := &ResponseHeader{}
h.noDefaultDate = true
h.Set("Foo", "bar")
h.Set(HeaderTrailer, "Baz")
h.Set("Baz", "test")
headerBytes := h.Header()
n, err := h.parseFirstLine(headerBytes)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if string(headerBytes[n:]) != "Foo: bar\r\nTrailer: Baz\r\n\r\n" {
t.Fatalf("Unexpected header: %q. Expected %s", headerBytes[n:], "Foo: bar\nTrailer: Baz\n\n")
}
if string(h.TrailerHeader()) != "Baz: test\r\n\r\n" {
t.Fatalf("Unexpected trailer header: %q. Expected %s", h.TrailerHeader(), "Baz: test\r\n\r\n")
}
}
func TestRequestHeaderSetTrailerGetBytes(t *testing.T) {
t.Parallel()
h := &RequestHeader{}
h.Set("Foo", "bar")
h.Set(HeaderTrailer, "Baz")
h.Set("Baz", "test")
headerBytes := h.Header()
n, err := h.parseFirstLine(headerBytes)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if string(headerBytes[n:]) != "Foo: bar\r\nTrailer: Baz\r\n\r\n" {
t.Fatalf("Unexpected header: %q. Expected %s", headerBytes[n:], "Foo: bar\nTrailer: Baz\n\n")
}
if string(h.TrailerHeader()) != "Baz: test\r\n\r\n" {
t.Fatalf("Unexpected trailer header: %q. Expected %s", h.TrailerHeader(), "Baz: test\r\n\r\n")
}
}
func TestAppendNormalizedHeaderKeyBytes(t *testing.T) {
t.Parallel()
@@ -1218,6 +1275,7 @@ func TestResponseHeaderCopyTo(t *testing.T) {
h.Set(HeaderSetCookie, "foo=bar")
h.Set(HeaderContentType, "foobar")
h.Set("AAA-BBB", "aaaa")
h.Set(HeaderTrailer, "foo, bar")
var h1 ResponseHeader
h.CopyTo(&h1)
@@ -1230,6 +1288,9 @@ func TestResponseHeaderCopyTo(t *testing.T) {
if !bytes.Equal(h1.Peek("aaa-bbb"), h.Peek("AAA-BBB")) {
t.Fatalf("unexpected aaa-bbb %q. Expected %q", h1.Peek("aaa-bbb"), h.Peek("aaa-bbb"))
}
if !bytes.Equal(h1.Peek(HeaderTrailer), h.Peek(HeaderTrailer)) {
t.Fatalf("unexpected trailer %q. Expected %q", h1.Peek(HeaderTrailer), h.Peek(HeaderTrailer))
}
// flush buf
h.bufKV = argsKV{}
@@ -1249,6 +1310,7 @@ func TestRequestHeaderCopyTo(t *testing.T) {
h.Set(HeaderContentType, "foobar")
h.Set(HeaderHost, "aaaa")
h.Set("aaaxxx", "123")
h.Set(HeaderTrailer, "foo, bar")
var h1 RequestHeader
h.CopyTo(&h1)
@@ -1264,6 +1326,9 @@ func TestRequestHeaderCopyTo(t *testing.T) {
if !bytes.Equal(h1.Peek("aaaxxx"), h.Peek("aaaxxx")) {
t.Fatalf("unexpected aaaxxx %q. Expected %q", h1.Peek("aaaxxx"), h.Peek("aaaxxx"))
}
if !bytes.Equal(h1.Peek(HeaderTrailer), h.Peek(HeaderTrailer)) {
t.Fatalf("unexpected trailer %q. Expected %q", h1.Peek(HeaderTrailer), h.Peek(HeaderTrailer))
}
// flush buf
h.bufKV = argsKV{}
@@ -1421,14 +1486,14 @@ func TestResponseHeaderVisitAll(t *testing.T) {
var h ResponseHeader
r := bytes.NewBufferString("HTTP/1.1 200 OK\r\nContent-Type: aa\r\nContent-Length: 123\r\nSet-Cookie: aa=bb; path=/foo/bar\r\nSet-Cookie: ccc\r\n\r\n")
r := bytes.NewBufferString("HTTP/1.1 200 OK\r\nContent-Type: aa\r\nContent-Length: 123\r\nSet-Cookie: aa=bb; path=/foo/bar\r\nSet-Cookie: ccc\r\nTrailer: Foo, Bar\r\n\r\n")
br := bufio.NewReader(r)
if err := h.Read(br); err != nil {
t.Fatalf("Unexpected error: %s", err)
}
if h.Len() != 4 {
t.Fatalf("Unexpected number of headers: %d. Expected 4", h.Len())
if h.Len() != 5 {
t.Fatalf("Unexpected number of headers: %d. Expected 5", h.Len())
}
contentLengthCount := 0
contentTypeCount := 0
@@ -1455,6 +1520,10 @@ func TestResponseHeaderVisitAll(t *testing.T) {
t.Fatalf("unexpected cookie header: %q. Expected %q", v, "ccc")
}
cookieCount++
case HeaderTrailer:
if v != "Foo, Bar" {
t.Fatalf("Unexpected trailer header %q. Expected %q", v, "Foo, Bar")
}
default:
t.Fatalf("unexpected header %q=%q", k, v)
}
@@ -1475,14 +1544,14 @@ func TestRequestHeaderVisitAll(t *testing.T) {
var h RequestHeader
r := bytes.NewBufferString("GET / HTTP/1.1\r\nHost: aa.com\r\nXX: YYY\r\nXX: ZZ\r\nCookie: a=b; c=d\r\n\r\n")
r := bytes.NewBufferString("GET / HTTP/1.1\r\nHost: aa.com\r\nXX: YYY\r\nXX: ZZ\r\nCookie: a=b; c=d\r\nTrailer: Foo, Bar\r\n\r\n")
br := bufio.NewReader(r)
if err := h.Read(br); err != nil {
t.Fatalf("Unexpected error: %s", err)
}
if h.Len() != 4 {
t.Fatalf("Unexpected number of header: %d. Expected 4", h.Len())
if h.Len() != 5 {
t.Fatalf("Unexpected number of header: %d. Expected 5", h.Len())
}
hostCount := 0
xxCount := 0
@@ -1509,6 +1578,10 @@ func TestRequestHeaderVisitAll(t *testing.T) {
t.Fatalf("Unexpected cookie %q. Expected %q", v, "a=b; c=d")
}
cookieCount++
case HeaderTrailer:
if v != "Foo, Bar" {
t.Fatalf("Unexpected trailer header %q. Expected %q", v, "Foo, Bar")
}
default:
t.Fatalf("Unexpected header %q=%q", k, v)
}
@@ -1524,7 +1597,7 @@ func TestRequestHeaderVisitAll(t *testing.T) {
}
}
func TestResponseHeaderVisitAllInOrder(t *testing.T) {
func TestRequestHeaderVisitAllInOrder(t *testing.T) {
t.Parallel()
var h RequestHeader
@@ -1567,6 +1640,38 @@ func TestResponseHeaderVisitAllInOrder(t *testing.T) {
})
}
func TestResponseHeaderAddTrailerError(t *testing.T) {
t.Parallel()
var h ResponseHeader
err := h.AddTrailer("Foo, Content-Length , Bar,Transfer-Encoding,")
expectedTrailer := "Foo, Bar"
if !errors.Is(err, ErrBadTrailer) {
t.Fatalf("unexpected err %q. Expected %q", err, ErrBadTrailer)
}
if trailer := string(h.Peek(HeaderTrailer)); trailer != expectedTrailer {
t.Fatalf("unexpected trailer %q. Expected %q", trailer, expectedTrailer)
}
}
func TestRequestHeaderAddTrailerError(t *testing.T) {
t.Parallel()
var h RequestHeader
err := h.AddTrailer("Foo, Content-Length , Bar,Transfer-Encoding,")
expectedTrailer := "Foo, Bar"
if !errors.Is(err, ErrBadTrailer) {
t.Fatalf("unexpected err %q. Expected %q", err, ErrBadTrailer)
}
if trailer := string(h.Peek(HeaderTrailer)); trailer != expectedTrailer {
t.Fatalf("unexpected trailer %q. Expected %q", trailer, expectedTrailer)
}
}
func TestResponseHeaderCookie(t *testing.T) {
t.Parallel()
@@ -2106,7 +2211,6 @@ func TestRequestHeaderBufioPeek(t *testing.T) {
t.Fatalf("Unexpected error when reading request: %s", err)
}
verifyRequestHeader(t, h, -2, "/", "foobar.com", "", "")
verifyTrailer(t, br, "aaaa")
}
func TestResponseHeaderBufioPeek(t *testing.T) {
@@ -2121,7 +2225,6 @@ func TestResponseHeaderBufioPeek(t *testing.T) {
t.Fatalf("Unexpected error when reading response: %s", err)
}
verifyResponseHeader(t, h, 200, 10, "aaa")
verifyTrailer(t, br, "0123456789")
}
func getHeaders(n int) string {
@@ -2139,143 +2242,127 @@ func TestResponseHeaderReadSuccess(t *testing.T) {
// straight order of content-length and content-type
testResponseHeaderReadSuccess(t, h, "HTTP/1.1 200 OK\r\nContent-Length: 123\r\nContent-Type: text/html\r\n\r\n",
200, 123, "text/html", "")
200, 123, "text/html")
if h.ConnectionClose() {
t.Fatalf("unexpected connection: close")
}
// reverse order of content-length and content-type
testResponseHeaderReadSuccess(t, h, "HTTP/1.1 202 OK\r\nContent-Type: text/plain; encoding=utf-8\r\nContent-Length: 543\r\nConnection: close\r\n\r\n",
202, 543, "text/plain; encoding=utf-8", "")
202, 543, "text/plain; encoding=utf-8")
if !h.ConnectionClose() {
t.Fatalf("expecting connection: close")
}
// tranfer-encoding: chunked
testResponseHeaderReadSuccess(t, h, "HTTP/1.1 505 Internal error\r\nContent-Type: text/html\r\nTransfer-Encoding: chunked\r\n\r\n",
505, -1, "text/html", "")
505, -1, "text/html")
if h.ConnectionClose() {
t.Fatalf("unexpected connection: close")
}
// reverse order of content-type and tranfer-encoding
testResponseHeaderReadSuccess(t, h, "HTTP/1.1 343 foobar\r\nTransfer-Encoding: chunked\r\nContent-Type: text/json\r\n\r\n",
343, -1, "text/json", "")
343, -1, "text/json")
// additional headers
testResponseHeaderReadSuccess(t, h, "HTTP/1.1 100 Continue\r\nFoobar: baz\r\nContent-Type: aaa/bbb\r\nUser-Agent: x\r\nContent-Length: 123\r\nZZZ: werer\r\n\r\n",
100, 123, "aaa/bbb", "")
// trailer (aka body)
testResponseHeaderReadSuccess(t, h, "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: 32245\r\n\r\nqwert aaa",
200, 32245, "text/plain", "qwert aaa")
100, 123, "aaa/bbb")
// ancient http protocol
testResponseHeaderReadSuccess(t, h, "HTTP/0.9 300 OK\r\nContent-Length: 123\r\nContent-Type: text/html\r\n\r\nqqqq",
300, 123, "text/html", "qqqq")
300, 123, "text/html")
// lf instead of crlf
testResponseHeaderReadSuccess(t, h, "HTTP/1.1 200 OK\nContent-Length: 123\nContent-Type: text/html\n\n",
200, 123, "text/html", "")
200, 123, "text/html")
// Zero-length headers with mixed crlf and lf
testResponseHeaderReadSuccess(t, h, "HTTP/1.1 400 OK\nContent-Length: 345\nZero-Value: \r\nContent-Type: aaa\n: zero-key\r\n\r\nooa",
400, 345, "aaa", "ooa")
400, 345, "aaa")
// No space after colon
testResponseHeaderReadSuccess(t, h, "HTTP/1.1 200 OK\nContent-Length:34\nContent-Type: sss\n\naaaa",
200, 34, "sss", "aaaa")
200, 34, "sss")
// invalid case
testResponseHeaderReadSuccess(t, h, "HTTP/1.1 400 OK\nconTEnt-leNGTH: 123\nConTENT-TYPE: ass\n\n",
400, 123, "ass", "")
400, 123, "ass")
// duplicate content-length
testResponseHeaderReadSuccess(t, h, "HTTP/1.1 200 OK\r\nContent-Length: 456\r\nContent-Type: foo/bar\r\nContent-Length: 321\r\n\r\n",
200, 321, "foo/bar", "")
200, 321, "foo/bar")
// duplicate content-type
testResponseHeaderReadSuccess(t, h, "HTTP/1.1 200 OK\r\nContent-Length: 234\r\nContent-Type: foo/bar\r\nContent-Type: baz/bar\r\n\r\n",
200, 234, "baz/bar", "")
// both transfer-encoding: chunked and content-length
testResponseHeaderReadSuccess(t, h, "HTTP/1.1 200 OK\r\nContent-Type: foo/bar\r\nContent-Length: 123\r\nTransfer-Encoding: chunked\r\n\r\n",
200, -1, "foo/bar", "")
200, 234, "baz/bar")
testResponseHeaderReadSuccess(t, h, "HTTP/1.1 300 OK\r\nContent-Type: foo/barr\r\nTransfer-Encoding: chunked\r\nContent-Length: 354\r\n\r\n",
300, -1, "foo/barr", "")
300, -1, "foo/barr")
// duplicate transfer-encoding: chunked
testResponseHeaderReadSuccess(t, h, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nTransfer-Encoding: chunked\r\nTransfer-Encoding: chunked\r\n\r\n",
200, -1, "text/html", "")
200, -1, "text/html")
// no reason string in the first line
testResponseHeaderReadSuccess(t, h, "HTTP/1.1 456\r\nContent-Type: xxx/yyy\r\nContent-Length: 134\r\n\r\naaaxxx",
456, 134, "xxx/yyy", "aaaxxx")
456, 134, "xxx/yyy")
// blank lines before the first line
testResponseHeaderReadSuccess(t, h, "\r\nHTTP/1.1 200 OK\r\nContent-Type: aa\r\nContent-Length: 0\r\n\r\nsss",
200, 0, "aa", "sss")
200, 0, "aa")
if h.ConnectionClose() {
t.Fatalf("unexpected connection: close")
}
// no content-length (informational responses)
testResponseHeaderReadSuccess(t, h, "HTTP/1.1 101 OK\r\n\r\n",
101, -2, "text/plain; charset=utf-8", "")
101, -2, "text/plain; charset=utf-8")
if h.ConnectionClose() {
t.Fatalf("expecting connection: keep-alive for informational response")
}
// no content-length (no-content responses)
testResponseHeaderReadSuccess(t, h, "HTTP/1.1 204 OK\r\n\r\n",
204, -2, "text/plain; charset=utf-8", "")
204, -2, "text/plain; charset=utf-8")
if h.ConnectionClose() {
t.Fatalf("expecting connection: keep-alive for no-content response")
}
// no content-length (not-modified responses)
testResponseHeaderReadSuccess(t, h, "HTTP/1.1 304 OK\r\n\r\n",
304, -2, "text/plain; charset=utf-8", "")
304, -2, "text/plain; charset=utf-8")
if h.ConnectionClose() {
t.Fatalf("expecting connection: keep-alive for not-modified response")
}
// no content-length (identity transfer-encoding)
testResponseHeaderReadSuccess(t, h, "HTTP/1.1 200 OK\r\nContent-Type: foo/bar\r\n\r\nabcdefg",
200, -2, "foo/bar", "abcdefg")
200, -2, "foo/bar")
if !h.ConnectionClose() {
t.Fatalf("expecting connection: close for identity response")
}
// non-numeric content-length
testResponseHeaderReadSuccess(t, h, "HTTP/1.1 200 OK\r\nContent-Length: faaa\r\nContent-Type: text/html\r\n\r\nfoobar",
200, -2, "text/html", "foobar")
testResponseHeaderReadSuccess(t, h, "HTTP/1.1 201 OK\r\nContent-Length: 123aa\r\nContent-Type: text/ht\r\n\r\naaa",
201, -2, "text/ht", "aaa")
testResponseHeaderReadSuccess(t, h, "HTTP/1.1 200 OK\r\nContent-Length: aa124\r\nContent-Type: html\r\n\r\nxx",
200, -2, "html", "xx")
// no content-type
testResponseHeaderReadSuccess(t, h, "HTTP/1.1 400 OK\r\nContent-Length: 123\r\n\r\nfoiaaa",
400, 123, string(defaultContentType), "foiaaa")
400, 123, string(defaultContentType))
// no content-type and no default
h.SetNoDefaultContentType(true)
testResponseHeaderReadSuccess(t, h, "HTTP/1.1 400 OK\r\nContent-Length: 123\r\n\r\nfoiaaa",
400, 123, "", "foiaaa")
400, 123, "")
h.SetNoDefaultContentType(false)
// no headers
testResponseHeaderReadSuccess(t, h, "HTTP/1.1 200 OK\r\n\r\naaaabbb",
200, -2, string(defaultContentType), "aaaabbb")
200, -2, string(defaultContentType))
if !h.IsHTTP11() {
t.Fatalf("expecting http/1.1 protocol")
}
// ancient http protocol
testResponseHeaderReadSuccess(t, h, "HTTP/1.0 203 OK\r\nContent-Length: 123\r\nContent-Type: foobar\r\n\r\naaa",
203, 123, "foobar", "aaa")
203, 123, "foobar")
if h.IsHTTP11() {
t.Fatalf("ancient protocol must be non-http/1.1")
}
@@ -2285,7 +2372,7 @@ func TestResponseHeaderReadSuccess(t *testing.T) {
// ancient http protocol with 'Connection: keep-alive' header.
testResponseHeaderReadSuccess(t, h, "HTTP/1.0 403 aa\r\nContent-Length: 0\r\nContent-Type: 2\r\nConnection: Keep-Alive\r\n\r\nww",
403, 0, "2", "ww")
403, 0, "2")
if h.IsHTTP11() {
t.Fatalf("ancient protocol must be non-http/1.1")
}
@@ -2301,21 +2388,21 @@ func TestRequestHeaderReadSuccess(t *testing.T) {
// simple headers
testRequestHeaderReadSuccess(t, h, "GET /foo/bar HTTP/1.1\r\nHost: google.com\r\n\r\n",
-2, "/foo/bar", "google.com", "", "", "")
-2, "/foo/bar", "google.com", "", "", nil)
if h.ConnectionClose() {
t.Fatalf("unexpected connection: close header")
}
// simple headers with body
testRequestHeaderReadSuccess(t, h, "GET /a/bar HTTP/1.1\r\nHost: gole.com\r\nconneCTION: close\r\n\r\nfoobar",
-2, "/a/bar", "gole.com", "", "", "foobar")
-2, "/a/bar", "gole.com", "", "", nil)
if !h.ConnectionClose() {
t.Fatalf("connection: close unset")
}
// ancient http protocol
testRequestHeaderReadSuccess(t, h, "GET /bar HTTP/1.0\r\nHost: gole\r\n\r\npppp",
-2, "/bar", "gole", "", "", "pppp")
-2, "/bar", "gole", "", "", nil)
if h.IsHTTP11() {
t.Fatalf("ancient http protocol cannot be http/1.1")
}
@@ -2325,7 +2412,7 @@ func TestRequestHeaderReadSuccess(t *testing.T) {
// ancient http protocol with 'Connection: keep-alive' header
testRequestHeaderReadSuccess(t, h, "GET /aa HTTP/1.0\r\nHost: bb\r\nConnection: keep-alive\r\n\r\nxxx",
-2, "/aa", "bb", "", "", "xxx")
-2, "/aa", "bb", "", "", nil)
if h.IsHTTP11() {
t.Fatalf("ancient http protocol cannot be http/1.1")
}
@@ -2335,7 +2422,7 @@ func TestRequestHeaderReadSuccess(t *testing.T) {
// complex headers with body
testRequestHeaderReadSuccess(t, h, "GET /aabar HTTP/1.1\r\nAAA: bbb\r\nHost: ole.com\r\nAA: bb\r\n\r\nzzz",
-2, "/aabar", "ole.com", "", "", "zzz")
-2, "/aabar", "ole.com", "", "", nil)
if !h.IsHTTP11() {
t.Fatalf("expecting http/1.1 protocol")
}
@@ -2345,103 +2432,103 @@ func TestRequestHeaderReadSuccess(t *testing.T) {
// lf instead of crlf
testRequestHeaderReadSuccess(t, h, "GET /foo/bar HTTP/1.1\nHost: google.com\n\n",
-2, "/foo/bar", "google.com", "", "", "")
-2, "/foo/bar", "google.com", "", "", nil)
// post method
testRequestHeaderReadSuccess(t, h, "POST /aaa?bbb HTTP/1.1\r\nHost: foobar.com\r\nContent-Length: 1235\r\nContent-Type: aaa\r\n\r\nabcdef",
1235, "/aaa?bbb", "foobar.com", "", "aaa", "abcdef")
1235, "/aaa?bbb", "foobar.com", "", "aaa", nil)
// zero-length headers with mixed crlf and lf
testRequestHeaderReadSuccess(t, h, "GET /a HTTP/1.1\nHost: aaa\r\nZero: \n: Zero-Value\n\r\nxccv",
-2, "/a", "aaa", "", "", "xccv")
-2, "/a", "aaa", "", "", nil)
// no space after colon
testRequestHeaderReadSuccess(t, h, "GET /a HTTP/1.1\nHost:aaaxd\n\nsdfds",
-2, "/a", "aaaxd", "", "", "sdfds")
-2, "/a", "aaaxd", "", "", nil)
// get with zero content-length
testRequestHeaderReadSuccess(t, h, "GET /xxx HTTP/1.1\nHost: aaa.com\nContent-Length: 0\n\n",
0, "/xxx", "aaa.com", "", "", "")
0, "/xxx", "aaa.com", "", "", nil)
// get with non-zero content-length
testRequestHeaderReadSuccess(t, h, "GET /xxx HTTP/1.1\nHost: aaa.com\nContent-Length: 123\n\n",
123, "/xxx", "aaa.com", "", "", "")
123, "/xxx", "aaa.com", "", "", nil)
// invalid case
testRequestHeaderReadSuccess(t, h, "GET /aaa HTTP/1.1\nhoST: bbb.com\n\naas",
-2, "/aaa", "bbb.com", "", "", "aas")
-2, "/aaa", "bbb.com", "", "", nil)
// referer
testRequestHeaderReadSuccess(t, h, "GET /asdf HTTP/1.1\nHost: aaa.com\nReferer: bb.com\n\naaa",
-2, "/asdf", "aaa.com", "bb.com", "", "aaa")
-2, "/asdf", "aaa.com", "bb.com", "", nil)
// duplicate host
testRequestHeaderReadSuccess(t, h, "GET /aa HTTP/1.1\r\nHost: aaaaaa.com\r\nHost: bb.com\r\n\r\n",
-2, "/aa", "bb.com", "", "", "")
-2, "/aa", "bb.com", "", "", nil)
// post with duplicate content-type
testRequestHeaderReadSuccess(t, h, "POST /a HTTP/1.1\r\nHost: aa\r\nContent-Type: ab\r\nContent-Length: 123\r\nContent-Type: xx\r\n\r\n",
123, "/a", "aa", "", "xx", "")
123, "/a", "aa", "", "xx", nil)
// post with duplicate content-length
testRequestHeaderReadSuccess(t, h, "POST /xx HTTP/1.1\r\nHost: aa\r\nContent-Type: s\r\nContent-Length: 13\r\nContent-Length: 1\r\n\r\n",
1, "/xx", "aa", "", "s", "")
1, "/xx", "aa", "", "s", nil)
// non-post with content-type
testRequestHeaderReadSuccess(t, h, "GET /aaa HTTP/1.1\r\nHost: bbb.com\r\nContent-Type: aaab\r\n\r\n",
-2, "/aaa", "bbb.com", "", "aaab", "")
-2, "/aaa", "bbb.com", "", "aaab", nil)
// non-post with content-length
testRequestHeaderReadSuccess(t, h, "HEAD / HTTP/1.1\r\nHost: aaa.com\r\nContent-Length: 123\r\n\r\n",
123, "/", "aaa.com", "", "", "")
123, "/", "aaa.com", "", "", nil)
// non-post with content-type and content-length
testRequestHeaderReadSuccess(t, h, "GET /aa HTTP/1.1\r\nHost: aa.com\r\nContent-Type: abd/test\r\nContent-Length: 123\r\n\r\n",
123, "/aa", "aa.com", "", "abd/test", "")
123, "/aa", "aa.com", "", "abd/test", nil)
// request uri with hostname
testRequestHeaderReadSuccess(t, h, "GET http://gooGle.com/foO/%20bar?xxx#aaa HTTP/1.1\r\nHost: aa.cOM\r\n\r\ntrail",
-2, "http://gooGle.com/foO/%20bar?xxx#aaa", "aa.cOM", "", "", "trail")
-2, "http://gooGle.com/foO/%20bar?xxx#aaa", "aa.cOM", "", "", nil)
// no protocol in the first line
testRequestHeaderReadSuccess(t, h, "GET /foo/bar\r\nHost: google.com\r\n\r\nisdD",
-2, "/foo/bar", "google.com", "", "", "isdD")
-2, "/foo/bar", "google.com", "", "", nil)
// blank lines before the first line
testRequestHeaderReadSuccess(t, h, "\r\n\n\r\nGET /aaa HTTP/1.1\r\nHost: aaa.com\r\n\r\nsss",
-2, "/aaa", "aaa.com", "", "", "sss")
-2, "/aaa", "aaa.com", "", "", nil)
// request uri with spaces
testRequestHeaderReadSuccess(t, h, "GET /foo/ bar baz HTTP/1.1\r\nHost: aa.com\r\n\r\nxxx",
-2, "/foo/ bar baz", "aa.com", "", "", "xxx")
-2, "/foo/ bar baz", "aa.com", "", "", nil)
// no host
testRequestHeaderReadSuccess(t, h, "GET /foo/bar HTTP/1.1\r\nFOObar: assdfd\r\n\r\naaa",
-2, "/foo/bar", "", "", "", "aaa")
-2, "/foo/bar", "", "", "", nil)
// no host, no headers
testRequestHeaderReadSuccess(t, h, "GET /foo/bar HTTP/1.1\r\n\r\nfoobar",
-2, "/foo/bar", "", "", "", "foobar")
-2, "/foo/bar", "", "", "", nil)
// post without content-length and content-type
testRequestHeaderReadSuccess(t, h, "POST /aaa HTTP/1.1\r\nHost: aaa.com\r\n\r\nzxc",
-2, "/aaa", "aaa.com", "", "", "zxc")
-2, "/aaa", "aaa.com", "", "", nil)
// post without content-type
testRequestHeaderReadSuccess(t, h, "POST /abc HTTP/1.1\r\nHost: aa.com\r\nContent-Length: 123\r\n\r\npoiuy",
123, "/abc", "aa.com", "", "", "poiuy")
123, "/abc", "aa.com", "", "", nil)
// post without content-length
testRequestHeaderReadSuccess(t, h, "POST /abc HTTP/1.1\r\nHost: aa.com\r\nContent-Type: adv\r\n\r\n123456",
-2, "/abc", "aa.com", "", "adv", "123456")
-2, "/abc", "aa.com", "", "adv", nil)
// invalid method
testRequestHeaderReadSuccess(t, h, "POST /foo/bar HTTP/1.1\r\nHost: google.com\r\n\r\nmnbv",
-2, "/foo/bar", "google.com", "", "", "mnbv")
-2, "/foo/bar", "google.com", "", "", nil)
// put request
testRequestHeaderReadSuccess(t, h, "PUT /faa HTTP/1.1\r\nHost: aaa.com\r\nContent-Length: 123\r\nContent-Type: aaa\r\n\r\nxwwere",
123, "/faa", "aaa.com", "", "aaa", "xwwere")
123, "/faa", "aaa.com", "", "aaa", nil)
}
func TestResponseHeaderReadError(t *testing.T) {
@@ -2462,11 +2549,19 @@ func TestResponseHeaderReadError(t *testing.T) {
testResponseHeaderReadError(t, h, "HTTP/1.1 123foobar OK\r\nContent-Length: 123\r\nContent-Type: text/html\r\n\r\n")
testResponseHeaderReadError(t, h, "HTTP/1.1 foobar344 OK\r\nContent-Length: 123\r\nContent-Type: text/html\r\n\r\n")
// non-numeric content-length
testResponseHeaderReadError(t, h, "HTTP/1.1 200 OK\r\nContent-Length: faaa\r\nContent-Type: text/html\r\n\r\nfoobar")
testResponseHeaderReadError(t, h, "HTTP/1.1 201 OK\r\nContent-Length: 123aa\r\nContent-Type: text/ht\r\n\r\naaa")
testResponseHeaderReadError(t, h, "HTTP/1.1 200 OK\r\nContent-Length: aa124\r\nContent-Type: html\r\n\r\nxx")
// no headers
testResponseHeaderReadError(t, h, "HTTP/1.1 200 OK\r\n")
// no trailing crlf
testResponseHeaderReadError(t, h, "HTTP/1.1 200 OK\r\nContent-Length: 123\r\nContent-Type: text/html\r\n")
// forbidden trailer
testResponseHeaderReadError(t, h, "HTTP/1.1 200 OK\r\nContent-Length: -1\r\nTrailer: Foo, Content-Length\r\n\r\n")
}
func TestResponseHeaderReadErrorSecureLog(t *testing.T) {
@@ -2511,6 +2606,9 @@ func TestRequestHeaderReadError(t *testing.T) {
// post with invalid content-length
testRequestHeaderReadError(t, h, "POST /a HTTP/1.1\r\nHost: bb\r\nContent-Type: aa\r\nContent-Length: dff\r\n\r\nqwerty")
// forbidden trailer
testRequestHeaderReadError(t, h, "POST /a HTTP/1.1\r\nContent-Length: -1\r\nTrailer: Foo, Content-Length\r\n\r\n")
}
func TestRequestHeaderReadSecuredError(t *testing.T) {
@@ -2541,7 +2639,7 @@ func testResponseHeaderReadError(t *testing.T, h *ResponseHeader, headers string
}
// make sure response header works after error
testResponseHeaderReadSuccess(t, h, "HTTP/1.1 200 OK\r\nContent-Type: foo/bar\r\nContent-Length: 12345\r\n\r\nsss",
200, 12345, "foo/bar", "sss")
200, 12345, "foo/bar")
}
func testResponseHeaderReadSecuredError(t *testing.T, h *ResponseHeader, headers string) {
@@ -2556,7 +2654,7 @@ func testResponseHeaderReadSecuredError(t *testing.T, h *ResponseHeader, headers
}
// make sure response header works after error
testResponseHeaderReadSuccess(t, h, "HTTP/1.1 200 OK\r\nContent-Type: foo/bar\r\nContent-Length: 12345\r\n\r\nsss",
200, 12345, "foo/bar", "sss")
200, 12345, "foo/bar")
}
func testRequestHeaderReadError(t *testing.T, h *RequestHeader, headers string) {
@@ -2569,7 +2667,7 @@ func testRequestHeaderReadError(t *testing.T, h *RequestHeader, headers string)
// make sure request header works after error
testRequestHeaderReadSuccess(t, h, "GET /foo/bar HTTP/1.1\r\nHost: aaaa\r\n\r\nxxx",
-2, "/foo/bar", "aaaa", "", "", "xxx")
-2, "/foo/bar", "aaaa", "", "", nil)
}
func testRequestHeaderReadSecuredError(t *testing.T, h *RequestHeader, headers string) {
@@ -2584,11 +2682,11 @@ func testRequestHeaderReadSecuredError(t *testing.T, h *RequestHeader, headers s
}
// make sure request header works after error
testRequestHeaderReadSuccess(t, h, "GET /foo/bar HTTP/1.1\r\nHost: aaaa\r\n\r\nxxx",
-2, "/foo/bar", "aaaa", "", "", "xxx")
-2, "/foo/bar", "aaaa", "", "", nil)
}
func testResponseHeaderReadSuccess(t *testing.T, h *ResponseHeader, headers string, expectedStatusCode, expectedContentLength int,
expectedContentType, expectedTrailer string) {
expectedContentType string) {
r := bytes.NewBufferString(headers)
br := bufio.NewReader(r)
err := h.Read(br)
@@ -2596,11 +2694,10 @@ func testResponseHeaderReadSuccess(t *testing.T, h *ResponseHeader, headers stri
t.Fatalf("Unexpected error when parsing response headers: %s. headers=%q", err, headers)
}
verifyResponseHeader(t, h, expectedStatusCode, expectedContentLength, expectedContentType)
verifyTrailer(t, br, expectedTrailer)
}
func testRequestHeaderReadSuccess(t *testing.T, h *RequestHeader, headers string, expectedContentLength int,
expectedRequestURI, expectedHost, expectedReferer, expectedContentType, expectedTrailer string) {
expectedRequestURI, expectedHost, expectedReferer, expectedContentType string, expectedTrailer map[string]string) {
r := bytes.NewBufferString(headers)
br := bufio.NewReader(r)
err := h.Read(br)
@@ -2608,7 +2705,6 @@ func testRequestHeaderReadSuccess(t *testing.T, h *RequestHeader, headers string
t.Fatalf("Unexpected error when parsing request headers: %s. headers=%q", err, headers)
}
verifyRequestHeader(t, h, expectedContentLength, expectedRequestURI, expectedHost, expectedReferer, expectedContentType)
verifyTrailer(t, br, expectedTrailer)
}
func verifyResponseHeader(t *testing.T, h *ResponseHeader, expectedStatusCode, expectedContentLength int, expectedContentType string) {
@@ -2648,12 +2744,45 @@ func verifyRequestHeader(t *testing.T, h *RequestHeader, expectedContentLength i
}
}
func verifyTrailer(t *testing.T, r *bufio.Reader, expectedTrailer string) {
trailer, err := ioutil.ReadAll(r)
func verifyResponseTrailer(t *testing.T, h *ResponseHeader, expectedTrailers map[string]string) {
for k, v := range expectedTrailers {
got := h.Peek(k)
if !bytes.Equal(got, []byte(v)) {
t.Fatalf("Unexpected trailer %s. Expected %s. Got %q", k, v, got)
}
}
}
func verifyRequestTrailer(t *testing.T, h *RequestHeader, expectedTrailers map[string]string) {
for k, v := range expectedTrailers {
got := h.Peek(k)
if !bytes.Equal(got, []byte(v)) {
t.Fatalf("Unexpected trailer %s. Expected %s. Got %q", k, v, got)
}
}
}
func verifyTrailer(t *testing.T, r *bufio.Reader, expectedTrailers map[string]string, isReq bool) {
if isReq {
req := Request{}
err := req.Header.ReadTrailer(r)
if err == io.EOF && expectedTrailers == nil {
return
}
if err != nil {
t.Fatalf("Cannot read trailer: %s", err)
}
verifyRequestTrailer(t, &req.Header, expectedTrailers)
return
}
resp := Response{}
err := resp.Header.ReadTrailer(r)
if err == io.EOF && expectedTrailers == nil {
return
}
if err != nil {
t.Fatalf("Cannot read trailer: %s", err)
}
if !bytes.Equal(trailer, []byte(expectedTrailer)) {
t.Fatalf("Unexpected trailer %q. Expected %q", trailer, expectedTrailer)
}
verifyResponseTrailer(t, &resp.Header, expectedTrailers)
}
+3 -2
View File
@@ -36,8 +36,9 @@ const (
HeaderVary = "Vary"
// Connection management
HeaderConnection = "Connection"
HeaderKeepAlive = "Keep-Alive"
HeaderConnection = "Connection"
HeaderKeepAlive = "Keep-Alive"
HeaderProxyConnection = "Proxy-Connection"
// Content negotiation
HeaderAccept = "Accept"
+72 -33
View File
@@ -1136,7 +1136,17 @@ func (req *Request) ContinueReadBody(r *bufio.Reader, maxBodySize int, preParseM
return nil
}
return req.ReadBody(r, contentLength, maxBodySize)
if err = req.ReadBody(r, contentLength, maxBodySize); err != nil {
return err
}
if req.Header.ContentLength() == -1 {
err = req.Header.ReadTrailer(r)
if err != nil && err != io.EOF {
return err
}
}
return nil
}
// ReadBody reads request body from the given r, limiting the body size.
@@ -1146,12 +1156,22 @@ func (req *Request) ContinueReadBody(r *bufio.Reader, maxBodySize int, preParseM
func (req *Request) ReadBody(r *bufio.Reader, contentLength int, maxBodySize int) (err error) {
bodyBuf := req.bodyBuffer()
bodyBuf.Reset()
bodyBuf.B, err = readBody(r, contentLength, maxBodySize, bodyBuf.B)
if contentLength >= 0 {
bodyBuf.B, err = readBody(r, contentLength, maxBodySize, bodyBuf.B)
} else if contentLength == -1 {
bodyBuf.B, err = readBodyChunked(r, maxBodySize, bodyBuf.B)
} else {
bodyBuf.B, err = readBodyIdentity(r, maxBodySize, bodyBuf.B)
req.Header.SetContentLength(len(bodyBuf.B))
}
if err != nil {
req.Reset()
return err
}
req.Header.SetContentLength(len(bodyBuf.B))
return nil
}
@@ -1197,12 +1217,12 @@ func (req *Request) ContinueReadBodyStream(r *bufio.Reader, maxBodySize int, pre
if err == ErrBodyTooLarge {
req.Header.SetContentLength(contentLength)
req.body = bodyBuf
req.bodyStream = acquireRequestStream(bodyBuf, r, contentLength)
req.bodyStream = acquireRequestStream(bodyBuf, r, &req.Header)
return nil
}
if err == errChunkedStream {
req.body = bodyBuf
req.bodyStream = acquireRequestStream(bodyBuf, r, -1)
req.bodyStream = acquireRequestStream(bodyBuf, r, &req.Header)
return nil
}
req.Reset()
@@ -1210,7 +1230,7 @@ func (req *Request) ContinueReadBodyStream(r *bufio.Reader, maxBodySize int, pre
}
req.body = bodyBuf
req.bodyStream = acquireRequestStream(bodyBuf, r, contentLength)
req.bodyStream = acquireRequestStream(bodyBuf, r, &req.Header)
req.Header.SetContentLength(contentLength)
return nil
}
@@ -1245,7 +1265,17 @@ func (resp *Response) ReadLimitBody(r *bufio.Reader, maxBodySize int) error {
}
if !resp.mustSkipBody() {
return resp.ReadBody(r, maxBodySize)
err = resp.ReadBody(r, maxBodySize)
if err != nil {
return err
}
}
if resp.Header.ContentLength() == -1 {
err = resp.Header.ReadTrailer(r)
if err != nil && err != io.EOF {
return err
}
}
return nil
}
@@ -1257,12 +1287,19 @@ func (resp *Response) ReadLimitBody(r *bufio.Reader, maxBodySize int) error {
func (resp *Response) ReadBody(r *bufio.Reader, maxBodySize int) (err error) {
bodyBuf := resp.bodyBuffer()
bodyBuf.Reset()
bodyBuf.B, err = readBody(r, resp.Header.ContentLength(), maxBodySize, bodyBuf.B)
if err != nil {
return err
contentLength := resp.Header.ContentLength()
if contentLength >= 0 {
bodyBuf.B, err = readBody(r, contentLength, maxBodySize, bodyBuf.B)
} else if contentLength == -1 {
bodyBuf.B, err = readBodyChunked(r, maxBodySize, bodyBuf.B)
} else {
bodyBuf.B, err = readBodyIdentity(r, maxBodySize, bodyBuf.B)
resp.Header.SetContentLength(len(bodyBuf.B))
}
resp.Header.SetContentLength(len(bodyBuf.B))
return nil
return err
}
func (resp *Response) mustSkipBody() bool {
@@ -1723,9 +1760,13 @@ func (req *Request) writeBodyStream(w *bufio.Writer) error {
}
} else {
req.Header.SetContentLength(-1)
if err = req.Header.Write(w); err == nil {
err = req.Header.Write(w)
if err == nil {
err = writeBodyChunked(w, req.bodyStream)
}
if err == nil {
err = req.Header.writeTrailer(w)
}
}
err1 := req.closeBodyStream()
if err == nil {
@@ -1779,6 +1820,9 @@ func (resp *Response) writeBodyStream(w *bufio.Writer, sendBody bool) (err error
if err == nil && sendBody {
err = writeBodyChunked(w, resp.bodyStream)
}
if err == nil {
err = resp.Header.writeTrailer(w)
}
}
}
err1 := resp.closeBodyStream()
@@ -1927,12 +1971,13 @@ func writeChunk(w *bufio.Writer, b []byte) error {
if _, err := w.Write(b); err != nil {
return err
}
_, err := w.Write(strCRLF)
err1 := w.Flush()
if err == nil {
err = err1
// If is end chunk, write CRLF after writing trailer
if n > 0 {
if _, err := w.Write(strCRLF); err != nil {
return err
}
}
return err
return w.Flush()
}
// ErrBodyTooLarge is returned if either request or response body exceeds
@@ -1940,17 +1985,10 @@ func writeChunk(w *bufio.Writer, b []byte) error {
var ErrBodyTooLarge = errors.New("body size exceeds the given limit")
func readBody(r *bufio.Reader, contentLength int, maxBodySize int, dst []byte) ([]byte, error) {
dst = dst[:0]
if contentLength >= 0 {
if maxBodySize > 0 && contentLength > maxBodySize {
return dst, ErrBodyTooLarge
}
return appendBodyFixedSize(r, dst, contentLength)
if maxBodySize > 0 && contentLength > maxBodySize {
return dst, ErrBodyTooLarge
}
if contentLength == -1 {
return readBodyChunked(r, maxBodySize, dst)
}
return readBodyIdentity(r, maxBodySize, dst)
return appendBodyFixedSize(r, dst, contentLength)
}
var errChunkedStream = errors.New("chunked stream")
@@ -2067,6 +2105,9 @@ func readBodyChunked(r *bufio.Reader, maxBodySize int, dst []byte) ([]byte, erro
if err != nil {
return dst, err
}
if chunkSize == 0 {
return dst, err
}
if maxBodySize > 0 && len(dst)+chunkSize > maxBodySize {
return dst, ErrBodyTooLarge
}
@@ -2080,9 +2121,6 @@ func readBodyChunked(r *bufio.Reader, maxBodySize int, dst []byte) ([]byte, erro
}
}
dst = dst[:len(dst)-strCRLFLen]
if chunkSize == 0 {
return dst, nil
}
}
}
@@ -2098,8 +2136,9 @@ func parseChunkSize(r *bufio.Reader) (int, error) {
error: fmt.Errorf("cannot read '\r' char at the end of chunk size: %s", err),
}
}
// Skip any trailing whitespace after chunk size.
if c == ' ' {
// Skip chunk extension after chunk size.
// Add support later if anyone needs it.
if c != '\r' {
continue
}
if err := r.UnreadByte(); err != nil {
+311 -81
View File
@@ -155,6 +155,122 @@ func testResponseCopyTo(t *testing.T, src *Response) {
}
}
func TestRequestBodyStreamWithTrailer(t *testing.T) {
t.Parallel()
testRequestBodyStreamWithTrailer(t, nil, false)
body := createFixedBody(1e5)
testRequestBodyStreamWithTrailer(t, body, false)
testRequestBodyStreamWithTrailer(t, body, true)
}
func testRequestBodyStreamWithTrailer(t *testing.T, body []byte, disableNormalizing bool) {
expectedTrailer := map[string]string{
"foo": "testfoo",
"bar": "testbar",
}
var req1 Request
req1.Header.disableNormalizing = disableNormalizing
req1.SetHost("google.com")
req1.SetBodyStream(bytes.NewBuffer(body), -1)
for k, v := range expectedTrailer {
err := req1.Header.AddTrailer(k)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
req1.Header.Set(k, v)
}
w := &bytes.Buffer{}
bw := bufio.NewWriter(w)
if err := req1.Write(bw); err != nil {
t.Fatalf("unexpected error: %s", err)
}
if err := bw.Flush(); err != nil {
t.Fatalf("unexpected error: %s", err)
}
var req2 Request
req2.Header.disableNormalizing = disableNormalizing
br := bufio.NewReader(w)
if err := req2.Read(br); err != nil {
t.Fatalf("unexpected error: %s", err)
}
reqBody := req2.Body()
if !bytes.Equal(reqBody, body) {
t.Fatalf("unexpected body: %q. Expecting %q", reqBody, body)
}
for k, v := range expectedTrailer {
kBytes := []byte(k)
normalizeHeaderKey(kBytes, disableNormalizing)
r := req2.Header.Peek(k)
if string(r) != v {
t.Fatalf("unexpected trailer header %q: %q. Expecting %s", kBytes, r, v)
}
}
}
func TestResponseBodyStreamWithTrailer(t *testing.T) {
t.Parallel()
testResponseBodyStreamWithTrailer(t, nil, false)
body := createFixedBody(1e5)
testResponseBodyStreamWithTrailer(t, body, false)
testResponseBodyStreamWithTrailer(t, body, true)
}
func testResponseBodyStreamWithTrailer(t *testing.T, body []byte, disableNormalizing bool) {
expectedTrailer := map[string]string{
"foo": "testfoo",
"bar": "testbar",
}
var resp1 Response
resp1.Header.disableNormalizing = disableNormalizing
resp1.SetBodyStream(bytes.NewReader(body), -1)
for k, v := range expectedTrailer {
err := resp1.Header.AddTrailer(k)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
resp1.Header.Set(k, v)
}
w := &bytes.Buffer{}
bw := bufio.NewWriter(w)
if err := resp1.Write(bw); err != nil {
t.Fatalf("unexpected error: %s", err)
}
if err := bw.Flush(); err != nil {
t.Fatalf("unexpected error: %s", err)
}
var resp2 Response
resp2.Header.disableNormalizing = disableNormalizing
br := bufio.NewReader(w)
if err := resp2.Read(br); err != nil {
t.Fatalf("unexpected error: %s", err)
}
respBody := resp2.Body()
if !bytes.Equal(respBody, body) {
t.Fatalf("unexpected body: %q. Expecting %q", respBody, body)
}
for k, v := range expectedTrailer {
kBytes := []byte(k)
normalizeHeaderKey(kBytes, disableNormalizing)
r := resp2.Header.Peek(k)
if string(r) != v {
t.Fatalf("unexpected trailer header %q: %q. Expecting %s", kBytes, r, v)
}
}
}
func TestResponseBodyStreamDeflate(t *testing.T) {
t.Parallel()
@@ -1344,17 +1460,19 @@ func TestResponseReadLimitBody(t *testing.T) {
// response with content-length
testResponseReadLimitBodySuccess(t, "HTTP/1.1 200 OK\r\nContent-Type: aa\r\nContent-Length: 10\r\n\r\n9876543210", 10)
testResponseReadLimitBodySuccess(t, "HTTP/1.1 200 OK\r\nContent-Type: aa\r\nContent-Length: 10\r\n\r\n9876543210", 100)
testResponseReadLimitBodyError(t, "HTTP/1.1 200 OK\r\nContent-Type: aa\r\nContent-Length: 10\r\n\r\n9876543210", 9)
testResponseReadLimitBodyError(t, "HTTP/1.1 200 OK\r\nContent-Type: aa\r\nContent-Length: 10\r\n\r\n9876543210", 9, ErrBodyTooLarge)
// chunked response
testResponseReadLimitBodySuccess(t, "HTTP/1.1 200 OK\r\nContent-Type: aa\r\nTransfer-Encoding: chunked\r\n\r\n6\r\nfoobar\r\n3\r\nbaz\r\n0\r\n\r\n", 9)
testResponseReadLimitBodySuccess(t, "HTTP/1.1 200 OK\r\nContent-Type: aa\r\nTransfer-Encoding: chunked\r\n\r\n6\r\nfoobar\r\n3\r\nbaz\r\n0\r\nFoo: bar\r\n\r\n", 9)
testResponseReadLimitBodySuccess(t, "HTTP/1.1 200 OK\r\nContent-Type: aa\r\nTransfer-Encoding: chunked\r\n\r\n6\r\nfoobar\r\n3\r\nbaz\r\n0\r\n\r\n", 100)
testResponseReadLimitBodyError(t, "HTTP/1.1 200 OK\r\nContent-Type: aa\r\nTransfer-Encoding: chunked\r\n\r\n6\r\nfoobar\r\n3\r\nbaz\r\n0\r\n\r\n", 2)
testResponseReadLimitBodySuccess(t, "HTTP/1.1 200 OK\r\nContent-Type: aa\r\nTransfer-Encoding: chunked\r\n\r\n6\r\nfoobar\r\n3\r\nbaz\r\n0\r\nfoobar\r\n\r\n", 100)
testResponseReadLimitBodyError(t, "HTTP/1.1 200 OK\r\nContent-Type: aa\r\nTransfer-Encoding: chunked\r\n\r\n6\r\nfoobar\r\n3\r\nbaz\r\n0\r\n\r\n", 2, ErrBodyTooLarge)
// identity response
testResponseReadLimitBodySuccess(t, "HTTP/1.1 400 OK\r\nContent-Type: aa\r\n\r\n123456", 6)
testResponseReadLimitBodySuccess(t, "HTTP/1.1 400 OK\r\nContent-Type: aa\r\n\r\n123456", 106)
testResponseReadLimitBodyError(t, "HTTP/1.1 400 OK\r\nContent-Type: aa\r\n\r\n123456", 5)
testResponseReadLimitBodyError(t, "HTTP/1.1 400 OK\r\nContent-Type: aa\r\n\r\n123456", 5, ErrBodyTooLarge)
}
func TestRequestReadLimitBody(t *testing.T) {
@@ -1363,15 +1481,17 @@ func TestRequestReadLimitBody(t *testing.T) {
// request with content-length
testRequestReadLimitBodySuccess(t, "POST /foo HTTP/1.1\r\nHost: aaa.com\r\nContent-Length: 9\r\nContent-Type: aaa\r\n\r\n123456789", 9)
testRequestReadLimitBodySuccess(t, "POST /foo HTTP/1.1\r\nHost: aaa.com\r\nContent-Length: 9\r\nContent-Type: aaa\r\n\r\n123456789", 92)
testRequestReadLimitBodyError(t, "POST /foo HTTP/1.1\r\nHost: aaa.com\r\nContent-Length: 9\r\nContent-Type: aaa\r\n\r\n123456789", 5)
testRequestReadLimitBodyError(t, "POST /foo HTTP/1.1\r\nHost: aaa.com\r\nContent-Length: 9\r\nContent-Type: aaa\r\n\r\n123456789", 5, ErrBodyTooLarge)
// chunked request
testRequestReadLimitBodySuccess(t, "POST /a HTTP/1.1\r\nHost: a.com\r\nTransfer-Encoding: chunked\r\nContent-Type: aa\r\n\r\n6\r\nfoobar\r\n3\r\nbaz\r\n0\r\n\r\n", 9)
testRequestReadLimitBodySuccess(t, "POST /a HTTP/1.1\nHost: a.com\nTransfer-Encoding: chunked\nContent-Type: aa\r\n\r\n6\r\nfoobar\r\n3\r\nbaz\r\n0\r\nFoo: bar\r\n\r\n", 9)
testRequestReadLimitBodySuccess(t, "POST /a HTTP/1.1\r\nHost: a.com\r\nTransfer-Encoding: chunked\r\nContent-Type: aa\r\n\r\n6\r\nfoobar\r\n3\r\nbaz\r\n0\r\n\r\n", 999)
testRequestReadLimitBodyError(t, "POST /a HTTP/1.1\r\nHost: a.com\r\nTransfer-Encoding: chunked\r\nContent-Type: aa\r\n\r\n6\r\nfoobar\r\n3\r\nbaz\r\n0\r\n\r\n", 8)
testRequestReadLimitBodySuccess(t, "POST /a HTTP/1.1\r\nHost: a.com\r\nTransfer-Encoding: chunked\r\nContent-Type: aa\r\n\r\n6\r\nfoobar\r\n3\r\nbaz\r\n0\r\nfoobar\r\n\r\n", 999)
testRequestReadLimitBodyError(t, "POST /a HTTP/1.1\r\nHost: a.com\r\nTransfer-Encoding: chunked\r\nContent-Type: aa\r\n\r\n6\r\nfoobar\r\n3\r\nbaz\r\n0\r\n\r\n", 8, ErrBodyTooLarge)
}
func testResponseReadLimitBodyError(t *testing.T, s string, maxBodySize int) {
func testResponseReadLimitBodyError(t *testing.T, s string, maxBodySize int, expectedErr error) {
var req Response
r := bytes.NewBufferString(s)
br := bufio.NewReader(r)
@@ -1379,8 +1499,8 @@ func testResponseReadLimitBodyError(t *testing.T, s string, maxBodySize int) {
if err == nil {
t.Fatalf("expecting error. s=%q, maxBodySize=%d", s, maxBodySize)
}
if err != ErrBodyTooLarge {
t.Fatalf("unexpected error: %s. Expecting %s. s=%q, maxBodySize=%d", err, ErrBodyTooLarge, s, maxBodySize)
if err != expectedErr {
t.Fatalf("unexpected error: %s. Expecting %s. s=%q, maxBodySize=%d", err, expectedErr, s, maxBodySize)
}
}
@@ -1393,7 +1513,7 @@ func testResponseReadLimitBodySuccess(t *testing.T, s string, maxBodySize int) {
}
}
func testRequestReadLimitBodyError(t *testing.T, s string, maxBodySize int) {
func testRequestReadLimitBodyError(t *testing.T, s string, maxBodySize int, expectedErr error) {
var req Request
r := bytes.NewBufferString(s)
br := bufio.NewReader(r)
@@ -1401,8 +1521,8 @@ func testRequestReadLimitBodyError(t *testing.T, s string, maxBodySize int) {
if err == nil {
t.Fatalf("expecting error. s=%q, maxBodySize=%d", s, maxBodySize)
}
if err != ErrBodyTooLarge {
t.Fatalf("unexpected error: %s. Expecting %s. s=%q, maxBodySize=%d", err, ErrBodyTooLarge, s, maxBodySize)
if err != expectedErr {
t.Fatalf("unexpected error: %s. Expecting %s. s=%q, maxBodySize=%d", err, expectedErr, s, maxBodySize)
}
}
@@ -1490,52 +1610,49 @@ func TestRequestWriteRequestURINoHost(t *testing.T) {
func TestSetRequestBodyStreamFixedSize(t *testing.T) {
t.Parallel()
testSetRequestBodyStream(t, "a", false)
testSetRequestBodyStream(t, string(createFixedBody(4097)), false)
testSetRequestBodyStream(t, string(createFixedBody(100500)), false)
testSetRequestBodyStream(t, "a")
testSetRequestBodyStream(t, string(createFixedBody(4097)))
testSetRequestBodyStream(t, string(createFixedBody(100500)))
}
func TestSetResponseBodyStreamFixedSize(t *testing.T) {
t.Parallel()
testSetResponseBodyStream(t, "a", false)
testSetResponseBodyStream(t, string(createFixedBody(4097)), false)
testSetResponseBodyStream(t, string(createFixedBody(100500)), false)
testSetResponseBodyStream(t, "a")
testSetResponseBodyStream(t, string(createFixedBody(4097)))
testSetResponseBodyStream(t, string(createFixedBody(100500)))
}
func TestSetRequestBodyStreamChunked(t *testing.T) {
t.Parallel()
testSetRequestBodyStream(t, "", true)
testSetRequestBodyStreamChunked(t, "", map[string]string{"Foo": "bar"})
body := "foobar baz aaa bbb ccc"
testSetRequestBodyStream(t, body, true)
testSetRequestBodyStreamChunked(t, body, nil)
body = string(createFixedBody(10001))
testSetRequestBodyStream(t, body, true)
testSetRequestBodyStreamChunked(t, body, map[string]string{"Foo": "test", "Bar": "test"})
}
func TestSetResponseBodyStreamChunked(t *testing.T) {
t.Parallel()
testSetResponseBodyStream(t, "", true)
testSetResponseBodyStreamChunked(t, "", map[string]string{"Foo": "bar"})
body := "foobar baz aaa bbb ccc"
testSetResponseBodyStream(t, body, true)
testSetResponseBodyStreamChunked(t, body, nil)
body = string(createFixedBody(10001))
testSetResponseBodyStream(t, body, true)
testSetResponseBodyStreamChunked(t, body, map[string]string{"Foo": "test", "Bar": "test"})
}
func testSetRequestBodyStream(t *testing.T, body string, chunked bool) {
func testSetRequestBodyStream(t *testing.T, body string) {
var req Request
req.Header.SetHost("foobar.com")
req.Header.SetMethod(MethodPost)
bodySize := len(body)
if chunked {
bodySize = -1
}
if req.IsBodyStream() {
t.Fatalf("IsBodyStream must return false")
}
@@ -1563,12 +1680,56 @@ func testSetRequestBodyStream(t *testing.T, body string, chunked bool) {
}
}
func testSetResponseBodyStream(t *testing.T, body string, chunked bool) {
func testSetRequestBodyStreamChunked(t *testing.T, body string, trailer map[string]string) {
var req Request
req.Header.SetHost("foobar.com")
req.Header.SetMethod(MethodPost)
if req.IsBodyStream() {
t.Fatalf("IsBodyStream must return false")
}
req.SetBodyStream(bytes.NewBufferString(body), -1)
if !req.IsBodyStream() {
t.Fatalf("IsBodyStream must return true")
}
var w bytes.Buffer
bw := bufio.NewWriter(&w)
for k := range trailer {
err := req.Header.AddTrailer(k)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
}
if err := req.Write(bw); err != nil {
t.Fatalf("unexpected error when writing request: %s. body=%q", err, body)
}
for k, v := range trailer {
req.Header.Set(k, v)
}
if err := bw.Flush(); err != nil {
t.Fatalf("unexpected error when flushing request: %s. body=%q", err, body)
}
var req1 Request
br := bufio.NewReader(&w)
if err := req1.Read(br); err != nil {
t.Fatalf("unexpected error when reading request: %s. body=%q", err, body)
}
if string(req1.Body()) != body {
t.Fatalf("unexpected body %q. Expecting %q", req1.Body(), body)
}
for k, v := range trailer {
r := req.Header.Peek(k)
if string(r) != v {
t.Fatalf("unexpected trailer %s. Expecting %s. Got %q", k, v, r)
}
}
}
func testSetResponseBodyStream(t *testing.T, body string) {
var resp Response
bodySize := len(body)
if chunked {
bodySize = -1
}
if resp.IsBodyStream() {
t.Fatalf("IsBodyStream must return false")
}
@@ -1596,6 +1757,50 @@ func testSetResponseBodyStream(t *testing.T, body string, chunked bool) {
}
}
func testSetResponseBodyStreamChunked(t *testing.T, body string, trailer map[string]string) {
var resp Response
if resp.IsBodyStream() {
t.Fatalf("IsBodyStream must return false")
}
resp.SetBodyStream(bytes.NewBufferString(body), -1)
if !resp.IsBodyStream() {
t.Fatalf("IsBodyStream must return true")
}
var w bytes.Buffer
bw := bufio.NewWriter(&w)
for k := range trailer {
err := resp.Header.AddTrailer(k)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
}
if err := resp.Write(bw); err != nil {
t.Fatalf("unexpected error when writing response: %s. body=%q", err, body)
}
if err := bw.Flush(); err != nil {
t.Fatalf("unexpected error when flushing response: %s. body=%q", err, body)
}
for k, v := range trailer {
resp.Header.Set(k, v)
}
var resp1 Response
br := bufio.NewReader(&w)
if err := resp1.Read(br); err != nil {
t.Fatalf("unexpected error when reading response: %s. body=%q", err, body)
}
if string(resp1.Body()) != body {
t.Fatalf("unexpected body %q. Expecting %q", resp1.Body(), body)
}
for k, v := range trailer {
r := resp.Header.Peek(k)
if string(r) != v {
t.Fatalf("unexpected trailer %s. Expecting %s. Got %q", k, v, r)
}
}
}
func TestRound2(t *testing.T) {
t.Parallel()
@@ -1622,7 +1827,7 @@ func TestRequestReadChunked(t *testing.T) {
var req Request
s := "POST /foo HTTP/1.1\r\nHost: google.com\r\nTransfer-Encoding: chunked\r\nContent-Type: aa/bb\r\n\r\n3\r\nabc\r\n5\r\n12345\r\n0\r\n\r\ntrail"
s := "POST /foo HTTP/1.1\r\nHost: google.com\r\nTransfer-Encoding: chunked\r\nContent-Type: aa/bb\r\n\r\n3\r\nabc\r\n5\r\n12345\r\n0\r\n\r\nTrail: test\r\n\r\n"
r := bytes.NewBufferString(s)
rb := bufio.NewReader(r)
err := req.Read(rb)
@@ -1633,8 +1838,8 @@ func TestRequestReadChunked(t *testing.T) {
if string(req.Body()) != expectedBody {
t.Fatalf("Unexpected body %q. Expected %q", req.Body(), expectedBody)
}
verifyRequestHeader(t, &req.Header, 8, "/foo", "google.com", "", "aa/bb")
verifyTrailer(t, rb, "trail")
verifyRequestHeader(t, &req.Header, -1, "/foo", "google.com", "", "aa/bb")
verifyTrailer(t, rb, map[string]string{"Trail": "test"}, true)
}
// See: https://github.com/erikdubbelboer/fasthttp/issues/34
@@ -1661,25 +1866,25 @@ func TestResponseReadWithoutBody(t *testing.T) {
var resp Response
testResponseReadWithoutBody(t, &resp, "HTTP/1.1 304 Not Modified\r\nContent-Type: aa\r\nContent-Length: 1235\r\n\r\nfoobar", false,
304, 1235, "aa", "foobar")
testResponseReadWithoutBody(t, &resp, "HTTP/1.1 304 Not Modified\r\nContent-Type: aa\r\nContent-Length: 1235\r\n\r\n", false,
304, 1235, "aa", nil)
testResponseReadWithoutBody(t, &resp, "HTTP/1.1 204 Foo Bar\r\nContent-Type: aab\r\nTransfer-Encoding: chunked\r\n\r\n123\r\nss", false,
204, -1, "aab", "123\r\nss")
testResponseReadWithoutBody(t, &resp, "HTTP/1.1 204 Foo Bar\r\nContent-Type: aab\r\nTransfer-Encoding: chunked\r\n\r\n0\r\nFoo: bar\r\n\r\n", false,
204, -1, "aab", map[string]string{"Foo": "bar"})
testResponseReadWithoutBody(t, &resp, "HTTP/1.1 123 AAA\r\nContent-Type: xxx\r\nContent-Length: 3434\r\n\r\naaaa", false,
123, 3434, "xxx", "aaaa")
testResponseReadWithoutBody(t, &resp, "HTTP/1.1 123 AAA\r\nContent-Type: xxx\r\nContent-Length: 3434\r\n\r\n", false,
123, 3434, "xxx", nil)
testResponseReadWithoutBody(t, &resp, "HTTP 200 OK\r\nContent-Type: text/xml\r\nContent-Length: 123\r\n\r\nxxxx", true,
200, 123, "text/xml", "xxxx")
testResponseReadWithoutBody(t, &resp, "HTTP 200 OK\r\nContent-Type: text/xml\r\nContent-Length: 123\r\n\r\nfoobar\r\n", true,
200, 123, "text/xml", nil)
// '100 Continue' must be skipped.
testResponseReadWithoutBody(t, &resp, "HTTP/1.1 100 Continue\r\nFoo-bar: baz\r\n\r\nHTTP/1.1 329 aaa\r\nContent-Type: qwe\r\nContent-Length: 894\r\n\r\nfoobar", true,
329, 894, "qwe", "foobar")
testResponseReadWithoutBody(t, &resp, "HTTP/1.1 100 Continue\r\nFoo-bar: baz\r\n\r\nHTTP/1.1 329 aaa\r\nContent-Type: qwe\r\nContent-Length: 894\r\n\r\n", true,
329, 894, "qwe", nil)
}
func testResponseReadWithoutBody(t *testing.T, resp *Response, s string, skipBody bool,
expectedStatusCode, expectedContentLength int, expectedContentType, expectedTrailer string) {
expectedStatusCode, expectedContentLength int, expectedContentType string, expectedTrailer map[string]string) {
r := bytes.NewBufferString(s)
rb := bufio.NewReader(r)
resp.SkipBody = skipBody
@@ -1691,12 +1896,12 @@ func testResponseReadWithoutBody(t *testing.T, resp *Response, s string, skipBod
t.Fatalf("Unexpected response body %q. Expected %q. response=%q", resp.Body(), "", s)
}
verifyResponseHeader(t, &resp.Header, expectedStatusCode, expectedContentLength, expectedContentType)
verifyTrailer(t, rb, expectedTrailer)
verifyResponseTrailer(t, &resp.Header, expectedTrailer)
// verify that ordinal response is read after null-body response
resp.SkipBody = false
testResponseReadSuccess(t, resp, "HTTP/1.1 300 OK\r\nContent-Length: 5\r\nContent-Type: bar\r\n\r\n56789aaa",
300, 5, "bar", "56789", "aaa")
300, 5, "bar", "56789", nil)
}
func TestRequestSuccess(t *testing.T) {
@@ -1872,40 +2077,54 @@ func TestResponseReadSuccess(t *testing.T) {
// usual response
testResponseReadSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Length: 10\r\nContent-Type: foo/bar\r\n\r\n0123456789",
200, 10, "foo/bar", "0123456789", "")
200, 10, "foo/bar", "0123456789", nil)
// zero response
testResponseReadSuccess(t, resp, "HTTP/1.1 500 OK\r\nContent-Length: 0\r\nContent-Type: foo/bar\r\n\r\n",
500, 0, "foo/bar", "", "")
500, 0, "foo/bar", "", nil)
// response with trailer
testResponseReadSuccess(t, resp, "HTTP/1.1 300 OK\r\nContent-Length: 5\r\nContent-Type: bar\r\n\r\n56789aaa",
300, 5, "bar", "56789", "aaa")
testResponseReadSuccess(t, resp, "HTTP/1.1 300 OK\r\nTransfer-Encoding: chunked\r\nContent-Type: bar\r\n\r\n5\r\n56789\r\n0\r\nfoo: bar\r\n\r\n",
300, -1, "bar", "56789", map[string]string{"Foo": "bar"})
// no conent-length ('identity' transfer-encoding)
testResponseReadSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: foobar\r\n\r\nzxxc",
200, 4, "foobar", "zxxc", "")
// response with trailer disableNormalizing
resp.Header.DisableNormalizing()
testResponseReadSuccess(t, resp, "HTTP/1.1 300 OK\r\nTransfer-Encoding: chunked\r\nContent-Type: bar\r\n\r\n5\r\n56789\r\n0\r\nfoo: bar\r\n\r\n",
300, -1, "bar", "56789", map[string]string{"foo": "bar"})
// no content-length ('identity' transfer-encoding)
testResponseReadSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: foobar\r\n\r\nzxxxx",
200, 5, "foobar", "zxxxx", nil)
// explicitly stated 'Transfer-Encoding: identity'
testResponseReadSuccess(t, resp, "HTTP/1.1 234 ss\r\nContent-Type: xxx\r\n\r\nxag",
234, 3, "xxx", "xag", "")
234, 3, "xxx", "xag", nil)
// big 'identity' response
body := string(createFixedBody(100500))
testResponseReadSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: aa\r\n\r\n"+body,
200, 100500, "aa", body, "")
200, 100500, "aa", body, nil)
// chunked response
testResponseReadSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nTransfer-Encoding: chunked\r\n\r\n4\r\nqwer\r\n2\r\nty\r\n0\r\n\r\nzzzzz",
200, 6, "text/html", "qwerty", "zzzzz")
testResponseReadSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nTransfer-Encoding: chunked\r\n\r\n4\r\nqwer\r\n2\r\nty\r\n0\r\nFoo2: bar2\r\n\r\n",
200, -1, "text/html", "qwerty", map[string]string{"Foo2": "bar2"})
// chunked response with non-chunked Transfer-Encoding.
testResponseReadSuccess(t, resp, "HTTP/1.1 230 OK\r\nContent-Type: text\r\nTransfer-Encoding: aaabbb\r\n\r\n2\r\ner\r\n2\r\nty\r\n0\r\n\r\nwe",
230, 4, "text", "erty", "we")
testResponseReadSuccess(t, resp, "HTTP/1.1 230 OK\r\nContent-Type: text\r\nTransfer-Encoding: aaabbb\r\n\r\n2\r\ner\r\n2\r\nty\r\n0\r\nFoo3: bar3\r\n\r\n",
230, -1, "text", "erty", map[string]string{"Foo3": "bar3"})
// chunked response with content-length
testResponseReadSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: foo/bar\r\nContent-Length: 123\r\nTransfer-Encoding: chunked\r\n\r\n4\r\ntest\r\n0\r\nFoo4:bar4\r\n\r\n",
200, -1, "foo/bar", "test", map[string]string{"Foo4": "bar4"})
// chunked response with empty body
testResponseReadSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nTransfer-Encoding: chunked\r\n\r\n0\r\nFoo5: bar5\r\n\r\n",
200, -1, "text/html", "", map[string]string{"Foo5": "bar5"})
// chunked response with chunk extension
testResponseReadSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nTransfer-Encoding: chunked\r\n\r\n3;ext\r\naaa\r\n0\r\nFoo6: bar6\r\n\r\n",
200, -1, "text/html", "aaa", map[string]string{"Foo6": "bar6"})
// zero chunked response
testResponseReadSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nTransfer-Encoding: chunked\r\n\r\n0\r\n\r\nzzz",
200, 0, "text/html", "", "zzz")
}
func TestResponseReadError(t *testing.T) {
@@ -1922,8 +2141,13 @@ func TestResponseReadError(t *testing.T) {
// empty body
testResponseReadError(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: aaa\r\nContent-Length: 1234\r\n\r\n")
// short body
// invalid chunked body
testResponseReadError(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: aaa\r\nContent-Length: 1234\r\n\r\nshort")
// chunked body without end chunk
testResponseReadError(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: aaa\r\nTransfer-Encoding: chunked\r\n\r\nfoo")
testResponseReadError(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: aaa\r\nTransfer-Encoding: chunked\r\n\r\n3\r\nfoo")
}
func testResponseReadError(t *testing.T, resp *Response, response string) {
@@ -1934,12 +2158,12 @@ func testResponseReadError(t *testing.T, resp *Response, response string) {
t.Fatalf("Expecting error for response=%q", response)
}
testResponseReadSuccess(t, resp, "HTTP/1.1 303 Redisred sedfs sdf\r\nContent-Type: aaa\r\nContent-Length: 5\r\n\r\nHELLOaaa",
303, 5, "aaa", "HELLO", "aaa")
testResponseReadSuccess(t, resp, "HTTP/1.1 303 Redisred sedfs sdf\r\nContent-Type: aaa\r\nContent-Length: 5\r\n\r\nHELLO",
303, 5, "aaa", "HELLO", nil)
}
func testResponseReadSuccess(t *testing.T, resp *Response, response string, expectedStatusCode, expectedContentLength int,
expectedContenType, expectedBody, expectedTrailer string) {
expectedContentType, expectedBody string, expectedTrailer map[string]string) {
r := bytes.NewBufferString(response)
rb := bufio.NewReader(r)
@@ -1948,11 +2172,11 @@ func testResponseReadSuccess(t *testing.T, resp *Response, response string, expe
t.Fatalf("Unexpected error: %s", err)
}
verifyResponseHeader(t, &resp.Header, expectedStatusCode, expectedContentLength, expectedContenType)
verifyResponseHeader(t, &resp.Header, expectedStatusCode, expectedContentLength, expectedContentType)
if !bytes.Equal(resp.Body(), []byte(expectedBody)) {
t.Fatalf("Unexpected body %q. Expected %q", resp.Body(), []byte(expectedBody))
}
verifyTrailer(t, rb, expectedTrailer)
verifyResponseTrailer(t, &resp.Header, expectedTrailer)
}
func TestReadBodyFixedSize(t *testing.T) {
@@ -2109,28 +2333,24 @@ func testRequestPostArgsSuccess(t *testing.T, req *Request, s string, expectedAr
func testReadBodyChunked(t *testing.T, bodySize int) {
body := createFixedBody(bodySize)
chunkedBody := createChunkedBody(body)
expectedTrailer := []byte("chunked shit")
chunkedBody = append(chunkedBody, expectedTrailer...)
expectedTrailer := map[string]string{"Foo": "bar"}
chunkedBody := createChunkedBody(body, expectedTrailer, true)
r := bytes.NewBuffer(chunkedBody)
br := bufio.NewReader(r)
b, err := readBody(br, -1, 0, nil)
b, err := readBodyChunked(br, 0, nil)
if err != nil {
t.Fatalf("Unexpected error for bodySize=%d: %s. body=%q, chunkedBody=%q", bodySize, err, body, chunkedBody)
}
if !bytes.Equal(b, body) {
t.Fatalf("Unexpected response read for bodySize=%d: %q. Expected %q. chunkedBody=%q", bodySize, b, body, chunkedBody)
}
verifyTrailer(t, br, string(expectedTrailer))
verifyTrailer(t, br, expectedTrailer, false)
}
func testReadBodyFixedSize(t *testing.T, bodySize int) {
body := createFixedBody(bodySize)
expectedTrailer := []byte("traler aaaa")
bodyWithTrailer := append(body, expectedTrailer...)
r := bytes.NewBuffer(bodyWithTrailer)
r := bytes.NewBuffer(body)
br := bufio.NewReader(r)
b, err := readBody(br, bodySize, 0, nil)
if err != nil {
@@ -2139,7 +2359,7 @@ func testReadBodyFixedSize(t *testing.T, bodySize int) {
if !bytes.Equal(b, body) {
t.Fatalf("Unexpected response read for bodySize=%d: %q. Expected %q", bodySize, b, body)
}
verifyTrailer(t, br, string(expectedTrailer))
verifyTrailer(t, br, nil, false)
}
func createFixedBody(bodySize int) []byte {
@@ -2150,7 +2370,7 @@ func createFixedBody(bodySize int) []byte {
return b
}
func createChunkedBody(body []byte) []byte {
func createChunkedBody(body []byte, trailer map[string]string, withEnd bool) []byte {
var b []byte
chunkSize := 1
for len(body) > 0 {
@@ -2163,7 +2383,17 @@ func createChunkedBody(body []byte) []byte {
body = body[chunkSize:]
chunkSize++
}
return append(b, []byte("0\r\n\r\n")...)
if withEnd {
b = append(b, "0\r\n"...)
for k, v := range trailer {
b = append(b, k...)
b = append(b, ": "...)
b = append(b, v...)
b = append(b, "\r\n"...)
}
b = append(b, "\r\n"...)
}
return b
}
func TestWriteMultipartForm(t *testing.T) {
+62 -1
View File
@@ -3609,7 +3609,7 @@ func TestStreamRequestBodyExceedMaxSize(t *testing.T) {
}
}
func TestStreamBodyReqestContentLength(t *testing.T) {
func TestStreamBodyRequestContentLength(t *testing.T) {
t.Parallel()
content := strings.Repeat("1", 1<<15) // 32K
contentLength := len(content)
@@ -3784,6 +3784,67 @@ func TestIncompleteBodyReturnsUnexpectedEOF(t *testing.T) {
}
}
func TestServerChunkedResponse(t *testing.T) {
t.Parallel()
trailer := map[string]string{
"AtEnd1": "1111",
"AtEnd2": "2222",
"AtEnd3": "3333",
}
h := func(ctx *RequestCtx) {
ctx.Response.Header.DisableNormalizing()
ctx.Response.Header.Set("Transfer-Encoding", "chunked")
for k := range trailer {
err := ctx.Response.Header.AddTrailer(k)
if err != nil {
t.Errorf("unexpected error: %s", err)
}
}
ctx.Response.SetBodyStreamWriter(func(w *bufio.Writer) {
for i := 0; i < 3; i++ {
fmt.Fprintf(w, "message %d", i)
if err := w.Flush(); err != nil {
t.Errorf("unexpected error: %s", err)
}
time.Sleep(time.Second)
}
})
for k, v := range trailer {
ctx.Response.Header.Set(k, v)
}
}
s := &Server{
Handler: h,
}
rw := &readWriter{}
rw.r.WriteString("GET / HTTP/1.1\r\nHost: test.com\r\n\r\n")
if err := s.ServeConn(rw); err != nil {
t.Fatalf("Unexpected error from serveConn: %s", err)
}
br := bufio.NewReader(&rw.w)
var resp Response
if err := resp.Read(br); err != nil {
t.Fatalf("Unexpected error when reading response: %s", err)
}
if resp.Header.ContentLength() != -1 {
t.Fatalf("Unexpected Content-Length %d. Expected %d", resp.Header.ContentLength(), -1)
}
if !bytes.Equal(resp.Body(), []byte("message 0"+"message 1"+"message 2")) {
t.Fatalf("Unexpected body %q. Expected %q", resp.Body(), "foobar")
}
for k, v := range trailer {
h := resp.Header.Peek(k)
if !bytes.Equal(resp.Header.Peek(k), []byte(v)) {
t.Fatalf("Unexpected trailer %s. Expected %s. Got %q", k, v, h)
}
}
}
func verifyResponse(t *testing.T, r *bufio.Reader, expectedStatusCode int, expectedContentType, expectedBody string) *Response {
var resp Response
if err := resp.Read(r); err != nil {
+12 -13
View File
@@ -10,10 +10,10 @@ import (
)
type requestStream struct {
header *RequestHeader
prefetchedBytes *bytes.Reader
reader *bufio.Reader
totalBytesRead int
contentLength int
chunkLeft int
}
@@ -22,18 +22,18 @@ func (rs *requestStream) Read(p []byte) (int, error) {
n int
err error
)
if rs.contentLength == -1 {
if rs.header.contentLength == -1 {
if rs.chunkLeft == 0 {
chunkSize, err := parseChunkSize(rs.reader)
if err != nil {
return 0, err
}
if chunkSize == 0 {
err = readCrLf(rs.reader)
if err == nil {
err = io.EOF
err = rs.header.ReadTrailer(rs.reader)
if err != nil && err != io.EOF {
return 0, err
}
return 0, err
return 0, io.EOF
}
rs.chunkLeft = chunkSize
}
@@ -52,7 +52,7 @@ func (rs *requestStream) Read(p []byte) (int, error) {
}
return n, err
}
if rs.totalBytesRead == rs.contentLength {
if rs.totalBytesRead == rs.header.contentLength {
return 0, io.EOF
}
prefetchedSize := int(rs.prefetchedBytes.Size())
@@ -63,12 +63,12 @@ func (rs *requestStream) Read(p []byte) (int, error) {
}
n, err := rs.prefetchedBytes.Read(p)
rs.totalBytesRead += n
if n == rs.contentLength {
if n == rs.header.contentLength {
return n, io.EOF
}
return n, err
} else {
left := rs.contentLength - rs.totalBytesRead
left := rs.header.contentLength - rs.totalBytesRead
if len(p) > left {
p = p[:left]
}
@@ -79,18 +79,17 @@ func (rs *requestStream) Read(p []byte) (int, error) {
}
}
if rs.totalBytesRead == rs.contentLength {
if rs.totalBytesRead == rs.header.contentLength {
err = io.EOF
}
return n, err
}
func acquireRequestStream(b *bytebufferpool.ByteBuffer, r *bufio.Reader, contentLength int) *requestStream {
func acquireRequestStream(b *bytebufferpool.ByteBuffer, r *bufio.Reader, h *RequestHeader) *requestStream {
rs := requestStreamPool.Get().(*requestStream)
rs.prefetchedBytes = bytes.NewReader(b.B)
rs.reader = r
rs.contentLength = contentLength
rs.header = h
return rs
}
+66 -1
View File
@@ -3,6 +3,7 @@ package fasthttp
import (
"bufio"
"bytes"
"fmt"
"io/ioutil"
"sync"
"testing"
@@ -102,7 +103,7 @@ aaaaaaaaaa`
func getChunkedTestEnv(t testing.TB) (*fasthttputil.InmemoryListener, []byte) {
body := createFixedBody(128 * 1024)
chunkedBody := createChunkedBody(body)
chunkedBody := createChunkedBody(body, nil, true)
testHandler := func(ctx *RequestCtx) {
bodyBytes, err := ioutil.ReadAll(ctx.RequestBodyStream())
@@ -142,6 +143,70 @@ func getChunkedTestEnv(t testing.TB) (*fasthttputil.InmemoryListener, []byte) {
return ln, formattedRequest
}
func TestRequestStreamChunkedWithTrailer(t *testing.T) {
t.Parallel()
body := createFixedBody(10)
expectedTrailer := map[string]string{
"Foo": "footest",
"Bar": "bartest",
}
chunkedBody := createChunkedBody(body, expectedTrailer, true)
req := fmt.Sprintf(`POST / HTTP/1.1
Host: example.com
Transfer-Encoding: chunked
Trailer: Foo, Bar
%s
`, chunkedBody)
ln := fasthttputil.NewInmemoryListener()
s := &Server{
StreamRequestBody: true,
Handler: func(ctx *RequestCtx) {
all, err := ioutil.ReadAll(ctx.RequestBodyStream())
if err != nil {
t.Errorf("unexpected error: %s", err)
}
if !bytes.Equal(all, body) {
t.Errorf("unexpected body %q. Expecting %q", all, body)
}
for k, v := range expectedTrailer {
r := ctx.Request.Header.Peek(k)
if string(r) != v {
t.Errorf("unexpected trailer %s. Expecting %s. Got %q", k, v, r)
}
}
},
}
ch := make(chan struct{})
go func() {
if err := s.Serve(ln); err != nil {
t.Errorf("unexpected error: %s", err)
}
close(ch)
}()
conn, err := ln.Dial()
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if _, err = conn.Write([]byte(req)); err != nil {
t.Fatalf("unexpected error: %s", err)
}
if err := ln.Close(); err != nil {
t.Fatalf("error when closing listener: %s", err)
}
select {
case <-ch:
case <-time.After(time.Second):
t.Fatal("timeout when waiting for the server to stop")
}
}
func TestRequestStream(t *testing.T) {
t.Parallel()
+29 -21
View File
@@ -20,31 +20,39 @@ var (
strColon = []byte(":")
strColonSlashSlash = []byte("://")
strColonSpace = []byte(": ")
strCommaSpace = []byte(", ")
strGMT = []byte("GMT")
strResponseContinue = []byte("HTTP/1.1 100 Continue\r\n\r\n")
strExpect = []byte(HeaderExpect)
strConnection = []byte(HeaderConnection)
strContentLength = []byte(HeaderContentLength)
strContentType = []byte(HeaderContentType)
strDate = []byte(HeaderDate)
strHost = []byte(HeaderHost)
strReferer = []byte(HeaderReferer)
strServer = []byte(HeaderServer)
strTransferEncoding = []byte(HeaderTransferEncoding)
strContentEncoding = []byte(HeaderContentEncoding)
strAcceptEncoding = []byte(HeaderAcceptEncoding)
strUserAgent = []byte(HeaderUserAgent)
strCookie = []byte(HeaderCookie)
strSetCookie = []byte(HeaderSetCookie)
strLocation = []byte(HeaderLocation)
strIfModifiedSince = []byte(HeaderIfModifiedSince)
strLastModified = []byte(HeaderLastModified)
strAcceptRanges = []byte(HeaderAcceptRanges)
strRange = []byte(HeaderRange)
strContentRange = []byte(HeaderContentRange)
strAuthorization = []byte(HeaderAuthorization)
strExpect = []byte(HeaderExpect)
strConnection = []byte(HeaderConnection)
strContentLength = []byte(HeaderContentLength)
strContentType = []byte(HeaderContentType)
strDate = []byte(HeaderDate)
strHost = []byte(HeaderHost)
strReferer = []byte(HeaderReferer)
strServer = []byte(HeaderServer)
strTransferEncoding = []byte(HeaderTransferEncoding)
strContentEncoding = []byte(HeaderContentEncoding)
strAcceptEncoding = []byte(HeaderAcceptEncoding)
strUserAgent = []byte(HeaderUserAgent)
strCookie = []byte(HeaderCookie)
strSetCookie = []byte(HeaderSetCookie)
strLocation = []byte(HeaderLocation)
strIfModifiedSince = []byte(HeaderIfModifiedSince)
strLastModified = []byte(HeaderLastModified)
strAcceptRanges = []byte(HeaderAcceptRanges)
strRange = []byte(HeaderRange)
strContentRange = []byte(HeaderContentRange)
strAuthorization = []byte(HeaderAuthorization)
strTE = []byte(HeaderTE)
strTrailer = []byte(HeaderTrailer)
strMaxForwards = []byte(HeaderMaxForwards)
strProxyConnection = []byte(HeaderProxyConnection)
strProxyAuthenticate = []byte(HeaderProxyAuthenticate)
strProxyAuthorization = []byte(HeaderProxyAuthorization)
strWWWAuthenticate = []byte(HeaderWWWAuthenticate)
strCookieExpires = []byte("expires")
strCookieDomain = []byte("domain")