1
0
mirror of https://github.com/facebookincubator/mvfst.git synced 2025-08-05 11:21:09 +03:00

Allow unset read callback during connection close

Summary:
setReadCallback didn't function properly during shutdown

1) it was completely ignored when state_ == CLOSING
2) cancelAllAppCallbacks made a copy of readCallbacks_

This is problematic for application constructs that use groups of streams -- eg: HTTP WebTransport.  When one stream (the WebTransport session) is reset during shutdown, it needs to clean up any dependent streams as well, including preventing them from getting error callbacks.

The fix is to use only the streamId's from readCallbacksCopy for iteration, but look up the actual callback value from readCallbacks_.

Note: there's an as yet unhandled corner case which is that a readError callback installs a new stream read callback, but it seems far fetched enough I'm not including a fix here.

Reviewed By: sharmafb

Differential Revision: D48159644

fbshipit-source-id: c9d522a6b200538193969d60d242a505831e4cd0
This commit is contained in:
Alan Frindell
2024-12-04 14:05:11 -08:00
committed by Facebook GitHub Bot
parent 185d853a9c
commit 900e22e18d
3 changed files with 41 additions and 13 deletions

View File

@@ -3094,12 +3094,15 @@ TEST_P(QuicTransportImplTestBase, TestGracefulCloseWithNoActiveStream) {
TEST_P(QuicTransportImplTestBase, TestImmediateClose) {
auto stream = transport->createBidirectionalStream().value();
auto stream2 = transport->createBidirectionalStream().value();
NiceMock<MockWriteCallback> wcb;
NiceMock<MockWriteCallback> wcbConn;
NiceMock<MockReadCallback> rcb;
NiceMock<MockReadCallback> rcb2;
NiceMock<MockPeekCallback> pcb;
NiceMock<MockDeliveryCallback> deliveryCb;
NiceMock<MockByteEventCallback> txCb;
uint8_t resetCount = 0;
EXPECT_CALL(
wcb,
onStreamWriteError(
@@ -3107,8 +3110,21 @@ TEST_P(QuicTransportImplTestBase, TestImmediateClose) {
EXPECT_CALL(
wcbConn,
onConnectionWriteError(IsAppError(GenericApplicationErrorCode::UNKNOWN)));
EXPECT_CALL(
rcb, readError(stream, IsAppError(GenericApplicationErrorCode::UNKNOWN)));
// The first stream to get a reset will clear the other read callback, so only
// one will receive a reset.
ON_CALL(
rcb, readError(stream, IsAppError(GenericApplicationErrorCode::UNKNOWN)))
.WillByDefault(InvokeWithoutArgs([this, stream2, &resetCount] {
transport->setReadCallback(stream2, nullptr);
resetCount++;
}));
ON_CALL(
rcb2,
readError(stream2, IsAppError(GenericApplicationErrorCode::UNKNOWN)))
.WillByDefault(InvokeWithoutArgs([this, stream, &resetCount] {
transport->setReadCallback(stream, nullptr);
resetCount++;
}));
EXPECT_CALL(
pcb, peekError(stream, IsAppError(GenericApplicationErrorCode::UNKNOWN)));
EXPECT_CALL(deliveryCb, onCanceled(stream, _));
@@ -3120,6 +3136,7 @@ TEST_P(QuicTransportImplTestBase, TestImmediateClose) {
transport->notifyPendingWriteOnConnection(&wcbConn);
transport->notifyPendingWriteOnStream(stream, &wcb);
transport->setReadCallback(stream, &rcb);
transport->setReadCallback(stream2, &rcb2);
transport->setPeekCallback(stream, &pcb);
EXPECT_CALL(*socketPtr, write(_, _, _))
.WillRepeatedly(SetErrnoAndReturn(EAGAIN, -1));
@@ -3151,6 +3168,7 @@ TEST_P(QuicTransportImplTestBase, TestImmediateClose) {
EXPECT_EQ(
transport->transportConn->streamManager->getStream(stream), nullptr);
qEvb->loopOnce();
EXPECT_EQ(resetCount, 1);
}
TEST_P(QuicTransportImplTestBase, ResetStreamUnsetWriteCallback) {