Skip to content

Avoid capturing raw this pointer in WebSocket module #9247

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

Merged
20 commits merged into from
Dec 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "Avoid capturing raw `this` pointer in WS and AsyncStorage'",
"packageName": "react-native-windows",
"email": "julio.rocha@microsoft.com",
"dependentChangeType": "patch"
}
2 changes: 1 addition & 1 deletion vnext/Desktop.DLL/react-native-win32.x64.def
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ EXPORTS
?GetConstants@ViewManagerBase@react@facebook@@UEBA?AUdynamic@folly@@XZ
?InitializeLogging@react@facebook@@YAX$$QEAV?$function@$$A6AXW4RCTLogLevel@react@facebook@@PEBD@Z@std@@@Z
?InitializeTracing@react@facebook@@YAXPEAUINativeTraceHandler@12@@Z
?Make@IWebSocketResource@React@Microsoft@@SA?AV?$shared_ptr@UIWebSocketResource@React@Microsoft@@@std@@$$QEAV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@5@@Z
?Make@IWebSocketResource@React@Microsoft@@SA?AV?$shared_ptr@UIWebSocketResource@React@Microsoft@@@std@@XZ
?Make@JSBigAbiString@react@facebook@@SA?AV?$unique_ptr@$$CBUJSBigAbiString@react@facebook@@U?$default_delete@$$CBUJSBigAbiString@react@facebook@@@std@@@std@@$$QEAV?$unique_ptr@U?$IAbiArray@D@AbiSafe@@UAbiObjectDeleter@2@@5@@Z
?at@dynamic@folly@@QEGBAAEBU12@V?$Range@PEBD@2@@Z
?atImpl@dynamic@folly@@AEGBAAEBU12@AEBU12@@Z
Expand Down
2 changes: 1 addition & 1 deletion vnext/Desktop.DLL/react-native-win32.x86.def
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ EXPORTS
?GetConstants@ViewManagerBase@react@facebook@@UBE?AUdynamic@folly@@XZ
?InitializeLogging@react@facebook@@YGX$$QAV?$function@$$A6GXW4RCTLogLevel@react@facebook@@PBD@Z@std@@@Z
?InitializeTracing@react@facebook@@YGXPAUINativeTraceHandler@12@@Z
?Make@IWebSocketResource@React@Microsoft@@SG?AV?$shared_ptr@UIWebSocketResource@React@Microsoft@@@std@@$$QAV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@5@@Z
?Make@IWebSocketResource@React@Microsoft@@SG?AV?$shared_ptr@UIWebSocketResource@React@Microsoft@@@std@@XZ
?Make@JSBigAbiString@react@facebook@@SG?AV?$unique_ptr@$$CBUJSBigAbiString@react@facebook@@U?$default_delete@$$CBUJSBigAbiString@react@facebook@@@std@@@std@@$$QAV?$unique_ptr@U?$IAbiArray@D@AbiSafe@@UAbiObjectDeleter@2@@5@@Z
?moduleNames@ModuleRegistry@react@facebook@@QAE?AV?$vector@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V?$allocator@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@2@@std@@XZ
?at@dynamic@folly@@QGBEABU12@V?$Range@PBD@2@@Z
Expand Down
32 changes: 16 additions & 16 deletions vnext/Desktop.IntegrationTests/WebSocketIntegrationTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ TEST_CLASS (WebSocketIntegrationTest)
string scheme = "ws";
if (isSecure)
scheme += "s";
auto ws = IWebSocketResource::Make(scheme + "://localhost:5556/");
auto ws = IWebSocketResource::Make();
promise<size_t> sentSizePromise;
ws->SetOnSend([&sentSizePromise](size_t size)
{
Expand All @@ -61,7 +61,7 @@ TEST_CLASS (WebSocketIntegrationTest)
server->Start();
string sent = "prefix";
auto expectedSize = sent.size();
ws->Connect();
ws->Connect(scheme + "://localhost:5556/");
ws->Send(std::move(sent));

// Block until response is received. Fail in case of a remote endpoint failure.
Expand All @@ -85,7 +85,7 @@ TEST_CLASS (WebSocketIntegrationTest)
TEST_METHOD(ConnectClose)
{
auto server = make_shared<Test::WebSocketServer>(5556);
auto ws = IWebSocketResource::Make("ws://localhost:5556/");
auto ws = IWebSocketResource::Make();
Assert::IsFalse(nullptr == ws);
bool connected = false;
bool closed = false;
Expand All @@ -105,7 +105,7 @@ TEST_CLASS (WebSocketIntegrationTest)
});

server->Start();
ws->Connect();
ws->Connect("ws://localhost:5556/");
ws->Close(CloseCode::Normal, "Closing");
server->Stop();

Expand All @@ -124,7 +124,7 @@ TEST_CLASS (WebSocketIntegrationTest)

// IWebSocketResource scope. Ensures object is closed implicitly by destructor.
{
auto ws = IWebSocketResource::Make("ws://localhost:5556");
auto ws = IWebSocketResource::Make();
ws->SetOnConnect([&connected]()
{
connected = true;
Expand All @@ -138,7 +138,7 @@ TEST_CLASS (WebSocketIntegrationTest)
errorMessage = error.Message;
});

ws->Connect();
ws->Connect("ws://localhost:5556");
ws->Close();//TODO: Either remove or rename test.
}

Expand All @@ -156,7 +156,7 @@ TEST_CLASS (WebSocketIntegrationTest)
auto server = make_shared<Test::WebSocketServer>(5556);
server->Start();

auto ws = IWebSocketResource::Make("ws://localhost:5556");
auto ws = IWebSocketResource::Make();
promise<bool> pingPromise;
ws->SetOnPing([&pingPromise]()
{
Expand All @@ -168,7 +168,7 @@ TEST_CLASS (WebSocketIntegrationTest)
errorString = err.Message;
});

ws->Connect();
ws->Connect("ws://localhost:5556");
ws->Ping();
auto pingFuture = pingPromise.get_future();
pingFuture.wait();
Expand All @@ -189,14 +189,14 @@ TEST_CLASS (WebSocketIntegrationTest)
TEST_METHOD(SendReceiveLargeMessage) {
auto server = make_shared<Test::WebSocketServer>(5556);
server->SetMessageFactory([](string &&message) { return message + "_response"; });
auto ws = IWebSocketResource::Make("ws://localhost:5556/");
auto ws = IWebSocketResource::Make();
promise<string> response;
ws->SetOnMessage([&response](size_t size, const string &message, bool isBinary) { response.set_value(message); });
string errorMessage;
ws->SetOnError([&errorMessage](Error err) { errorMessage = err.Message; });

server->Start();
ws->Connect();
ws->Connect("ws://localhost:5556");

char digits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
#define LEN 4096 + 4096 * 2 + 1
Expand Down Expand Up @@ -246,12 +246,12 @@ TEST_CLASS (WebSocketIntegrationTest)
auto cookie = response[boost::beast::http::field::cookie].to_string();
server->SetMessageFactory([cookie](string &&) { return cookie; });
});
auto ws = IWebSocketResource::Make("ws://localhost:5556/");
auto ws = IWebSocketResource::Make();
promise<string> response;
ws->SetOnMessage([&response](size_t size, const string &message, bool isBinary) { response.set_value(message); });

