aboutsummaryrefslogtreecommitdiff
path: root/header.go
diff options
context:
space:
mode:
authorGravatar ichx <czn16@qq.com> 2021-12-05 21:11:51 +0800
committerGravatar GitHub <noreply@github.com> 2021-12-05 14:11:51 +0100
commitda7ff7a2080953e370e8bd712f5ae8ef8b0cb4e1 (patch)
treed361832c2ab7da262ce69a646377cf3301968cca /header.go
parentfix: reset request after reset user values on keep-alive connections (#1162) (diff)
downloadfasthttp-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.go642
1 files changed, 633 insertions, 9 deletions
diff --git a/header.go b/header.go
index c14532c..105f0c3 100644
--- a/header.go
+++ b/header.go
@@ -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")