Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ignore Duplicate Stream Read Shutdowns #1822

Merged
merged 3 commits into from
Jul 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/core/stream_recv.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ QuicStreamRecvShutdown(
goto Exit;
}

if (Stream->Flags.SentStopSending) {
//
// We've already aborted locally. Just ignore any additional shutdowns.
//
goto Exit;
}

//
// Disable all future receive events.
//
Expand Down
2 changes: 1 addition & 1 deletion src/test/bin/quic_gtest.h
Original file line number Diff line number Diff line change
Expand Up @@ -655,7 +655,7 @@ struct ValidateStreamEventArgs {
uint32_t Test;
static ::std::vector<ValidateStreamEventArgs> Generate() {
::std::vector<ValidateStreamEventArgs> list;
for (uint32_t Test = 0; Test < 6; ++Test)
for (uint32_t Test = 0; Test < 7; ++Test)
list.push_back({ Test });
return list;
}
Expand Down
149 changes: 133 additions & 16 deletions src/test/lib/EventTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,16 @@ struct StreamEventValidator {
uint8_t Actions;
StreamEventValidator(QUIC_STREAM_EVENT_TYPE type, uint8_t actions = 0, bool optional = false) : Success(false),
Optional(optional), Type(type), Actions(actions) { }
virtual void Validate(_In_ HQUIC Stream, _Inout_ QUIC_STREAM_EVENT* Event) {
void Validate(_In_ HQUIC Stream, _Inout_ QUIC_STREAM_EVENT* Event) {
if (Event->Type != Type) {
if (!Optional) {
TEST_FAILURE("StreamEventValidator: Expected %u. Actual %u", Type, Event->Type);
}
return;
}
if (!ValidateMore(Stream, Event)) {
return;
}
Success = true;
if (Actions & QUIC_EVENT_ACTION_SHUTDOWN_STREAM) {
MsQuic->StreamShutdown(Stream, QUIC_STREAM_SHUTDOWN_FLAG_GRACEFUL, 0);
Expand All @@ -42,6 +45,7 @@ struct StreamEventValidator {
MsQuic->ConnectionShutdown(Stream, QUIC_CONNECTION_SHUTDOWN_FLAG_NONE, 0);
}
}
virtual bool ValidateMore(_In_ HQUIC, _Inout_ QUIC_STREAM_EVENT*) { return true; }
virtual ~StreamEventValidator() { }
};

Expand All @@ -50,24 +54,26 @@ struct StreamStartCompleteEventValidator : StreamEventValidator {
StreamStartCompleteEventValidator(bool PeerAccepted = false, uint8_t actions = 0, bool optional = false) :
StreamEventValidator(QUIC_STREAM_EVENT_START_COMPLETE, actions, optional),
PeerAccepted(PeerAccepted ? TRUE : FALSE) { }
virtual void Validate(_In_ HQUIC Stream, _Inout_ QUIC_STREAM_EVENT* Event) {
if (Event->Type != Type) {
if (!Optional) {
TEST_FAILURE("StreamEventValidator: Expected %u. Actual %u", Type, Event->Type);
}
return;
}
virtual bool ValidateMore(_In_ HQUIC, _Inout_ QUIC_STREAM_EVENT* Event) {
if (Event->START_COMPLETE.PeerAccepted != PeerAccepted) {
TEST_FAILURE("PeerAccepted mismatch: Expected %u. Actual %u", PeerAccepted, Event->START_COMPLETE.PeerAccepted);
return;
}
Success = true;
if (Actions & QUIC_EVENT_ACTION_SHUTDOWN_STREAM) {
MsQuic->StreamShutdown(Stream, QUIC_STREAM_SHUTDOWN_FLAG_GRACEFUL, 0);
return false;
}
if (Actions & QUIC_EVENT_ACTION_SHUTDOWN_CONNECTION) {
MsQuic->ConnectionShutdown(Stream, QUIC_CONNECTION_SHUTDOWN_FLAG_NONE, 0);
return true;
}
};

struct StreamPeerRecvAbortEventValidator : StreamEventValidator {
QUIC_UINT62 ErrorCode;
StreamPeerRecvAbortEventValidator(QUIC_UINT62 errorcode, uint8_t actions = 0, bool optional = false) :
StreamEventValidator(QUIC_STREAM_EVENT_PEER_RECEIVE_ABORTED, actions, optional),
ErrorCode(errorcode) { }
virtual bool ValidateMore(_In_ HQUIC, _Inout_ QUIC_STREAM_EVENT* Event) {
if (Event->PEER_RECEIVE_ABORTED.ErrorCode != ErrorCode) {
TEST_FAILURE("PeerRecvAbort mismatch: Expected %llu. Actual %llu", ErrorCode, Event->PEER_RECEIVE_ABORTED.ErrorCode);
return false;
}
return true;
}
};

Expand Down Expand Up @@ -1177,6 +1183,116 @@ QuicTestValidateStreamEvents6(
} // Connections scope
}

void
QuicTestValidateStreamEvents7(
_In_ MsQuicRegistration& Registration,
_In_ HQUIC Listener,
_In_ QuicAddr& ServerLocalAddr
)
{
TestScopeLogger ScopeLogger(__FUNCTION__);

MsQuicSettings Settings;
Settings.SetPeerBidiStreamCount(1).SetMinimumMtu(1280).SetMaximumMtu(1280);
MsQuicConfiguration ServerConfiguration(Registration, "MsQuicTest", Settings, ServerSelfSignedCredConfig);
TEST_TRUE(ServerConfiguration.IsValid());

MsQuicConfiguration ClientConfiguration(Registration, "MsQuicTest", MsQuicSettings().SetMinimumMtu(1280).SetMaximumMtu(1280), MsQuicCredentialConfig());
TEST_TRUE(ClientConfiguration.IsValid());

{ // Connections scope
ConnValidator Client, Server(ServerConfiguration);

MsQuic->SetContext(Listener, &Server);

TEST_QUIC_SUCCEEDED(
MsQuic->ConnectionOpen(
Registration,
ConnValidatorCallback,
&Client,
&Client.Handle));

{ // Stream scope

StreamValidator ClientStream(
new(std::nothrow) StreamEventValidator* [4] {
new(std::nothrow) StreamStartCompleteEventValidator(),
new(std::nothrow) StreamEventValidator(QUIC_STREAM_EVENT_SEND_SHUTDOWN_COMPLETE, 0, true),
new(std::nothrow) StreamEventValidator(QUIC_STREAM_EVENT_SHUTDOWN_COMPLETE, QUIC_EVENT_ACTION_SHUTDOWN_CONNECTION),
nullptr
});
StreamValidator ServerStream(
new(std::nothrow) StreamEventValidator* [6] {
new(std::nothrow) StreamPeerRecvAbortEventValidator(0),
new(std::nothrow) StreamEventValidator(QUIC_STREAM_EVENT_RECEIVE),
new(std::nothrow) StreamEventValidator(QUIC_STREAM_EVENT_PEER_SEND_SHUTDOWN),
new(std::nothrow) StreamEventValidator(QUIC_STREAM_EVENT_SEND_SHUTDOWN_COMPLETE),
new(std::nothrow) StreamEventValidator(QUIC_STREAM_EVENT_SHUTDOWN_COMPLETE),
nullptr
});

Client.SetExpectedEvents(
new(std::nothrow) ConnEventValidator* [7] {
new(std::nothrow) ConnEventValidator(QUIC_CONNECTION_EVENT_STREAMS_AVAILABLE),
new(std::nothrow) ConnEventValidator(QUIC_CONNECTION_EVENT_DATAGRAM_STATE_CHANGED),
new(std::nothrow) ConnEventValidator(QUIC_CONNECTION_EVENT_CONNECTED),
new(std::nothrow) ConnEventValidator(QUIC_CONNECTION_EVENT_STREAMS_AVAILABLE, 0, true),
new(std::nothrow) ConnEventValidator(QUIC_CONNECTION_EVENT_RESUMPTION_TICKET_RECEIVED, 0, true), // TODO - Schannel does resumption regardless
new(std::nothrow) ConnEventValidator(QUIC_CONNECTION_EVENT_SHUTDOWN_COMPLETE),
nullptr
});
Server.SetExpectedEvents(
new(std::nothrow) ConnEventValidator* [6] {
new(std::nothrow) ConnEventValidator(QUIC_CONNECTION_EVENT_CONNECTED),
new(std::nothrow) NewStreamEventValidator(&ServerStream),
new(std::nothrow) ConnEventValidator(QUIC_CONNECTION_EVENT_SHUTDOWN_INITIATED_BY_PEER),
new(std::nothrow) ConnEventValidator(QUIC_CONNECTION_EVENT_SHUTDOWN_COMPLETE),
nullptr
});

TEST_QUIC_SUCCEEDED(
MsQuic->StreamOpen(
Client.Handle,
QUIC_STREAM_OPEN_FLAG_NONE,
StreamValidatorCallback,
&ClientStream,
&ClientStream.Handle));
TEST_QUIC_SUCCEEDED(
MsQuic->StreamStart(
ClientStream.Handle,
QUIC_STREAM_START_FLAG_NONE));
TEST_QUIC_SUCCEEDED(
MsQuic->StreamShutdown(
ClientStream.Handle,
QUIC_STREAM_SHUTDOWN_FLAG_GRACEFUL,
0));
TEST_QUIC_SUCCEEDED(
MsQuic->StreamShutdown(
ClientStream.Handle,
QUIC_STREAM_SHUTDOWN_FLAG_ABORT_RECEIVE,
0));
TEST_QUIC_SUCCEEDED(
MsQuic->StreamShutdown(
ClientStream.Handle,
QUIC_STREAM_SHUTDOWN_FLAG_ABORT_RECEIVE,
0xFFFF));

TEST_QUIC_SUCCEEDED(
MsQuic->ConnectionStart(
Client.Handle,
ClientConfiguration,
QuicAddrGetFamily(&ServerLocalAddr.SockAddr),
QUIC_LOCALHOST_FOR_AF(
QuicAddrGetFamily(&ServerLocalAddr.SockAddr)),
ServerLocalAddr.GetPort()));

TEST_TRUE(Client.Complete.WaitTimeout(2000));
TEST_TRUE(Server.Complete.WaitTimeout(1000));

} // Stream scope
} // Connections scope
}

void QuicTestValidateStreamEvents(uint32_t Test)
{
MsQuicRegistration Registration(true);
Expand All @@ -1200,7 +1316,8 @@ void QuicTestValidateStreamEvents(uint32_t Test)
QuicTestValidateStreamEvents3,
QuicTestValidateStreamEvents4,
QuicTestValidateStreamEvents5,
QuicTestValidateStreamEvents6
QuicTestValidateStreamEvents6,
QuicTestValidateStreamEvents7
};

Tests[Test](Registration, Listener, ServerLocalAddr);
Expand Down