@@ -446,6 +446,148 @@ async fn stream_error_release_connection_capacity() {
446446 join ( srv, client) . await ;
447447}
448448
449+ // Regression test for TODO
450+ #[ tokio:: test]
451+ async fn padded_data_stream_error_releases_connection_capacity ( ) {
452+ h2_support:: trace_init!( ) ;
453+ let ( io, mut srv) = mock:: new ( ) ;
454+
455+ // Padded EOS frame: 1 byte pad_len + 8 bytes data + 1 byte padding.
456+ // flow_controlled_len = 10, payload (data only) = 8.
457+ let mut padded_eos = vec ! [ 0u8 ; 10 ] ;
458+ padded_eos[ 0 ] = 1 ;
459+
460+ let srv = async move {
461+ let settings = srv. assert_client_handshake ( ) . await ;
462+ assert_default_settings ! ( settings) ;
463+ srv. recv_frame (
464+ frames:: headers ( 1 )
465+ . request ( "GET" , "https://http2.akamai.com/" )
466+ . eos ( ) ,
467+ )
468+ . await ;
469+ // Wrong content-length triggers a stream error on the padded EOS frame.
470+ srv. send_frame (
471+ frames:: headers ( 1 )
472+ . response ( 200 )
473+ . field ( "content-length" , & * ( 16_384 * 3 ) . to_string ( ) ) ,
474+ )
475+ . await ;
476+ srv. send_frame ( frames:: data ( 1 , vec ! [ 0 ; 16_384 ] ) ) . await ;
477+ srv. send_frame ( frames:: data ( 1 , vec ! [ 0 ; 16_384 ] ) ) . await ;
478+ srv. send_frame ( frames:: data ( 1 , & padded_eos[ ..] ) . padded ( ) . eos ( ) )
479+ . await ;
480+ srv. recv_frame ( frames:: reset ( 1 ) . protocol_error ( ) ) . await ;
481+ // Released capacity must include the padded frame's full
482+ // flow_controlled_len (10), not just its payload (8).
483+ srv. recv_frame ( frames:: window_update ( 0 , 16_384 * 2 + 10 ) )
484+ . await ;
485+ } ;
486+
487+ let client = async move {
488+ let ( mut client, mut conn) = client:: handshake ( io) . await . unwrap ( ) ;
489+ let request = Request :: builder ( )
490+ . uri ( "https://http2.akamai.com/" )
491+ . body ( ( ) )
492+ . unwrap ( ) ;
493+
494+ let req = async {
495+ let resp = client
496+ . send_request ( request, true )
497+ . unwrap ( )
498+ . 0
499+ . await
500+ . expect ( "response" ) ;
501+ assert_eq ! ( resp. status( ) , StatusCode :: OK ) ;
502+ let mut body = resp. into_parts ( ) . 1 ;
503+ let mut cap = body. flow_control ( ) . clone ( ) ;
504+ let to_release = 16_384 * 2 ;
505+ let mut should_recv_bytes = to_release;
506+ let mut should_recv_frames = 2usize ;
507+
508+ let err = body
509+ . try_for_each ( |bytes| async move {
510+ should_recv_bytes -= bytes. len ( ) ;
511+ should_recv_frames -= 1 ;
512+ if should_recv_bytes == 0 {
513+ assert_eq ! ( should_recv_frames, 0 ) ;
514+ }
515+ Ok ( ( ) )
516+ } )
517+ . await
518+ . expect_err ( "body" ) ;
519+ assert_eq ! (
520+ err. to_string( ) ,
521+ "stream error detected: unspecific protocol error detected"
522+ ) ;
523+ cap. release_capacity ( to_release) . expect ( "release_capacity" ) ;
524+ } ;
525+ conn. drive ( req) . await ;
526+ conn. await . expect ( "client" ) ;
527+ } ;
528+
529+ join ( srv, client) . await ;
530+ }
531+
532+ // Regression test for TODO
533+ #[ tokio:: test]
534+ async fn padded_data_on_forgotten_stream_releases_connection_capacity ( ) {
535+ h2_support:: trace_init!( ) ;
536+ let ( io, mut srv) = mock:: new ( ) ;
537+
538+ // Padded frame: 1 byte pad_len + 16378 bytes data + 5 bytes padding.
539+ // flow_controlled_len = 16384, payload (data only) = 16378.
540+ let mut padded = vec ! [ 0u8 ; 16_384 ] ;
541+ padded[ 0 ] = 5 ;
542+
543+ let srv = async move {
544+ let settings = srv. assert_client_handshake ( ) . await ;
545+ assert_default_settings ! ( settings) ;
546+ srv. recv_frame (
547+ frames:: headers ( 1 )
548+ . request ( "GET" , "https://example.com/" )
549+ . eos ( ) ,
550+ )
551+ . await ;
552+ srv. send_frame ( frames:: headers ( 1 ) . response ( 200 ) ) . await ;
553+ srv. send_frame ( frames:: data ( 1 , vec ! [ 0 ; 16_384 ] ) ) . await ;
554+ srv. recv_frame ( frames:: reset ( 1 ) . cancel ( ) ) . await ;
555+ // Wait for the reset to expire so the stream is forgotten.
556+ idle_ms ( 50 ) . await ;
557+ srv. ping_pong ( [ 1 ; 8 ] ) . await ;
558+ // Stream 1 has been evicted. Send a padded DATA frame for it.
559+ srv. send_frame ( frames:: data ( 1 , & padded[ ..] ) . padded ( ) . eos ( ) )
560+ . await ;
561+ // Released capacity must cover both frames using their full
562+ // flow_controlled_len. There used to be a bug where the padded
563+ // frame would release only 16378 (payload) instead of 16384.
564+ srv. recv_frame ( frames:: window_update ( 0 , 16_384 * 2 ) ) . await ;
565+ srv. recv_frame ( frames:: reset ( 1 ) . stream_closed ( ) ) . await ;
566+ } ;
567+
568+ let client = async move {
569+ let ( mut client, conn) = client:: Builder :: new ( )
570+ . reset_stream_duration ( Duration :: from_millis ( 10 ) )
571+ . handshake :: < _ , Bytes > ( io)
572+ . await
573+ . expect ( "handshake" ) ;
574+
575+ let req = async {
576+ let resp = client. get ( "https://example.com/" ) . await . expect ( "response" ) ;
577+ assert_eq ! ( resp. status( ) , StatusCode :: OK ) ;
578+ // This drop sends RST_STREAM(CANCEL)
579+ drop ( resp) ;
580+ } ;
581+
582+ let mut conn = Box :: pin ( async move { conn. await . expect ( "client" ) } ) ;
583+ conn. drive ( req) . await ;
584+ conn. await ;
585+ drop ( client) ;
586+ } ;
587+
588+ join ( srv, client) . await ;
589+ }
590+
449591#[ tokio:: test]
450592async fn stream_close_by_data_frame_releases_capacity ( ) {
451593 h2_support:: trace_init!( ) ;
0 commit comments