diff options
author | ichx <czn16@qq.com> | 2021-12-05 21:11:51 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-12-05 14:11:51 +0100 |
commit | da7ff7a2080953e370e8bd712f5ae8ef8b0cb4e1 (patch) | |
tree | d361832c2ab7da262ce69a646377cf3301968cca /header.go | |
parent | fix: reset request after reset user values on keep-alive connections (#1162) (diff) | |
download | fasthttp-da7ff7a2080953e370e8bd712f5ae8ef8b0cb4e1.tar.gz fasthttp-da7ff7a2080953e370e8bd712f5ae8ef8b0cb4e1.tar.bz2 fasthttp-da7ff7a2080953e370e8bd712f5ae8ef8b0cb4e1.zip |
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
Diffstat (limited to 'header.go')
-rw-r--r-- | header.go | 642 |
1 files changed, 633 insertions, 9 deletions
@@ -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") |