@@ -454,16 +454,14 @@ impl tokio::io::AsyncWrite for Substream {
454454 flag : Some ( Flag :: Fin ) ,
455455 } ) {
456456 Ok ( ( ) ) => {
457- // Transition to FinSent FIRST so that on_message can recognize FIN_ACK.
458- // If we registered the waker first, a FIN_ACK arriving before state transition
459- // would be ignored (on_message checks for FinSent state).
457+ // Race condition mitigation strategy:
458+ // 1. Transition to FinSent FIRST so on_message can recognize FIN_ACK (if waker
459+ // registered first, FIN_ACK would be ignored since state != FinSent)
460+ // 2. Register waker so we'll be notified on future FIN_ACK arrivals
461+ // 3. Re-check state to catch FIN_ACK that arrived between steps 1 and 2 (wake()
462+ // called before waker registered has no effect, but state changed)
460463 * self . state . lock ( ) = State :: FinSent ;
461-
462- // Now register waker so we'll be notified when FIN_ACK arrives
463464 self . shutdown_waker . register ( cx. waker ( ) ) ;
464-
465- // Re-check state in case FIN_ACK arrived between state transition and
466- // waker registration (on_message may have already transitioned us to FinAcked)
467465 if matches ! ( * self . state. lock( ) , State :: FinAcked ) {
468466 return Poll :: Ready ( Ok ( ( ) ) ) ;
469467 }
@@ -1483,4 +1481,35 @@ mod tests {
14831481 // Shutdown should complete successfully
14841482 shutdown_task. await . unwrap ( ) ;
14851483 }
1484+
1485+ #[ tokio:: test]
1486+ async fn fin_with_payload_delivers_data_before_close ( ) {
1487+ // Test that when a FIN message contains payload data, the data is delivered
1488+ // to the substream before the RecvClosed event. This is important because
1489+ // the spec allows a FIN message to contain final data.
1490+
1491+ let ( mut substream, handle) = Substream :: new ( ) ;
1492+
1493+ // Simulate receiving FIN with payload from remote
1494+ handle
1495+ . on_message ( WebRtcMessage {
1496+ payload : Some ( b"final data" . to_vec ( ) ) ,
1497+ flag : Some ( Flag :: Fin ) ,
1498+ } )
1499+ . await
1500+ . unwrap ( ) ;
1501+
1502+ // First, we should receive the payload data
1503+ let mut buf = vec ! [ 0u8 ; 1024 ] ;
1504+ let n = substream. read ( & mut buf) . await . unwrap ( ) ;
1505+ assert_eq ! ( & buf[ ..n] , b"final data" ) ;
1506+
1507+ // Then, subsequent read should fail with BrokenPipe (RecvClosed)
1508+ match substream. read ( & mut buf) . await {
1509+ Err ( e) if e. kind ( ) == std:: io:: ErrorKind :: BrokenPipe => {
1510+ // Expected - read half closed after FIN
1511+ }
1512+ other => panic ! ( "Expected BrokenPipe error, got: {:?}" , other) ,
1513+ }
1514+ }
14861515}
0 commit comments