@@ -1993,6 +1993,72 @@ async fn server_drop_connection_after_go_away() {
19931993 join ( srv, h2) . await ;
19941994}
19951995
1996+ #[ tokio:: test]
1997+ async fn reset_before_headers_reaches_peer_without_headers ( ) {
1998+ // Repro: body future errors immediately and hyper/h2 converts that into a
1999+ // RST_STREAM before the queued HEADERS are ever written, so the peer sees
2000+ // a reset for an idle stream and treats it as a PROTOCOL_ERROR.
2001+ h2_support:: trace_init!( ) ;
2002+
2003+ let ( io, srv) = mock:: new ( ) ;
2004+
2005+ // Server task: perform handshake then observe the first frame.
2006+ let srv = async move {
2007+ let mut srv = srv;
2008+ let settings = srv. assert_client_handshake ( ) . await ;
2009+ assert_default_settings ! ( settings) ;
2010+
2011+ let frame = tokio:: time:: timeout ( Duration :: from_secs ( 1 ) , srv. next ( ) )
2012+ . await
2013+ . expect ( "timed out waiting for first frame" )
2014+ . expect ( "unexpected EOF" )
2015+ . expect ( "frame error" ) ;
2016+
2017+ match frame {
2018+ frame:: Frame :: Headers ( h) if h. stream_id ( ) == StreamId :: from ( 1 ) => {
2019+ assert ! ( h. is_end_stream( ) == false ) ;
2020+ }
2021+ frame:: Frame :: Reset ( rst) if rst. stream_id ( ) == StreamId :: from ( 1 ) => {
2022+ panic ! (
2023+ "BUG: client sent RST_STREAM before any HEADERS on stream 1; reason={:?}" ,
2024+ rst. reason( )
2025+ ) ;
2026+ }
2027+ other => panic ! ( "unexpected first frame: {:?}" , other) ,
2028+ }
2029+ } ;
2030+
2031+ // Client task: queue HEADERS, immediately reset, then drive the connection.
2032+ let client = async move {
2033+ let ( client, conn) = client:: handshake ( io) . await . unwrap ( ) ;
2034+
2035+ let req = Request :: builder ( )
2036+ . method ( "POST" )
2037+ . uri ( "https://example.com/" )
2038+ . body ( ( ) )
2039+ . unwrap ( ) ;
2040+ let mut client = client. ready ( ) . await . expect ( "poll_ready" ) ;
2041+ let ( _resp_fut, mut send_stream) = client. send_request ( req, false ) . unwrap ( ) ;
2042+
2043+ // Simulate body error (reqwest wraps into io::Error::Other) by resetting
2044+ // immediately after the stream is created.
2045+ send_stream. send_reset ( Reason :: INTERNAL_ERROR ) ;
2046+
2047+ // Now start driving the connection so the queued frames get written.
2048+ let conn_task = tokio:: spawn ( async move {
2049+ let _ = conn. await ;
2050+ } ) ;
2051+
2052+ // Give the connection a moment to flush frames.
2053+ tokio:: time:: sleep ( Duration :: from_millis ( 10 ) ) . await ;
2054+
2055+ drop ( send_stream) ;
2056+ let _ = conn_task. await ;
2057+ } ;
2058+
2059+ join ( srv, client) . await ;
2060+ }
2061+
19962062const SETTINGS : & [ u8 ] = & [ 0 , 0 , 0 , 4 , 0 , 0 , 0 , 0 , 0 ] ;
19972063const SETTINGS_ACK : & [ u8 ] = & [ 0 , 0 , 0 , 4 , 1 , 0 , 0 , 0 , 0 ] ;
19982064
0 commit comments