@@ -425,11 +425,11 @@ type transferReader struct {
425
425
ProtoMajor int
426
426
ProtoMinor int
427
427
// Output
428
- Body io.ReadCloser
429
- ContentLength int64
430
- TransferEncoding [] string
431
- Close bool
432
- Trailer Header
428
+ Body io.ReadCloser
429
+ ContentLength int64
430
+ Chunked bool
431
+ Close bool
432
+ Trailer Header
433
433
}
434
434
435
435
func (t * transferReader ) protoAtLeast (m , n int ) bool {
@@ -501,13 +501,12 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err error) {
501
501
t .ProtoMajor , t .ProtoMinor = 1 , 1
502
502
}
503
503
504
- // Transfer encoding, content length
505
- err = t .fixTransferEncoding ()
506
- if err != nil {
504
+ // Transfer-Encoding: chunked, and overriding Content-Length.
505
+ if err := t .parseTransferEncoding (); err != nil {
507
506
return err
508
507
}
509
508
510
- realLength , err := fixLength (isResponse , t .StatusCode , t .RequestMethod , t .Header , t .TransferEncoding )
509
+ realLength , err := fixLength (isResponse , t .StatusCode , t .RequestMethod , t .Header , t .Chunked )
511
510
if err != nil {
512
511
return err
513
512
}
@@ -522,7 +521,7 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err error) {
522
521
}
523
522
524
523
// Trailer
525
- t .Trailer , err = fixTrailer (t .Header , t .TransferEncoding )
524
+ t .Trailer , err = fixTrailer (t .Header , t .Chunked )
526
525
if err != nil {
527
526
return err
528
527
}
@@ -532,9 +531,7 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err error) {
532
531
// See RFC 7230, section 3.3.
533
532
switch msg .(type ) {
534
533
case * Response :
535
- if realLength == - 1 &&
536
- ! chunked (t .TransferEncoding ) &&
537
- bodyAllowedForStatus (t .StatusCode ) {
534
+ if realLength == - 1 && ! t .Chunked && bodyAllowedForStatus (t .StatusCode ) {
538
535
// Unbounded body.
539
536
t .Close = true
540
537
}
@@ -543,7 +540,7 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err error) {
543
540
// Prepare body reader. ContentLength < 0 means chunked encoding
544
541
// or close connection when finished, since multipart is not supported yet
545
542
switch {
546
- case chunked ( t . TransferEncoding ) :
543
+ case t . Chunked :
547
544
if noResponseBodyExpected (t .RequestMethod ) || ! bodyAllowedForStatus (t .StatusCode ) {
548
545
t .Body = NoBody
549
546
} else {
@@ -569,13 +566,17 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err error) {
569
566
case * Request :
570
567
rr .Body = t .Body
571
568
rr .ContentLength = t .ContentLength
572
- rr .TransferEncoding = t .TransferEncoding
569
+ if t .Chunked {
570
+ rr .TransferEncoding = []string {"chunked" }
571
+ }
573
572
rr .Close = t .Close
574
573
rr .Trailer = t .Trailer
575
574
case * Response :
576
575
rr .Body = t .Body
577
576
rr .ContentLength = t .ContentLength
578
- rr .TransferEncoding = t .TransferEncoding
577
+ if t .Chunked {
578
+ rr .TransferEncoding = []string {"chunked" }
579
+ }
579
580
rr .Close = t .Close
580
581
rr .Trailer = t .Trailer
581
582
}
@@ -605,8 +606,8 @@ func isUnsupportedTEError(err error) bool {
605
606
return ok
606
607
}
607
608
608
- // fixTransferEncoding sanitizes t.TransferEncoding, if needed .
609
- func (t * transferReader ) fixTransferEncoding () error {
609
+ // parseTransferEncoding sets t.Chunked based on the Transfer-Encoding header .
610
+ func (t * transferReader ) parseTransferEncoding () error {
610
611
raw , present := t .Header ["Transfer-Encoding" ]
611
612
if ! present {
612
613
return nil
@@ -618,56 +619,38 @@ func (t *transferReader) fixTransferEncoding() error {
618
619
return nil
619
620
}
620
621
621
- encodings := strings .Split (raw [0 ], "," )
622
- te := make ([]string , 0 , len (encodings ))
623
- // TODO: Even though we only support "identity" and "chunked"
624
- // encodings, the loop below is designed with foresight. One
625
- // invariant that must be maintained is that, if present,
626
- // chunked encoding must always come first.
627
- for _ , encoding := range encodings {
628
- encoding = strings .ToLower (strings .TrimSpace (encoding ))
629
- // "identity" encoding is not recorded
630
- if encoding == "identity" {
631
- break
632
- }
633
- if encoding != "chunked" {
634
- return & unsupportedTEError {fmt .Sprintf ("unsupported transfer encoding: %q" , encoding )}
635
- }
636
- te = te [0 : len (te )+ 1 ]
637
- te [len (te )- 1 ] = encoding
638
- }
639
- if len (te ) > 1 {
640
- return badStringError ("too many transfer encodings" , strings .Join (te , "," ))
641
- }
642
- if len (te ) > 0 {
643
- // RFC 7230 3.3.2 says "A sender MUST NOT send a
644
- // Content-Length header field in any message that
645
- // contains a Transfer-Encoding header field."
646
- //
647
- // but also:
648
- // "If a message is received with both a
649
- // Transfer-Encoding and a Content-Length header
650
- // field, the Transfer-Encoding overrides the
651
- // Content-Length. Such a message might indicate an
652
- // attempt to perform request smuggling (Section 9.5)
653
- // or response splitting (Section 9.4) and ought to be
654
- // handled as an error. A sender MUST remove the
655
- // received Content-Length field prior to forwarding
656
- // such a message downstream."
657
- //
658
- // Reportedly, these appear in the wild.
659
- delete (t .Header , "Content-Length" )
660
- t .TransferEncoding = te
661
- return nil
622
+ // Like nginx, we only support a single Transfer-Encoding header field, and
623
+ // only if set to "chunked". This is one of the most security sensitive
624
+ // surfaces in HTTP/1.1 due to the risk of request smuggling, so we keep it
625
+ // strict and simple.
626
+ if len (raw ) != 1 {
627
+ return & unsupportedTEError {fmt .Sprintf ("too many transfer encodings: %q" , raw )}
628
+ }
629
+ if strings .ToLower (textproto .TrimString (raw [0 ])) != "chunked" {
630
+ return & unsupportedTEError {fmt .Sprintf ("unsupported transfer encoding: %q" , raw [0 ])}
662
631
}
663
632
633
+ // RFC 7230 3.3.2 says "A sender MUST NOT send a Content-Length header field
634
+ // in any message that contains a Transfer-Encoding header field."
635
+ //
636
+ // but also: "If a message is received with both a Transfer-Encoding and a
637
+ // Content-Length header field, the Transfer-Encoding overrides the
638
+ // Content-Length. Such a message might indicate an attempt to perform
639
+ // request smuggling (Section 9.5) or response splitting (Section 9.4) and
640
+ // ought to be handled as an error. A sender MUST remove the received
641
+ // Content-Length field prior to forwarding such a message downstream."
642
+ //
643
+ // Reportedly, these appear in the wild.
644
+ delete (t .Header , "Content-Length" )
645
+
646
+ t .Chunked = true
664
647
return nil
665
648
}
666
649
667
650
// Determine the expected body length, using RFC 7230 Section 3.3. This
668
651
// function is not a method, because ultimately it should be shared by
669
652
// ReadResponse and ReadRequest.
670
- func fixLength (isResponse bool , status int , requestMethod string , header Header , te [] string ) (int64 , error ) {
653
+ func fixLength (isResponse bool , status int , requestMethod string , header Header , chunked bool ) (int64 , error ) {
671
654
isRequest := ! isResponse
672
655
contentLens := header ["Content-Length" ]
673
656
@@ -711,7 +694,7 @@ func fixLength(isResponse bool, status int, requestMethod string, header Header,
711
694
}
712
695
713
696
// Logic based on Transfer-Encoding
714
- if chunked ( te ) {
697
+ if chunked {
715
698
return - 1 , nil
716
699
}
717
700
@@ -766,12 +749,12 @@ func shouldClose(major, minor int, header Header, removeCloseHeader bool) bool {
766
749
}
767
750
768
751
// Parse the trailer header
769
- func fixTrailer (header Header , te [] string ) (Header , error ) {
752
+ func fixTrailer (header Header , chunked bool ) (Header , error ) {
770
753
vv , ok := header ["Trailer" ]
771
754
if ! ok {
772
755
return nil , nil
773
756
}
774
- if ! chunked ( te ) {
757
+ if ! chunked {
775
758
// Trailer and no chunking:
776
759
// this is an invalid use case for trailer header.
777
760
// Nevertheless, no error will be returned and we
0 commit comments