Skip to content

Commit 01d533a

Browse files
Fix RequestHeader.ContentLength() if disableSpecialHeader is true (#2042)
1 parent a1c842f commit 01d533a

File tree

2 files changed

+112
-1
lines changed

2 files changed

+112
-1
lines changed

header.go

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,33 @@ func (h *ResponseHeader) PeekCookie(key string) []byte {
194194
// It may be negative:
195195
// -1 means Transfer-Encoding: chunked.
196196
// -2 means Transfer-Encoding: identity.
197-
func (h *header) ContentLength() int {
197+
func (h *ResponseHeader) ContentLength() int {
198+
return h.contentLength
199+
}
200+
201+
// ContentLength returns Content-Length header value.
202+
//
203+
// It may be negative:
204+
// -1 means Transfer-Encoding: chunked.
205+
// -2 means Transfer-Encoding: identity.
206+
func (h *RequestHeader) ContentLength() int {
207+
if h.disableSpecialHeader {
208+
// Parse Content-Length from raw headers when special headers are disabled
209+
v := peekArgBytes(h.h, strContentLength)
210+
if len(v) == 0 {
211+
// Check for Transfer-Encoding: chunked
212+
te := peekArgBytes(h.h, strTransferEncoding)
213+
if bytes.Equal(te, strChunked) {
214+
return -1 // chunked
215+
}
216+
return -2 // identity
217+
}
218+
n, err := parseContentLength(v)
219+
if err != nil {
220+
return -2 // identity on parse error
221+
}
222+
return n
223+
}
198224
return h.contentLength
199225
}
200226

header_test.go

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,7 @@ func TestRequestRawHeaders(t *testing.T) {
491491
func TestRequestDisableSpecialHeaders(t *testing.T) {
492492
t.Parallel()
493493

494+
// Test original header functionality
494495
kvs := "Host: foobar\r\n" +
495496
"User-Agent: ua\r\n" +
496497
"Non-Special: val\r\n" +
@@ -515,6 +516,90 @@ func TestRequestDisableSpecialHeaders(t *testing.T) {
515516
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" {
516517
t.Fatalf("custom special header ordering failed: %q", h.String())
517518
}
519+
520+
// Test body parsing with DisableSpecialHeader - should work correctly after fix
521+
testBody := "a=b&test=123"
522+
rawRequest := "POST /test HTTP/1.1\r\n" +
523+
"Host: example.com\r\n" +
524+
"Content-Type: application/x-www-form-urlencoded\r\n" +
525+
"Content-Length: " + strconv.Itoa(len(testBody)) + "\r\n" +
526+
"\r\n" +
527+
testBody
528+
529+
var req Request
530+
req.Header.DisableSpecialHeader()
531+
532+
br2 := bufio.NewReader(bytes.NewBufferString(rawRequest))
533+
if err := req.ReadLimitBody(br2, 0); err != nil {
534+
t.Fatalf("unexpected error reading request: %v", err)
535+
}
536+
537+
// Verify Content-Length is correctly parsed with DisableSpecialHeader
538+
if req.Header.ContentLength() != len(testBody) {
539+
t.Fatalf("ContentLength() incorrect with DisableSpecialHeader: got %d, expected %d",
540+
req.Header.ContentLength(), len(testBody))
541+
}
542+
543+
// Verify body is preserved with DisableSpecialHeader
544+
if string(req.Body()) != testBody {
545+
t.Fatalf("body content incorrect with DisableSpecialHeader: got %q, expected %q",
546+
string(req.Body()), testBody)
547+
}
548+
}
549+
550+
func TestRequestDisableSpecialHeadersChunked(t *testing.T) {
551+
t.Parallel()
552+
553+
testBody := "chunked-test"
554+
rawRequest := "POST /test HTTP/1.1\r\n" +
555+
"Host: example.com\r\n" +
556+
"Transfer-Encoding: chunked\r\n" +
557+
"\r\n" +
558+
"c\r\n" +
559+
testBody + "\r\n" +
560+
"0\r\n\r\n"
561+
562+
var req Request
563+
req.Header.DisableSpecialHeader()
564+
565+
br := bufio.NewReader(bytes.NewBufferString(rawRequest))
566+
if err := req.ReadLimitBody(br, 0); err != nil {
567+
t.Fatalf("unexpected error reading chunked request: %v", err)
568+
}
569+
570+
// Verify chunked encoding is detected
571+
if req.Header.ContentLength() != -1 {
572+
t.Fatalf("chunked encoding not detected with DisableSpecialHeader: got %d, expected -1",
573+
req.Header.ContentLength())
574+
}
575+
576+
// Verify chunked body is preserved
577+
if string(req.Body()) != testBody {
578+
t.Fatalf("chunked body incorrect with DisableSpecialHeader: got %q, expected %q",
579+
string(req.Body()), testBody)
580+
}
581+
}
582+
583+
func TestRequestDisableSpecialHeadersIdentity(t *testing.T) {
584+
t.Parallel()
585+
586+
rawRequest := "GET /test HTTP/1.1\r\n" +
587+
"Host: example.com\r\n" +
588+
"\r\n"
589+
590+
var req Request
591+
req.Header.DisableSpecialHeader()
592+
593+
br := bufio.NewReader(bytes.NewBufferString(rawRequest))
594+
if err := req.ReadLimitBody(br, 0); err != nil {
595+
t.Fatalf("unexpected error reading identity request: %v", err)
596+
}
597+
598+
// Verify identity encoding is detected
599+
if req.Header.ContentLength() != -2 {
600+
t.Fatalf("identity encoding not detected with DisableSpecialHeader: got %d, expected -2",
601+
req.Header.ContentLength())
602+
}
518603
}
519604

520605
func TestRequestHeaderSetCookieWithSpecialChars(t *testing.T) {

0 commit comments

Comments
 (0)