@@ -260,16 +260,13 @@ impl HttpSession {
260260 let contains_content_length =
261261 request_header. headers . contains_key ( CONTENT_LENGTH ) ;
262262
263- // Transfer encoding overrides content length, so when
264- // both are present, we can remove content length. This
265- // is per https://datatracker.ietf.org/doc/html/rfc9112#section-6.3
266- //
267- // RFC 9112 Section 6.1 (https://datatracker.ietf.org/doc/html/rfc9112#section-6.1-15)
268- // also requires us to disable keepalive when both headers are present.
269263 let has_both_te_and_cl =
270264 contains_content_length && contains_transfer_encoding;
271265 if has_both_te_and_cl {
272- request_header. remove_header ( & CONTENT_LENGTH ) ;
266+ return Error :: e_explain (
267+ InvalidHTTPHeader ,
268+ "request contains both Transfer-Encoding and Content-Length" ,
269+ ) ;
273270 }
274271
275272 self . buf = buf;
@@ -279,10 +276,6 @@ impl HttpSession {
279276 self . response_written = None ;
280277 self . respect_keepalive ( ) ;
281278
282- // Disable keepalive if both Transfer-Encoding and Content-Length were present
283- if has_both_te_and_cl {
284- self . set_keepalive ( None ) ;
285- }
286279 self . validate_request ( ) ?;
287280
288281 return Ok ( Some ( s) ) ;
@@ -1662,18 +1655,24 @@ mod tests_stream {
16621655 . read ( input2. as_bytes ( ) )
16631656 . build ( ) ;
16641657 let mut http_stream = HttpSession :: new ( Box :: new ( mock_io) ) ;
1665- let _ = http_stream. read_request ( ) . await . unwrap ( ) ;
1658+ let read_result = http_stream. read_request ( ) . await ;
16661659
16671660 match ( content_length_header, transfer_encoding_header) {
1668- ( Some ( _) | None , Some ( _) ) => {
1661+ ( Some ( _) , Some ( _) ) => {
1662+ assert ! ( read_result. is_err( ) ) ;
1663+ }
1664+ ( None , Some ( _) ) => {
1665+ read_result. unwrap ( ) ;
16691666 assert ! ( http_stream. get_header( TRANSFER_ENCODING ) . is_some( ) ) ;
16701667 assert ! ( http_stream. get_header( CONTENT_LENGTH ) . is_none( ) ) ;
16711668 }
16721669 ( Some ( _) , None ) => {
1670+ read_result. unwrap ( ) ;
16731671 assert ! ( http_stream. get_header( TRANSFER_ENCODING ) . is_none( ) ) ;
16741672 assert ! ( http_stream. get_header( CONTENT_LENGTH ) . is_some( ) ) ;
16751673 }
16761674 _ => {
1675+ read_result. unwrap ( ) ;
16771676 assert ! ( http_stream. get_header( CONTENT_LENGTH ) . is_none( ) ) ;
16781677 assert ! ( http_stream. get_header( TRANSFER_ENCODING ) . is_none( ) ) ;
16791678 }
@@ -2505,10 +2504,7 @@ mod tests_stream {
25052504 }
25062505
25072506 #[ tokio:: test]
2508- async fn test_te_and_cl_disables_keepalive ( ) {
2509- // When both Transfer-Encoding and Content-Length are present,
2510- // we must disable keepalive per RFC 9112 Section 6.1
2511- // https://datatracker.ietf.org/doc/html/rfc9112#section-6.1-15
2507+ async fn test_te_and_cl_is_rejected ( ) {
25122508 let input = b"POST / HTTP/1.1\r \n \
25132509 Host: pingora.org\r \n \
25142510 Transfer-Encoding: chunked\r \n \
@@ -2520,22 +2516,10 @@ hello\r\n\
25202516\r \n ";
25212517 let mock_io = Builder :: new ( ) . read ( & input[ ..] ) . build ( ) ;
25222518 let mut http_stream = HttpSession :: new ( Box :: new ( mock_io) ) ;
2523- http_stream. read_request ( ) . await . unwrap ( ) ;
2524-
2525- // Keepalive should be disabled
2526- assert_eq ! ( http_stream. keepalive_timeout, KeepaliveStatus :: Off ) ;
2527-
2528- // Content-Length header should have been removed
2529- assert ! ( !http_stream
2530- . req_header( )
2531- . headers
2532- . contains_key( CONTENT_LENGTH ) ) ;
2533-
2534- // Transfer-Encoding should still be present
2535- assert ! ( http_stream
2536- . req_header( )
2537- . headers
2538- . contains_key( TRANSFER_ENCODING ) ) ;
2519+ let error = http_stream. read_request ( ) . await . unwrap_err ( ) ;
2520+ assert ! ( error
2521+ . to_string( )
2522+ . contains( "request contains both Transfer-Encoding and Content-Length" ) ) ;
25392523 }
25402524
25412525 #[ tokio:: test]
0 commit comments