@@ -285,6 +285,62 @@ async fn stream_copy_preserves_both_directions_under_partial_write_backpressure(
285285 assert_eq ! ( server_result. unwrap( ) , downstream_payload) ;
286286}
287287
288+ #[ tokio:: test( start_paused = true ) ]
289+ async fn stream_copy_one_way_activity_keeps_shared_idle_deadline_alive ( ) {
290+ const IDLE_TIMEOUT : Duration = Duration :: from_secs ( 10 ) ;
291+ const ACTIVITY_INTERVAL : Duration = Duration :: from_secs ( 8 ) ;
292+
293+ let ( mut downstream_proxy, downstream_client) = tokio:: io:: duplex ( 16 ) ;
294+ let ( mut upstream_proxy, upstream_server) = tokio:: io:: duplex ( 16 ) ;
295+ let copy = copy_bidirectional_with_limits (
296+ & mut downstream_proxy,
297+ & mut upstream_proxy,
298+ IDLE_TIMEOUT ,
299+ None ,
300+ ) ;
301+ let traffic = async move {
302+ let ( mut downstream_reader, mut downstream_writer) = tokio:: io:: split ( downstream_client) ;
303+ let ( mut upstream_reader, mut upstream_writer) = tokio:: io:: split ( upstream_server) ;
304+ upstream_writer. shutdown ( ) . await ?;
305+
306+ for byte in b"alive" {
307+ downstream_writer. write_all ( & [ * byte] ) . await ?;
308+ tokio:: task:: yield_now ( ) . await ;
309+ tokio:: time:: advance ( ACTIVITY_INTERVAL ) . await ;
310+ }
311+ downstream_writer. shutdown ( ) . await ?;
312+
313+ let mut forwarded = Vec :: new ( ) ;
314+ upstream_reader. read_to_end ( & mut forwarded) . await ?;
315+ let mut reverse = Vec :: new ( ) ;
316+ downstream_reader. read_to_end ( & mut reverse) . await ?;
317+ Ok :: < _ , io:: Error > ( ( forwarded, reverse) )
318+ } ;
319+
320+ let ( copy_result, traffic_result) = tokio:: join!( copy, traffic) ;
321+ assert_eq ! ( copy_result. unwrap( ) , ( 5 , 0 ) ) ;
322+ assert_eq ! ( traffic_result. unwrap( ) , ( b"alive" . to_vec( ) , Vec :: new( ) ) ) ;
323+ }
324+
325+ #[ tokio:: test( start_paused = true ) ]
326+ async fn stream_copy_true_idle_returns_timeout ( ) {
327+ let ( mut downstream_proxy, _downstream_client) = tokio:: io:: duplex ( 16 ) ;
328+ let ( mut upstream_proxy, _upstream_server) = tokio:: io:: duplex ( 16 ) ;
329+ let idle_timeout = Duration :: from_secs ( 10 ) ;
330+ let copy = copy_bidirectional_with_limits (
331+ & mut downstream_proxy,
332+ & mut upstream_proxy,
333+ idle_timeout,
334+ None ,
335+ ) ;
336+ let advance_past_deadline = async {
337+ tokio:: time:: advance ( idle_timeout + Duration :: from_secs ( 1 ) ) . await ;
338+ } ;
339+
340+ let ( copy_result, ( ) ) = tokio:: join!( copy, advance_past_deadline) ;
341+ assert ! ( matches!( copy_result, Err ( FluxError :: Timeout { .. } ) ) ) ;
342+ }
343+
288344async fn exchange_duplex_payload (
289345 stream : tokio:: io:: DuplexStream ,
290346 payload : Vec < u8 > ,
0 commit comments