server->Start();
ws->Connect({}, {{L"Cookie", "JSESSIONID=AD9A320CC4034641997FF903F1D10906"}});
ws->Connect("ws://localhost:5556/", {}, {{L"Cookie", "JSESSIONID=AD9A320CC4034641997FF903F1D10906"}});
ws->Send("");

auto future = response.get_future();
Expand Down Expand Up @@ -285,7 +285,7 @@ TEST_CLASS (WebSocketIntegrationTest)
return message;
});

auto ws = IWebSocketResource::Make("ws://localhost:5556/");
auto ws = IWebSocketResource::Make();

std::vector<string> messages{
"AQ==", // [ 01 ]
Expand All @@ -307,7 +307,7 @@ TEST_CLASS (WebSocketIntegrationTest)
});

server->Start();
ws->Connect();
ws->Connect("ws://localhost:5556");

// Send all but the last message.
// Compare result with the next message in the sequence.
Expand Down Expand Up @@ -336,7 +336,7 @@ TEST_CLASS (WebSocketIntegrationTest)
{
return message;
});
auto ws = IWebSocketResource::Make("ws://localhost:5556/");
auto ws = IWebSocketResource::Make();

string expected = "ABCDEFGHIJ";
string result(expected.size(), '0');
Expand All @@ -356,7 +356,7 @@ TEST_CLASS (WebSocketIntegrationTest)
});

server->Start();
ws->Connect();
ws->Connect("ws://localhost:5556");

