Skip to content

Commit d537573

Browse files
committed
webrtc: Add test for FIN with payload and clarify race condition comments
1 parent a36a04e commit d537573

File tree

1 file changed

+37
-8
lines changed

1 file changed

+37
-8
lines changed

src/transport/webrtc/substream.rs

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)