Skip to content

Commit

Permalink
add DisableSpecialHeaders option (#1573)
Browse files Browse the repository at this point in the history
* add DisableSpecialHeaders option

* polishing up disableSpecialHeader option

* forgot to uncomment

* fix silly mistakes

* dont parse special headers
  • Loading branch information
antter committed Jun 12, 2023
1 parent f0865d4 commit b79233f
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 7 deletions.
46 changes: 39 additions & 7 deletions header.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ type RequestHeader struct {
noHTTP11 bool
connectionClose bool
noDefaultContentType bool
disableSpecialHeader bool

// These two fields have been moved close to other bool fields
// for reducing RequestHeader object size.
Expand Down Expand Up @@ -360,6 +361,9 @@ func (h *ResponseHeader) SetServerBytes(server []byte) {

// ContentType returns Content-Type header value.
func (h *RequestHeader) ContentType() []byte {
if h.disableSpecialHeader {
return peekArgBytes(h.h, []byte(HeaderContentType))
}
return h.contentType
}

Expand Down Expand Up @@ -573,6 +577,9 @@ func (h *RequestHeader) MultipartFormBoundary() []byte {

// Host returns Host header value.
func (h *RequestHeader) Host() []byte {
if h.disableSpecialHeader {
return peekArgBytes(h.h, []byte(HeaderHost))
}
return h.host
}

Expand All @@ -588,6 +595,9 @@ func (h *RequestHeader) SetHostBytes(host []byte) {

// UserAgent returns User-Agent header value.
func (h *RequestHeader) UserAgent() []byte {
if h.disableSpecialHeader {
return peekArgBytes(h.h, []byte(HeaderUserAgent))
}
return h.userAgent
}

Expand Down Expand Up @@ -882,6 +892,23 @@ func (h *RequestHeader) Len() int {
return n
}

// DisableSpecialHeader disables special header processing.
// fasthttp will not set any special headers for you, such as Host, Content-Type, User-Agent, etc.
// You must set everything yourself.
// If RequestHeader.Read() is called, special headers will be ignored.
// This can be used to control case and order of special headers.
// This is generally not recommended.
func (h *RequestHeader) DisableSpecialHeader() {
h.disableSpecialHeader = true
}

// EnableSpecialHeader enables special header processing.
// fasthttp will send Host, Content-Type, User-Agent, etc headers for you.
// This is suggested and enabled by default.
func (h *RequestHeader) EnableSpecialHeader() {
h.disableSpecialHeader = false
}

// DisableNormalizing disables header names' normalization.
//
// By default all the header names are normalized by uppercasing
Expand Down Expand Up @@ -1316,7 +1343,7 @@ 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 {
if len(key) == 0 {
if len(key) == 0 || h.disableSpecialHeader {
return false
}

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

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

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

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

Expand All @@ -2513,14 +2540,14 @@ func (h *RequestHeader) AppendBytes(dst []byte) []byte {
// 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 {
if n > 0 && !h.disableSpecialHeader {
dst = append(dst, strCookie...)
dst = append(dst, strColonSpace...)
dst = appendRequestCookieBytes(dst, h.cookies)
dst = append(dst, strCRLF...)
}

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

Expand Down Expand Up @@ -2904,6 +2931,11 @@ func (h *RequestHeader) parseHeaders(buf []byte) (int, error) {
continue
}

if h.disableSpecialHeader {
h.h = appendArgBytes(h.h, s.key, s.value, argsHasValue)
continue
}

switch s.key[0] | 0x20 {
case 'h':
if caseInsensitiveCompare(s.key, strHost) {
Expand Down
29 changes: 29 additions & 0 deletions header_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,35 @@ func TestRequestRawHeaders(t *testing.T) {
})
}

func TestRequestDisableSpecialHeaders(t *testing.T) {
t.Parallel()

kvs := "Host: foobar\r\n" +
"User-Agent: ua\r\n" +
"Non-Special: val\r\n" +
"\r\n"

var h RequestHeader
h.DisableSpecialHeader()

s := "GET / HTTP/1.0\r\n" + kvs
br := bufio.NewReader(bytes.NewBufferString(s))
if err := h.Read(br); err != nil {
t.Fatalf("unexpected error: %v", err)
}
// assert order of all headers preserved
if h.String() != s {
t.Fatalf("Headers not equal: %q. Expecting %q", h.String(), s)
}
h.SetCanonical([]byte("host"), []byte("notfoobar"))
if string(h.Host()) != "foobar" {
t.Fatalf("unexpected: %q. Expecting %q", h.Host(), "foobar")
}
if h.String() != "GET / HTTP/1.0\r\nHost: foobar\r\nUser-Agent: ua\r\nNon-Special: val\r\nhost: notfoobar\r\n\r\n" {
t.Fatalf("custom special header ordering failed: %q", h.String())
}
}

func TestRequestHeaderSetCookieWithSpecialChars(t *testing.T) {
t.Parallel()

Expand Down

0 comments on commit b79233f

Please sign in to comment.