Skip to content

Commit 6738aad

Browse files
authored
Fix unhandled synchronous throw from WebSocket ConnectAsync (#15783) (#15788)
* Fix unhandled synchronous throw from ConnectAsync (Generated summary) ConnectAsync can throw synchronously (e.g. WININET_E_INVALID_CA / 0x80072EE1 on certificate validation failure) before returning an IAsyncAction. Move the call inside the existing try/catch block so such exceptions are routed through Fail() instead of propagating unhandled. Add ConnectAsyncThrowsSynchronously unit test to cover this path. * Change files * Add explaining comment * clang format
1 parent d1c29ff commit 6738aad

3 files changed

Lines changed: 45 additions & 4 deletions

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "prerelease",
3+
"comment": "Fix unhandled synchronous throw from ConnectAsync",
4+
"packageName": "react-native-windows",
5+
"email": "julio.rocha@microsoft.com",
6+
"dependentChangeType": "patch"
7+
}

vnext/Desktop.UnitTests/WinRTWebSocketResourceUnitTest.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,39 @@ TEST_CLASS (WinRTWebSocketResourceUnitTest) {
105105
Assert::IsFalse(connected);
106106
}
107107

108+
BEGIN_TEST_METHOD_ATTRIBUTE(ConnectAsyncThrowsSynchronously)
109+
END_TEST_METHOD_ATTRIBUTE()
110+
TEST_METHOD(ConnectAsyncThrowsSynchronously) {
111+
Logger::WriteMessage("Microsoft::React::Test::WinRTWebSocketResourceUnitTest::ConnectAsyncThrowsSynchronously");
112+
bool connected = false;
113+
string errorMessage;
114+
promise<void> donePromise;
115+
116+
auto imws{winrt::make<MockMessageWebSocket>()};
117+
auto mws{imws.as<MockMessageWebSocket>()};
118+
// Simulate a synchronous throw from ConnectAsync (e.g. WININET_E_INVALID_CA)
119+
// before any IAsyncAction is returned.
120+
mws->Mocks.ConnectAsync = [](const Uri &) -> IAsyncAction {
121+
throw winrt::hresult_error(winrt::hresult(0x80072EE1), L"Invalid CA");
122+
};
123+
124+
auto rc = make_shared<WinRTWebSocketResource2>(
125+
std::move(imws), MockDataWriter{}, CertExceptions{}, Mso::DispatchQueue::MakeSerialQueue());
126+
rc->SetOnConnect([&connected]() { connected = true; });
127+
rc->SetOnError([&errorMessage, &donePromise](Error &&error) {
128+
errorMessage = error.Message;
129+
donePromise.set_value();
130+
});
131+
132+
rc->Connect(testUrl, {}, {});
133+
rc->Close(CloseCode::Normal, {});
134+
135+
donePromise.get_future().wait();
136+
137+
Assert::AreEqual({"[0x80072ee1] Invalid CA"}, errorMessage);
138+
Assert::IsFalse(connected);
139+
}
140+
108141
BEGIN_TEST_METHOD_ATTRIBUTE(SetRequestHeaderFails)
109142
END_TEST_METHOD_ATTRIBUTE()
110143
TEST_METHOD(SetRequestHeaderFails) {

vnext/Shared/Networking/WinRTWebSocketResource.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -227,11 +227,12 @@ fire_and_forget WinRTWebSocketResource2::PerformConnect(Uri &&uri) noexcept {
227227
[self = self->shared_from_this(), coUri = std::move(movedUri)]() -> IAsyncAction {
228228
auto coSelf = self->shared_from_this();
229229

230-
auto async = coSelf->m_socket.ConnectAsync(coUri);
231-
co_await lessthrow_await_adapter<IAsyncAction>{async};
232-
233-
auto result = async.ErrorCode();
234230
try {
231+
// `ConnectAsync` MAY throw synchronously (e.g. WININET_E_INVALID_CA)
232+
auto async = coSelf->m_socket.ConnectAsync(coUri);
233+
co_await lessthrow_await_adapter<IAsyncAction>{async};
234+
235+
auto result = async.ErrorCode();
235236
if (result >= 0) { // Non-failing HRESULT
236237
coSelf->m_readyState = ReadyState::Open;
237238

0 commit comments

Comments
 (0)