File tree Expand file tree Collapse file tree 3 files changed +59
-0
lines changed Expand file tree Collapse file tree 3 files changed +59
-0
lines changed Original file line number Diff line number Diff line change @@ -254,6 +254,10 @@ impl Headers {
254254 & mut self . header_block . pseudo
255255 }
256256
257+ pub ( crate ) fn pseudo ( & self ) -> & Pseudo {
258+ & self . header_block . pseudo
259+ }
260+
257261 /// Whether it has status 1xx
258262 pub ( crate ) fn is_informational ( & self ) -> bool {
259263 self . header_block . pseudo . is_informational ( )
Original file line number Diff line number Diff line change @@ -185,6 +185,18 @@ impl Recv {
185185 } ;
186186
187187 stream. content_length = ContentLength :: Remaining ( content_length) ;
188+ // END_STREAM on headers frame with non-zero content-length is malformed.
189+ // https://datatracker.ietf.org/doc/html/rfc9113#section-8.1.1
190+ if frame. is_end_stream ( )
191+ && content_length > 0
192+ && frame
193+ . pseudo ( )
194+ . status
195+ . map_or ( true , |status| status != 204 && status != 304 )
196+ {
197+ proto_err ! ( stream: "recv_headers with END_STREAM: content-length is not zero; stream={:?};" , stream. id) ;
198+ return Err ( Error :: library_reset ( stream. id , Reason :: PROTOCOL_ERROR ) . into ( ) ) ;
199+ }
188200 }
189201 }
190202
Original file line number Diff line number Diff line change @@ -1351,6 +1351,49 @@ async fn allow_empty_data_for_head() {
13511351 join ( srv, h2) . await ;
13521352}
13531353
1354+ #[ tokio:: test]
1355+ async fn reject_none_zero_content_length_header_with_end_stream ( ) {
1356+ h2_support:: trace_init!( ) ;
1357+ let ( io, mut srv) = mock:: new ( ) ;
1358+
1359+ let srv = async move {
1360+ let settings = srv. assert_client_handshake ( ) . await ;
1361+ assert_default_settings ! ( settings) ;
1362+ srv. recv_frame (
1363+ frames:: headers ( 1 )
1364+ . request ( "GET" , "https://example.com/" )
1365+ . eos ( ) ,
1366+ )
1367+ . await ;
1368+ srv. send_frame (
1369+ frames:: headers ( 1 )
1370+ . response ( 200 )
1371+ . field ( "content-length" , 100 )
1372+ . eos ( ) ,
1373+ )
1374+ . await ;
1375+ } ;
1376+
1377+ let h2 = async move {
1378+ let ( mut client, h2) = client:: Builder :: new ( )
1379+ . handshake :: < _ , Bytes > ( io)
1380+ . await
1381+ . unwrap ( ) ;
1382+ tokio:: spawn ( async {
1383+ h2. await . expect ( "connection failed" ) ;
1384+ } ) ;
1385+ let request = Request :: builder ( )
1386+ . method ( Method :: GET )
1387+ . uri ( "https://example.com/" )
1388+ . body ( ( ) )
1389+ . unwrap ( ) ;
1390+ let ( response, _) = client. send_request ( request, true ) . unwrap ( ) ;
1391+ let _ = response. await . unwrap_err ( ) ;
1392+ } ;
1393+
1394+ join ( srv, h2) . await ;
1395+ }
1396+
13541397#[ tokio:: test]
13551398async fn early_hints ( ) {
13561399 h2_support:: trace_init!( ) ;
You can’t perform that action at this time.
0 commit comments