Skip to content

Commit 92cd588

Browse files
OskarEichlermeta-codesync[bot]
authored andcommitted
Fix Cxx WebSocket write queue state (#58201)
Summary: The Cxx WebSocket client can write before its handshake, drop queued pre-connect and empty frames, allow concurrent Beast writes through a check-then-set race, and retain writer ownership after errors. Gate draining on a successful connection, atomically claim the single writer, preserve empty frames, and release state on every completion. Fixes #58200. ## Changelog: [GENERAL] [FIXED] - Preserve buffered and empty Cxx WebSocket frames and serialize writes. Pull Request resolved: #58201 Test Plan: - Exact temporary localhost Boost server harness on untouched main: `Operation canceled`, both expected frames missing, exit 1. - Fixed source harness: received the pre-connect buffered payload and empty frame, exit 0. - Actual source and harness compiled under C++20 with `-Wall -Werror`; only a local third-party Boost/Clang deprecation was demoted. - Repository clang-format and `git diff --check` passed. - Temporary harness/build wiring was removed; this PR contains one runtime file only. No UI change; screenshots are not applicable. Reviewed By: cortinico Differential Revision: D118266757 Pulled By: javache fbshipit-source-id: 2881893bdcb4c6873087e5cd821a82af76e8bbb8
1 parent 619c8ae commit 92cd588

1 file changed

Lines changed: 14 additions & 15 deletions

File tree

packages/react-native/ReactCxxPlatform/react/http/platform/cxx/WebSocketClient.cpp

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ struct WebSocketClient::Impl final : public std::enable_shared_from_this<Impl> {
5353
std::mutex mutexOut_;
5454
std::queue<std::string> messagesOut_;
5555
std::atomic<bool> isWriting_{false};
56+
std::atomic<bool> isConnected_{false};
5657
std::atomic<bool> isClosing_{false};
5758
};
5859

@@ -215,6 +216,7 @@ void WebSocketClient::Impl::onHandshakeCompleted(boost::system::error_code ec) {
215216
return;
216217
}
217218

219+
isConnected_ = true;
218220
onConnectCallback(true, "Connected");
219221

220222
// Listen for any messages from the server
@@ -252,26 +254,22 @@ void WebSocketClient::Impl::listen() {
252254
}
253255

254256
void WebSocketClient::Impl::write() {
255-
if (isClosing_) {
257+
if (isClosing_ || !isConnected_) {
256258
return;
257259
}
258-
if (isWriting_) {
260+
if (isWriting_.exchange(true)) {
259261
return;
260262
}
261-
isWriting_ = true;
262263

263264
std::shared_ptr<std::string> message;
264265
{
265266
std::lock_guard<std::mutex> lock(mutexOut_);
266-
if (!messagesOut_.empty()) {
267-
message = std::make_shared<std::string>(messagesOut_.front());
268-
messagesOut_.pop();
267+
if (messagesOut_.empty()) {
268+
isWriting_ = false;
269+
return;
269270
}
270-
}
271-
272-
if (!message || message->empty()) {
273-
isWriting_ = false;
274-
return;
271+
message = std::make_shared<std::string>(messagesOut_.front());
272+
messagesOut_.pop();
275273
}
276274

277275
auto ws = ws_.wlock();
@@ -281,16 +279,17 @@ void WebSocketClient::Impl::write() {
281279
boost::beast::error_code ec,
282280
std::size_t /*bytes_transferred*/) mutable {
283281
auto impl = weakImpl.lock();
284-
if (!impl || impl->isClosing_) {
282+
if (!impl) {
283+
return;
284+
}
285+
impl->isWriting_ = false;
286+
if (impl->isClosing_) {
285287
return;
286288
}
287289
if (ec) {
288290
LOG(ERROR) << "Error writing to websocket: " << ec.message();
289-
return;
290291
}
291-
impl->isWriting_ = false;
292292
impl->write();
293-
message.reset(); // Release the message after it's been sent
294293
});
295294
}
296295

0 commit comments

Comments
 (0)