File tree 3 files changed +59
-0
lines changed
3 files changed +59
-0
lines changed Original file line number Diff line number Diff line change @@ -254,6 +254,10 @@ impl Headers {
254
254
& mut self . header_block . pseudo
255
255
}
256
256
257
+ pub ( crate ) fn pseudo ( & self ) -> & Pseudo {
258
+ & self . header_block . pseudo
259
+ }
260
+
257
261
/// Whether it has status 1xx
258
262
pub ( crate ) fn is_informational ( & self ) -> bool {
259
263
self . header_block . pseudo . is_informational ( )
Original file line number Diff line number Diff line change @@ -185,6 +185,18 @@ impl Recv {
185
185
} ;
186
186
187
187
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
+ }
188
200
}
189
201
}
190
202
Original file line number Diff line number Diff line change @@ -1351,6 +1351,49 @@ async fn allow_empty_data_for_head() {
1351
1351
join ( srv, h2) . await ;
1352
1352
}
1353
1353
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
+
1354
1397
#[ tokio:: test]
1355
1398
async fn early_hints ( ) {
1356
1399
h2_support:: trace_init!( ) ;
You can’t perform that action at this time.
0 commit comments