// Consecutive immediate writes should be enqueued.
// The WebSocket library (WinRT or Beast) can't handle multiple write operations
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ TEST_CLASS (WebSocketResourcePerformanceTest) {
{
vector<shared_ptr<IWebSocketResource>> resources;
for (int i = 0; i < resourceTotal; i++) {
auto ws = IWebSocketResource::Make("ws://localhost:5555/"); // TODO: Switch to port 5556
auto ws = IWebSocketResource::Make();
ws->SetOnMessage([this, &threadCount, &errorFound](size_t size, const string &message, bool isBinary) {
if (errorFound)
return;
Expand Down Expand Up @@ -103,7 +103,7 @@ TEST_CLASS (WebSocketResourcePerformanceTest) {
errorFound = true;
errorMessage = error.Message;
});
ws->Connect();
ws->Connect("ws://localhost:5555"); // TODO: Switch to port 5556

resources.push_back(std::move(ws));
} // Create and store WS resources.
Expand Down
24 changes: 15 additions & 9 deletions vnext/Desktop.UnitTests/BaseWebSocketTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ using std::make_unique;
using std::promise;
using std::string;

namespace {

constexpr char testUrl[] = "ws://localhost";

} // namespace

namespace Microsoft::React::Test {

using CloseCode = IWebSocketResource::CloseCode;
Expand All @@ -30,7 +36,7 @@ TEST_CLASS(BaseWebSocketTest){
END_TEST_CLASS_ATTRIBUTE()

TEST_METHOD(CreateAndSetHandlers){
auto ws = make_unique<TestWebSocketResource>(Url("ws://localhost"));
auto ws = make_unique<TestWebSocketResource>(Url(testUrl));

Assert::IsFalse(nullptr == ws);
ws->SetOnConnect([]() {});
Expand All @@ -44,11 +50,11 @@ TEST_CLASS(BaseWebSocketTest){
TEST_METHOD(ConnectSucceeds) {
string errorMessage;
bool connected = false;
auto ws = make_unique<TestWebSocketResource>(Url("ws://localhost"));
auto ws = make_unique<TestWebSocketResource>(Url(testUrl));
ws->SetOnError([&errorMessage](Error err) { errorMessage = err.Message; });
ws->SetOnConnect([&connected]() { connected = true; });

ws->Connect({}, {});
ws->Connect(testUrl, {}, {});
ws->Close(CloseCode::Normal, {});

Assert::AreEqual({}, errorMessage);
Expand All @@ -58,14 +64,14 @@ TEST_CLASS(BaseWebSocketTest){
TEST_METHOD(ConnectFails) {
string errorMessage;
bool connected = false;
auto ws = make_unique<TestWebSocketResource>(Url("ws://localhost"));
auto ws = make_unique<TestWebSocketResource>(Url(testUrl));
ws->SetOnError([&errorMessage](Error err) { errorMessage = err.Message; });
ws->SetOnConnect([&connected]() { connected = true; });
ws->SetConnectResult([]() -> error_code {
return make_error_code(errc::state_not_recoverable);
});

ws->Connect({}, {});
ws->Connect(testUrl, {}, {});
ws->Close(CloseCode::Normal, {});

Assert::AreNotEqual({}, errorMessage);
Expand All @@ -78,14 +84,14 @@ TEST_CLASS(BaseWebSocketTest){
TEST_METHOD(HandshakeFails) {
string errorMessage;
bool connected = false;
auto ws = make_unique<TestWebSocketResource>(Url("ws://localhost"));
auto ws = make_unique<TestWebSocketResource>(Url(testUrl));
ws->SetOnError([&errorMessage](Error err) { errorMessage = err.Message; });
ws->SetOnConnect([&connected]() { connected = true; });
ws->SetHandshakeResult([](string, string) -> error_code {
return make_error_code(errc::state_not_recoverable);
});

ws->Connect({}, {});
ws->Connect(testUrl, {}, {});
ws->Close(CloseCode::Normal, {});

Assert::AreNotEqual({}, errorMessage);
Expand All @@ -99,7 +105,7 @@ TEST_CLASS(BaseWebSocketTest){
string errorMessage;
promise<void> connected;
bool closed = false;
auto ws = make_unique<TestWebSocketResource>(Url("ws://localhost"));
auto ws = make_unique<TestWebSocketResource>(Url(testUrl));
ws->SetOnError([&errorMessage](Error err) { errorMessage = err.Message; });
ws->SetOnConnect([&connected]() { connected.set_value(); });
ws->SetOnClose(
Expand All @@ -108,7 +114,7 @@ TEST_CLASS(BaseWebSocketTest){
ws->SetCloseResult(
[]() -> error_code { return make_error_code(errc::success); });

ws->Connect({}, {});
ws->Connect(testUrl, {}, {});
connected.get_future().wait();

ws->Close(CloseCode::Normal, "Normal");
Expand Down
7 changes: 5 additions & 2 deletions vnext/Desktop.UnitTests/WebSocketMocks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@ MockWebSocketResource::~MockWebSocketResource() {}

#pragma region IWebSocketResource overrides

void MockWebSocketResource::Connect(const Protocols &protocols, const Options &options) noexcept /*override*/
void MockWebSocketResource::Connect(
string &&url,
const Protocols &protocols,
const Options &options) noexcept /*override*/
{
if (Mocks.Connect)
return Mocks.Connect(protocols, options);
return Mocks.Connect(std::move(url), protocols, options);
}

void MockWebSocketResource::Ping() noexcept /*override*/
Expand Down
4 changes: 2 additions & 2 deletions vnext/Desktop.UnitTests/WebSocketMocks.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ struct MockWebSocketResource : public IWebSocketResource {
~MockWebSocketResource();

struct Mocks {
std::function<void(const Protocols &, const Options &)> Connect;
std::function<void(std::string &&url, const Protocols &, const Options &)> Connect;
std::function<void()> Ping;
std::function<void(const std::string &)> Send;
std::function<void(const std::string &)> SendBinary;
Expand All @@ -25,7 +25,7 @@ struct MockWebSocketResource : public IWebSocketResource {

#pragma region IWebSocketResource overrides

void Connect(const Protocols &, const Options &) noexcept override;
void Connect(std::string &&url, const Protocols &, const Options &) noexcept override;

void Ping() noexcept override;

Expand Down
2 changes: 1 addition & 1 deletion vnext/Desktop.UnitTests/WebSocketModuleTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ TEST_CLASS (WebSocketModuleTest) {
module->setInstance(instance);
module->SetResourceFactory([](const string &) {
auto rc = make_shared<MockWebSocketResource>();
rc->Mocks.Connect = [rc](const IWebSocketResource::Protocols &, const IWebSocketResource::Options &) {
rc->Mocks.Connect = [rc](string &&, const IWebSocketResource::Protocols &, const IWebSocketResource::Options &) {
rc->OnConnect();
};

Expand Down
14 changes: 7 additions & 7 deletions vnext/Desktop.UnitTests/WinRTWebSocketResourceUnitTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ IAsyncAction ThrowAsync() {
co_return;
}

constexpr char testUrl[] = "ws://host:0";

} // namespace

namespace Microsoft::React::Test {
Expand All @@ -51,12 +53,11 @@ TEST_CLASS (WinRTWebSocketResourceUnitTest) {
mws->Mocks.Close = [](uint16_t, const hstring &) {};

// Test APIs
auto rc =
make_shared<WinRTWebSocketResource>(std::move(imws), MockDataWriter{}, Uri{L"ws://host:0"}, CertExceptions{});
auto rc = make_shared<WinRTWebSocketResource>(std::move(imws), MockDataWriter{}, CertExceptions{});
rc->SetOnConnect([&connected]() { connected = true; });
rc->SetOnError([&errorMessage](Error &&error) { errorMessage = error.Message; });

rc->Connect({}, {});
rc->Connect(testUrl, {}, {});
rc->Close(CloseCode::Normal, {});

Assert::AreEqual({}, errorMessage);
Expand All @@ -77,12 +78,11 @@ TEST_CLASS (WinRTWebSocketResourceUnitTest) {
mws->Mocks.Close = [](uint16_t, const hstring &) {};

// Test APIs
auto rc =
make_shared<WinRTWebSocketResource>(std::move(imws), MockDataWriter{}, Uri{L"ws://host:0"}, CertExceptions{});
auto rc = make_shared<WinRTWebSocketResource>(std::move(imws), MockDataWriter{}, CertExceptions{});
rc->SetOnConnect([&connected]() { connected = true; });
rc->SetOnError([&errorMessage](Error &&error) { errorMessage = error.Message; });

rc->Connect({}, {});
rc->Connect(testUrl, {}, {});
rc->Close(CloseCode::Normal, {});

Assert::AreEqual({"[0x80004005] Expected Failure"}, errorMessage);
Expand All @@ -95,7 +95,7 @@ TEST_CLASS (WinRTWebSocketResourceUnitTest) {

auto lambda = [&rc]() mutable {
rc = make_shared<WinRTWebSocketResource>(
winrt::make<ThrowingMessageWebSocket>(), MockDataWriter{}, Uri{L"ws://host:0"}, CertExceptions{});
winrt::make<ThrowingMessageWebSocket>(), MockDataWriter{}, CertExceptions{});
};

Assert::ExpectException<winrt::hresult_error>(lambda);
Expand Down
Loading