Skip to content

Commit

Permalink
client: retain order of headers in requests as specified
Browse files Browse the repository at this point in the history
  • Loading branch information
Fusl committed Mar 15, 2023
1 parent 7846101 commit 89f39ea
Showing 1 changed file with 108 additions and 65 deletions.
173 changes: 108 additions & 65 deletions header.go
Expand Up @@ -82,6 +82,14 @@ type RequestHeader struct {
userAgent []byte
mulHeader [][]byte

explicitHost bool
explicitUserAgent bool
explicitContentType bool
explicitContentLength bool
explicitCookies bool
explicitTrailer bool
explicitConnection bool

h []argsKV
trailer []argsKV
bufKV argsKV
Expand Down Expand Up @@ -1134,33 +1142,47 @@ func (h *RequestHeader) VisitAllCookie(f func(key, value []byte)) {
//
// To get the headers in order they were received use VisitAllInOrder.
func (h *RequestHeader) VisitAll(f func(key, value []byte)) {
host := h.Host()
if len(host) > 0 {
f(strHost, host)
if !h.explicitHost {
host := h.Host()
if len(host) > 0 {
f(strHost, host)
}
}
if len(h.contentLengthBytes) > 0 {
f(strContentLength, h.contentLengthBytes)
if !h.explicitContentLength {
if len(h.contentLengthBytes) > 0 {
f(strContentLength, h.contentLengthBytes)
}
}
contentType := h.ContentType()
if len(contentType) > 0 {
f(strContentType, contentType)
if !h.explicitContentType {
contentType := h.ContentType()
if len(contentType) > 0 {
f(strContentType, contentType)
}
}
userAgent := h.UserAgent()
if len(userAgent) > 0 {
f(strUserAgent, userAgent)
if !h.explicitUserAgent {
userAgent := h.UserAgent()
if len(userAgent) > 0 {
f(strUserAgent, userAgent)
}
}
if len(h.trailer) > 0 {
f(strTrailer, appendArgsKeyBytes(nil, h.trailer, strCommaSpace))
if !h.explicitTrailer {
if len(h.trailer) > 0 {
f(strTrailer, appendArgsKeyBytes(nil, h.trailer, strCommaSpace))
}
}

h.collectCookies()
if len(h.cookies) > 0 {
h.bufKV.value = appendRequestCookieBytes(h.bufKV.value[:0], h.cookies)
f(strCookie, h.bufKV.value)
if !h.explicitCookies {
h.collectCookies()
if len(h.cookies) > 0 {
h.bufKV.value = appendRequestCookieBytes(h.bufKV.value[:0], h.cookies)
f(strCookie, h.bufKV.value)
}
}
visitArgs(h.h, f)
if h.ConnectionClose() {
f(strConnection, strClose)
if !h.explicitConnection {
if h.ConnectionClose() {
f(strConnection, strClose)
}
}
}

Expand Down Expand Up @@ -1233,24 +1255,31 @@ func (h *RequestHeader) del(key []byte) {
switch string(key) {
case HeaderHost:
h.host = h.host[:0]
h.explicitHost = false
case HeaderContentType:
h.contentType = h.contentType[:0]
h.explicitContentType = false
case HeaderUserAgent:
h.userAgent = h.userAgent[:0]
h.explicitUserAgent = false
case HeaderCookie:
h.cookies = h.cookies[:0]
h.explicitCookies = false
case HeaderContentLength:
h.contentLength = 0
h.contentLengthBytes = h.contentLengthBytes[:0]
h.explicitContentLength = false
case HeaderConnection:
h.connectionClose = false
h.explicitConnection = false
case HeaderTrailer:
h.trailer = h.trailer[:0]
h.explicitTrailer = false
}
h.h = delAllArgsBytes(h.h, key)
}

// setSpecialHeader handles special headers and return true when a header is processed.
// setSpecialHeader handles special headers.
func (h *ResponseHeader) setSpecialHeader(key, value []byte) bool {
if len(key) == 0 {
return false
Expand Down Expand Up @@ -1314,56 +1343,61 @@ func (h *ResponseHeader) setNonSpecial(key []byte, value []byte) {
}

// setSpecialHeader handles special headers and return true when a header is processed.
func (h *RequestHeader) setSpecialHeader(key, value []byte) bool {
func (h *RequestHeader) setSpecialHeader(key, value []byte) {
if len(key) == 0 {
return false
return
}

switch key[0] | 0x20 {
case 'c':
if caseInsensitiveCompare(strContentType, key) {
h.SetContentTypeBytes(value)
return true
h.explicitContentType = true
return
} else if caseInsensitiveCompare(strContentLength, key) {
if contentLength, err := parseContentLength(value); err == nil {
h.contentLength = contentLength
h.contentLengthBytes = append(h.contentLengthBytes[:0], value...)
h.explicitContentLength = true
}
return true
return
} else if caseInsensitiveCompare(strConnection, key) {
if bytes.Equal(strClose, value) {
h.SetConnectionClose()
} else {
h.ResetConnectionClose()
h.setNonSpecial(key, value)
}
return true
h.explicitConnection = true
return
} else if caseInsensitiveCompare(strCookie, key) {
h.collectCookies()
h.cookies = parseRequestCookies(h.cookies, value)
return true
h.explicitCookies = true
return
}
case 't':
if caseInsensitiveCompare(strTransferEncoding, key) {
// Transfer-Encoding is managed automatically.
return true
return
} else if caseInsensitiveCompare(strTrailer, key) {
_ = h.SetTrailerBytes(value)
return true
h.explicitTrailer = true
return
}
case 'h':
if caseInsensitiveCompare(strHost, key) {
h.SetHostBytes(value)
return true
h.explicitHost = true
return
}
case 'u':
if caseInsensitiveCompare(strUserAgent, key) {
h.SetUserAgentBytes(value)
return true
h.explicitUserAgent = true
return
}
}

return false
}

// setNonSpecial directly put into map i.e. not a basic header
Expand Down Expand Up @@ -1639,10 +1673,7 @@ func (h *RequestHeader) AddBytesV(key string, value []byte) {
// 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
}

h.setSpecialHeader(key, value)
k := getHeaderKeyBytes(&h.bufKV, b2s(key), h.disableNormalizing)
h.h = appendArgBytes(h.h, k, value, argsHasValue)
}
Expand Down Expand Up @@ -1698,9 +1729,7 @@ func (h *RequestHeader) SetBytesKV(key, value []byte) {
// 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
}
h.setSpecialHeader(key, value)
h.setNonSpecial(key, value)
}

Expand Down Expand Up @@ -2469,25 +2498,33 @@ func (h *RequestHeader) AppendBytes(dst []byte) []byte {
dst = append(dst, h.Protocol()...)
dst = append(dst, strCRLF...)

userAgent := h.UserAgent()
if len(userAgent) > 0 {
dst = appendHeaderLine(dst, strUserAgent, userAgent)
if !h.explicitUserAgent {
userAgent := h.UserAgent()
if len(userAgent) > 0 {
dst = appendHeaderLine(dst, strUserAgent, userAgent)
}
}

host := h.Host()
if len(host) > 0 {
dst = appendHeaderLine(dst, strHost, host)
if !h.explicitHost {
host := h.Host()
if len(host) > 0 {
dst = appendHeaderLine(dst, strHost, host)
}
}

contentType := h.ContentType()
if !h.noDefaultContentType && len(contentType) == 0 && !h.ignoreBody() {
contentType = strDefaultContentType
}
if len(contentType) > 0 {
dst = appendHeaderLine(dst, strContentType, contentType)
if !h.explicitContentType {
contentType := h.ContentType()
if !h.noDefaultContentType && len(contentType) == 0 && !h.ignoreBody() {
contentType = strDefaultContentType
}
if len(contentType) > 0 {
dst = appendHeaderLine(dst, strContentType, contentType)
}
}
if len(h.contentLengthBytes) > 0 {
dst = appendHeaderLine(dst, strContentLength, h.contentLengthBytes)
if !h.explicitContentLength {
if len(h.contentLengthBytes) > 0 {
dst = appendHeaderLine(dst, strContentLength, h.contentLengthBytes)
}
}

for i, n := 0, len(h.h); i < n; i++ {
Expand All @@ -2505,22 +2542,28 @@ func (h *RequestHeader) AppendBytes(dst []byte) []byte {
}
}

if len(h.trailer) > 0 {
dst = appendHeaderLine(dst, strTrailer, appendArgsKeyBytes(nil, h.trailer, strCommaSpace))
if !h.explicitTrailer {
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,
// they all are located in h.h.
n := len(h.cookies)
if n > 0 {
dst = append(dst, strCookie...)
dst = append(dst, strColonSpace...)
dst = appendRequestCookieBytes(dst, h.cookies)
dst = append(dst, strCRLF...)
if !h.explicitCookies {
// there is no need in h.collectCookies() here, since if cookies aren't collected yet,
// they all are located in h.h.
n := len(h.cookies)
if n > 0 {
dst = append(dst, strCookie...)
dst = append(dst, strColonSpace...)
dst = appendRequestCookieBytes(dst, h.cookies)
dst = append(dst, strCRLF...)
}
}

if h.ConnectionClose() {
dst = appendHeaderLine(dst, strConnection, strClose)
if !h.explicitConnection {
if h.ConnectionClose() {
dst = appendHeaderLine(dst, strConnection, strClose)
}
}

return append(dst, strCRLF...)
Expand Down

0 comments on commit 89f39ea

Please sign in to comment.