From 856aaf4baaa18dda035fba10ff360dacb6d006ea Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Thu, 6 Jan 2022 04:12:54 -0500 Subject: [PATCH 01/91] Added BlobModule and IWSModuleContHandler headers --- vnext/Shared/Modules/BlobModule.h | 73 +++++++++++++++++++ .../Modules/IWebSocketModuleContentHandler.h | 30 ++++++++ vnext/Shared/Shared.vcxitems | 2 + vnext/Shared/Shared.vcxitems.filters | 6 ++ 4 files changed, 111 insertions(+) create mode 100644 vnext/Shared/Modules/BlobModule.h create mode 100644 vnext/Shared/Modules/IWebSocketModuleContentHandler.h diff --git a/vnext/Shared/Modules/BlobModule.h b/vnext/Shared/Modules/BlobModule.h new file mode 100644 index 00000000000..bfb676b9595 --- /dev/null +++ b/vnext/Shared/Modules/BlobModule.h @@ -0,0 +1,73 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#pragma once + +#include + +// React Native +#include + +// Standard Library +#include +#include +#include +#include +#include + +namespace Microsoft::React { + +class BlobModule : public facebook::xplat::module::CxxModule { + std::shared_ptr m_contentHandler; + + public: + enum class MethodId { + AddNetworkingHandler = 0, + AddWebSocketHandler = 1, + RemoveWebSocketHandler = 2, + SendOverSocket = 3, + CreateFromParts = 4, + Release = 5, + SIZE = 6 + }; + + BlobModule() noexcept; + +#pragma region CxxModule overrides + + /// + /// + /// + std::string getName() override; + + /// + /// + /// + std::map getConstants() override; + + /// + /// + /// + /// See See react-native/Libraries/WebSocket/WebSocket.js + std::vector getMethods() override; + +#pragma endregion CxxModule overrides +}; + +class BlobWebSocketModuleContentHandler final : public IWebSocketModuleContentHandler { + std::unordered_map> m_blobs; + std::mutex m_blobsMutex; + std::unordered_set m_socketIDs; + std::mutex m_socketIDsMutex; + + public: +#pragma region IWebSocketModuleContentHandler overrides + + void ProcessMessage(std::string &&message, folly::dynamic ¶ms) override; + + void ProcessMessage(std::vector &&message, folly::dynamic ¶ms) override; + +#pragma endregion IWebSocketModuleContentHandler overrides +}; + +} // namespace Microsoft::React diff --git a/vnext/Shared/Modules/IWebSocketModuleContentHandler.h b/vnext/Shared/Modules/IWebSocketModuleContentHandler.h new file mode 100644 index 00000000000..0130a6b28cf --- /dev/null +++ b/vnext/Shared/Modules/IWebSocketModuleContentHandler.h @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#pragma once + +// React Native +#include + +// Standard Library +#include +#include + +namespace Microsoft::React { + +/// +/// See https://github.com/facebook/react-native/blob/v0.63.2/React/CoreModules/RCTWebSocketModule.h#L12 +/// +struct IWebSocketModuleContentHandler { + + static std::weak_ptr GetInstance() noexcept; + + virtual ~IWebSocketModuleContentHandler() noexcept {} + + virtual void ProcessMessage(std::string &&message, folly::dynamic ¶ms) = 0; + + virtual void ProcessMessage(std::vector &&message, folly::dynamic ¶ms) = 0; + +}; + +} // namespace diff --git a/vnext/Shared/Shared.vcxitems b/vnext/Shared/Shared.vcxitems index f4929bd7296..8dead25f77e 100644 --- a/vnext/Shared/Shared.vcxitems +++ b/vnext/Shared/Shared.vcxitems @@ -83,6 +83,8 @@ + + diff --git a/vnext/Shared/Shared.vcxitems.filters b/vnext/Shared/Shared.vcxitems.filters index 31d69a43b7e..0eb5f589e64 100644 --- a/vnext/Shared/Shared.vcxitems.filters +++ b/vnext/Shared/Shared.vcxitems.filters @@ -381,6 +381,12 @@ Header Files + + Header Files\Modules + + + Header Files\Modules + From 7efb7604047f2734aac5437b73da77b2d4264043 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Fri, 7 Jan 2022 01:12:36 -0500 Subject: [PATCH 02/91] Implement Blob module --- vnext/Shared/Modules/BlobModule.cpp | 202 +++++++++++++++++++++++++++ vnext/Shared/Modules/BlobModule.h | 50 ++++--- vnext/Shared/Shared.vcxitems | 1 + vnext/Shared/Shared.vcxitems.filters | 3 + 4 files changed, 237 insertions(+), 19 deletions(-) create mode 100644 vnext/Shared/Modules/BlobModule.cpp diff --git a/vnext/Shared/Modules/BlobModule.cpp b/vnext/Shared/Modules/BlobModule.cpp new file mode 100644 index 00000000000..9f7ea70fddd --- /dev/null +++ b/vnext/Shared/Modules/BlobModule.cpp @@ -0,0 +1,202 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "BlobModule.h" + +#include + +// React Native +#include +#include +#include + +// Windows API +#include +#include + +using namespace facebook::xplat; + +using folly::dynamic; +using std::lock_guard; +using std::mutex; +using std::string; +using std::vector; +using std::weak_ptr; +using winrt::Windows::Foundation::GuidHelper; +using winrt::Windows::Security::Cryptography::CryptographicBuffer; + +namespace { +constexpr char moduleName[] = "BlobModule"; +constexpr char blobURIScheme[] = "blob"; + +weak_ptr s_contentHandler; +} // namespace + +namespace Microsoft::React { + +#pragma region BlobModule + +BlobModule::BlobModule() noexcept { + m_contentHandler = std::static_pointer_cast(s_contentHandler.lock()); + if (!m_contentHandler) { + m_contentHandler = std::make_shared(); + s_contentHandler = m_contentHandler; + } +} + +#pragma region CxxModule + +string BlobModule::getName() { + return moduleName; +} + +std::map BlobModule::getConstants() { + return {{"BLOB_URI_SCHEME", blobURIScheme}, {"BLOB_URI_HOST", {}}}; +} + +std::vector BlobModule::getMethods() { + return { + {"addNetworkingHandler", + [this](dynamic args) { + // TODO: Implement #6081 + }}, + + {"addWebSocketHandler", + [this](dynamic args) { + auto id = jsArgAsInt(args, 0); + + m_contentHandler->Register(id); + }}, + + {"removeWebSocketHandler", + [this](dynamic args) { + auto id = jsArgAsInt(args, 0); + + m_contentHandler->Unregister(id); + }}, + + {"sendOverSocket", + [this](dynamic args) { + auto blob = jsArgAsObject(args, 0); + auto blobId = blob["blobId"].getString(); + auto offset = blob["offset"].getInt(); + auto size = blob["size"].getInt(); + auto socketID = jsArgAsInt(args, 1); + + auto data = m_contentHandler->ResolveMessage(std::move(blobId), offset, size); + + if (auto instance = getInstance().lock()) { + auto buffer = CryptographicBuffer::CreateFromByteArray(data); + auto winrtString = CryptographicBuffer::EncodeToBase64String(std::move(buffer)); + auto base64String = Common::Unicode::Utf16ToUtf8(std::move(winrtString)); + + auto sendArgs = dynamic::array(std::move(base64String), socketID); + instance->callJSFunction("WebSocketModule", "sendBinary", std::move(sendArgs)); + } + }}, + + {"createFromParts", + [this](dynamic args) { + auto parts = jsArgAsArray(args, 0); // Array + auto blobId = jsArgAsString(args, 1); + vector buffer{}; + + for (const auto &part : parts) { + auto type = part["type"]; + if (type == "blob") { + auto blob = part["data"]; + auto bufferPart = m_contentHandler->ResolveMessage( + blob["blobId"].asString(), blob["offset"].asInt(), blob["size"].asInt()); + buffer.reserve(buffer.size() + bufferPart.size()); + buffer.insert(buffer.end(), bufferPart.begin(), bufferPart.end()); + } else if (type == "string") { + auto data = part["data"].asString(); + auto bufferPart = vector(data.begin(), data.end()); + + buffer.reserve(buffer.size() + bufferPart.size()); + buffer.insert(buffer.end(), bufferPart.begin(), bufferPart.end()); + } else { + // TODO: Send error message to instance? + return; + } + + m_contentHandler->StoreMessage(std::move(buffer), std::move(blobId)); + } + }}, + + {"release", + [this](dynamic args) // blobId: string + { + auto blobId = jsArgAsString(args, 0); + + m_contentHandler->RemoveMessage(std::move(blobId)); + }}}; +} + +#pragma endregion CxxModule + +#pragma endregion BlobModule + +#pragma region BlobWebSocketModuleContentHandler + +#pragma region IWebSocketModuleContentHandler + +void BlobWebSocketModuleContentHandler::ProcessMessage(string &&message, dynamic ¶ms) /*override*/ { + params["data"] = std::move(message); +} + +void BlobWebSocketModuleContentHandler::ProcessMessage(vector &&message, dynamic ¶ms) /*override*/ { + auto blob = dynamic::object(); + blob("offset", 0); + blob("size", message.size()); + + // substr(1, 36) strips curly braces from a GUID. + string blobId = winrt::to_string(winrt::to_hstring(GuidHelper::CreateNewGuid())).substr(1, 36); + StoreMessage(std::move(message), std::move(blobId)); + + params["data"] = std::move(blob); + params["type"] = "blob"; +} +#pragma endregion IWebSocketModuleContentHandler + +void BlobWebSocketModuleContentHandler::Register(int64_t socketID) noexcept { + lock_guard lock{m_socketIDsMutex}; + m_socketIDs.insert(socketID); +} + +void BlobWebSocketModuleContentHandler::Unregister(int64_t socketID) noexcept { + lock_guard lock{m_socketIDsMutex}; + if (m_socketIDs.find(socketID) != m_socketIDs.end()) + m_socketIDs.erase(socketID); +} + +vector +BlobWebSocketModuleContentHandler::ResolveMessage(string&& blobId, int64_t offset, int64_t size) noexcept { + lock_guard lock{m_blobsMutex}; + + auto data = m_blobs.at(std::move(blobId)); + auto start = data.cbegin() + static_cast(offset); + auto end = start + static_cast(size); + + return vector(start, end); +} + +void BlobWebSocketModuleContentHandler::RemoveMessage(string&& blobId) noexcept { + lock_guard lock{m_blobsMutex}; + + m_blobs.erase(std::move(blobId)); +} + +void BlobWebSocketModuleContentHandler::StoreMessage(vector&& message, string&& blobId) noexcept { + lock_guard lock{m_blobsMutex}; + + m_blobs.insert_or_assign(std::move(blobId), std::move(message)); +} + +#pragma endregion BlobWebSocketModuleContentHandler + +/*static*/ weak_ptr IWebSocketModuleContentHandler::GetInstance() noexcept { + return s_contentHandler; +} + +}// namespace diff --git a/vnext/Shared/Modules/BlobModule.h b/vnext/Shared/Modules/BlobModule.h index bfb676b9595..535066a7cc4 100644 --- a/vnext/Shared/Modules/BlobModule.h +++ b/vnext/Shared/Modules/BlobModule.h @@ -17,8 +17,36 @@ namespace Microsoft::React { +class BlobWebSocketModuleContentHandler final : public IWebSocketModuleContentHandler { + std::unordered_map> m_blobs; + std::mutex m_blobsMutex; + std::unordered_set m_socketIDs; + std::mutex m_socketIDsMutex; + + public: +#pragma region IWebSocketModuleContentHandler + + void ProcessMessage(std::string &&message, folly::dynamic ¶ms) override; + + void ProcessMessage(std::vector &&message, folly::dynamic ¶ms) override; + +#pragma endregion IWebSocketModuleContentHandler + + void Register(int64_t socketID) noexcept; + + void Unregister(int64_t socketID) noexcept; + + // const bool IsRegistered(std::int64_t socketID) noexcept override; + + std::vector ResolveMessage(std::string &&blobId, int64_t offset, int64_t size) noexcept; + + void RemoveMessage(std::string &&blobId) noexcept; + + void StoreMessage(std::vector &&message, std::string &&blobId) noexcept; +}; + class BlobModule : public facebook::xplat::module::CxxModule { - std::shared_ptr m_contentHandler; + std::shared_ptr m_contentHandler; public: enum class MethodId { @@ -33,7 +61,7 @@ class BlobModule : public facebook::xplat::module::CxxModule { BlobModule() noexcept; -#pragma region CxxModule overrides +#pragma region CxxModule /// /// @@ -51,23 +79,7 @@ class BlobModule : public facebook::xplat::module::CxxModule { /// See See react-native/Libraries/WebSocket/WebSocket.js std::vector getMethods() override; -#pragma endregion CxxModule overrides -}; - -class BlobWebSocketModuleContentHandler final : public IWebSocketModuleContentHandler { - std::unordered_map> m_blobs; - std::mutex m_blobsMutex; - std::unordered_set m_socketIDs; - std::mutex m_socketIDsMutex; - - public: -#pragma region IWebSocketModuleContentHandler overrides - - void ProcessMessage(std::string &&message, folly::dynamic ¶ms) override; - - void ProcessMessage(std::vector &&message, folly::dynamic ¶ms) override; - -#pragma endregion IWebSocketModuleContentHandler overrides +#pragma endregion CxxModule }; } // namespace Microsoft::React diff --git a/vnext/Shared/Shared.vcxitems b/vnext/Shared/Shared.vcxitems index 8dead25f77e..20401b87ee5 100644 --- a/vnext/Shared/Shared.vcxitems +++ b/vnext/Shared/Shared.vcxitems @@ -45,6 +45,7 @@ true + diff --git a/vnext/Shared/Shared.vcxitems.filters b/vnext/Shared/Shared.vcxitems.filters index 0eb5f589e64..dee270cc3ac 100644 --- a/vnext/Shared/Shared.vcxitems.filters +++ b/vnext/Shared/Shared.vcxitems.filters @@ -133,6 +133,9 @@ Source Files + + Source Files\Modules + From b67aa7b37499ca7b02935690ad45fcef6c73c758 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Fri, 7 Jan 2022 02:47:25 -0500 Subject: [PATCH 03/91] Avoid raw self pointer in BlobModule --- vnext/Shared/Modules/BlobModule.cpp | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/vnext/Shared/Modules/BlobModule.cpp b/vnext/Shared/Modules/BlobModule.cpp index 9f7ea70fddd..f22241376b0 100644 --- a/vnext/Shared/Modules/BlobModule.cpp +++ b/vnext/Shared/Modules/BlobModule.cpp @@ -16,6 +16,7 @@ using namespace facebook::xplat; +using facebook::react::Instance; using folly::dynamic; using std::lock_guard; using std::mutex; @@ -57,35 +58,35 @@ std::map BlobModule::getConstants() { std::vector BlobModule::getMethods() { return { {"addNetworkingHandler", - [this](dynamic args) { + [](dynamic args) { // TODO: Implement #6081 }}, {"addWebSocketHandler", - [this](dynamic args) { + [contentHandler = m_contentHandler](dynamic args) { auto id = jsArgAsInt(args, 0); - m_contentHandler->Register(id); + contentHandler->Register(id); }}, {"removeWebSocketHandler", - [this](dynamic args) { + [contentHandler = m_contentHandler](dynamic args) { auto id = jsArgAsInt(args, 0); - m_contentHandler->Unregister(id); + contentHandler->Unregister(id); }}, {"sendOverSocket", - [this](dynamic args) { + [contentHandler = m_contentHandler, weakInstance = weak_ptr(getInstance())](dynamic args) { auto blob = jsArgAsObject(args, 0); auto blobId = blob["blobId"].getString(); auto offset = blob["offset"].getInt(); auto size = blob["size"].getInt(); auto socketID = jsArgAsInt(args, 1); - auto data = m_contentHandler->ResolveMessage(std::move(blobId), offset, size); + auto data = contentHandler->ResolveMessage(std::move(blobId), offset, size); - if (auto instance = getInstance().lock()) { + if (auto instance = weakInstance.lock()) { auto buffer = CryptographicBuffer::CreateFromByteArray(data); auto winrtString = CryptographicBuffer::EncodeToBase64String(std::move(buffer)); auto base64String = Common::Unicode::Utf16ToUtf8(std::move(winrtString)); @@ -96,7 +97,7 @@ std::vector BlobModule::getMethods() { }}, {"createFromParts", - [this](dynamic args) { + [contentHandler = m_contentHandler](dynamic args) { auto parts = jsArgAsArray(args, 0); // Array auto blobId = jsArgAsString(args, 1); vector buffer{}; @@ -105,7 +106,7 @@ std::vector BlobModule::getMethods() { auto type = part["type"]; if (type == "blob") { auto blob = part["data"]; - auto bufferPart = m_contentHandler->ResolveMessage( + auto bufferPart = contentHandler->ResolveMessage( blob["blobId"].asString(), blob["offset"].asInt(), blob["size"].asInt()); buffer.reserve(buffer.size() + bufferPart.size()); buffer.insert(buffer.end(), bufferPart.begin(), bufferPart.end()); @@ -120,16 +121,16 @@ std::vector BlobModule::getMethods() { return; } - m_contentHandler->StoreMessage(std::move(buffer), std::move(blobId)); + contentHandler->StoreMessage(std::move(buffer), std::move(blobId)); } }}, {"release", - [this](dynamic args) // blobId: string + [contentHandler = m_contentHandler](dynamic args) // blobId: string { auto blobId = jsArgAsString(args, 0); - m_contentHandler->RemoveMessage(std::move(blobId)); + contentHandler->RemoveMessage(std::move(blobId)); }}}; } From bad5d0c2717cef70dbecf0f54e5b37b92bbb2786 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Fri, 7 Jan 2022 02:47:52 -0500 Subject: [PATCH 04/91] Implement WebSocketModule msg processing --- vnext/Shared/Modules/WebSocketModule.cpp | 31 +++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/vnext/Shared/Modules/WebSocketModule.cpp b/vnext/Shared/Modules/WebSocketModule.cpp index 3e70ebe8c67..23feb5d5d84 100644 --- a/vnext/Shared/Modules/WebSocketModule.cpp +++ b/vnext/Shared/Modules/WebSocketModule.cpp @@ -5,11 +5,15 @@ #include -#include +#include #include #include #include "Unicode.h" +// Windows API +#include +#include + // Standard Libriary #include @@ -18,13 +22,14 @@ using namespace facebook::xplat; using facebook::react::Instance; using folly::dynamic; -using Microsoft::Common::Unicode::Utf16ToUtf8; using Microsoft::Common::Unicode::Utf8ToUtf16; using std::shared_ptr; using std::string; using std::weak_ptr; +using winrt::Windows::Security::Cryptography::CryptographicBuffer; + namespace { using Microsoft::React::IWebSocketResource; using Microsoft::React::WebSocketModule; @@ -96,7 +101,27 @@ GetOrCreateWebSocket(int64_t id, string &&url, weak_ptr arr; + CryptographicBuffer::CopyToByteArray(buffer, arr); + auto data = std::vector(arr.begin(), arr.end()); + + contentHandler->ProcessMessage(std::move(data), args); + } + else { + contentHandler->ProcessMessage(string{message}, args); + } + SendEvent(weakInstance, "websocketMessage", std::move(args)); }); ws->SetOnClose([id, weakInstance](IWebSocketResource::CloseCode code, const string &reason) { From a7fb222e0ed5a5893f741c44cd65f9a7a6f56f63 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Fri, 7 Jan 2022 02:50:02 -0500 Subject: [PATCH 05/91] clang format --- vnext/Shared/Modules/BlobModule.cpp | 18 +++++++++--------- .../Modules/IWebSocketModuleContentHandler.h | 4 +--- vnext/Shared/Modules/WebSocketModule.cpp | 3 +-- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/vnext/Shared/Modules/BlobModule.cpp b/vnext/Shared/Modules/BlobModule.cpp index f22241376b0..9b51da8c1c4 100644 --- a/vnext/Shared/Modules/BlobModule.cpp +++ b/vnext/Shared/Modules/BlobModule.cpp @@ -90,7 +90,7 @@ std::vector BlobModule::getMethods() { auto buffer = CryptographicBuffer::CreateFromByteArray(data); auto winrtString = CryptographicBuffer::EncodeToBase64String(std::move(buffer)); auto base64String = Common::Unicode::Utf16ToUtf8(std::move(winrtString)); - + auto sendArgs = dynamic::array(std::move(base64String), socketID); instance->callJSFunction("WebSocketModule", "sendBinary", std::move(sendArgs)); } @@ -106,10 +106,10 @@ std::vector BlobModule::getMethods() { auto type = part["type"]; if (type == "blob") { auto blob = part["data"]; - auto bufferPart = contentHandler->ResolveMessage( - blob["blobId"].asString(), blob["offset"].asInt(), blob["size"].asInt()); - buffer.reserve(buffer.size() + bufferPart.size()); - buffer.insert(buffer.end(), bufferPart.begin(), bufferPart.end()); + auto bufferPart = contentHandler->ResolveMessage( + blob["blobId"].asString(), blob["offset"].asInt(), blob["size"].asInt()); + buffer.reserve(buffer.size() + bufferPart.size()); + buffer.insert(buffer.end(), bufferPart.begin(), bufferPart.end()); } else if (type == "string") { auto data = part["data"].asString(); auto bufferPart = vector(data.begin(), data.end()); @@ -172,7 +172,7 @@ void BlobWebSocketModuleContentHandler::Unregister(int64_t socketID) noexcept { } vector -BlobWebSocketModuleContentHandler::ResolveMessage(string&& blobId, int64_t offset, int64_t size) noexcept { +BlobWebSocketModuleContentHandler::ResolveMessage(string &&blobId, int64_t offset, int64_t size) noexcept { lock_guard lock{m_blobsMutex}; auto data = m_blobs.at(std::move(blobId)); @@ -182,13 +182,13 @@ BlobWebSocketModuleContentHandler::ResolveMessage(string&& blobId, int64_t offse return vector(start, end); } -void BlobWebSocketModuleContentHandler::RemoveMessage(string&& blobId) noexcept { +void BlobWebSocketModuleContentHandler::RemoveMessage(string &&blobId) noexcept { lock_guard lock{m_blobsMutex}; m_blobs.erase(std::move(blobId)); } -void BlobWebSocketModuleContentHandler::StoreMessage(vector&& message, string&& blobId) noexcept { +void BlobWebSocketModuleContentHandler::StoreMessage(vector &&message, string &&blobId) noexcept { lock_guard lock{m_blobsMutex}; m_blobs.insert_or_assign(std::move(blobId), std::move(message)); @@ -200,4 +200,4 @@ void BlobWebSocketModuleContentHandler::StoreMessage(vector&& message, return s_contentHandler; } -}// namespace +} // namespace Microsoft::React diff --git a/vnext/Shared/Modules/IWebSocketModuleContentHandler.h b/vnext/Shared/Modules/IWebSocketModuleContentHandler.h index 0130a6b28cf..36eb7824d0b 100644 --- a/vnext/Shared/Modules/IWebSocketModuleContentHandler.h +++ b/vnext/Shared/Modules/IWebSocketModuleContentHandler.h @@ -16,7 +16,6 @@ namespace Microsoft::React { /// See https://github.com/facebook/react-native/blob/v0.63.2/React/CoreModules/RCTWebSocketModule.h#L12 /// struct IWebSocketModuleContentHandler { - static std::weak_ptr GetInstance() noexcept; virtual ~IWebSocketModuleContentHandler() noexcept {} @@ -24,7 +23,6 @@ struct IWebSocketModuleContentHandler { virtual void ProcessMessage(std::string &&message, folly::dynamic ¶ms) = 0; virtual void ProcessMessage(std::vector &&message, folly::dynamic ¶ms) = 0; - }; -} // namespace +} // namespace Microsoft::React diff --git a/vnext/Shared/Modules/WebSocketModule.cpp b/vnext/Shared/Modules/WebSocketModule.cpp index 23feb5d5d84..e2ec3e9e27e 100644 --- a/vnext/Shared/Modules/WebSocketModule.cpp +++ b/vnext/Shared/Modules/WebSocketModule.cpp @@ -117,8 +117,7 @@ GetOrCreateWebSocket(int64_t id, string &&url, weak_ptr(arr.begin(), arr.end()); contentHandler->ProcessMessage(std::move(data), args); - } - else { + } else { contentHandler->ProcessMessage(string{message}, args); } From a1e20434bb3812b86b2bf243533c4d5a19e48d93 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Sat, 8 Jan 2022 15:25:36 -0500 Subject: [PATCH 06/91] Don't return until websocketMessage event is sent --- vnext/Shared/Modules/WebSocketModule.cpp | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/vnext/Shared/Modules/WebSocketModule.cpp b/vnext/Shared/Modules/WebSocketModule.cpp index e2ec3e9e27e..fcaea13f0e8 100644 --- a/vnext/Shared/Modules/WebSocketModule.cpp +++ b/vnext/Shared/Modules/WebSocketModule.cpp @@ -106,19 +106,17 @@ GetOrCreateWebSocket(int64_t id, string &&url, weak_ptr arr; - CryptographicBuffer::CopyToByteArray(buffer, arr); - auto data = std::vector(arr.begin(), arr.end()); - - contentHandler->ProcessMessage(std::move(data), args); } else { - contentHandler->ProcessMessage(string{message}, args); + if (isBinary) { + auto buffer = CryptographicBuffer::DecodeFromBase64String(Utf8ToUtf16(message)); + winrt::com_array arr; + CryptographicBuffer::CopyToByteArray(buffer, arr); + auto data = std::vector(arr.begin(), arr.end()); + + contentHandler->ProcessMessage(std::move(data), args); + } else { + contentHandler->ProcessMessage(string{message}, args); + } } SendEvent(weakInstance, "websocketMessage", std::move(args)); From 2fa17fada38176414773b855f8bd70016814b19a Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Sat, 8 Jan 2022 15:49:29 -0500 Subject: [PATCH 07/91] Define CreateBlobModule() --- vnext/Shared/CreateModules.h | 2 ++ vnext/Shared/Modules/BlobModule.cpp | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/vnext/Shared/CreateModules.h b/vnext/Shared/CreateModules.h index 6386b13130e..cc2ad4b6dc1 100644 --- a/vnext/Shared/CreateModules.h +++ b/vnext/Shared/CreateModules.h @@ -34,4 +34,6 @@ namespace Microsoft::React { extern std::unique_ptr CreateWebSocketModule() noexcept; +extern std::unique_ptr CreateBlobModule() noexcept; + } // namespace Microsoft::React diff --git a/vnext/Shared/Modules/BlobModule.cpp b/vnext/Shared/Modules/BlobModule.cpp index 9b51da8c1c4..772c69b03e0 100644 --- a/vnext/Shared/Modules/BlobModule.cpp +++ b/vnext/Shared/Modules/BlobModule.cpp @@ -200,4 +200,8 @@ void BlobWebSocketModuleContentHandler::StoreMessage(vector &&message, return s_contentHandler; } +/*extern*/ std::unique_ptr CreateBlobModule() noexcept { + return std::make_unique(); +} + } // namespace Microsoft::React From 8be75e608e77a0456447d72bea8a5a4528551d23 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Sat, 8 Jan 2022 16:11:52 -0500 Subject: [PATCH 08/91] Add DEF exports --- vnext/Desktop.DLL/react-native-win32.x64.def | 1 + vnext/Desktop.DLL/react-native-win32.x86.def | 1 + 2 files changed, 2 insertions(+) diff --git a/vnext/Desktop.DLL/react-native-win32.x64.def b/vnext/Desktop.DLL/react-native-win32.x64.def index bf32f6fc7c6..456201a142a 100644 --- a/vnext/Desktop.DLL/react-native-win32.x64.def +++ b/vnext/Desktop.DLL/react-native-win32.x64.def @@ -67,6 +67,7 @@ EXPORTS ??0WebSocketModule@React@Microsoft@@QEAA@XZ ??0NetworkingModule@React@Microsoft@@QEAA@XZ ?CreateAsyncStorageModule@react@facebook@@YA?AV?$unique_ptr@VCxxModule@module@xplat@facebook@@U?$default_delete@VCxxModule@module@xplat@facebook@@@std@@@std@@PEB_W@Z +?CreateBlobModule@React@Microsoft@@YA?AV?$unique_ptr@VCxxModule@module@xplat@facebook@@U?$default_delete@VCxxModule@module@xplat@facebook@@@std@@@std@@XZ ?MakeJSQueueThread@ReactNative@Microsoft@@YA?AV?$shared_ptr@VMessageQueueThread@react@facebook@@@std@@XZ ?Hash128@SpookyHashV2@hash@folly@@SAXPEBX_KPEA_K2@Z ??1Instance@react@facebook@@QEAA@XZ diff --git a/vnext/Desktop.DLL/react-native-win32.x86.def b/vnext/Desktop.DLL/react-native-win32.x86.def index d3487e29742..79ab8148641 100644 --- a/vnext/Desktop.DLL/react-native-win32.x86.def +++ b/vnext/Desktop.DLL/react-native-win32.x86.def @@ -64,6 +64,7 @@ EXPORTS ?CreateTimingModule@react@facebook@@YG?AV?$unique_ptr@VCxxModule@module@xplat@facebook@@U?$default_delete@VCxxModule@module@xplat@facebook@@@std@@@std@@ABV?$shared_ptr@VMessageQueueThread@react@facebook@@@4@@Z ??0WebSocketModule@React@Microsoft@@QAE@XZ ?CreateAsyncStorageModule@react@facebook@@YG?AV?$unique_ptr@VCxxModule@module@xplat@facebook@@U?$default_delete@VCxxModule@module@xplat@facebook@@@std@@@std@@PB_W@Z +?CreateBlobModule@React@Microsoft@@YG?AV?$unique_ptr@VCxxModule@module@xplat@facebook@@U?$default_delete@VCxxModule@module@xplat@facebook@@@std@@@std@@XZ ?Make@IHttpResource@React@Microsoft@@SG?AV?$unique_ptr@UIHttpResource@React@Microsoft@@U?$default_delete@UIHttpResource@React@Microsoft@@@std@@@std@@XZ ??0NetworkingModule@React@Microsoft@@QAE@XZ ?MakeJSQueueThread@ReactNative@Microsoft@@YG?AV?$shared_ptr@VMessageQueueThread@react@facebook@@@std@@XZ From 06de5794c48103ad34b2ad59abececb594b5a37b Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Sat, 8 Jan 2022 19:33:00 -0500 Subject: [PATCH 09/91] Add Blob JS tests --- .../Desktop.IntegrationTests/RNTesterIntegrationTests.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp b/vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp index f75e72c8730..c2bb07d8720 100644 --- a/vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp +++ b/vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp @@ -213,5 +213,13 @@ TEST_CLASS (RNTesterIntegrationTests) { Assert::AreEqual(TestStatus::Passed, result.Status, result.Message.c_str()); } + BEGIN_TEST_METHOD_ATTRIBUTE(WebSocketBlob) + TEST_IGNORE() + END_TEST_METHOD_ATTRIBUTE() + TEST_METHOD(WebSocketBlob) { + auto result = m_runner.RunTest("IntegrationTests/WebSocketBlobTest", "WebSocketBlobTest"); + Assert::AreEqual(TestStatus::Passed, result.Status, result.Message.c_str()); + } + #pragma endregion Extended Tests }; From c17f1ee427a630a982af9523ef4fb98b3b9acbad Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Sat, 8 Jan 2022 19:33:11 -0500 Subject: [PATCH 10/91] Add Blob JS tests --- .../src/IntegrationTests/WebSocketBlobTest.js | 168 ++++++++++++++++++ .../websocket_integration_test_server_blob.js | 34 ++++ 2 files changed, 202 insertions(+) create mode 100644 vnext/src/IntegrationTests/WebSocketBlobTest.js create mode 100644 vnext/src/IntegrationTests/websocket_integration_test_server_blob.js diff --git a/vnext/src/IntegrationTests/WebSocketBlobTest.js b/vnext/src/IntegrationTests/WebSocketBlobTest.js new file mode 100644 index 00000000000..5bea4dc2cb6 --- /dev/null +++ b/vnext/src/IntegrationTests/WebSocketBlobTest.js @@ -0,0 +1,168 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + * @flow + */ + + 'use strict'; + + const React = require('react'); + const ReactNative = require('react-native'); + const {AppRegistry, View} = ReactNative; + const {TestModule} = ReactNative.NativeModules; + + const DEFAULT_WS_URL = 'ws://localhost:5557/'; + + const WS_EVENTS = ['close', 'error', 'message', 'open']; + + type State = { + url: string, + fetchStatus: ?string, + socket: ?WebSocket, + socketState: ?number, + lastSocketEvent: ?string, + lastMessage: ?Blob, + testMessage: Uint8Array, + testExpectedResponse: Uint8Array, + ... + }; + + class WebSocketBlobTest extends React.Component<{}, State> { + state: State = { + url: DEFAULT_WS_URL, + fetchStatus: null, + socket: null, + socketState: null, + lastSocketEvent: null, + lastMessage: null, + testMessage: new Uint8Array([1, 2, 3]), + testExpectedResponse: new Uint8Array([4, 5, 6, 7]), + }; + + _waitFor = (condition: any, timeout: any, callback: any) => { + let remaining = timeout; + const timeoutFunction = function() { + if (condition()) { + callback(true); + return; + } + remaining--; + if (remaining === 0) { + callback(false); + } else { + setTimeout(timeoutFunction, 1000); + } + }; + setTimeout(timeoutFunction, 1000); + }; + + _connect = () => { + const socket = new WebSocket(this.state.url); + socket.binaryType = 'blob'; + WS_EVENTS.forEach(ev => socket.addEventListener(ev, this._onSocketEvent)); + this.setState({ + socket, + socketState: socket.readyState, + }); + }; + + _socketIsConnected = () => { + return this.state.socketState === 1; //'OPEN' + }; + + _socketIsDisconnected = () => { + return this.state.socketState === 3; //'CLOSED' + }; + + _disconnect = () => { + if (!this.state.socket) { + return; + } + this.state.socket.close(); + }; + + _onSocketEvent = (event: any) => { + const state: any = { + socketState: event.target.readyState, + lastSocketEvent: event.type, + }; + if (event.type === 'message') { + state.lastMessage = event.data; + } + this.setState(state); + }; + + _sendBinary = (message: Blob) => { + if (!this.state.socket) { + return; + } + this.state.socket.send(message); + }; + + _sendTestMessage = () => { + this._sendBinary(this.state.testMessage); + }; + + _receivedTestExpectedResponse = () => { + if ( + this.state.lastMessage?.size !== this.state.testExpectedResponse.length + ) { + return false; + } + + //for (var i = 0; i < expected.length; i++) { + // if (expected[i] !== result[i]) { + // return false; + // } + //} + + return true; + }; + + componentDidMount() { + this.testConnect(); + } + + testConnect: () => void = () => { + this._connect(); + this._waitFor(this._socketIsConnected, 5, connectSucceeded => { + if (!connectSucceeded) { + TestModule.markTestPassed(false); + return; + } + this.testSendAndReceive(); + }); + }; + + testSendAndReceive: () => void = () => { + this._sendTestMessage(); + this._waitFor(this._receivedTestExpectedResponse, 5, messageReceived => { + if (!messageReceived) { + TestModule.markTestPassed(false); + return; + } + this.testDisconnect(); + }); + }; + + testDisconnect: () => void = () => { + this._disconnect(); + this._waitFor(this._socketIsDisconnected, 5, disconnectSucceeded => { + TestModule.markTestPassed(disconnectSucceeded); + }); + }; + + render(): React.Node { + return ; + } + } + + WebSocketBlobTest.displayName = 'WebSocketBlobTest'; + + AppRegistry.registerComponent('WebSocketBlobTest', () => WebSocketBlobTest); + + module.exports = WebSocketBlobTest; diff --git a/vnext/src/IntegrationTests/websocket_integration_test_server_blob.js b/vnext/src/IntegrationTests/websocket_integration_test_server_blob.js new file mode 100644 index 00000000000..f415043d917 --- /dev/null +++ b/vnext/src/IntegrationTests/websocket_integration_test_server_blob.js @@ -0,0 +1,34 @@ +#!/usr/bin/env node +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + * @format + */ + +'use strict'; + +/* eslint-env node */ + +const WebSocket = require('ws'); +const Blob = require('node-fetch'); + +console.log(`\ +WebSocket binary integration test server + +This will send each incoming message back, in binary form. + +`); + +const server = new WebSocket.Server({port: 5557}); +server.on('connection', ws => { + ws.binaryType = "blob"; + ws.on('message', message => { + console.log(message); + + ws.send([4, 5, 6, 7]); + }); +}); From 8b072cf3b326e07bc14762ef174646429797c30a Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Sat, 8 Jan 2022 23:53:18 -0500 Subject: [PATCH 11/91] Change files --- ...ative-windows-2c5e8e12-8a2e-46b9-8b4f-3f9bdec5faca.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 change/react-native-windows-2c5e8e12-8a2e-46b9-8b4f-3f9bdec5faca.json diff --git a/change/react-native-windows-2c5e8e12-8a2e-46b9-8b4f-3f9bdec5faca.json b/change/react-native-windows-2c5e8e12-8a2e-46b9-8b4f-3f9bdec5faca.json new file mode 100644 index 00000000000..9c6be33fb71 --- /dev/null +++ b/change/react-native-windows-2c5e8e12-8a2e-46b9-8b4f-3f9bdec5faca.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "Implement Blob module", + "packageName": "react-native-windows", + "email": "julio.rocha@microsoft.com", + "dependentChangeType": "patch" +} From cf193675b99e5a20fd3eaa520f4deb1405251b6c Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Mon, 10 Jan 2022 19:24:19 -0800 Subject: [PATCH 12/91] yarn lint --- .../src/IntegrationTests/WebSocketBlobTest.js | 315 +++++++++--------- .../websocket_integration_test_server_blob.js | 6 +- 2 files changed, 160 insertions(+), 161 deletions(-) diff --git a/vnext/src/IntegrationTests/WebSocketBlobTest.js b/vnext/src/IntegrationTests/WebSocketBlobTest.js index 5bea4dc2cb6..00a2cf45cb1 100644 --- a/vnext/src/IntegrationTests/WebSocketBlobTest.js +++ b/vnext/src/IntegrationTests/WebSocketBlobTest.js @@ -8,161 +8,160 @@ * @flow */ - 'use strict'; - - const React = require('react'); - const ReactNative = require('react-native'); - const {AppRegistry, View} = ReactNative; - const {TestModule} = ReactNative.NativeModules; - - const DEFAULT_WS_URL = 'ws://localhost:5557/'; - - const WS_EVENTS = ['close', 'error', 'message', 'open']; - - type State = { - url: string, - fetchStatus: ?string, - socket: ?WebSocket, - socketState: ?number, - lastSocketEvent: ?string, - lastMessage: ?Blob, - testMessage: Uint8Array, - testExpectedResponse: Uint8Array, - ... - }; - - class WebSocketBlobTest extends React.Component<{}, State> { - state: State = { - url: DEFAULT_WS_URL, - fetchStatus: null, - socket: null, - socketState: null, - lastSocketEvent: null, - lastMessage: null, - testMessage: new Uint8Array([1, 2, 3]), - testExpectedResponse: new Uint8Array([4, 5, 6, 7]), - }; - - _waitFor = (condition: any, timeout: any, callback: any) => { - let remaining = timeout; - const timeoutFunction = function() { - if (condition()) { - callback(true); - return; - } - remaining--; - if (remaining === 0) { - callback(false); - } else { - setTimeout(timeoutFunction, 1000); - } - }; - setTimeout(timeoutFunction, 1000); - }; - - _connect = () => { - const socket = new WebSocket(this.state.url); - socket.binaryType = 'blob'; - WS_EVENTS.forEach(ev => socket.addEventListener(ev, this._onSocketEvent)); - this.setState({ - socket, - socketState: socket.readyState, - }); - }; - - _socketIsConnected = () => { - return this.state.socketState === 1; //'OPEN' - }; - - _socketIsDisconnected = () => { - return this.state.socketState === 3; //'CLOSED' - }; - - _disconnect = () => { - if (!this.state.socket) { - return; - } - this.state.socket.close(); - }; - - _onSocketEvent = (event: any) => { - const state: any = { - socketState: event.target.readyState, - lastSocketEvent: event.type, - }; - if (event.type === 'message') { - state.lastMessage = event.data; - } - this.setState(state); - }; - - _sendBinary = (message: Blob) => { - if (!this.state.socket) { - return; - } - this.state.socket.send(message); - }; - - _sendTestMessage = () => { - this._sendBinary(this.state.testMessage); - }; - - _receivedTestExpectedResponse = () => { - if ( - this.state.lastMessage?.size !== this.state.testExpectedResponse.length - ) { - return false; - } - - //for (var i = 0; i < expected.length; i++) { - // if (expected[i] !== result[i]) { - // return false; - // } - //} - - return true; - }; - - componentDidMount() { - this.testConnect(); - } - - testConnect: () => void = () => { - this._connect(); - this._waitFor(this._socketIsConnected, 5, connectSucceeded => { - if (!connectSucceeded) { - TestModule.markTestPassed(false); - return; - } - this.testSendAndReceive(); - }); - }; - - testSendAndReceive: () => void = () => { - this._sendTestMessage(); - this._waitFor(this._receivedTestExpectedResponse, 5, messageReceived => { - if (!messageReceived) { - TestModule.markTestPassed(false); - return; - } - this.testDisconnect(); - }); - }; - - testDisconnect: () => void = () => { - this._disconnect(); - this._waitFor(this._socketIsDisconnected, 5, disconnectSucceeded => { - TestModule.markTestPassed(disconnectSucceeded); - }); - }; - - render(): React.Node { - return ; - } - } - - WebSocketBlobTest.displayName = 'WebSocketBlobTest'; - - AppRegistry.registerComponent('WebSocketBlobTest', () => WebSocketBlobTest); - - module.exports = WebSocketBlobTest; +'use strict'; + +const React = require('react'); +const ReactNative = require('react-native'); +const {AppRegistry, View} = ReactNative; +const {TestModule} = ReactNative.NativeModules; + +const DEFAULT_WS_URL = 'ws://localhost:5557/'; + +const WS_EVENTS = ['close', 'error', 'message', 'open']; + +type State = { + url: string, + fetchStatus: ?string, + socket: ?WebSocket, + socketState: ?number, + lastSocketEvent: ?string, + lastMessage: ?Blob, + testMessage: Uint8Array, + testExpectedResponse: Uint8Array, + ... +}; + +class WebSocketBlobTest extends React.Component<{}, State> { + state: State = { + url: DEFAULT_WS_URL, + fetchStatus: null, + socket: null, + socketState: null, + lastSocketEvent: null, + lastMessage: null, + testMessage: new Uint8Array([1, 2, 3]), + testExpectedResponse: new Uint8Array([4, 5, 6, 7]), + }; + + _waitFor = (condition: any, timeout: any, callback: any) => { + let remaining = timeout; + const timeoutFunction = function () { + if (condition()) { + callback(true); + return; + } + remaining--; + if (remaining === 0) { + callback(false); + } else { + setTimeout(timeoutFunction, 1000); + } + }; + setTimeout(timeoutFunction, 1000); + }; + + _connect = () => { + const socket = new WebSocket(this.state.url); + socket.binaryType = 'blob'; + WS_EVENTS.forEach((ev) => socket.addEventListener(ev, this._onSocketEvent)); + this.setState({ + socket, + socketState: socket.readyState, + }); + }; + + _socketIsConnected = () => { + return this.state.socketState === 1; //'OPEN' + }; + + _socketIsDisconnected = () => { + return this.state.socketState === 3; //'CLOSED' + }; + + _disconnect = () => { + if (!this.state.socket) { + return; + } + this.state.socket.close(); + }; + + _onSocketEvent = (event: any) => { + const state: any = { + socketState: event.target.readyState, + lastSocketEvent: event.type, + }; + if (event.type === 'message') { + state.lastMessage = event.data; + } + this.setState(state); + }; + + _sendBinary = (message: Uint8Array) => { + if (!this.state.socket) { + return; + } + this.state.socket.send(message); + }; + + _sendTestMessage = () => { + this._sendBinary(this.state.testMessage); + }; + + _receivedTestExpectedResponse = () => { + if ( + this.state.lastMessage?.size !== this.state.testExpectedResponse.length + ) { + return false; + } + + //for (var i = 0; i < expected.length; i++) { + // if (expected[i] !== result[i]) { + // return false; + // } + //} + + return true; + }; + + componentDidMount() { + this.testConnect(); + } + + testConnect: () => void = () => { + this._connect(); + this._waitFor(this._socketIsConnected, 5, (connectSucceeded) => { + if (!connectSucceeded) { + TestModule.markTestPassed(false); + return; + } + this.testSendAndReceive(); + }); + }; + + testSendAndReceive: () => void = () => { + this._sendTestMessage(); + this._waitFor(this._receivedTestExpectedResponse, 5, (messageReceived) => { + if (!messageReceived) { + TestModule.markTestPassed(false); + return; + } + this.testDisconnect(); + }); + }; + + testDisconnect: () => void = () => { + this._disconnect(); + this._waitFor(this._socketIsDisconnected, 5, (disconnectSucceeded) => { + TestModule.markTestPassed(disconnectSucceeded); + }); + }; + render(): React.Node { + return ; + } +} // class WebSocketBlobTest + +WebSocketBlobTest.displayName = 'WebSocketBlobTest'; + +AppRegistry.registerComponent('WebSocketBlobTest', () => WebSocketBlobTest); + +module.exports = WebSocketBlobTest; diff --git a/vnext/src/IntegrationTests/websocket_integration_test_server_blob.js b/vnext/src/IntegrationTests/websocket_integration_test_server_blob.js index f415043d917..7fb46a949d2 100644 --- a/vnext/src/IntegrationTests/websocket_integration_test_server_blob.js +++ b/vnext/src/IntegrationTests/websocket_integration_test_server_blob.js @@ -24,9 +24,9 @@ This will send each incoming message back, in binary form. `); const server = new WebSocket.Server({port: 5557}); -server.on('connection', ws => { - ws.binaryType = "blob"; - ws.on('message', message => { +server.on('connection', (ws) => { + ws.binaryType = 'blob'; + ws.on('message', (message) => { console.log(message); ws.send([4, 5, 6, 7]); From a4ffa1f8d63608c9a8ee0cedb690a7fef44ac8ca Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Mon, 10 Jan 2022 22:57:45 -0800 Subject: [PATCH 13/91] Add overrides --- vnext/overrides.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/vnext/overrides.json b/vnext/overrides.json index ba5b3b2a811..3c22096e228 100644 --- a/vnext/overrides.json +++ b/vnext/overrides.json @@ -91,10 +91,18 @@ "type": "platform", "file": "src/IntegrationTests/websocket_integration_test_server_binary.js" }, + { + "type": "platform", + "file": "src/IntegrationTests/websocket_integration_test_server_blob.js" + }, { "type": "platform", "file": "src/IntegrationTests/WebSocketBinaryTest.js" }, + { + "type": "platform", + "file": "src/IntegrationTests/WebSocketBlobTest.js" + }, { "type": "platform", "file": "src/IntegrationTests/XHRTest.js" @@ -475,4 +483,4 @@ "file": "src/typings-index.ts" } ] -} \ No newline at end of file +} From 8c21458723f82e6afe780035dfaa9e6a572a2141 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Tue, 11 Jan 2022 19:16:47 -0800 Subject: [PATCH 14/91] Register BlobModule in DesktopTestRunner --- vnext/Desktop.DLL/react-native-win32.x64.def | 2 +- vnext/Desktop.DLL/react-native-win32.x86.def | 2 +- vnext/Desktop.IntegrationTests/DesktopTestRunner.cpp | 6 ++++-- vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/vnext/Desktop.DLL/react-native-win32.x64.def b/vnext/Desktop.DLL/react-native-win32.x64.def index 2a9e0d989e7..1f353c5e42e 100644 --- a/vnext/Desktop.DLL/react-native-win32.x64.def +++ b/vnext/Desktop.DLL/react-native-win32.x64.def @@ -61,10 +61,10 @@ EXPORTS ?makeChakraRuntime@JSI@Microsoft@@YA?AV?$unique_ptr@VRuntime@jsi@facebook@@U?$default_delete@VRuntime@jsi@facebook@@@std@@@std@@$$QEAUChakraRuntimeArgs@12@@Z ?Make@IHttpResource@React@Microsoft@@SA?AV?$unique_ptr@UIHttpResource@React@Microsoft@@U?$default_delete@UIHttpResource@React@Microsoft@@@std@@@std@@XZ ?CreateTimingModule@react@facebook@@YA?AV?$unique_ptr@VCxxModule@module@xplat@facebook@@U?$default_delete@VCxxModule@module@xplat@facebook@@@std@@@std@@AEBV?$shared_ptr@VMessageQueueThread@react@facebook@@@4@@Z -??0WebSocketModule@React@Microsoft@@QEAA@XZ ??0NetworkingModule@React@Microsoft@@QEAA@XZ ?CreateAsyncStorageModule@react@facebook@@YA?AV?$unique_ptr@VCxxModule@module@xplat@facebook@@U?$default_delete@VCxxModule@module@xplat@facebook@@@std@@@std@@PEB_W@Z ?CreateBlobModule@React@Microsoft@@YA?AV?$unique_ptr@VCxxModule@module@xplat@facebook@@U?$default_delete@VCxxModule@module@xplat@facebook@@@std@@@std@@XZ +?CreateWebSocketModule@React@Microsoft@@YA?AV?$unique_ptr@VCxxModule@module@xplat@facebook@@U?$default_delete@VCxxModule@module@xplat@facebook@@@std@@@std@@XZ ?MakeJSQueueThread@ReactNative@Microsoft@@YA?AV?$shared_ptr@VMessageQueueThread@react@facebook@@@std@@XZ ?Hash128@SpookyHashV2@hash@folly@@SAXPEBX_KPEA_K2@Z ??1Instance@react@facebook@@QEAA@XZ diff --git a/vnext/Desktop.DLL/react-native-win32.x86.def b/vnext/Desktop.DLL/react-native-win32.x86.def index 9823d4e139b..c48dac54b16 100644 --- a/vnext/Desktop.DLL/react-native-win32.x86.def +++ b/vnext/Desktop.DLL/react-native-win32.x86.def @@ -59,9 +59,9 @@ EXPORTS ?GetRuntimeOptionInt@React@Microsoft@@YA?BHABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z ?makeChakraRuntime@JSI@Microsoft@@YG?AV?$unique_ptr@VRuntime@jsi@facebook@@U?$default_delete@VRuntime@jsi@facebook@@@std@@@std@@$$QAUChakraRuntimeArgs@12@@Z ?CreateTimingModule@react@facebook@@YG?AV?$unique_ptr@VCxxModule@module@xplat@facebook@@U?$default_delete@VCxxModule@module@xplat@facebook@@@std@@@std@@ABV?$shared_ptr@VMessageQueueThread@react@facebook@@@4@@Z -??0WebSocketModule@React@Microsoft@@QAE@XZ ?CreateAsyncStorageModule@react@facebook@@YG?AV?$unique_ptr@VCxxModule@module@xplat@facebook@@U?$default_delete@VCxxModule@module@xplat@facebook@@@std@@@std@@PB_W@Z ?CreateBlobModule@React@Microsoft@@YG?AV?$unique_ptr@VCxxModule@module@xplat@facebook@@U?$default_delete@VCxxModule@module@xplat@facebook@@@std@@@std@@XZ +?CreateWebSocketModule@React@Microsoft@@YG?AV?$unique_ptr@VCxxModule@module@xplat@facebook@@U?$default_delete@VCxxModule@module@xplat@facebook@@@std@@@std@@XZ ?Make@IHttpResource@React@Microsoft@@SG?AV?$unique_ptr@UIHttpResource@React@Microsoft@@U?$default_delete@UIHttpResource@React@Microsoft@@@std@@@std@@XZ ??0NetworkingModule@React@Microsoft@@QAE@XZ ?MakeJSQueueThread@ReactNative@Microsoft@@YG?AV?$shared_ptr@VMessageQueueThread@react@facebook@@@std@@XZ diff --git a/vnext/Desktop.IntegrationTests/DesktopTestRunner.cpp b/vnext/Desktop.IntegrationTests/DesktopTestRunner.cpp index b9de42423bb..ba6c534ed35 100644 --- a/vnext/Desktop.IntegrationTests/DesktopTestRunner.cpp +++ b/vnext/Desktop.IntegrationTests/DesktopTestRunner.cpp @@ -50,7 +50,7 @@ shared_ptr TestRunner::GetInstance( }, nativeQueue}, - {"WebSocketModule", []() -> unique_ptr { return std::make_unique(); }, nativeQueue}, + {"WebSocketModule", []() -> unique_ptr { return CreateWebSocketModule(); }, nativeQueue}, {"Networking", []() -> unique_ptr { return std::make_unique(); }, @@ -77,7 +77,9 @@ shared_ptr TestRunner::GetInstance( {TestImageLoaderModule::name, []() -> unique_ptr { return std::make_unique(); }, - nativeQueue}}; + nativeQueue}, + + {"BlobModule", []() -> unique_ptr { return CreateBlobModule(); }, nativeQueue}}; // <0> string // <1> CxxModule::Provider diff --git a/vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp b/vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp index c2bb07d8720..1212249470c 100644 --- a/vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp +++ b/vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp @@ -214,7 +214,7 @@ TEST_CLASS (RNTesterIntegrationTests) { } BEGIN_TEST_METHOD_ATTRIBUTE(WebSocketBlob) - TEST_IGNORE() + // TEST_IGNORE() END_TEST_METHOD_ATTRIBUTE() TEST_METHOD(WebSocketBlob) { auto result = m_runner.RunTest("IntegrationTests/WebSocketBlobTest", "WebSocketBlobTest"); From 44c87e90169b4394b04e274aaffe52f91d0c0f84 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Tue, 11 Jan 2022 19:17:49 -0800 Subject: [PATCH 15/91] Keep ignoring WebSocketBlob test by default --- vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp b/vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp index 1212249470c..c2bb07d8720 100644 --- a/vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp +++ b/vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp @@ -214,7 +214,7 @@ TEST_CLASS (RNTesterIntegrationTests) { } BEGIN_TEST_METHOD_ATTRIBUTE(WebSocketBlob) - // TEST_IGNORE() + TEST_IGNORE() END_TEST_METHOD_ATTRIBUTE() TEST_METHOD(WebSocketBlob) { auto result = m_runner.RunTest("IntegrationTests/WebSocketBlobTest", "WebSocketBlobTest"); From 880ff5c0d32fdae0b8c92a87ddd4f0bbb630ecbb Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Wed, 12 Jan 2022 16:49:46 -0800 Subject: [PATCH 16/91] Add BlobModule to default modules list --- vnext/Desktop.IntegrationTests/DesktopTestRunner.cpp | 4 +--- vnext/Shared/OInstance.cpp | 3 +++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/vnext/Desktop.IntegrationTests/DesktopTestRunner.cpp b/vnext/Desktop.IntegrationTests/DesktopTestRunner.cpp index ba6c534ed35..3240bbaf2a2 100644 --- a/vnext/Desktop.IntegrationTests/DesktopTestRunner.cpp +++ b/vnext/Desktop.IntegrationTests/DesktopTestRunner.cpp @@ -77,9 +77,7 @@ shared_ptr TestRunner::GetInstance( {TestImageLoaderModule::name, []() -> unique_ptr { return std::make_unique(); }, - nativeQueue}, - - {"BlobModule", []() -> unique_ptr { return CreateBlobModule(); }, nativeQueue}}; + nativeQueue}}; // <0> string // <1> CxxModule::Provider diff --git a/vnext/Shared/OInstance.cpp b/vnext/Shared/OInstance.cpp index 3fbdc1476ce..4073802af9e 100644 --- a/vnext/Shared/OInstance.cpp +++ b/vnext/Shared/OInstance.cpp @@ -576,6 +576,9 @@ std::vector> InstanceImpl::GetDefaultNativeModules []() { return std::make_unique(); }, nativeQueue)); + modules.push_back(std::make_unique( + m_innerInstance, "BlobModule", []() { return Microsoft::React::CreateBlobModule(); }, nativeQueue)); + return modules; } From 07e0135e1db2f3c0501309d81676f2fd7ebf95ff Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Wed, 12 Jan 2022 20:51:57 -0800 Subject: [PATCH 17/91] Allow 'blob' responseType in HTTP module --- vnext/Shared/Modules/NetworkingModule.cpp | 2 +- vnext/src/IntegrationTests/WebSocketBlobTest.js | 17 ++++------------- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/vnext/Shared/Modules/NetworkingModule.cpp b/vnext/Shared/Modules/NetworkingModule.cpp index c2006257804..3bdceb93ca6 100644 --- a/vnext/Shared/Modules/NetworkingModule.cpp +++ b/vnext/Shared/Modules/NetworkingModule.cpp @@ -261,7 +261,7 @@ void NetworkingModule::NetworkingHelper::SendRequest( int64_t requestId = ++s_lastRequestId; // Enforce supported args - assert(responseType == "text" || responseType == "base64"); + assert(responseType == "text" || responseType == "base64" || responseType == "blob"); // Callback with the requestId cb({requestId}); diff --git a/vnext/src/IntegrationTests/WebSocketBlobTest.js b/vnext/src/IntegrationTests/WebSocketBlobTest.js index 00a2cf45cb1..e0870109b00 100644 --- a/vnext/src/IntegrationTests/WebSocketBlobTest.js +++ b/vnext/src/IntegrationTests/WebSocketBlobTest.js @@ -108,19 +108,10 @@ class WebSocketBlobTest extends React.Component<{}, State> { }; _receivedTestExpectedResponse = () => { - if ( - this.state.lastMessage?.size !== this.state.testExpectedResponse.length - ) { - return false; - } - - //for (var i = 0; i < expected.length; i++) { - // if (expected[i] !== result[i]) { - // return false; - // } - //} - - return true; + // Can't iterate through Blob response. Blob.arrayBuffer() not supported. + return ( + this.state.lastMessage?.size === this.state.testExpectedResponse.length + ); }; componentDidMount() { From a01f5e968d655d7f4b2c6aa37f600336d0542c58 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Wed, 12 Jan 2022 22:44:08 -0800 Subject: [PATCH 18/91] Ensure React Instance can be accessed when using older versions of react-native --- vnext/Shared/Modules/BlobModule.cpp | 19 +++++++++++++++---- vnext/Shared/Modules/BlobModule.h | 15 +++++++++++++++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/vnext/Shared/Modules/BlobModule.cpp b/vnext/Shared/Modules/BlobModule.cpp index 772c69b03e0..9016eeefe37 100644 --- a/vnext/Shared/Modules/BlobModule.cpp +++ b/vnext/Shared/Modules/BlobModule.cpp @@ -37,12 +37,18 @@ namespace Microsoft::React { #pragma region BlobModule -BlobModule::BlobModule() noexcept { +BlobModule::BlobModule() noexcept : m_sharedState{std::make_shared()} { m_contentHandler = std::static_pointer_cast(s_contentHandler.lock()); if (!m_contentHandler) { m_contentHandler = std::make_shared(); s_contentHandler = m_contentHandler; } + + m_sharedState->Module = this; +} + +BlobModule::~BlobModule() noexcept /*override*/ { + m_sharedState->Module = nullptr; } #pragma region CxxModule @@ -77,7 +83,10 @@ std::vector BlobModule::getMethods() { }}, {"sendOverSocket", - [contentHandler = m_contentHandler, weakInstance = weak_ptr(getInstance())](dynamic args) { + // As of React Native 0.67, instance is set AFTER CxxModule::getMethods() is invoked. + // Directly use getInstance() once + // https://github.com/facebook/react-native/commit/1d45b20b6c6ba66df0485cdb9be36463d96cf182 becomes available. + [contentHandler = m_contentHandler, weakState = weak_ptr(m_sharedState)](dynamic args) { auto blob = jsArgAsObject(args, 0); auto blobId = blob["blobId"].getString(); auto offset = blob["offset"].getInt(); @@ -86,13 +95,15 @@ std::vector BlobModule::getMethods() { auto data = contentHandler->ResolveMessage(std::move(blobId), offset, size); - if (auto instance = weakInstance.lock()) { + if (auto state = weakState.lock()) { auto buffer = CryptographicBuffer::CreateFromByteArray(data); auto winrtString = CryptographicBuffer::EncodeToBase64String(std::move(buffer)); auto base64String = Common::Unicode::Utf16ToUtf8(std::move(winrtString)); auto sendArgs = dynamic::array(std::move(base64String), socketID); - instance->callJSFunction("WebSocketModule", "sendBinary", std::move(sendArgs)); + if (auto instance = state->Module->getInstance().lock()) { + instance->callJSFunction("WebSocketModule", "sendBinary", std::move(sendArgs)); + } } }}, diff --git a/vnext/Shared/Modules/BlobModule.h b/vnext/Shared/Modules/BlobModule.h index 535066a7cc4..e3cfff33fbd 100644 --- a/vnext/Shared/Modules/BlobModule.h +++ b/vnext/Shared/Modules/BlobModule.h @@ -61,6 +61,15 @@ class BlobModule : public facebook::xplat::module::CxxModule { BlobModule() noexcept; + ~BlobModule() noexcept override; + + struct SharedState { + /// + /// Keeps a raw reference to the module object to lazily retrieve the React Instance as needed. + /// + CxxModule *Module{nullptr}; + }; + #pragma region CxxModule /// @@ -80,6 +89,12 @@ class BlobModule : public facebook::xplat::module::CxxModule { std::vector getMethods() override; #pragma endregion CxxModule + + private: + /// + /// Keeps members that can be accessed threads other than this module's owner accessible. + /// + std::shared_ptr m_sharedState; }; } // namespace Microsoft::React From 7be9fec5060a840aac3a2756618e58679a513844 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Wed, 12 Jan 2022 23:07:48 -0800 Subject: [PATCH 19/91] Emit error message on createFromParts failure --- vnext/Shared/Modules/BlobModule.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/vnext/Shared/Modules/BlobModule.cpp b/vnext/Shared/Modules/BlobModule.cpp index 9016eeefe37..94bb9105451 100644 --- a/vnext/Shared/Modules/BlobModule.cpp +++ b/vnext/Shared/Modules/BlobModule.cpp @@ -108,7 +108,10 @@ std::vector BlobModule::getMethods() { }}, {"createFromParts", - [contentHandler = m_contentHandler](dynamic args) { + // As of React Native 0.67, instance is set AFTER CxxModule::getMethods() is invoked. + // Directly use getInstance() once + // https://github.com/facebook/react-native/commit/1d45b20b6c6ba66df0485cdb9be36463d96cf182 becomes available. + [contentHandler = m_contentHandler, weakState = weak_ptr(m_sharedState)](dynamic args) { auto parts = jsArgAsArray(args, 0); // Array auto blobId = jsArgAsString(args, 1); vector buffer{}; @@ -128,7 +131,13 @@ std::vector BlobModule::getMethods() { buffer.reserve(buffer.size() + bufferPart.size()); buffer.insert(buffer.end(), bufferPart.begin(), bufferPart.end()); } else { - // TODO: Send error message to instance? + if (auto state = weakState.lock()) { + if (auto instance = state->Module->getInstance().lock()) { + string message = "Invalid type for blob: " + type.asString(); + instance->callJSFunction( + "RCTDeviceEventEmitter", "emit", dynamic::array("blobFailed", std::move(message))); + } + } return; } From a68f7e3b5a6677513bd5612508c0405b68da924d Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Thu, 13 Jan 2022 02:31:41 -0800 Subject: [PATCH 20/91] Remove redundant extra modules in Desktop integration tests --- vnext/Desktop.IntegrationTests/DesktopTestRunner.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/vnext/Desktop.IntegrationTests/DesktopTestRunner.cpp b/vnext/Desktop.IntegrationTests/DesktopTestRunner.cpp index 3240bbaf2a2..4607835a887 100644 --- a/vnext/Desktop.IntegrationTests/DesktopTestRunner.cpp +++ b/vnext/Desktop.IntegrationTests/DesktopTestRunner.cpp @@ -43,6 +43,7 @@ shared_ptr TestRunner::GetInstance( auto nativeQueue = Microsoft::ReactNative::MakeJSQueueThread(); auto jsQueue = Microsoft::ReactNative::MakeJSQueueThread(); + // See InstanceImpl::GetDefaultNativeModules at OInstance.cpp vector>> extraModules{ {"AsyncLocalStorage", []() -> unique_ptr { @@ -50,14 +51,10 @@ shared_ptr TestRunner::GetInstance( }, nativeQueue}, - {"WebSocketModule", []() -> unique_ptr { return CreateWebSocketModule(); }, nativeQueue}, - {"Networking", []() -> unique_ptr { return std::make_unique(); }, nativeQueue}, - {"Timing", [nativeQueue]() -> unique_ptr { return CreateTimingModule(nativeQueue); }, nativeQueue}, - // Apparently mandatory for /IntegrationTests {TestAppStateModule::name, []() -> unique_ptr { return std::make_unique(); }, From 5eec6a528ffd17c6dd8dd6d056919c41334fd4b0 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Thu, 13 Jan 2022 02:36:18 -0800 Subject: [PATCH 21/91] Declare IWebSocketModuleProxy --- vnext/Shared/IWebSocketModuleProxy.h | 24 ++++++++++++++++++++++++ vnext/Shared/Shared.vcxitems | 1 + vnext/Shared/Shared.vcxitems.filters | 3 +++ 3 files changed, 28 insertions(+) create mode 100644 vnext/Shared/IWebSocketModuleProxy.h diff --git a/vnext/Shared/IWebSocketModuleProxy.h b/vnext/Shared/IWebSocketModuleProxy.h new file mode 100644 index 00000000000..46a3c0e0def --- /dev/null +++ b/vnext/Shared/IWebSocketModuleProxy.h @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#pragma once + +// Standard Library +#include +#include + +namespace Microsoft::React { + +/// +/// Provides partial access to WebSocketModule methods directly to other native modules +/// without switching to the JavaScript queue thread. +/// +struct IWebSocketModuleProxy { + static std::weak_ptr GetInstance() noexcept; + + virtual ~IWebSocketModuleProxy() noexcept {} + + virtual void SendBinary(std::string &&base64String, int64_t id) noexcept = 0; +}; + +} // namespace Microsoft::React diff --git a/vnext/Shared/Shared.vcxitems b/vnext/Shared/Shared.vcxitems index 79d5b6596c9..c7ccd1d1830 100644 --- a/vnext/Shared/Shared.vcxitems +++ b/vnext/Shared/Shared.vcxitems @@ -76,6 +76,7 @@ + diff --git a/vnext/Shared/Shared.vcxitems.filters b/vnext/Shared/Shared.vcxitems.filters index dc89044cd13..1c2e7645372 100644 --- a/vnext/Shared/Shared.vcxitems.filters +++ b/vnext/Shared/Shared.vcxitems.filters @@ -387,6 +387,9 @@ Header Files\Modules + + Header Files\Modules + From 51bc7b2b241753cbca63551b20db85ffa99edb77 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Thu, 13 Jan 2022 02:43:34 -0800 Subject: [PATCH 22/91] Remove Blob and WS module factories from DLL boundary --- vnext/Desktop.DLL/react-native-win32.x64.def | 2 -- vnext/Desktop.DLL/react-native-win32.x86.def | 2 -- vnext/Shared/Modules/BlobModule.cpp | 1 - 3 files changed, 5 deletions(-) diff --git a/vnext/Desktop.DLL/react-native-win32.x64.def b/vnext/Desktop.DLL/react-native-win32.x64.def index 1f353c5e42e..6b8dd05e12b 100644 --- a/vnext/Desktop.DLL/react-native-win32.x64.def +++ b/vnext/Desktop.DLL/react-native-win32.x64.def @@ -63,8 +63,6 @@ EXPORTS ?CreateTimingModule@react@facebook@@YA?AV?$unique_ptr@VCxxModule@module@xplat@facebook@@U?$default_delete@VCxxModule@module@xplat@facebook@@@std@@@std@@AEBV?$shared_ptr@VMessageQueueThread@react@facebook@@@4@@Z ??0NetworkingModule@React@Microsoft@@QEAA@XZ ?CreateAsyncStorageModule@react@facebook@@YA?AV?$unique_ptr@VCxxModule@module@xplat@facebook@@U?$default_delete@VCxxModule@module@xplat@facebook@@@std@@@std@@PEB_W@Z -?CreateBlobModule@React@Microsoft@@YA?AV?$unique_ptr@VCxxModule@module@xplat@facebook@@U?$default_delete@VCxxModule@module@xplat@facebook@@@std@@@std@@XZ -?CreateWebSocketModule@React@Microsoft@@YA?AV?$unique_ptr@VCxxModule@module@xplat@facebook@@U?$default_delete@VCxxModule@module@xplat@facebook@@@std@@@std@@XZ ?MakeJSQueueThread@ReactNative@Microsoft@@YA?AV?$shared_ptr@VMessageQueueThread@react@facebook@@@std@@XZ ?Hash128@SpookyHashV2@hash@folly@@SAXPEBX_KPEA_K2@Z ??1Instance@react@facebook@@QEAA@XZ diff --git a/vnext/Desktop.DLL/react-native-win32.x86.def b/vnext/Desktop.DLL/react-native-win32.x86.def index c48dac54b16..c9bf4fd3b59 100644 --- a/vnext/Desktop.DLL/react-native-win32.x86.def +++ b/vnext/Desktop.DLL/react-native-win32.x86.def @@ -60,8 +60,6 @@ EXPORTS ?makeChakraRuntime@JSI@Microsoft@@YG?AV?$unique_ptr@VRuntime@jsi@facebook@@U?$default_delete@VRuntime@jsi@facebook@@@std@@@std@@$$QAUChakraRuntimeArgs@12@@Z ?CreateTimingModule@react@facebook@@YG?AV?$unique_ptr@VCxxModule@module@xplat@facebook@@U?$default_delete@VCxxModule@module@xplat@facebook@@@std@@@std@@ABV?$shared_ptr@VMessageQueueThread@react@facebook@@@4@@Z ?CreateAsyncStorageModule@react@facebook@@YG?AV?$unique_ptr@VCxxModule@module@xplat@facebook@@U?$default_delete@VCxxModule@module@xplat@facebook@@@std@@@std@@PB_W@Z -?CreateBlobModule@React@Microsoft@@YG?AV?$unique_ptr@VCxxModule@module@xplat@facebook@@U?$default_delete@VCxxModule@module@xplat@facebook@@@std@@@std@@XZ -?CreateWebSocketModule@React@Microsoft@@YG?AV?$unique_ptr@VCxxModule@module@xplat@facebook@@U?$default_delete@VCxxModule@module@xplat@facebook@@@std@@@std@@XZ ?Make@IHttpResource@React@Microsoft@@SG?AV?$unique_ptr@UIHttpResource@React@Microsoft@@U?$default_delete@UIHttpResource@React@Microsoft@@@std@@@std@@XZ ??0NetworkingModule@React@Microsoft@@QAE@XZ ?MakeJSQueueThread@ReactNative@Microsoft@@YG?AV?$shared_ptr@VMessageQueueThread@react@facebook@@@std@@XZ diff --git a/vnext/Shared/Modules/BlobModule.cpp b/vnext/Shared/Modules/BlobModule.cpp index 94bb9105451..63398ed7127 100644 --- a/vnext/Shared/Modules/BlobModule.cpp +++ b/vnext/Shared/Modules/BlobModule.cpp @@ -8,7 +8,6 @@ // React Native #include #include -#include // Windows API #include From 5cd6a99909e46960e01f5efacb475d1304a2e724 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Thu, 13 Jan 2022 17:51:31 -0800 Subject: [PATCH 23/91] Implement IWebSocketModuleProxy --- vnext/Shared/Modules/BlobModule.cpp | 26 +++++++--------- .../{ => Modules}/IWebSocketModuleProxy.h | 0 vnext/Shared/Modules/WebSocketModule.cpp | 31 +++++++++++++++++++ vnext/Shared/Modules/WebSocketModule.h | 19 +++++++++++- vnext/Shared/Shared.vcxitems | 2 +- vnext/Shared/Shared.vcxitems.filters | 2 +- 6 files changed, 63 insertions(+), 17 deletions(-) rename vnext/Shared/{ => Modules}/IWebSocketModuleProxy.h (100%) diff --git a/vnext/Shared/Modules/BlobModule.cpp b/vnext/Shared/Modules/BlobModule.cpp index 63398ed7127..546952cfb41 100644 --- a/vnext/Shared/Modules/BlobModule.cpp +++ b/vnext/Shared/Modules/BlobModule.cpp @@ -3,6 +3,7 @@ #include "BlobModule.h" +#include #include // React Native @@ -82,10 +83,12 @@ std::vector BlobModule::getMethods() { }}, {"sendOverSocket", - // As of React Native 0.67, instance is set AFTER CxxModule::getMethods() is invoked. - // Directly use getInstance() once - // https://github.com/facebook/react-native/commit/1d45b20b6c6ba66df0485cdb9be36463d96cf182 becomes available. - [contentHandler = m_contentHandler, weakState = weak_ptr(m_sharedState)](dynamic args) { + [contentHandler = m_contentHandler](dynamic args) { + auto wsProxy = IWebSocketModuleProxy::GetInstance().lock(); + if (!wsProxy) { + return; + } + auto blob = jsArgAsObject(args, 0); auto blobId = blob["blobId"].getString(); auto offset = blob["offset"].getInt(); @@ -94,16 +97,11 @@ std::vector BlobModule::getMethods() { auto data = contentHandler->ResolveMessage(std::move(blobId), offset, size); - if (auto state = weakState.lock()) { - auto buffer = CryptographicBuffer::CreateFromByteArray(data); - auto winrtString = CryptographicBuffer::EncodeToBase64String(std::move(buffer)); - auto base64String = Common::Unicode::Utf16ToUtf8(std::move(winrtString)); - - auto sendArgs = dynamic::array(std::move(base64String), socketID); - if (auto instance = state->Module->getInstance().lock()) { - instance->callJSFunction("WebSocketModule", "sendBinary", std::move(sendArgs)); - } - } + auto buffer = CryptographicBuffer::CreateFromByteArray(data); + auto winrtString = CryptographicBuffer::EncodeToBase64String(std::move(buffer)); + auto base64String = Common::Unicode::Utf16ToUtf8(std::move(winrtString)); + + wsProxy->SendBinary(std::move(base64String), socketID); }}, {"createFromParts", diff --git a/vnext/Shared/IWebSocketModuleProxy.h b/vnext/Shared/Modules/IWebSocketModuleProxy.h similarity index 100% rename from vnext/Shared/IWebSocketModuleProxy.h rename to vnext/Shared/Modules/IWebSocketModuleProxy.h diff --git a/vnext/Shared/Modules/WebSocketModule.cpp b/vnext/Shared/Modules/WebSocketModule.cpp index fcaea13f0e8..7a1036d1236 100644 --- a/vnext/Shared/Modules/WebSocketModule.cpp +++ b/vnext/Shared/Modules/WebSocketModule.cpp @@ -6,6 +6,7 @@ #include #include + #include #include #include "Unicode.h" @@ -31,11 +32,15 @@ using std::weak_ptr; using winrt::Windows::Security::Cryptography::CryptographicBuffer; namespace { +using Microsoft::React::IWebSocketModuleProxy; using Microsoft::React::IWebSocketResource; using Microsoft::React::WebSocketModule; constexpr char moduleName[] = "WebSocketModule"; +weak_ptr s_sharedState; +weak_ptr s_proxy; + static void SendEvent(weak_ptr weakInstance, string &&eventName, dynamic &&args) { if (auto instance = weakInstance.lock()) { instance->callJSFunction("RCTDeviceEventEmitter", "emit", dynamic::array(std::move(eventName), std::move(args))); @@ -141,9 +146,18 @@ GetOrCreateWebSocket(int64_t id, string &&url, weak_ptr()} { m_sharedState->ResourceFactory = [](string &&url) { return IWebSocketResource::Make(); }; m_sharedState->Module = this; + s_sharedState = weak_ptr(m_sharedState); + + m_proxy = std::static_pointer_cast(s_proxy.lock()); + if (!m_proxy) { + m_proxy = std::make_shared(); + s_proxy = m_proxy; + } } WebSocketModule::~WebSocketModule() noexcept /*override*/ { @@ -268,6 +282,23 @@ std::vector WebSocketModule::getMeth } // getMethods // clang-format on +#pragma endregion WebSocketModule + +#pragma region WebSocketModuleProxy + +void WebSocketModuleProxy::SendBinary(std::string&& base64String, int64_t id) noexcept /*override*/ { + weak_ptr weakWs = GetOrCreateWebSocket(id, {}, s_sharedState); + if (auto sharedWs = weakWs.lock()) { + sharedWs->SendBinary(std::move(base64String)); + } +} + +#pragma endregion WebSocketModuleProxy + +/*static*/ weak_ptr IWebSocketModuleProxy::GetInstance() noexcept { + return s_proxy; +} + /*extern*/ std::unique_ptr CreateWebSocketModule() noexcept { return std::make_unique(); } diff --git a/vnext/Shared/Modules/WebSocketModule.h b/vnext/Shared/Modules/WebSocketModule.h index e9ecc504d50..b5dcf715a83 100644 --- a/vnext/Shared/Modules/WebSocketModule.h +++ b/vnext/Shared/Modules/WebSocketModule.h @@ -3,11 +3,23 @@ #pragma once +#include + +#include + +// React Native #include -#include "IWebSocketResource.h" namespace Microsoft::React { +class WebSocketModuleProxy final : public IWebSocketModuleProxy { +#pragma region IWebSocketModuleProxy + + void SendBinary(std::string &&base64String, int64_t id) noexcept override; + +#pragma endregion +}; + /// /// Realizes NativeModules projection. /// See react-native/Libraries/WebSocket/WebSocket.js @@ -71,6 +83,11 @@ class WebSocketModule : public facebook::xplat::module::CxxModule { /// Keeps members that can be accessed threads other than this module's owner accessible. /// std::shared_ptr m_sharedState; + + /// + /// Exposes direct partial functionality to other modules. + /// + std::shared_ptr m_proxy; }; } // namespace Microsoft::React diff --git a/vnext/Shared/Shared.vcxitems b/vnext/Shared/Shared.vcxitems index c7ccd1d1830..0cfcd7bb33c 100644 --- a/vnext/Shared/Shared.vcxitems +++ b/vnext/Shared/Shared.vcxitems @@ -76,7 +76,6 @@ - @@ -87,6 +86,7 @@ + diff --git a/vnext/Shared/Shared.vcxitems.filters b/vnext/Shared/Shared.vcxitems.filters index 1c2e7645372..e9cce8c85b2 100644 --- a/vnext/Shared/Shared.vcxitems.filters +++ b/vnext/Shared/Shared.vcxitems.filters @@ -387,7 +387,7 @@ Header Files\Modules - + Header Files\Modules From be039d315d80b7fc4879966c4bb4168940db8887 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Thu, 13 Jan 2022 18:06:38 -0800 Subject: [PATCH 24/91] clang format --- vnext/Shared/Modules/BlobModule.cpp | 2 +- vnext/Shared/Modules/WebSocketModule.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/vnext/Shared/Modules/BlobModule.cpp b/vnext/Shared/Modules/BlobModule.cpp index 546952cfb41..0710026011e 100644 --- a/vnext/Shared/Modules/BlobModule.cpp +++ b/vnext/Shared/Modules/BlobModule.cpp @@ -100,7 +100,7 @@ std::vector BlobModule::getMethods() { auto buffer = CryptographicBuffer::CreateFromByteArray(data); auto winrtString = CryptographicBuffer::EncodeToBase64String(std::move(buffer)); auto base64String = Common::Unicode::Utf16ToUtf8(std::move(winrtString)); - + wsProxy->SendBinary(std::move(base64String), socketID); }}, diff --git a/vnext/Shared/Modules/WebSocketModule.cpp b/vnext/Shared/Modules/WebSocketModule.cpp index 7a1036d1236..6285adee323 100644 --- a/vnext/Shared/Modules/WebSocketModule.cpp +++ b/vnext/Shared/Modules/WebSocketModule.cpp @@ -286,7 +286,7 @@ std::vector WebSocketModule::getMeth #pragma region WebSocketModuleProxy -void WebSocketModuleProxy::SendBinary(std::string&& base64String, int64_t id) noexcept /*override*/ { +void WebSocketModuleProxy::SendBinary(std::string &&base64String, int64_t id) noexcept /*override*/ { weak_ptr weakWs = GetOrCreateWebSocket(id, {}, s_sharedState); if (auto sharedWs = weakWs.lock()) { sharedWs->SendBinary(std::move(base64String)); From 1ff70b9522020662a439d6cf1f398d9dfb0e817c Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Fri, 14 Jan 2022 15:11:19 -0800 Subject: [PATCH 25/91] Update packages.lock --- .../Microsoft.ReactNative.Managed.UnitTests/packages.lock.json | 2 +- vnext/Microsoft.ReactNative.Managed/packages.lock.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/vnext/Microsoft.ReactNative.Managed.UnitTests/packages.lock.json b/vnext/Microsoft.ReactNative.Managed.UnitTests/packages.lock.json index aa158c683bd..cd6a3fa6923 100644 --- a/vnext/Microsoft.ReactNative.Managed.UnitTests/packages.lock.json +++ b/vnext/Microsoft.ReactNative.Managed.UnitTests/packages.lock.json @@ -62,7 +62,7 @@ "Microsoft.NETCore.Platforms": { "type": "Transitive", "resolved": "2.1.0", - "contentHash": "GmkKfoyerqmsHMn7OZj0AKpcBabD+GaafqphvX2Mw406IwiJRy1pKcKqdCfKJfYmkRyJ6+e+RaUylgdJoDa1jQ==" + "contentHash": "ok+RPAtESz/9MUXeIEz6Lv5XAGQsaNmEYXMsgVALj4D7kqC8gveKWXWXbufLySR2fWrwZf8smyN5RmHu0e4BHA==" }, "Microsoft.NETCore.Targets": { "type": "Transitive", diff --git a/vnext/Microsoft.ReactNative.Managed/packages.lock.json b/vnext/Microsoft.ReactNative.Managed/packages.lock.json index 04e54db679e..14d1ec0d868 100644 --- a/vnext/Microsoft.ReactNative.Managed/packages.lock.json +++ b/vnext/Microsoft.ReactNative.Managed/packages.lock.json @@ -53,7 +53,7 @@ "Microsoft.NETCore.Platforms": { "type": "Transitive", "resolved": "2.1.0", - "contentHash": "GmkKfoyerqmsHMn7OZj0AKpcBabD+GaafqphvX2Mw406IwiJRy1pKcKqdCfKJfYmkRyJ6+e+RaUylgdJoDa1jQ==" + "contentHash": "ok+RPAtESz/9MUXeIEz6Lv5XAGQsaNmEYXMsgVALj4D7kqC8gveKWXWXbufLySR2fWrwZf8smyN5RmHu0e4BHA==" }, "Microsoft.SourceLink.Common": { "type": "Transitive", From 610aa872cb3bbd727d8d519e0f2cf4e5d1f6ffcc Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Fri, 14 Jan 2022 19:14:42 -0800 Subject: [PATCH 26/91] Use winrt::array_view directly in ResolveMessage --- vnext/Shared/Modules/BlobModule.cpp | 8 +++----- vnext/Shared/Modules/BlobModule.h | 7 ++++--- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/vnext/Shared/Modules/BlobModule.cpp b/vnext/Shared/Modules/BlobModule.cpp index 0710026011e..7c7f876071b 100644 --- a/vnext/Shared/Modules/BlobModule.cpp +++ b/vnext/Shared/Modules/BlobModule.cpp @@ -188,15 +188,13 @@ void BlobWebSocketModuleContentHandler::Unregister(int64_t socketID) noexcept { m_socketIDs.erase(socketID); } -vector +winrt::array_view BlobWebSocketModuleContentHandler::ResolveMessage(string &&blobId, int64_t offset, int64_t size) noexcept { lock_guard lock{m_blobsMutex}; - auto data = m_blobs.at(std::move(blobId)); - auto start = data.cbegin() + static_cast(offset); - auto end = start + static_cast(size); + auto &data = m_blobs.at(std::move(blobId)); - return vector(start, end); + return winrt::array_view{data}; } void BlobWebSocketModuleContentHandler::RemoveMessage(string &&blobId) noexcept { diff --git a/vnext/Shared/Modules/BlobModule.h b/vnext/Shared/Modules/BlobModule.h index e3cfff33fbd..71c9c39c07a 100644 --- a/vnext/Shared/Modules/BlobModule.h +++ b/vnext/Shared/Modules/BlobModule.h @@ -8,6 +8,9 @@ // React Native #include +// Windows API +#include + // Standard Library #include #include @@ -36,9 +39,7 @@ class BlobWebSocketModuleContentHandler final : public IWebSocketModuleContentHa void Unregister(int64_t socketID) noexcept; - // const bool IsRegistered(std::int64_t socketID) noexcept override; - - std::vector ResolveMessage(std::string &&blobId, int64_t offset, int64_t size) noexcept; + winrt::array_view ResolveMessage(std::string &&blobId, int64_t offset, int64_t size) noexcept; void RemoveMessage(std::string &&blobId) noexcept; From 666847afb4d7550c72b470c0e693cc00ce9d124d Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Thu, 21 Apr 2022 14:08:45 -0700 Subject: [PATCH 27/91] Define InstanceImpl::m_transitionalModuleProperties --- .../RNTesterIntegrationTests.cpp | 2 +- vnext/Shared/CreateModules.h | 12 ++++++++++-- vnext/Shared/Modules/BlobModule.cpp | 15 ++++++++++++--- vnext/Shared/Modules/BlobModule.h | 3 ++- vnext/Shared/Modules/WebSocketModule.cpp | 12 +++++++++--- vnext/Shared/Modules/WebSocketModule.h | 3 ++- vnext/Shared/OInstance.cpp | 17 +++++++++++++---- vnext/Shared/OInstance.h | 18 ++++++++++++++---- 8 files changed, 63 insertions(+), 19 deletions(-) diff --git a/vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp b/vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp index 482d563d09e..5b104697de0 100644 --- a/vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp +++ b/vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp @@ -217,7 +217,7 @@ TEST_CLASS (RNTesterIntegrationTests) { } BEGIN_TEST_METHOD_ATTRIBUTE(WebSocketBlob) - TEST_IGNORE() + //TEST_IGNORE() END_TEST_METHOD_ATTRIBUTE() TEST_METHOD(WebSocketBlob) { auto result = m_runner.RunTest("IntegrationTests/WebSocketBlobTest", "WebSocketBlobTest"); diff --git a/vnext/Shared/CreateModules.h b/vnext/Shared/CreateModules.h index 1d0aa1a205e..7f178817bbb 100644 --- a/vnext/Shared/CreateModules.h +++ b/vnext/Shared/CreateModules.h @@ -3,9 +3,14 @@ #pragma once +// React Native #include #include +// Windows API +#include + +// Standard Library #include // Forward declarations. Desktop projects can not access @@ -33,8 +38,11 @@ extern const char *GetHttpModuleName() noexcept; extern std::unique_ptr CreateHttpModule() noexcept; extern const char *GetWebSocketModuleName() noexcept; -extern std::unique_ptr CreateWebSocketModule() noexcept; +extern std::unique_ptr CreateWebSocketModule( + winrt::Windows::Foundation::IInspectable const &properties) noexcept; -extern std::unique_ptr CreateBlobModule() noexcept; +extern const char *GetBlobModuleName() noexcept; +extern std::unique_ptr CreateBlobModule( + winrt::Windows::Foundation::IInspectable const &properties) noexcept; } // namespace Microsoft::React diff --git a/vnext/Shared/Modules/BlobModule.cpp b/vnext/Shared/Modules/BlobModule.cpp index 7c7f876071b..b2a435c4880 100644 --- a/vnext/Shared/Modules/BlobModule.cpp +++ b/vnext/Shared/Modules/BlobModule.cpp @@ -23,6 +23,7 @@ using std::mutex; using std::string; using std::vector; using std::weak_ptr; +using winrt::Microsoft::ReactNative::ReactPropertyBagHelper; using winrt::Windows::Foundation::GuidHelper; using winrt::Windows::Security::Cryptography::CryptographicBuffer; @@ -37,7 +38,8 @@ namespace Microsoft::React { #pragma region BlobModule -BlobModule::BlobModule() noexcept : m_sharedState{std::make_shared()} { +BlobModule::BlobModule(winrt::Microsoft::ReactNative::IReactPropertyBag const& properties) noexcept + : m_sharedState{std::make_shared()} { m_contentHandler = std::static_pointer_cast(s_contentHandler.lock()); if (!m_contentHandler) { m_contentHandler = std::make_shared(); @@ -215,8 +217,15 @@ void BlobWebSocketModuleContentHandler::StoreMessage(vector &&message, return s_contentHandler; } -/*extern*/ std::unique_ptr CreateBlobModule() noexcept { - return std::make_unique(); +/*extern*/ const char *GetBlobModuleName() noexcept { + return moduleName; +} + +/*extern*/ std::unique_ptr CreateBlobModule(winrt::Windows::Foundation::IInspectable const& iProps) noexcept { + if (auto props = iProps.try_as()) + return std::make_unique(props); + + return nullptr; } } // namespace Microsoft::React diff --git a/vnext/Shared/Modules/BlobModule.h b/vnext/Shared/Modules/BlobModule.h index 71c9c39c07a..e28352b3944 100644 --- a/vnext/Shared/Modules/BlobModule.h +++ b/vnext/Shared/Modules/BlobModule.h @@ -4,6 +4,7 @@ #pragma once #include +#include // React Native #include @@ -60,7 +61,7 @@ class BlobModule : public facebook::xplat::module::CxxModule { SIZE = 6 }; - BlobModule() noexcept; + BlobModule(winrt::Microsoft::ReactNative::IReactPropertyBag const& properties) noexcept; ~BlobModule() noexcept override; diff --git a/vnext/Shared/Modules/WebSocketModule.cpp b/vnext/Shared/Modules/WebSocketModule.cpp index 781e442cadd..1e3dd138b4f 100644 --- a/vnext/Shared/Modules/WebSocketModule.cpp +++ b/vnext/Shared/Modules/WebSocketModule.cpp @@ -6,10 +6,12 @@ #include #include +#include +#include "Unicode.h" +// React Native #include #include -#include "Unicode.h" // Windows API #include @@ -29,6 +31,7 @@ using std::shared_ptr; using std::string; using std::weak_ptr; +using winrt::Microsoft::ReactNative::ReactPropertyBagHelper; using winrt::Windows::Security::Cryptography::CryptographicBuffer; namespace { @@ -148,7 +151,8 @@ namespace Microsoft::React { #pragma region WebSocketModule -WebSocketModule::WebSocketModule() : m_sharedState{std::make_shared()} { +WebSocketModule::WebSocketModule(/*winrt::Microsoft::ReactNative::IReactPropertyBag const &properties*/) + : m_sharedState{std::make_shared()} { m_sharedState->ResourceFactory = [](string &&url) { return IWebSocketResource::Make(); }; m_sharedState->Module = this; s_sharedState = weak_ptr(m_sharedState); @@ -303,7 +307,9 @@ void WebSocketModuleProxy::SendBinary(std::string &&base64String, int64_t id) no return moduleName; } -/*extern*/ std::unique_ptr CreateWebSocketModule() noexcept { +/*extern*/ std::unique_ptr CreateWebSocketModule( + winrt::Windows::Foundation::IInspectable const &properties) noexcept { + return std::make_unique(); } diff --git a/vnext/Shared/Modules/WebSocketModule.h b/vnext/Shared/Modules/WebSocketModule.h index 9181283588c..1258bd06779 100644 --- a/vnext/Shared/Modules/WebSocketModule.h +++ b/vnext/Shared/Modules/WebSocketModule.h @@ -5,6 +5,7 @@ #include #include +//#include // React Native #include @@ -27,7 +28,7 @@ class WebSocketModule : public facebook::xplat::module::CxxModule { public: enum MethodId { Connect = 0, Close = 1, Send = 2, SendBinary = 3, Ping = 4, SIZE = 5 }; - WebSocketModule(); + WebSocketModule(/*winrt::Microsoft::ReactNative::IReactPropertyBag const& properties*/); ~WebSocketModule() noexcept override; diff --git a/vnext/Shared/OInstance.cpp b/vnext/Shared/OInstance.cpp index 6b0a418fcff..3d2856bb5f1 100644 --- a/vnext/Shared/OInstance.cpp +++ b/vnext/Shared/OInstance.cpp @@ -67,6 +67,7 @@ using namespace facebook; using namespace Microsoft::JSI; using std::make_shared; +using winrt::Microsoft::ReactNative::ReactPropertyBagHelper; namespace Microsoft::React { @@ -244,7 +245,8 @@ InstanceImpl::InstanceImpl( m_jsBundleBasePath(std::move(jsBundleBasePath)), m_devSettings(std::move(devSettings)), m_devManager(std::move(devManager)), - m_innerInstance(std::move(instance)) { + m_innerInstance(std::move(instance)), + m_transitionalModuleProperties{ReactPropertyBagHelper::CreatePropertyBag()} { // Temp set the logmarker here facebook::react::ReactMarker::logTaggedMarker = logMarker; @@ -541,6 +543,8 @@ InstanceImpl::~InstanceImpl() { std::vector> InstanceImpl::GetDefaultNativeModules( std::shared_ptr nativeQueue) { std::vector> modules; + auto ns = ReactPropertyBagHelper::GetNamespace(L"CxxModule"); + auto transitionalProps = m_transitionalModuleProperties; modules.push_back(std::make_unique( m_innerInstance, @@ -551,8 +555,8 @@ std::vector> InstanceImpl::GetDefaultNativeModules modules.push_back(std::make_unique( m_innerInstance, Microsoft::React::GetWebSocketModuleName(), - [nativeQueue]() -> std::unique_ptr { - return Microsoft::React::CreateWebSocketModule(); + [nativeQueue, transitionalProps]() -> std::unique_ptr { + return Microsoft::React::CreateWebSocketModule(transitionalProps); }, nativeQueue)); @@ -615,7 +619,12 @@ std::vector> InstanceImpl::GetDefaultNativeModules nativeQueue)); modules.push_back(std::make_unique( - m_innerInstance, "BlobModule", []() { return Microsoft::React::CreateBlobModule(); }, nativeQueue)); + m_innerInstance, + Microsoft::React::GetBlobModuleName(), + [transitionalProps]() { + return Microsoft::React::CreateBlobModule(transitionalProps); + }, + nativeQueue)); return modules; } diff --git a/vnext/Shared/OInstance.h b/vnext/Shared/OInstance.h index 6803816d1b6..bf0956ec7df 100644 --- a/vnext/Shared/OInstance.h +++ b/vnext/Shared/OInstance.h @@ -5,14 +5,18 @@ #pragma once +#include +#include +#include "InstanceManager.h" + +// React Native +#include + +// Standard Libriary #include #include #include -#include -#include -#include "InstanceManager.h" - namespace facebook { namespace react { @@ -104,6 +108,12 @@ class InstanceImpl final : public InstanceWrapper, private ::std::enable_shared_ std::shared_ptr m_devManager; std::shared_ptr m_devSettings; bool m_isInError{false}; + + /// + /// Internal property bag meant to share arbitrary data between CxxModule instances. + /// This element should be deprecated once full ABI safety is achieved. + /// + winrt::Microsoft::ReactNative::IReactPropertyBag m_transitionalModuleProperties; }; } // namespace react From 3bba7835f8e1c2da4a2b0338407048906ad1e62d Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Thu, 21 Apr 2022 14:23:06 -0700 Subject: [PATCH 28/91] Include CreateModules.h in projects accessing MSRN.Cxx --- vnext/Desktop.IntegrationTests/DesktopTestRunner.cpp | 3 --- vnext/Shared/CreateModules.h | 4 ++-- vnext/Shared/Modules/BlobModule.cpp | 6 +++--- vnext/Shared/Modules/BlobModule.h | 2 +- vnext/Shared/Modules/WebSocketModule.cpp | 12 ++++++------ vnext/Shared/Modules/WebSocketModule.h | 7 +++++-- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/vnext/Desktop.IntegrationTests/DesktopTestRunner.cpp b/vnext/Desktop.IntegrationTests/DesktopTestRunner.cpp index db877e5b9eb..b1ad4b1540b 100644 --- a/vnext/Desktop.IntegrationTests/DesktopTestRunner.cpp +++ b/vnext/Desktop.IntegrationTests/DesktopTestRunner.cpp @@ -3,9 +3,6 @@ #include -#include -#include -#include #include #include #include "ChakraRuntimeHolder.h" diff --git a/vnext/Shared/CreateModules.h b/vnext/Shared/CreateModules.h index 7f178817bbb..4a0f7ad12c5 100644 --- a/vnext/Shared/CreateModules.h +++ b/vnext/Shared/CreateModules.h @@ -39,10 +39,10 @@ extern std::unique_ptr CreateHttpModule() no extern const char *GetWebSocketModuleName() noexcept; extern std::unique_ptr CreateWebSocketModule( - winrt::Windows::Foundation::IInspectable const &properties) noexcept; + winrt::Windows::Foundation::IInspectable const &iProperties) noexcept; extern const char *GetBlobModuleName() noexcept; extern std::unique_ptr CreateBlobModule( - winrt::Windows::Foundation::IInspectable const &properties) noexcept; + winrt::Windows::Foundation::IInspectable const &iProperties) noexcept; } // namespace Microsoft::React diff --git a/vnext/Shared/Modules/BlobModule.cpp b/vnext/Shared/Modules/BlobModule.cpp index b2a435c4880..c90f4e73b6b 100644 --- a/vnext/Shared/Modules/BlobModule.cpp +++ b/vnext/Shared/Modules/BlobModule.cpp @@ -221,9 +221,9 @@ void BlobWebSocketModuleContentHandler::StoreMessage(vector &&message, return moduleName; } -/*extern*/ std::unique_ptr CreateBlobModule(winrt::Windows::Foundation::IInspectable const& iProps) noexcept { - if (auto props = iProps.try_as()) - return std::make_unique(props); +/*extern*/ std::unique_ptr CreateBlobModule(winrt::Windows::Foundation::IInspectable const& iProperties) noexcept { + if (auto properties = iProperties.try_as()) + return std::make_unique(properties); return nullptr; } diff --git a/vnext/Shared/Modules/BlobModule.h b/vnext/Shared/Modules/BlobModule.h index e28352b3944..f7d08b7ff0b 100644 --- a/vnext/Shared/Modules/BlobModule.h +++ b/vnext/Shared/Modules/BlobModule.h @@ -61,7 +61,7 @@ class BlobModule : public facebook::xplat::module::CxxModule { SIZE = 6 }; - BlobModule(winrt::Microsoft::ReactNative::IReactPropertyBag const& properties) noexcept; + BlobModule(winrt::Microsoft::ReactNative::IReactPropertyBag const& iProperties) noexcept; ~BlobModule() noexcept override; diff --git a/vnext/Shared/Modules/WebSocketModule.cpp b/vnext/Shared/Modules/WebSocketModule.cpp index 1e3dd138b4f..9b11575ae08 100644 --- a/vnext/Shared/Modules/WebSocketModule.cpp +++ b/vnext/Shared/Modules/WebSocketModule.cpp @@ -6,7 +6,6 @@ #include #include -#include #include "Unicode.h" // React Native @@ -14,7 +13,6 @@ #include // Windows API -#include #include // Standard Libriary @@ -151,7 +149,7 @@ namespace Microsoft::React { #pragma region WebSocketModule -WebSocketModule::WebSocketModule(/*winrt::Microsoft::ReactNative::IReactPropertyBag const &properties*/) +WebSocketModule::WebSocketModule(winrt::Microsoft::ReactNative::IReactPropertyBag const &properties) : m_sharedState{std::make_shared()} { m_sharedState->ResourceFactory = [](string &&url) { return IWebSocketResource::Make(); }; m_sharedState->Module = this; @@ -308,9 +306,11 @@ void WebSocketModuleProxy::SendBinary(std::string &&base64String, int64_t id) no } /*extern*/ std::unique_ptr CreateWebSocketModule( - winrt::Windows::Foundation::IInspectable const &properties) noexcept { - - return std::make_unique(); + winrt::Windows::Foundation::IInspectable const &iProperties) noexcept { + if (auto properties = iProperties.try_as()) + return std::make_unique(properties); + + return nullptr; } } // namespace Microsoft::React diff --git a/vnext/Shared/Modules/WebSocketModule.h b/vnext/Shared/Modules/WebSocketModule.h index 1258bd06779..8abddf02dd4 100644 --- a/vnext/Shared/Modules/WebSocketModule.h +++ b/vnext/Shared/Modules/WebSocketModule.h @@ -5,11 +5,14 @@ #include #include -//#include +#include // React Native #include +// Windows API +#include + namespace Microsoft::React { class WebSocketModuleProxy final : public IWebSocketModuleProxy { @@ -28,7 +31,7 @@ class WebSocketModule : public facebook::xplat::module::CxxModule { public: enum MethodId { Connect = 0, Close = 1, Send = 2, SendBinary = 3, Ping = 4, SIZE = 5 }; - WebSocketModule(/*winrt::Microsoft::ReactNative::IReactPropertyBag const& properties*/); + WebSocketModule(winrt::Microsoft::ReactNative::IReactPropertyBag const &iProperties); ~WebSocketModule() noexcept override; From 2f246f649d461e9d9d305ec388d42a3491ff5a13 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Thu, 21 Apr 2022 15:06:07 -0700 Subject: [PATCH 29/91] Define WinRT class WebSocketModuleContentHandler - Have BlobModule constructor register the content handler in transitive property bag CxxNativeModule/WebSocketModuleContentHandler --- vnext/Shared/Modules/BlobModule.cpp | 62 +++++++++++++++++++ vnext/Shared/Modules/BlobModule.h | 2 + .../Modules/IWebSocketModuleContentHandler.h | 27 ++++++++ vnext/Shared/OInstance.cpp | 1 - 4 files changed, 91 insertions(+), 1 deletion(-) diff --git a/vnext/Shared/Modules/BlobModule.cpp b/vnext/Shared/Modules/BlobModule.cpp index c90f4e73b6b..1088d2f29db 100644 --- a/vnext/Shared/Modules/BlobModule.cpp +++ b/vnext/Shared/Modules/BlobModule.cpp @@ -20,6 +20,7 @@ using facebook::react::Instance; using folly::dynamic; using std::lock_guard; using std::mutex; +using std::scoped_lock; using std::string; using std::vector; using std::weak_ptr; @@ -46,6 +47,10 @@ BlobModule::BlobModule(winrt::Microsoft::ReactNative::IReactPropertyBag const& p s_contentHandler = m_contentHandler; } + auto contentHandler = winrt::make(); + properties.Set( + ReactPropertyBagHelper::GetName(ReactPropertyBagHelper::GetNamespace(L"CxxNativeModule"), L"WebSocketModuleContentHandler"), contentHandler); + m_sharedState->Module = this; } @@ -213,6 +218,63 @@ void BlobWebSocketModuleContentHandler::StoreMessage(vector &&message, #pragma endregion BlobWebSocketModuleContentHandler +#pragma region WebSocketModuleContentHandler + +void WebSocketModuleContentHandler::ProcessMessage(string&& message, dynamic& params) +{ + params["data"] = std::move(message); +} + +void WebSocketModuleContentHandler::ProcessMessage(vector&& message, dynamic& params) +{ + auto blob = dynamic::object(); + blob("offset", 0); + blob("size", message.size()); + + // substr(1, 36) strips curly braces from a GUID + string blobId = winrt::to_string(winrt::to_hstring(GuidHelper::CreateNewGuid())).substr(1, 36); + //TODO: StoreMessage + + params["data"] = std::move(blob); + params["type"] = "blob"; +} + +void WebSocketModuleContentHandler::Register(int64_t socketId) noexcept +{ + scoped_lock lock{m_mutex}; + m_socketIds.insert(socketId); +} + +void WebSocketModuleContentHandler::Unregister(int64_t socketId) noexcept +{ + scoped_lock lock{m_mutex}; + if (m_socketIds.find(socketId) != m_socketIds.end()) + m_socketIds.erase(socketId); +} + +winrt::array_view +WebSocketModuleContentHandler::ResolveMessage(string&& blobId, int64_t offset, int64_t size) noexcept +{ + scoped_lock lock{m_mutex}; + auto &data = m_blobs.at(std::move(blobId)); + + return winrt::array_view{data}; +} + +void WebSocketModuleContentHandler::StoreMessage(vector&& message, string&& blobId) noexcept +{ + scoped_lock lock{m_mutex}; + m_blobs.insert_or_assign(std::move(blobId), std::move(message)); +} + +void WebSocketModuleContentHandler::RemoveMessage(string&& blobId) noexcept +{ + scoped_lock lock{m_mutex}; + m_blobs.erase(std::move(blobId)); +} + +#pragma endregion WebSocketModuleContentHandler + /*static*/ weak_ptr IWebSocketModuleContentHandler::GetInstance() noexcept { return s_contentHandler; } diff --git a/vnext/Shared/Modules/BlobModule.h b/vnext/Shared/Modules/BlobModule.h index f7d08b7ff0b..cf94a0d84fb 100644 --- a/vnext/Shared/Modules/BlobModule.h +++ b/vnext/Shared/Modules/BlobModule.h @@ -21,6 +21,8 @@ namespace Microsoft::React { +//class OriginPolicyHttpFilter +// : public winrt::implements { class BlobWebSocketModuleContentHandler final : public IWebSocketModuleContentHandler { std::unordered_map> m_blobs; std::mutex m_blobsMutex; diff --git a/vnext/Shared/Modules/IWebSocketModuleContentHandler.h b/vnext/Shared/Modules/IWebSocketModuleContentHandler.h index 36eb7824d0b..b85aa5c818f 100644 --- a/vnext/Shared/Modules/IWebSocketModuleContentHandler.h +++ b/vnext/Shared/Modules/IWebSocketModuleContentHandler.h @@ -6,10 +6,16 @@ // React Native #include +// Windows API +#include + // Standard Library #include #include +#include +#include + namespace Microsoft::React { /// @@ -25,4 +31,25 @@ struct IWebSocketModuleContentHandler { virtual void ProcessMessage(std::vector &&message, folly::dynamic ¶ms) = 0; }; +class WebSocketModuleContentHandler + : public winrt::implements { + std::unordered_map> m_blobs; + std::mutex m_mutex; + std::unordered_set m_socketIds; + +public: + void ProcessMessage(std::string &&message, folly::dynamic ¶ms); //TODO: noexcept? + + void ProcessMessage(std::vector &&message, folly::dynamic ¶ms); //TODO: noexcept? + + void Register(int64_t socketId) noexcept; + + void Unregister(int64_t socketId) noexcept; + + winrt::array_view ResolveMessage(std::string &&blogId, int64_t offset, int64_t size) noexcept; + + void StoreMessage(std::vector &&message, std::string &&blobId) noexcept; + + void RemoveMessage(std::string &&blobId) noexcept; +}; } // namespace Microsoft::React diff --git a/vnext/Shared/OInstance.cpp b/vnext/Shared/OInstance.cpp index 3d2856bb5f1..9587d10ae71 100644 --- a/vnext/Shared/OInstance.cpp +++ b/vnext/Shared/OInstance.cpp @@ -543,7 +543,6 @@ InstanceImpl::~InstanceImpl() { std::vector> InstanceImpl::GetDefaultNativeModules( std::shared_ptr nativeQueue) { std::vector> modules; - auto ns = ReactPropertyBagHelper::GetNamespace(L"CxxModule"); auto transitionalProps = m_transitionalModuleProperties; modules.push_back(std::make_unique( From 9592c53cb91176664b37ede18ca572c6e86aeee2 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Fri, 22 Apr 2022 00:15:47 -0700 Subject: [PATCH 30/91] Have WebSocketModule use IInspectable as props arg --- vnext/Desktop.UnitTests/WebSocketModuleTest.cpp | 6 +++--- vnext/Shared/Modules/WebSocketModule.cpp | 3 ++- vnext/Shared/Modules/WebSocketModule.h | 3 +-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/vnext/Desktop.UnitTests/WebSocketModuleTest.cpp b/vnext/Desktop.UnitTests/WebSocketModuleTest.cpp index 5fec25b8e13..c431e5b94af 100644 --- a/vnext/Desktop.UnitTests/WebSocketModuleTest.cpp +++ b/vnext/Desktop.UnitTests/WebSocketModuleTest.cpp @@ -26,7 +26,7 @@ TEST_CLASS (WebSocketModuleTest) { "connect", "close", "send", "sendBinary", "ping"}; TEST_METHOD(CreateModule) { - auto module = make_unique(); + auto module = make_unique(nullptr /*iProperties*/); Assert::IsFalse(module == nullptr); Assert::AreEqual(string("WebSocketModule"), module->getName()); @@ -40,7 +40,7 @@ TEST_CLASS (WebSocketModuleTest) { } TEST_METHOD(ConnectEmptyUriFails) { - auto module = make_unique(); + auto module = make_unique(nullptr /*iProperties*/); module->getMethods() .at(WebSocketModule::MethodId::Connect) @@ -70,7 +70,7 @@ TEST_CLASS (WebSocketModuleTest) { }; auto instance = CreateMockInstance(jsef); - auto module = make_unique(); + auto module = make_unique(nullptr /*iProperties*/); module->setInstance(instance); module->SetResourceFactory([](const string &) { auto rc = make_shared(); diff --git a/vnext/Shared/Modules/WebSocketModule.cpp b/vnext/Shared/Modules/WebSocketModule.cpp index 9b11575ae08..cf6900b51f9 100644 --- a/vnext/Shared/Modules/WebSocketModule.cpp +++ b/vnext/Shared/Modules/WebSocketModule.cpp @@ -6,6 +6,7 @@ #include #include +#include #include "Unicode.h" // React Native @@ -149,7 +150,7 @@ namespace Microsoft::React { #pragma region WebSocketModule -WebSocketModule::WebSocketModule(winrt::Microsoft::ReactNative::IReactPropertyBag const &properties) +WebSocketModule::WebSocketModule(winrt::Windows::Foundation::IInspectable const &properties) : m_sharedState{std::make_shared()} { m_sharedState->ResourceFactory = [](string &&url) { return IWebSocketResource::Make(); }; m_sharedState->Module = this; diff --git a/vnext/Shared/Modules/WebSocketModule.h b/vnext/Shared/Modules/WebSocketModule.h index 8abddf02dd4..d829a5eaaa5 100644 --- a/vnext/Shared/Modules/WebSocketModule.h +++ b/vnext/Shared/Modules/WebSocketModule.h @@ -5,7 +5,6 @@ #include #include -#include // React Native #include @@ -31,7 +30,7 @@ class WebSocketModule : public facebook::xplat::module::CxxModule { public: enum MethodId { Connect = 0, Close = 1, Send = 2, SendBinary = 3, Ping = 4, SIZE = 5 }; - WebSocketModule(winrt::Microsoft::ReactNative::IReactPropertyBag const &iProperties); + WebSocketModule(winrt::Windows::Foundation::IInspectable const &iProperties); ~WebSocketModule() noexcept override; From a7306f3d20275dfa6117089565be602230dff734 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Tue, 26 Apr 2022 23:07:24 -0700 Subject: [PATCH 31/91] Use property bag instead of global singletons for blob helpers --- vnext/Shared/Modules/BlobModule.cpp | 41 +++++++++---------- vnext/Shared/Modules/BlobModule.h | 5 ++- .../Modules/IWebSocketModuleContentHandler.h | 2 - vnext/Shared/Modules/IWebSocketModuleProxy.h | 2 - vnext/Shared/Modules/WebSocketModule.cpp | 40 ++++++++++-------- vnext/Shared/Modules/WebSocketModule.h | 3 ++ vnext/Shared/OInstance.cpp | 4 ++ 7 files changed, 54 insertions(+), 43 deletions(-) diff --git a/vnext/Shared/Modules/BlobModule.cpp b/vnext/Shared/Modules/BlobModule.cpp index 1088d2f29db..061e39d0b1c 100644 --- a/vnext/Shared/Modules/BlobModule.cpp +++ b/vnext/Shared/Modules/BlobModule.cpp @@ -21,35 +21,36 @@ using folly::dynamic; using std::lock_guard; using std::mutex; using std::scoped_lock; +using std::shared_ptr; using std::string; using std::vector; using std::weak_ptr; -using winrt::Microsoft::ReactNative::ReactPropertyBagHelper; +using winrt::Microsoft::ReactNative::IReactPropertyBag; +using winrt::Microsoft::ReactNative::ReactNonAbiValue; +using winrt::Microsoft::ReactNative::ReactPropertyBag; +using winrt::Microsoft::ReactNative::ReactPropertyId; +using winrt::Windows::Foundation::IInspectable; using winrt::Windows::Foundation::GuidHelper; using winrt::Windows::Security::Cryptography::CryptographicBuffer; namespace { constexpr char moduleName[] = "BlobModule"; constexpr char blobURIScheme[] = "blob"; - -weak_ptr s_contentHandler; } // namespace namespace Microsoft::React { #pragma region BlobModule -BlobModule::BlobModule(winrt::Microsoft::ReactNative::IReactPropertyBag const& properties) noexcept - : m_sharedState{std::make_shared()} { - m_contentHandler = std::static_pointer_cast(s_contentHandler.lock()); - if (!m_contentHandler) { - m_contentHandler = std::make_shared(); - s_contentHandler = m_contentHandler; - } +BlobModule::BlobModule(winrt::Windows::Foundation::IInspectable const &iProperties) noexcept + : m_sharedState{std::make_shared()}, + m_contentHandler{std::make_shared()}, + m_iProperties{iProperties} { - auto contentHandler = winrt::make(); - properties.Set( - ReactPropertyBagHelper::GetName(ReactPropertyBagHelper::GetNamespace(L"CxxNativeModule"), L"WebSocketModuleContentHandler"), contentHandler); + auto propId = ReactPropertyId>>{L"BlobModule.ContentHandler"}; + auto propBag = ReactPropertyBag{m_iProperties.try_as()}; + auto contentHandler = m_contentHandler; + propBag.Set(propId, std::move(contentHandler)); m_sharedState->Module = this; } @@ -90,8 +91,10 @@ std::vector BlobModule::getMethods() { }}, {"sendOverSocket", - [contentHandler = m_contentHandler](dynamic args) { - auto wsProxy = IWebSocketModuleProxy::GetInstance().lock(); + [contentHandler = m_contentHandler, + propBag = ReactPropertyBag{m_iProperties.try_as()}](dynamic args) { + auto propId = ReactPropertyId>>{L"WebSocketModule.Proxy"}; + auto wsProxy = propBag.Get(propId).Value(); if (!wsProxy) { return; } @@ -275,16 +278,12 @@ void WebSocketModuleContentHandler::RemoveMessage(string&& blobId) noexcept #pragma endregion WebSocketModuleContentHandler -/*static*/ weak_ptr IWebSocketModuleContentHandler::GetInstance() noexcept { - return s_contentHandler; -} - /*extern*/ const char *GetBlobModuleName() noexcept { return moduleName; } -/*extern*/ std::unique_ptr CreateBlobModule(winrt::Windows::Foundation::IInspectable const& iProperties) noexcept { - if (auto properties = iProperties.try_as()) +/*extern*/ std::unique_ptr CreateBlobModule(IInspectable const& iProperties) noexcept { + if (auto properties = iProperties.try_as()) return std::make_unique(properties); return nullptr; diff --git a/vnext/Shared/Modules/BlobModule.h b/vnext/Shared/Modules/BlobModule.h index cf94a0d84fb..4f784c4989a 100644 --- a/vnext/Shared/Modules/BlobModule.h +++ b/vnext/Shared/Modules/BlobModule.h @@ -52,6 +52,9 @@ class BlobWebSocketModuleContentHandler final : public IWebSocketModuleContentHa class BlobModule : public facebook::xplat::module::CxxModule { std::shared_ptr m_contentHandler; + // Property bag high level reference. + winrt::Windows::Foundation::IInspectable m_iProperties; + public: enum class MethodId { AddNetworkingHandler = 0, @@ -63,7 +66,7 @@ class BlobModule : public facebook::xplat::module::CxxModule { SIZE = 6 }; - BlobModule(winrt::Microsoft::ReactNative::IReactPropertyBag const& iProperties) noexcept; + BlobModule(winrt::Windows::Foundation::IInspectable const &iProperties) noexcept; ~BlobModule() noexcept override; diff --git a/vnext/Shared/Modules/IWebSocketModuleContentHandler.h b/vnext/Shared/Modules/IWebSocketModuleContentHandler.h index b85aa5c818f..a6c08cb1aa9 100644 --- a/vnext/Shared/Modules/IWebSocketModuleContentHandler.h +++ b/vnext/Shared/Modules/IWebSocketModuleContentHandler.h @@ -22,8 +22,6 @@ namespace Microsoft::React { /// See https://github.com/facebook/react-native/blob/v0.63.2/React/CoreModules/RCTWebSocketModule.h#L12 /// struct IWebSocketModuleContentHandler { - static std::weak_ptr GetInstance() noexcept; - virtual ~IWebSocketModuleContentHandler() noexcept {} virtual void ProcessMessage(std::string &&message, folly::dynamic ¶ms) = 0; diff --git a/vnext/Shared/Modules/IWebSocketModuleProxy.h b/vnext/Shared/Modules/IWebSocketModuleProxy.h index 46a3c0e0def..61320097d8f 100644 --- a/vnext/Shared/Modules/IWebSocketModuleProxy.h +++ b/vnext/Shared/Modules/IWebSocketModuleProxy.h @@ -14,8 +14,6 @@ namespace Microsoft::React { /// without switching to the JavaScript queue thread. /// struct IWebSocketModuleProxy { - static std::weak_ptr GetInstance() noexcept; - virtual ~IWebSocketModuleProxy() noexcept {} virtual void SendBinary(std::string &&base64String, int64_t id) noexcept = 0; diff --git a/vnext/Shared/Modules/WebSocketModule.cpp b/vnext/Shared/Modules/WebSocketModule.cpp index cf6900b51f9..a33a4b87596 100644 --- a/vnext/Shared/Modules/WebSocketModule.cpp +++ b/vnext/Shared/Modules/WebSocketModule.cpp @@ -30,7 +30,12 @@ using std::shared_ptr; using std::string; using std::weak_ptr; -using winrt::Microsoft::ReactNative::ReactPropertyBagHelper; +using winrt::Microsoft::ReactNative::IReactPropertyBag; +using winrt::Microsoft::ReactNative::ReactNonAbiValue; +using winrt::Microsoft::ReactNative::ReactPropertyBag; +using winrt::Microsoft::ReactNative::ReactPropertyId; + +using winrt::Windows::Foundation::IInspectable; using winrt::Windows::Security::Cryptography::CryptographicBuffer; namespace { @@ -41,7 +46,6 @@ using Microsoft::React::Networking::IWebSocketResource; constexpr char moduleName[] = "WebSocketModule"; weak_ptr s_sharedState; -weak_ptr s_proxy; static void SendEvent(weak_ptr weakInstance, string &&eventName, dynamic &&args) { if (auto instance = weakInstance.lock()) { @@ -103,14 +107,19 @@ GetOrCreateWebSocket(int64_t id, string &&url, weak_ptrSetOnMessage([id, weakInstance](size_t length, const string &message, bool isBinary) { + ws->SetOnMessage( + [id, weakInstance, propBag = ReactPropertyBag{state->InspectableProps.try_as()}]( + size_t length, const string &message, bool isBinary) { auto strongInstance = weakInstance.lock(); if (!strongInstance) return; dynamic args = dynamic::object("id", id)("type", isBinary ? "binary" : "text"); - auto contentHandler = Microsoft::React::IWebSocketModuleContentHandler::GetInstance().lock(); + auto propId = ReactPropertyId>>{ + L"BlobModule.ContentHandler"}; + auto contentHandler = propBag.Get(propId).Value(); + if (!contentHandler) { args["data"] = message; } else { @@ -150,17 +159,18 @@ namespace Microsoft::React { #pragma region WebSocketModule -WebSocketModule::WebSocketModule(winrt::Windows::Foundation::IInspectable const &properties) - : m_sharedState{std::make_shared()} { +WebSocketModule::WebSocketModule(winrt::Windows::Foundation::IInspectable const &iProperties) + : m_sharedState{std::make_shared()}, + m_proxy{std::make_shared()} { m_sharedState->ResourceFactory = [](string &&url) { return IWebSocketResource::Make(); }; m_sharedState->Module = this; + m_sharedState->InspectableProps = iProperties; s_sharedState = weak_ptr(m_sharedState); - m_proxy = std::static_pointer_cast(s_proxy.lock()); - if (!m_proxy) { - m_proxy = std::make_shared(); - s_proxy = m_proxy; - } + auto propId = ReactPropertyId>>{L"WebSocketModule.Proxy"}; + auto propBag = ReactPropertyBag{m_sharedState->InspectableProps.try_as()}; + auto proxy = m_proxy; + propBag.Set(propId, std::move(proxy)); } WebSocketModule::~WebSocketModule() noexcept /*override*/ { @@ -298,17 +308,13 @@ void WebSocketModuleProxy::SendBinary(std::string &&base64String, int64_t id) no #pragma endregion WebSocketModuleProxy -/*static*/ weak_ptr IWebSocketModuleProxy::GetInstance() noexcept { - return s_proxy; -} - /*extern*/ const char *GetWebSocketModuleName() noexcept { return moduleName; } /*extern*/ std::unique_ptr CreateWebSocketModule( - winrt::Windows::Foundation::IInspectable const &iProperties) noexcept { - if (auto properties = iProperties.try_as()) + IInspectable const &iProperties) noexcept { + if (auto properties = iProperties.try_as()) return std::make_unique(properties); return nullptr; diff --git a/vnext/Shared/Modules/WebSocketModule.h b/vnext/Shared/Modules/WebSocketModule.h index d829a5eaaa5..03b12c8730f 100644 --- a/vnext/Shared/Modules/WebSocketModule.h +++ b/vnext/Shared/Modules/WebSocketModule.h @@ -50,6 +50,9 @@ class WebSocketModule : public facebook::xplat::module::CxxModule { /// Keeps a raw reference to the module object to lazily retrieve the React Instance as needed. /// CxxModule *Module{nullptr}; + + // Property bag high level reference. + winrt::Windows::Foundation::IInspectable InspectableProps; }; #pragma region CxxModule overrides diff --git a/vnext/Shared/OInstance.cpp b/vnext/Shared/OInstance.cpp index 9587d10ae71..02b6c91bdfd 100644 --- a/vnext/Shared/OInstance.cpp +++ b/vnext/Shared/OInstance.cpp @@ -544,6 +544,10 @@ std::vector> InstanceImpl::GetDefaultNativeModules std::shared_ptr nativeQueue) { std::vector> modules; auto transitionalProps = m_transitionalModuleProperties; + auto propBag = winrt::Microsoft::ReactNative::ReactPropertyBag{transitionalProps}; + winrt::Microsoft::ReactNative::ReactPropertyId> propId{ + L"erasme"}; + propBag.Set(propId, 99); modules.push_back(std::make_unique( m_innerInstance, From d87cd3279be04b11ab8346915eb6648d2d072879 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Tue, 26 Apr 2022 23:16:54 -0700 Subject: [PATCH 32/91] Store blob helpers in prop bag as weak_ptr --- vnext/Shared/Modules/BlobModule.cpp | 8 ++++---- vnext/Shared/Modules/WebSocketModule.cpp | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/vnext/Shared/Modules/BlobModule.cpp b/vnext/Shared/Modules/BlobModule.cpp index 061e39d0b1c..8811371d515 100644 --- a/vnext/Shared/Modules/BlobModule.cpp +++ b/vnext/Shared/Modules/BlobModule.cpp @@ -47,9 +47,9 @@ BlobModule::BlobModule(winrt::Windows::Foundation::IInspectable const &iProperti m_contentHandler{std::make_shared()}, m_iProperties{iProperties} { - auto propId = ReactPropertyId>>{L"BlobModule.ContentHandler"}; + auto propId = ReactPropertyId>>{L"BlobModule.ContentHandler"}; auto propBag = ReactPropertyBag{m_iProperties.try_as()}; - auto contentHandler = m_contentHandler; + auto contentHandler = weak_ptr{m_contentHandler}; propBag.Set(propId, std::move(contentHandler)); m_sharedState->Module = this; @@ -93,8 +93,8 @@ std::vector BlobModule::getMethods() { {"sendOverSocket", [contentHandler = m_contentHandler, propBag = ReactPropertyBag{m_iProperties.try_as()}](dynamic args) { - auto propId = ReactPropertyId>>{L"WebSocketModule.Proxy"}; - auto wsProxy = propBag.Get(propId).Value(); + auto propId = ReactPropertyId>>{L"WebSocketModule.Proxy"}; + auto wsProxy = propBag.Get(propId).Value().lock(); if (!wsProxy) { return; } diff --git a/vnext/Shared/Modules/WebSocketModule.cpp b/vnext/Shared/Modules/WebSocketModule.cpp index a33a4b87596..3785fc2a99f 100644 --- a/vnext/Shared/Modules/WebSocketModule.cpp +++ b/vnext/Shared/Modules/WebSocketModule.cpp @@ -116,9 +116,9 @@ GetOrCreateWebSocket(int64_t id, string &&url, weak_ptr>>{ + auto propId = ReactPropertyId>>{ L"BlobModule.ContentHandler"}; - auto contentHandler = propBag.Get(propId).Value(); + auto contentHandler = propBag.Get(propId).Value().lock(); if (!contentHandler) { args["data"] = message; @@ -167,9 +167,9 @@ WebSocketModule::WebSocketModule(winrt::Windows::Foundation::IInspectable const m_sharedState->InspectableProps = iProperties; s_sharedState = weak_ptr(m_sharedState); - auto propId = ReactPropertyId>>{L"WebSocketModule.Proxy"}; + auto propId = ReactPropertyId>>{L"WebSocketModule.Proxy"}; auto propBag = ReactPropertyBag{m_sharedState->InspectableProps.try_as()}; - auto proxy = m_proxy; + auto proxy = weak_ptr{m_proxy}; propBag.Set(propId, std::move(proxy)); } From bd31756c3b66aa592db36e16f5187f23d6b16279 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Wed, 27 Apr 2022 15:50:35 -0500 Subject: [PATCH 33/91] Replace remaining lock_guard in BlobModule --- vnext/Shared/Modules/BlobModule.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/vnext/Shared/Modules/BlobModule.cpp b/vnext/Shared/Modules/BlobModule.cpp index 8811371d515..7e837e1f35a 100644 --- a/vnext/Shared/Modules/BlobModule.cpp +++ b/vnext/Shared/Modules/BlobModule.cpp @@ -18,8 +18,6 @@ using namespace facebook::xplat; using facebook::react::Instance; using folly::dynamic; -using std::lock_guard; -using std::mutex; using std::scoped_lock; using std::shared_ptr; using std::string; @@ -188,19 +186,19 @@ void BlobWebSocketModuleContentHandler::ProcessMessage(vector &&message #pragma endregion IWebSocketModuleContentHandler void BlobWebSocketModuleContentHandler::Register(int64_t socketID) noexcept { - lock_guard lock{m_socketIDsMutex}; + scoped_lock lock{m_socketIDsMutex}; m_socketIDs.insert(socketID); } void BlobWebSocketModuleContentHandler::Unregister(int64_t socketID) noexcept { - lock_guard lock{m_socketIDsMutex}; + scoped_lock lock{m_socketIDsMutex}; if (m_socketIDs.find(socketID) != m_socketIDs.end()) m_socketIDs.erase(socketID); } winrt::array_view BlobWebSocketModuleContentHandler::ResolveMessage(string &&blobId, int64_t offset, int64_t size) noexcept { - lock_guard lock{m_blobsMutex}; + scoped_lock lock{m_blobsMutex}; auto &data = m_blobs.at(std::move(blobId)); @@ -208,13 +206,13 @@ BlobWebSocketModuleContentHandler::ResolveMessage(string &&blobId, int64_t offse } void BlobWebSocketModuleContentHandler::RemoveMessage(string &&blobId) noexcept { - lock_guard lock{m_blobsMutex}; + scoped_lock lock{m_blobsMutex}; m_blobs.erase(std::move(blobId)); } void BlobWebSocketModuleContentHandler::StoreMessage(vector &&message, string &&blobId) noexcept { - lock_guard lock{m_blobsMutex}; + scoped_lock lock{m_blobsMutex}; m_blobs.insert_or_assign(std::move(blobId), std::move(message)); } From e0c24597f4ef7366deb9aa8482767bc8457d99f2 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Wed, 27 Apr 2022 19:40:53 -0500 Subject: [PATCH 34/91] Define IUriHandler, IReqBodyHandler, IRespHandler. --- vnext/Shared/Modules/BlobModule.cpp | 1 + vnext/Shared/Modules/BlobModule.h | 39 ++++++++++++++++++++-- vnext/Shared/Modules/HttpModule.cpp | 16 ++++++++- vnext/Shared/Modules/HttpModule.h | 28 +++++++++++++++- vnext/Shared/Modules/IHttpModuleProxy.h | 29 ++++++++++++++++ vnext/Shared/Modules/IRequestBodyHandler.h | 31 +++++++++++++++++ vnext/Shared/Modules/IResponseHandler.h | 26 +++++++++++++++ vnext/Shared/Modules/IUriHandler.h | 32 ++++++++++++++++++ vnext/Shared/Modules/WebSocketModule.h | 2 +- vnext/Shared/OInstance.cpp | 10 +++--- vnext/Shared/Shared.vcxitems | 4 +++ vnext/Shared/Shared.vcxitems.filters | 12 +++++++ 12 files changed, 218 insertions(+), 12 deletions(-) create mode 100644 vnext/Shared/Modules/IHttpModuleProxy.h create mode 100644 vnext/Shared/Modules/IRequestBodyHandler.h create mode 100644 vnext/Shared/Modules/IResponseHandler.h create mode 100644 vnext/Shared/Modules/IUriHandler.h diff --git a/vnext/Shared/Modules/BlobModule.cpp b/vnext/Shared/Modules/BlobModule.cpp index 7e837e1f35a..c587d136f94 100644 --- a/vnext/Shared/Modules/BlobModule.cpp +++ b/vnext/Shared/Modules/BlobModule.cpp @@ -4,6 +4,7 @@ #include "BlobModule.h" #include +#include #include // React Native diff --git a/vnext/Shared/Modules/BlobModule.h b/vnext/Shared/Modules/BlobModule.h index 4f784c4989a..5263990a30d 100644 --- a/vnext/Shared/Modules/BlobModule.h +++ b/vnext/Shared/Modules/BlobModule.h @@ -3,8 +3,10 @@ #pragma once +#include +#include +#include #include -#include // React Native #include @@ -21,8 +23,6 @@ namespace Microsoft::React { -//class OriginPolicyHttpFilter -// : public winrt::implements { class BlobWebSocketModuleContentHandler final : public IWebSocketModuleContentHandler { std::unordered_map> m_blobs; std::mutex m_blobsMutex; @@ -49,6 +49,39 @@ class BlobWebSocketModuleContentHandler final : public IWebSocketModuleContentHa void StoreMessage(std::vector &&message, std::string &&blobId) noexcept; }; +class BlobModuleUriHandler final : public IUriHandler { +#pragma region IUriHandler + + bool Supports(std::string& uri, std::string& responseType) override; + + folly::dynamic Fetch(std::string& uri) override; + +#pragma endregion IUriHandler +}; + +class BlobModuleRequestBodyHandler final : public IRequestBodyHandler +{ +#pragma region IRequestBodyHandler + + bool Supports(folly::dynamic& data) override; + + void * /*RequestBody*/ ToRequestBody(folly::dynamic &data, std::string& contentType) + override; + +#pragma endregion IRequestBodyHandler +}; + +class BlobModuleResponseHandler final : public IResponseHandler +{ +#pragma region IResponseHandler + + bool Supports(std::string responseType) override; + + folly::dynamic ToResponseData(folly::dynamic& body) override; + +#pragma endregion IResponseHandler +}; + class BlobModule : public facebook::xplat::module::CxxModule { std::shared_ptr m_contentHandler; diff --git a/vnext/Shared/Modules/HttpModule.cpp b/vnext/Shared/Modules/HttpModule.cpp index 61e57a724cd..43af5577c9d 100644 --- a/vnext/Shared/Modules/HttpModule.cpp +++ b/vnext/Shared/Modules/HttpModule.cpp @@ -5,6 +5,8 @@ #include "HttpModule.h" +#include + // React Native #include #include @@ -14,6 +16,11 @@ using folly::dynamic; using std::shared_ptr; using std::string; using std::weak_ptr; +using winrt::Microsoft::ReactNative::IReactPropertyBag; +using winrt::Microsoft::ReactNative::ReactNonAbiValue; +using winrt::Microsoft::ReactNative::ReactPropertyBag; +using winrt::Microsoft::ReactNative::ReactPropertyId; +using winrt::Windows::Foundation::IInspectable; namespace { @@ -65,8 +72,15 @@ static shared_ptr CreateHttpResource(weak_ptr weakReact namespace Microsoft::React { -HttpModule::HttpModule() noexcept : m_holder{std::make_shared()} { +HttpModule::HttpModule(winrt::Windows::Foundation::IInspectable const &iProperties) noexcept + : m_holder{std::make_shared()}, m_inspectableProperties{iProperties} { m_holder->Module = this; + + auto propId = + ReactPropertyId>>{L"HttpModule.Proxy"}; + auto propBag = ReactPropertyBag{m_inspectableProperties.try_as()}; + auto contentHandler = weak_ptr{m_proxy}; + propBag.Set(propId, std::move(contentHandler)); } HttpModule::~HttpModule() noexcept /*override*/ { diff --git a/vnext/Shared/Modules/HttpModule.h b/vnext/Shared/Modules/HttpModule.h index cb870835547..55e7bffef6d 100644 --- a/vnext/Shared/Modules/HttpModule.h +++ b/vnext/Shared/Modules/HttpModule.h @@ -3,13 +3,31 @@ #pragma once +#include +#include #include // React Native #include +// Windows API +#include + namespace Microsoft::React { +class HttpModuleProxy final : public IHttpModuleProxy +{ +#pragma region IHttpModuleProxy + + void AddUriHandler(std::shared_ptr uriHandler) noexcept override; + + void AddRequestBodyHandler(std::shared_ptr requestBodyHandler) noexcept override; + + void AddResponseHandler(std::shared_ptr responseHandler) noexcept override; + +#pragma endregion IHttpModuleProxy +}; + /// /// Realizes NativeModules projection. /// See src\Libraries\Network\RCTNetworkingWinShared.js @@ -18,7 +36,7 @@ class HttpModule : public facebook::xplat::module::CxxModule { public: enum MethodId { SendRequest = 0, AbortRequest = 1, ClearCookies = 2, LAST = ClearCookies }; - HttpModule() noexcept; + HttpModule(winrt::Windows::Foundation::IInspectable const &iProperties) noexcept; ~HttpModule() noexcept override; @@ -49,5 +67,13 @@ class HttpModule : public facebook::xplat::module::CxxModule { std::shared_ptr m_resource; std::shared_ptr m_holder; + + /// + /// Exposes a subset of the module's methods. + /// + std::shared_ptr m_proxy; + + // Property bag high level reference. + winrt::Windows::Foundation::IInspectable m_inspectableProperties; }; } // namespace Microsoft::React diff --git a/vnext/Shared/Modules/IHttpModuleProxy.h b/vnext/Shared/Modules/IHttpModuleProxy.h new file mode 100644 index 00000000000..c0f3c5030fd --- /dev/null +++ b/vnext/Shared/Modules/IHttpModuleProxy.h @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#pragma once + +#include "IRequestBodyHandler.h" +#include "IResponseHandler.h" +#include "IUriHandler.h" + +// Standard Library +#include + +namespace Microsoft::React { + +/// +/// Provides partial access to HttpModule methods directly to other native modules +/// without switching to the JavaScript queue thread. +/// +struct IHttpModuleProxy { + virtual ~IHttpModuleProxy() noexcept {} + + virtual void AddUriHandler(std::shared_ptr uriHandler) noexcept = 0; + + virtual void AddRequestBodyHandler(std::shared_ptr requestBodyHandler) noexcept = 0; + + virtual void AddResponseHandler(std::shared_ptr responseHandler) noexcept = 0; +}; + +} // namespace Microsoft::React diff --git a/vnext/Shared/Modules/IRequestBodyHandler.h b/vnext/Shared/Modules/IRequestBodyHandler.h new file mode 100644 index 00000000000..8680c6dd382 --- /dev/null +++ b/vnext/Shared/Modules/IRequestBodyHandler.h @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#pragma once + +// Folly +#include + +// Standard Library +#include + +namespace Microsoft::React { + + /// + /// Allows adding custom handling to build the {@link RequestBody} from the JS body payload. + /// +struct IRequestBodyHandler { + + /// + /// Returns if the handler should be used for a JS body payload. + /// + virtual bool Supports(folly::dynamic& data) = 0; + + /// + /// Returns the {@link RequestBody} for the JS body payload. + /// + virtual void * /*RequestBody*/ ToRequestBody(folly::dynamic& data, std::string& contentType) = 0; +}; + + +} // namespace diff --git a/vnext/Shared/Modules/IResponseHandler.h b/vnext/Shared/Modules/IResponseHandler.h new file mode 100644 index 00000000000..a385a65c460 --- /dev/null +++ b/vnext/Shared/Modules/IResponseHandler.h @@ -0,0 +1,26 @@ +#pragma once + +// Folly +#include + +// Standard Library +#include + +namespace Microsoft::React { + + /// + /// Allows adding custom handling to build the JS body payload from the {@link ResponseBody}. + /// +struct IResponseHandler { + /// + /// Returns if the handler should be used for a response type. + /// + virtual bool Supports(std::string responseType) = 0; + + /// + /// Returns the JS body payload for the {@link ResponseBody}. + /// + virtual folly::dynamic ToResponseData(folly::dynamic& body) = 0; +}; + +} // namespace diff --git a/vnext/Shared/Modules/IUriHandler.h b/vnext/Shared/Modules/IUriHandler.h new file mode 100644 index 00000000000..1c21bba5f38 --- /dev/null +++ b/vnext/Shared/Modules/IUriHandler.h @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#pragma once + +// Folly +#include + +// Standard Library +#include + +namespace Microsoft::React { + +/// +/// Allows to implement a custom fetching process for specific URIs. It is the handler's job to +/// fetch the URI and return the JS body payload. +/// +struct IUriHandler { + + /// + /// Returns if the handler should be used for an URI. + /// + virtual bool Supports(std::string& uri, std::string& responseType) = 0; + + /// + /// Fetch the URI and return the JS body payload. + /// + virtual folly::dynamic Fetch(std::string& uri) = 0; + +}; + +} // namespace Microsoft::React diff --git a/vnext/Shared/Modules/WebSocketModule.h b/vnext/Shared/Modules/WebSocketModule.h index 03b12c8730f..4ca2423caa5 100644 --- a/vnext/Shared/Modules/WebSocketModule.h +++ b/vnext/Shared/Modules/WebSocketModule.h @@ -91,7 +91,7 @@ class WebSocketModule : public facebook::xplat::module::CxxModule { std::shared_ptr m_sharedState; /// - /// Exposes direct partial functionality to other modules. + /// Exposes a subset of the module's methods. /// std::shared_ptr m_proxy; }; diff --git a/vnext/Shared/OInstance.cpp b/vnext/Shared/OInstance.cpp index 02b6c91bdfd..4f9104e7c08 100644 --- a/vnext/Shared/OInstance.cpp +++ b/vnext/Shared/OInstance.cpp @@ -71,11 +71,12 @@ using winrt::Microsoft::ReactNative::ReactPropertyBagHelper; namespace Microsoft::React { -/*extern*/ std::unique_ptr CreateHttpModule() noexcept { +/*extern*/ std::unique_ptr CreateHttpModule( + winrt::Windows::Foundation::IInspectable const &iProperties) noexcept { if (GetRuntimeOptionBool("Http.UseMonolithicModule")) { return std::make_unique(); } else { - return std::make_unique(); + return std::make_unique(iProperties); } } @@ -545,14 +546,11 @@ std::vector> InstanceImpl::GetDefaultNativeModules std::vector> modules; auto transitionalProps = m_transitionalModuleProperties; auto propBag = winrt::Microsoft::ReactNative::ReactPropertyBag{transitionalProps}; - winrt::Microsoft::ReactNative::ReactPropertyId> propId{ - L"erasme"}; - propBag.Set(propId, 99); modules.push_back(std::make_unique( m_innerInstance, Microsoft::React::GetHttpModuleName(), - [nativeQueue]() -> std::unique_ptr { return Microsoft::React::CreateHttpModule(); }, + [nativeQueue, transitionalProps]() -> std::unique_ptr { return Microsoft::React::CreateHttpModule(transitionalProps); }, nativeQueue)); modules.push_back(std::make_unique( diff --git a/vnext/Shared/Shared.vcxitems b/vnext/Shared/Shared.vcxitems index 7ccc198ae0b..3eacc3b842a 100644 --- a/vnext/Shared/Shared.vcxitems +++ b/vnext/Shared/Shared.vcxitems @@ -89,6 +89,10 @@ + + + + diff --git a/vnext/Shared/Shared.vcxitems.filters b/vnext/Shared/Shared.vcxitems.filters index 1169970cbc6..da93b5f6164 100644 --- a/vnext/Shared/Shared.vcxitems.filters +++ b/vnext/Shared/Shared.vcxitems.filters @@ -429,6 +429,18 @@ Header Files + + Header Files\Modules + + + Header Files\Modules + + + Header Files\Modules + + + Header Files\Modules + From 48ca06f9d2581f779c813d7596bb2c0a982c7a4d Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Wed, 27 Apr 2022 23:41:25 -0700 Subject: [PATCH 35/91] IHttpResource::SendRequest - add folly::dynamic data arg --- .../RNTesterIntegrationTests.cpp | 2 +- vnext/Shared/Modules/BlobModule.cpp | 32 +++++------ vnext/Shared/Modules/BlobModule.h | 17 +++--- vnext/Shared/Modules/HttpModule.cpp | 4 +- vnext/Shared/Modules/HttpModule.h | 3 +- vnext/Shared/Modules/IRequestBodyHandler.h | 14 ++--- vnext/Shared/Modules/IResponseHandler.h | 10 ++-- vnext/Shared/Modules/IUriHandler.h | 6 +- .../Modules/IWebSocketModuleContentHandler.h | 6 +- vnext/Shared/Modules/WebSocketModule.cpp | 55 +++++++++---------- vnext/Shared/Networking/IHttpResource.h | 4 ++ vnext/Shared/Networking/WinRTHttpResource.cpp | 3 + vnext/Shared/Networking/WinRTHttpResource.h | 1 + vnext/Shared/OInstance.cpp | 8 +-- vnext/Shared/OInstance.h | 2 +- 15 files changed, 80 insertions(+), 87 deletions(-) diff --git a/vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp b/vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp index 5b104697de0..64600cec1e5 100644 --- a/vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp +++ b/vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp @@ -217,7 +217,7 @@ TEST_CLASS (RNTesterIntegrationTests) { } BEGIN_TEST_METHOD_ATTRIBUTE(WebSocketBlob) - //TEST_IGNORE() + // TEST_IGNORE() END_TEST_METHOD_ATTRIBUTE() TEST_METHOD(WebSocketBlob) { auto result = m_runner.RunTest("IntegrationTests/WebSocketBlobTest", "WebSocketBlobTest"); diff --git a/vnext/Shared/Modules/BlobModule.cpp b/vnext/Shared/Modules/BlobModule.cpp index c587d136f94..c976c49ffa1 100644 --- a/vnext/Shared/Modules/BlobModule.cpp +++ b/vnext/Shared/Modules/BlobModule.cpp @@ -28,8 +28,8 @@ using winrt::Microsoft::ReactNative::IReactPropertyBag; using winrt::Microsoft::ReactNative::ReactNonAbiValue; using winrt::Microsoft::ReactNative::ReactPropertyBag; using winrt::Microsoft::ReactNative::ReactPropertyId; -using winrt::Windows::Foundation::IInspectable; using winrt::Windows::Foundation::GuidHelper; +using winrt::Windows::Foundation::IInspectable; using winrt::Windows::Security::Cryptography::CryptographicBuffer; namespace { @@ -45,8 +45,8 @@ BlobModule::BlobModule(winrt::Windows::Foundation::IInspectable const &iProperti : m_sharedState{std::make_shared()}, m_contentHandler{std::make_shared()}, m_iProperties{iProperties} { - - auto propId = ReactPropertyId>>{L"BlobModule.ContentHandler"}; + auto propId = + ReactPropertyId>>{L"BlobModule.ContentHandler"}; auto propBag = ReactPropertyBag{m_iProperties.try_as()}; auto contentHandler = weak_ptr{m_contentHandler}; propBag.Set(propId, std::move(contentHandler)); @@ -222,55 +222,48 @@ void BlobWebSocketModuleContentHandler::StoreMessage(vector &&message, #pragma region WebSocketModuleContentHandler -void WebSocketModuleContentHandler::ProcessMessage(string&& message, dynamic& params) -{ +void WebSocketModuleContentHandler::ProcessMessage(string &&message, dynamic ¶ms) { params["data"] = std::move(message); } -void WebSocketModuleContentHandler::ProcessMessage(vector&& message, dynamic& params) -{ +void WebSocketModuleContentHandler::ProcessMessage(vector &&message, dynamic ¶ms) { auto blob = dynamic::object(); blob("offset", 0); blob("size", message.size()); // substr(1, 36) strips curly braces from a GUID string blobId = winrt::to_string(winrt::to_hstring(GuidHelper::CreateNewGuid())).substr(1, 36); - //TODO: StoreMessage + // TODO: StoreMessage params["data"] = std::move(blob); params["type"] = "blob"; } -void WebSocketModuleContentHandler::Register(int64_t socketId) noexcept -{ +void WebSocketModuleContentHandler::Register(int64_t socketId) noexcept { scoped_lock lock{m_mutex}; m_socketIds.insert(socketId); } -void WebSocketModuleContentHandler::Unregister(int64_t socketId) noexcept -{ +void WebSocketModuleContentHandler::Unregister(int64_t socketId) noexcept { scoped_lock lock{m_mutex}; if (m_socketIds.find(socketId) != m_socketIds.end()) m_socketIds.erase(socketId); } winrt::array_view -WebSocketModuleContentHandler::ResolveMessage(string&& blobId, int64_t offset, int64_t size) noexcept -{ +WebSocketModuleContentHandler::ResolveMessage(string &&blobId, int64_t offset, int64_t size) noexcept { scoped_lock lock{m_mutex}; auto &data = m_blobs.at(std::move(blobId)); return winrt::array_view{data}; } -void WebSocketModuleContentHandler::StoreMessage(vector&& message, string&& blobId) noexcept -{ +void WebSocketModuleContentHandler::StoreMessage(vector &&message, string &&blobId) noexcept { scoped_lock lock{m_mutex}; m_blobs.insert_or_assign(std::move(blobId), std::move(message)); } -void WebSocketModuleContentHandler::RemoveMessage(string&& blobId) noexcept -{ +void WebSocketModuleContentHandler::RemoveMessage(string &&blobId) noexcept { scoped_lock lock{m_mutex}; m_blobs.erase(std::move(blobId)); } @@ -281,7 +274,8 @@ void WebSocketModuleContentHandler::RemoveMessage(string&& blobId) noexcept return moduleName; } -/*extern*/ std::unique_ptr CreateBlobModule(IInspectable const& iProperties) noexcept { +/*extern*/ std::unique_ptr CreateBlobModule( + IInspectable const &iProperties) noexcept { if (auto properties = iProperties.try_as()) return std::make_unique(properties); diff --git a/vnext/Shared/Modules/BlobModule.h b/vnext/Shared/Modules/BlobModule.h index 5263990a30d..5bd8231374f 100644 --- a/vnext/Shared/Modules/BlobModule.h +++ b/vnext/Shared/Modules/BlobModule.h @@ -52,32 +52,29 @@ class BlobWebSocketModuleContentHandler final : public IWebSocketModuleContentHa class BlobModuleUriHandler final : public IUriHandler { #pragma region IUriHandler - bool Supports(std::string& uri, std::string& responseType) override; + bool Supports(std::string &uri, std::string &responseType) override; - folly::dynamic Fetch(std::string& uri) override; + folly::dynamic Fetch(std::string &uri) override; #pragma endregion IUriHandler }; -class BlobModuleRequestBodyHandler final : public IRequestBodyHandler -{ +class BlobModuleRequestBodyHandler final : public IRequestBodyHandler { #pragma region IRequestBodyHandler - bool Supports(folly::dynamic& data) override; + bool Supports(folly::dynamic &data) override; - void * /*RequestBody*/ ToRequestBody(folly::dynamic &data, std::string& contentType) - override; + void * /*RequestBody*/ ToRequestBody(folly::dynamic &data, std::string &contentType) override; #pragma endregion IRequestBodyHandler }; -class BlobModuleResponseHandler final : public IResponseHandler -{ +class BlobModuleResponseHandler final : public IResponseHandler { #pragma region IResponseHandler bool Supports(std::string responseType) override; - folly::dynamic ToResponseData(folly::dynamic& body) override; + folly::dynamic ToResponseData(folly::dynamic &body) override; #pragma endregion IResponseHandler }; diff --git a/vnext/Shared/Modules/HttpModule.cpp b/vnext/Shared/Modules/HttpModule.cpp index 43af5577c9d..2e3829facc7 100644 --- a/vnext/Shared/Modules/HttpModule.cpp +++ b/vnext/Shared/Modules/HttpModule.cpp @@ -76,8 +76,7 @@ HttpModule::HttpModule(winrt::Windows::Foundation::IInspectable const &iProperti : m_holder{std::make_shared()}, m_inspectableProperties{iProperties} { m_holder->Module = this; - auto propId = - ReactPropertyId>>{L"HttpModule.Proxy"}; + auto propId = ReactPropertyId>>{L"HttpModule.Proxy"}; auto propBag = ReactPropertyBag{m_inspectableProperties.try_as()}; auto contentHandler = weak_ptr{m_proxy}; propBag.Set(propId, std::move(contentHandler)); @@ -158,6 +157,7 @@ std::vector HttpModule::getMethods() params["incrementalUpdates"].asBool(), static_cast(params["timeout"].asDouble()), false,//withCredentials, + std::move(data), [cxxCallback = std::move(cxxCallback)](int64_t requestId) { cxxCallback({requestId}); } diff --git a/vnext/Shared/Modules/HttpModule.h b/vnext/Shared/Modules/HttpModule.h index 55e7bffef6d..8d22f0c2d38 100644 --- a/vnext/Shared/Modules/HttpModule.h +++ b/vnext/Shared/Modules/HttpModule.h @@ -15,8 +15,7 @@ namespace Microsoft::React { -class HttpModuleProxy final : public IHttpModuleProxy -{ +class HttpModuleProxy final : public IHttpModuleProxy { #pragma region IHttpModuleProxy void AddUriHandler(std::shared_ptr uriHandler) noexcept override; diff --git a/vnext/Shared/Modules/IRequestBodyHandler.h b/vnext/Shared/Modules/IRequestBodyHandler.h index 8680c6dd382..5179fa68cb3 100644 --- a/vnext/Shared/Modules/IRequestBodyHandler.h +++ b/vnext/Shared/Modules/IRequestBodyHandler.h @@ -11,21 +11,19 @@ namespace Microsoft::React { - /// - /// Allows adding custom handling to build the {@link RequestBody} from the JS body payload. - /// +/// +/// Allows adding custom handling to build the {@link RequestBody} from the JS body payload. +/// struct IRequestBodyHandler { - /// /// Returns if the handler should be used for a JS body payload. /// - virtual bool Supports(folly::dynamic& data) = 0; + virtual bool Supports(folly::dynamic &data) = 0; /// /// Returns the {@link RequestBody} for the JS body payload. /// - virtual void * /*RequestBody*/ ToRequestBody(folly::dynamic& data, std::string& contentType) = 0; + virtual void * /*RequestBody*/ ToRequestBody(folly::dynamic &data, std::string &contentType) = 0; }; - -} // namespace +} // namespace Microsoft::React diff --git a/vnext/Shared/Modules/IResponseHandler.h b/vnext/Shared/Modules/IResponseHandler.h index a385a65c460..add55bb95ff 100644 --- a/vnext/Shared/Modules/IResponseHandler.h +++ b/vnext/Shared/Modules/IResponseHandler.h @@ -8,9 +8,9 @@ namespace Microsoft::React { - /// - /// Allows adding custom handling to build the JS body payload from the {@link ResponseBody}. - /// +/// +/// Allows adding custom handling to build the JS body payload from the {@link ResponseBody}. +/// struct IResponseHandler { /// /// Returns if the handler should be used for a response type. @@ -20,7 +20,7 @@ struct IResponseHandler { /// /// Returns the JS body payload for the {@link ResponseBody}. /// - virtual folly::dynamic ToResponseData(folly::dynamic& body) = 0; + virtual folly::dynamic ToResponseData(folly::dynamic &body) = 0; }; -} // namespace +} // namespace Microsoft::React diff --git a/vnext/Shared/Modules/IUriHandler.h b/vnext/Shared/Modules/IUriHandler.h index 1c21bba5f38..7bb8b44ebe0 100644 --- a/vnext/Shared/Modules/IUriHandler.h +++ b/vnext/Shared/Modules/IUriHandler.h @@ -16,17 +16,15 @@ namespace Microsoft::React { /// fetch the URI and return the JS body payload. /// struct IUriHandler { - /// /// Returns if the handler should be used for an URI. /// - virtual bool Supports(std::string& uri, std::string& responseType) = 0; + virtual bool Supports(std::string &uri, std::string &responseType) = 0; /// /// Fetch the URI and return the JS body payload. /// - virtual folly::dynamic Fetch(std::string& uri) = 0; - + virtual folly::dynamic Fetch(std::string &uri) = 0; }; } // namespace Microsoft::React diff --git a/vnext/Shared/Modules/IWebSocketModuleContentHandler.h b/vnext/Shared/Modules/IWebSocketModuleContentHandler.h index a6c08cb1aa9..43791b2d327 100644 --- a/vnext/Shared/Modules/IWebSocketModuleContentHandler.h +++ b/vnext/Shared/Modules/IWebSocketModuleContentHandler.h @@ -35,10 +35,10 @@ class WebSocketModuleContentHandler std::mutex m_mutex; std::unordered_set m_socketIds; -public: - void ProcessMessage(std::string &&message, folly::dynamic ¶ms); //TODO: noexcept? + public: + void ProcessMessage(std::string &&message, folly::dynamic ¶ms); // TODO: noexcept? - void ProcessMessage(std::vector &&message, folly::dynamic ¶ms); //TODO: noexcept? + void ProcessMessage(std::vector &&message, folly::dynamic ¶ms); // TODO: noexcept? void Register(int64_t socketId) noexcept; diff --git a/vnext/Shared/Modules/WebSocketModule.cpp b/vnext/Shared/Modules/WebSocketModule.cpp index 3785fc2a99f..89306f0152a 100644 --- a/vnext/Shared/Modules/WebSocketModule.cpp +++ b/vnext/Shared/Modules/WebSocketModule.cpp @@ -110,33 +110,33 @@ GetOrCreateWebSocket(int64_t id, string &&url, weak_ptrSetOnMessage( [id, weakInstance, propBag = ReactPropertyBag{state->InspectableProps.try_as()}]( size_t length, const string &message, bool isBinary) { - auto strongInstance = weakInstance.lock(); - if (!strongInstance) - return; - - dynamic args = dynamic::object("id", id)("type", isBinary ? "binary" : "text"); - - auto propId = ReactPropertyId>>{ - L"BlobModule.ContentHandler"}; - auto contentHandler = propBag.Get(propId).Value().lock(); - - if (!contentHandler) { - args["data"] = message; - } else { - if (isBinary) { - auto buffer = CryptographicBuffer::DecodeFromBase64String(Utf8ToUtf16(message)); - winrt::com_array arr; - CryptographicBuffer::CopyToByteArray(buffer, arr); - auto data = std::vector(arr.begin(), arr.end()); - - contentHandler->ProcessMessage(std::move(data), args); - } else { - contentHandler->ProcessMessage(string{message}, args); - } - } + auto strongInstance = weakInstance.lock(); + if (!strongInstance) + return; + + dynamic args = dynamic::object("id", id)("type", isBinary ? "binary" : "text"); + + auto propId = ReactPropertyId>>{ + L"BlobModule.ContentHandler"}; + auto contentHandler = propBag.Get(propId).Value().lock(); + + if (!contentHandler) { + args["data"] = message; + } else { + if (isBinary) { + auto buffer = CryptographicBuffer::DecodeFromBase64String(Utf8ToUtf16(message)); + winrt::com_array arr; + CryptographicBuffer::CopyToByteArray(buffer, arr); + auto data = std::vector(arr.begin(), arr.end()); + + contentHandler->ProcessMessage(std::move(data), args); + } else { + contentHandler->ProcessMessage(string{message}, args); + } + } - SendEvent(weakInstance, "websocketMessage", std::move(args)); - }); + SendEvent(weakInstance, "websocketMessage", std::move(args)); + }); ws->SetOnClose([id, weakInstance](IWebSocketResource::CloseCode code, const string &reason) { auto strongInstance = weakInstance.lock(); if (!strongInstance) @@ -160,8 +160,7 @@ namespace Microsoft::React { #pragma region WebSocketModule WebSocketModule::WebSocketModule(winrt::Windows::Foundation::IInspectable const &iProperties) - : m_sharedState{std::make_shared()}, - m_proxy{std::make_shared()} { + : m_sharedState{std::make_shared()}, m_proxy{std::make_shared()} { m_sharedState->ResourceFactory = [](string &&url) { return IWebSocketResource::Make(); }; m_sharedState->Module = this; m_sharedState->InspectableProps = iProperties; diff --git a/vnext/Shared/Networking/IHttpResource.h b/vnext/Shared/Networking/IHttpResource.h index 2e999bb5755..2f319231031 100644 --- a/vnext/Shared/Networking/IHttpResource.h +++ b/vnext/Shared/Networking/IHttpResource.h @@ -3,6 +3,9 @@ #pragma once +// Folly +#include + // Standard Library #include #include @@ -38,6 +41,7 @@ struct IHttpResource { bool useIncrementalUpdates, int64_t timeout, bool withCredentials, + folly::dynamic &&data, // TODO: Drop usage of folly::dynamic once content marshalling is implemented. std::function &&callback) noexcept = 0; virtual void AbortRequest(int64_t requestId) noexcept = 0; diff --git a/vnext/Shared/Networking/WinRTHttpResource.cpp b/vnext/Shared/Networking/WinRTHttpResource.cpp index 1911427748f..8f60976de4c 100644 --- a/vnext/Shared/Networking/WinRTHttpResource.cpp +++ b/vnext/Shared/Networking/WinRTHttpResource.cpp @@ -17,6 +17,8 @@ #include #include +using folly::dynamic; + using std::function; using std::scoped_lock; using std::shared_ptr; @@ -63,6 +65,7 @@ void WinRTHttpResource::SendRequest( bool useIncrementalUpdates, int64_t timeout, bool withCredentials, + dynamic &&data, std::function &&callback) noexcept /*override*/ { auto requestId = ++s_lastRequestId; diff --git a/vnext/Shared/Networking/WinRTHttpResource.h b/vnext/Shared/Networking/WinRTHttpResource.h index 86615fdb6a1..d0be4ba13ad 100644 --- a/vnext/Shared/Networking/WinRTHttpResource.h +++ b/vnext/Shared/Networking/WinRTHttpResource.h @@ -51,6 +51,7 @@ class WinRTHttpResource : public IHttpResource, public std::enable_shared_from_t bool useIncrementalUpdates, int64_t timeout, bool withCredentials, + folly::dynamic &&data, std::function &&callback) noexcept override; void AbortRequest(int64_t requestId) noexcept override; void ClearCookies() noexcept override; diff --git a/vnext/Shared/OInstance.cpp b/vnext/Shared/OInstance.cpp index 4f9104e7c08..9130214ae0b 100644 --- a/vnext/Shared/OInstance.cpp +++ b/vnext/Shared/OInstance.cpp @@ -550,7 +550,9 @@ std::vector> InstanceImpl::GetDefaultNativeModules modules.push_back(std::make_unique( m_innerInstance, Microsoft::React::GetHttpModuleName(), - [nativeQueue, transitionalProps]() -> std::unique_ptr { return Microsoft::React::CreateHttpModule(transitionalProps); }, + [nativeQueue, transitionalProps]() -> std::unique_ptr { + return Microsoft::React::CreateHttpModule(transitionalProps); + }, nativeQueue)); modules.push_back(std::make_unique( @@ -622,9 +624,7 @@ std::vector> InstanceImpl::GetDefaultNativeModules modules.push_back(std::make_unique( m_innerInstance, Microsoft::React::GetBlobModuleName(), - [transitionalProps]() { - return Microsoft::React::CreateBlobModule(transitionalProps); - }, + [transitionalProps]() { return Microsoft::React::CreateBlobModule(transitionalProps); }, nativeQueue)); return modules; diff --git a/vnext/Shared/OInstance.h b/vnext/Shared/OInstance.h index bf0956ec7df..476a1f8302a 100644 --- a/vnext/Shared/OInstance.h +++ b/vnext/Shared/OInstance.h @@ -5,8 +5,8 @@ #pragma once -#include #include +#include #include "InstanceManager.h" // React Native From 9fce91b3589817fa483c6e32ce8093f9c57bb5c1 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Thu, 28 Apr 2022 12:52:34 -0700 Subject: [PATCH 36/91] Add data arg to test SendRequest calls --- .../HttpOriginPolicyIntegrationTest.cpp | 3 +++ .../HttpResourceIntegrationTests.cpp | 7 ++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/vnext/Desktop.IntegrationTests/HttpOriginPolicyIntegrationTest.cpp b/vnext/Desktop.IntegrationTests/HttpOriginPolicyIntegrationTest.cpp index ce9fb266d61..8844f2077c1 100644 --- a/vnext/Desktop.IntegrationTests/HttpOriginPolicyIntegrationTest.cpp +++ b/vnext/Desktop.IntegrationTests/HttpOriginPolicyIntegrationTest.cpp @@ -148,6 +148,7 @@ TEST_CLASS(HttpOriginPolicyIntegrationTest) false, /*useIncrementalUpdates*/ 1000, /*timeout*/ clientArgs.WithCredentials, /*withCredentials*/ + {}, /*data*/ [](int64_t){} /*reactCallback*/ ); @@ -201,6 +202,7 @@ TEST_CLASS(HttpOriginPolicyIntegrationTest) false, /*useIncrementalUpdates*/ 1000, /*timeout*/ clientArgs.WithCredentials, /*withCredentials*/ + {}, /*data*/ [](int64_t) {} /*reactCallback*/ ); @@ -298,6 +300,7 @@ TEST_CLASS(HttpOriginPolicyIntegrationTest) false /*useIncrementalUpdates*/, 1000 /*timeout*/, false /*withCredentials*/, + {}, /*data*/ [](int64_t) {} /*callback*/ ); diff --git a/vnext/Desktop.IntegrationTests/HttpResourceIntegrationTests.cpp b/vnext/Desktop.IntegrationTests/HttpResourceIntegrationTests.cpp index 6d73e68aa63..1a9e5aa6a26 100644 --- a/vnext/Desktop.IntegrationTests/HttpResourceIntegrationTests.cpp +++ b/vnext/Desktop.IntegrationTests/HttpResourceIntegrationTests.cpp @@ -79,6 +79,7 @@ TEST_CLASS (HttpResourceIntegrationTest) { false, 1000 /*timeout*/, false /*withCredentials*/, + {}, /*data*/ [](int64_t) {}); // Synchronize response. @@ -139,6 +140,7 @@ TEST_CLASS (HttpResourceIntegrationTest) { false, 1000 /*timeout*/, false /*withCredentials*/, + {}, /*data*/ [](int64_t) {}); //clang-format on @@ -170,7 +172,7 @@ TEST_CLASS (HttpResourceIntegrationTest) { promise.set_value(); }); - resource->SendRequest("GET", "http://nonexistinghost", {}, {}, "text", false, 1000, false, [](int64_t) {}); + resource->SendRequest("GET", "http://nonexistinghost", {}, {}, "text", false, 1000, false, {}, [](int64_t){}); promise.get_future().wait(); @@ -244,6 +246,7 @@ TEST_CLASS (HttpResourceIntegrationTest) { false, 1000 /*timeout*/, false /*withCredentials*/, + {}, /*data*/ [](int64_t) {}); resource->SendRequest( "GET", @@ -254,6 +257,7 @@ TEST_CLASS (HttpResourceIntegrationTest) { false, 1000 /*timeout*/, false /*withCredentials*/, + {}, /*data*/ [](int64_t) {}); //clang-format on @@ -336,6 +340,7 @@ TEST_CLASS (HttpResourceIntegrationTest) { false, /*useIncrementalUpdates*/ 1000 /*timeout*/, false /*withCredentials*/, + {}, /*data*/ [](int64_t) {}); //clang-format on From 4e2a2be49489ffc08a84e2246e3971f94b9b593d Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Thu, 28 Apr 2022 23:08:00 -0700 Subject: [PATCH 37/91] First implementation for BlobModuleUriHandler --- vnext/Shared/Modules/BlobModule.cpp | 80 +++++++++++++++++++++++++++++ vnext/Shared/Modules/BlobModule.h | 9 ++++ 2 files changed, 89 insertions(+) diff --git a/vnext/Shared/Modules/BlobModule.cpp b/vnext/Shared/Modules/BlobModule.cpp index c976c49ffa1..ea92e49e4cd 100644 --- a/vnext/Shared/Modules/BlobModule.cpp +++ b/vnext/Shared/Modules/BlobModule.cpp @@ -30,6 +30,7 @@ using winrt::Microsoft::ReactNative::ReactPropertyBag; using winrt::Microsoft::ReactNative::ReactPropertyId; using winrt::Windows::Foundation::GuidHelper; using winrt::Windows::Foundation::IInspectable; +using winrt::Windows::Foundation::Uri; using winrt::Windows::Security::Cryptography::CryptographicBuffer; namespace { @@ -270,6 +271,85 @@ void WebSocketModuleContentHandler::RemoveMessage(string &&blobId) noexcept { #pragma endregion WebSocketModuleContentHandler +#pragma region BlobModuleUriHandler + +#pragma region IUriHandler + +bool BlobModuleUriHandler::Supports(string &uri, string &responseType) /*override*/ { + auto uriObj = Uri{winrt::to_hstring(uri)}; + + return !(L"http" == uriObj.SchemeName() || L"https" == uriObj.SchemeName()) && "blob" == responseType; +} + +dynamic BlobModuleUriHandler::Fetch(string &uri) /*override*/ { + auto blob = dynamic{}; + + blob["blobId"] = string{}; // TODO: Store (See BlobWebSocketModuleContentHandler::StoreMessage) + blob["offset"] = 0; + blob["size"] = uri.size(); + blob["type"] = GetMimeTypeFromUri(uri); + + // Needed for files + blob["name"] = GetNameFromUri(uri); + blob["lastModified"] = GetLastModifiedFromUri(uri); + + return blob; +} + +#pragma endregion IUriHandler + +string BlobModuleUriHandler::GetMimeTypeFromUri(string& uri) noexcept { + //TODO: content resolver. + // See https://developer.android.com/reference/android/content/ContentResolver + // See https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/content/ContentResolver.java + + return "blob"; +} + +string BlobModuleUriHandler::GetNameFromUri(string& uri) noexcept { + auto uriObj = Uri { winrt::to_hstring(uri) }; + auto path = uriObj.Path(); + if (L"file" == uriObj.SchemeName()) { + return GetLastPathSegment(path); + } + + //TODO: Lookup "_display_name" + + return GetLastPathSegment(path); +} + +string BlobModuleUriHandler::GetLastPathSegment(winrt::hstring& path) noexcept { + auto start = path.size(); + auto end = start; + while (end > 0) { + if (path[end - 1] != '/') { + start = end - 1; + break; + } else { + end--; + } + } + + // No name characters found + if (start >= end) + return {}; + + while (start > 0 && path[start - 1] != '/') { + start--; + } + + return winrt::to_string(path).substr(start, /*count*/ end - start); +} + +int64_t BlobModuleUriHandler::GetLastModifiedFromUri(string& uri) noexcept { + //TODO: Handle StorageFile URIs + // https://stackoverflow.com/questions/31860360 + + return 0; +} + +#pragma endregion BlobModuleUriHandler + /*extern*/ const char *GetBlobModuleName() noexcept { return moduleName; } diff --git a/vnext/Shared/Modules/BlobModule.h b/vnext/Shared/Modules/BlobModule.h index 5bd8231374f..fe607b796f8 100644 --- a/vnext/Shared/Modules/BlobModule.h +++ b/vnext/Shared/Modules/BlobModule.h @@ -50,6 +50,7 @@ class BlobWebSocketModuleContentHandler final : public IWebSocketModuleContentHa }; class BlobModuleUriHandler final : public IUriHandler { + public: #pragma region IUriHandler bool Supports(std::string &uri, std::string &responseType) override; @@ -57,6 +58,14 @@ class BlobModuleUriHandler final : public IUriHandler { folly::dynamic Fetch(std::string &uri) override; #pragma endregion IUriHandler + + std::string GetMimeTypeFromUri(std::string &uri) noexcept; + + std::string GetNameFromUri(std::string &uri) noexcept; + + std::string GetLastPathSegment(winrt::hstring &path) noexcept; + + int64_t GetLastModifiedFromUri(std::string &uri) noexcept; }; class BlobModuleRequestBodyHandler final : public IRequestBodyHandler { From 37c08b82441da3120a519199fd042f1bd2bafa13 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Thu, 28 Apr 2022 23:16:19 -0700 Subject: [PATCH 38/91] Remove WebSocketModuleContentHandler WinRT class --- vnext/Shared/Modules/BlobModule.cpp | 50 ------------------- .../Modules/IWebSocketModuleContentHandler.h | 24 --------- 2 files changed, 74 deletions(-) diff --git a/vnext/Shared/Modules/BlobModule.cpp b/vnext/Shared/Modules/BlobModule.cpp index ea92e49e4cd..19ac9fcff45 100644 --- a/vnext/Shared/Modules/BlobModule.cpp +++ b/vnext/Shared/Modules/BlobModule.cpp @@ -221,56 +221,6 @@ void BlobWebSocketModuleContentHandler::StoreMessage(vector &&message, #pragma endregion BlobWebSocketModuleContentHandler -#pragma region WebSocketModuleContentHandler - -void WebSocketModuleContentHandler::ProcessMessage(string &&message, dynamic ¶ms) { - params["data"] = std::move(message); -} - -void WebSocketModuleContentHandler::ProcessMessage(vector &&message, dynamic ¶ms) { - auto blob = dynamic::object(); - blob("offset", 0); - blob("size", message.size()); - - // substr(1, 36) strips curly braces from a GUID - string blobId = winrt::to_string(winrt::to_hstring(GuidHelper::CreateNewGuid())).substr(1, 36); - // TODO: StoreMessage - - params["data"] = std::move(blob); - params["type"] = "blob"; -} - -void WebSocketModuleContentHandler::Register(int64_t socketId) noexcept { - scoped_lock lock{m_mutex}; - m_socketIds.insert(socketId); -} - -void WebSocketModuleContentHandler::Unregister(int64_t socketId) noexcept { - scoped_lock lock{m_mutex}; - if (m_socketIds.find(socketId) != m_socketIds.end()) - m_socketIds.erase(socketId); -} - -winrt::array_view -WebSocketModuleContentHandler::ResolveMessage(string &&blobId, int64_t offset, int64_t size) noexcept { - scoped_lock lock{m_mutex}; - auto &data = m_blobs.at(std::move(blobId)); - - return winrt::array_view{data}; -} - -void WebSocketModuleContentHandler::StoreMessage(vector &&message, string &&blobId) noexcept { - scoped_lock lock{m_mutex}; - m_blobs.insert_or_assign(std::move(blobId), std::move(message)); -} - -void WebSocketModuleContentHandler::RemoveMessage(string &&blobId) noexcept { - scoped_lock lock{m_mutex}; - m_blobs.erase(std::move(blobId)); -} - -#pragma endregion WebSocketModuleContentHandler - #pragma region BlobModuleUriHandler #pragma region IUriHandler diff --git a/vnext/Shared/Modules/IWebSocketModuleContentHandler.h b/vnext/Shared/Modules/IWebSocketModuleContentHandler.h index 43791b2d327..fb50015bcd2 100644 --- a/vnext/Shared/Modules/IWebSocketModuleContentHandler.h +++ b/vnext/Shared/Modules/IWebSocketModuleContentHandler.h @@ -6,9 +6,6 @@ // React Native #include -// Windows API -#include - // Standard Library #include #include @@ -29,25 +26,4 @@ struct IWebSocketModuleContentHandler { virtual void ProcessMessage(std::vector &&message, folly::dynamic ¶ms) = 0; }; -class WebSocketModuleContentHandler - : public winrt::implements { - std::unordered_map> m_blobs; - std::mutex m_mutex; - std::unordered_set m_socketIds; - - public: - void ProcessMessage(std::string &&message, folly::dynamic ¶ms); // TODO: noexcept? - - void ProcessMessage(std::vector &&message, folly::dynamic ¶ms); // TODO: noexcept? - - void Register(int64_t socketId) noexcept; - - void Unregister(int64_t socketId) noexcept; - - winrt::array_view ResolveMessage(std::string &&blogId, int64_t offset, int64_t size) noexcept; - - void StoreMessage(std::vector &&message, std::string &&blobId) noexcept; - - void RemoveMessage(std::string &&blobId) noexcept; -}; } // namespace Microsoft::React From 3406290157e7dab7bed3ef70496e1ddeab5ed0c7 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Thu, 28 Apr 2022 23:54:57 -0700 Subject: [PATCH 39/91] Implement IBlobPersistor, MemoryBlobPersistor --- vnext/Shared/Modules/BlobModule.cpp | 84 +++++++++++-------- vnext/Shared/Modules/BlobModule.h | 34 +++++--- vnext/Shared/Modules/IBlobPersistor.h | 24 ++++++ .../Modules/IWebSocketModuleContentHandler.h | 3 - vnext/Shared/Shared.vcxitems | 1 + vnext/Shared/Shared.vcxitems.filters | 3 + 6 files changed, 101 insertions(+), 48 deletions(-) create mode 100644 vnext/Shared/Modules/IBlobPersistor.h diff --git a/vnext/Shared/Modules/BlobModule.cpp b/vnext/Shared/Modules/BlobModule.cpp index 19ac9fcff45..e33bdc7ab72 100644 --- a/vnext/Shared/Modules/BlobModule.cpp +++ b/vnext/Shared/Modules/BlobModule.cpp @@ -44,7 +44,8 @@ namespace Microsoft::React { BlobModule::BlobModule(winrt::Windows::Foundation::IInspectable const &iProperties) noexcept : m_sharedState{std::make_shared()}, - m_contentHandler{std::make_shared()}, + m_blobPersistor{std::make_shared()}, + m_contentHandler{std::make_shared(m_blobPersistor)}, m_iProperties{iProperties} { auto propId = ReactPropertyId>>{L"BlobModule.ContentHandler"}; @@ -91,7 +92,7 @@ std::vector BlobModule::getMethods() { }}, {"sendOverSocket", - [contentHandler = m_contentHandler, + [persistor = m_blobPersistor, propBag = ReactPropertyBag{m_iProperties.try_as()}](dynamic args) { auto propId = ReactPropertyId>>{L"WebSocketModule.Proxy"}; auto wsProxy = propBag.Get(propId).Value().lock(); @@ -105,7 +106,7 @@ std::vector BlobModule::getMethods() { auto size = blob["size"].getInt(); auto socketID = jsArgAsInt(args, 1); - auto data = contentHandler->ResolveMessage(std::move(blobId), offset, size); + auto data = persistor->ResolveMessage(std::move(blobId), offset, size); auto buffer = CryptographicBuffer::CreateFromByteArray(data); auto winrtString = CryptographicBuffer::EncodeToBase64String(std::move(buffer)); @@ -118,7 +119,7 @@ std::vector BlobModule::getMethods() { // As of React Native 0.67, instance is set AFTER CxxModule::getMethods() is invoked. // Directly use getInstance() once // https://github.com/facebook/react-native/commit/1d45b20b6c6ba66df0485cdb9be36463d96cf182 becomes available. - [contentHandler = m_contentHandler, weakState = weak_ptr(m_sharedState)](dynamic args) { + [persistor = m_blobPersistor, weakState = weak_ptr(m_sharedState)](dynamic args) { auto parts = jsArgAsArray(args, 0); // Array auto blobId = jsArgAsString(args, 1); vector buffer{}; @@ -127,7 +128,7 @@ std::vector BlobModule::getMethods() { auto type = part["type"]; if (type == "blob") { auto blob = part["data"]; - auto bufferPart = contentHandler->ResolveMessage( + auto bufferPart = persistor->ResolveMessage( blob["blobId"].asString(), blob["offset"].asInt(), blob["size"].asInt()); buffer.reserve(buffer.size() + bufferPart.size()); buffer.insert(buffer.end(), bufferPart.begin(), bufferPart.end()); @@ -148,16 +149,16 @@ std::vector BlobModule::getMethods() { return; } - contentHandler->StoreMessage(std::move(buffer), std::move(blobId)); + persistor->StoreMessage(std::move(buffer), std::move(blobId)); } }}, {"release", - [contentHandler = m_contentHandler](dynamic args) // blobId: string + [persistor = m_blobPersistor](dynamic args) // blobId: string { auto blobId = jsArgAsString(args, 0); - contentHandler->RemoveMessage(std::move(blobId)); + persistor->RemoveMessage(std::move(blobId)); }}}; } @@ -165,8 +166,41 @@ std::vector BlobModule::getMethods() { #pragma endregion BlobModule +#pragma region MemoryBlobPersistor + +#pragma region IBlobPersistor + +winrt::array_view MemoryBlobPersistor::ResolveMessage(string &&blobId, int64_t offset, int64_t size) noexcept { + scoped_lock lock{m_mutex}; + + auto &data = m_blobs.at(std::move(blobId)); + + return winrt::array_view{data}; +} + +void MemoryBlobPersistor::RemoveMessage(string &&blobId) noexcept { + scoped_lock lock{m_mutex}; + + m_blobs.erase(std::move(blobId)); +} + +void MemoryBlobPersistor::StoreMessage(vector &&message, string &&blobId) noexcept { + scoped_lock lock{m_mutex}; + + m_blobs.insert_or_assign(std::move(blobId), std::move(message)); +} + +#pragma endregion IBlobPersistor + +#pragma endregion MemoryBlobPersistor + #pragma region BlobWebSocketModuleContentHandler +BlobWebSocketModuleContentHandler::BlobWebSocketModuleContentHandler( + shared_ptr blobPersistor) noexcept + : m_blobPersistor{blobPersistor} { +} + #pragma region IWebSocketModuleContentHandler void BlobWebSocketModuleContentHandler::ProcessMessage(string &&message, dynamic ¶ms) /*override*/ { @@ -180,7 +214,8 @@ void BlobWebSocketModuleContentHandler::ProcessMessage(vector &&message // substr(1, 36) strips curly braces from a GUID. string blobId = winrt::to_string(winrt::to_hstring(GuidHelper::CreateNewGuid())).substr(1, 36); - StoreMessage(std::move(message), std::move(blobId)); + + m_blobPersistor->StoreMessage(std::move(message), std::move(blobId)); params["data"] = std::move(blob); params["type"] = "blob"; @@ -188,35 +223,14 @@ void BlobWebSocketModuleContentHandler::ProcessMessage(vector &&message #pragma endregion IWebSocketModuleContentHandler void BlobWebSocketModuleContentHandler::Register(int64_t socketID) noexcept { - scoped_lock lock{m_socketIDsMutex}; - m_socketIDs.insert(socketID); + scoped_lock lock{m_mutex}; + m_socketIds.insert(socketID); } void BlobWebSocketModuleContentHandler::Unregister(int64_t socketID) noexcept { - scoped_lock lock{m_socketIDsMutex}; - if (m_socketIDs.find(socketID) != m_socketIDs.end()) - m_socketIDs.erase(socketID); -} - -winrt::array_view -BlobWebSocketModuleContentHandler::ResolveMessage(string &&blobId, int64_t offset, int64_t size) noexcept { - scoped_lock lock{m_blobsMutex}; - - auto &data = m_blobs.at(std::move(blobId)); - - return winrt::array_view{data}; -} - -void BlobWebSocketModuleContentHandler::RemoveMessage(string &&blobId) noexcept { - scoped_lock lock{m_blobsMutex}; - - m_blobs.erase(std::move(blobId)); -} - -void BlobWebSocketModuleContentHandler::StoreMessage(vector &&message, string &&blobId) noexcept { - scoped_lock lock{m_blobsMutex}; - - m_blobs.insert_or_assign(std::move(blobId), std::move(message)); + scoped_lock lock{m_mutex}; + if (m_socketIds.find(socketID) != m_socketIds.end()) + m_socketIds.erase(socketID); } #pragma endregion BlobWebSocketModuleContentHandler diff --git a/vnext/Shared/Modules/BlobModule.h b/vnext/Shared/Modules/BlobModule.h index fe607b796f8..eae1832ac85 100644 --- a/vnext/Shared/Modules/BlobModule.h +++ b/vnext/Shared/Modules/BlobModule.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include #include @@ -23,13 +24,31 @@ namespace Microsoft::React { -class BlobWebSocketModuleContentHandler final : public IWebSocketModuleContentHandler { +class MemoryBlobPersistor final : public IBlobPersistor +{ std::unordered_map> m_blobs; - std::mutex m_blobsMutex; - std::unordered_set m_socketIDs; - std::mutex m_socketIDsMutex; + std::mutex m_mutex; + + public: +#pragma region IBlobPersistor + + winrt::array_view ResolveMessage(std::string &&blobId, int64_t offset, int64_t size) noexcept override; + + void RemoveMessage(std::string &&blobId) noexcept override; + + void StoreMessage(std::vector &&message, std::string &&blobId) noexcept override; + +#pragma endregion IBlobPersistor +}; + +class BlobWebSocketModuleContentHandler final : public IWebSocketModuleContentHandler { + std::unordered_set m_socketIds; + std::mutex m_mutex; + std::shared_ptr m_blobPersistor; public: + BlobWebSocketModuleContentHandler(std::shared_ptr blobPersistor) noexcept; + #pragma region IWebSocketModuleContentHandler void ProcessMessage(std::string &&message, folly::dynamic ¶ms) override; @@ -41,12 +60,6 @@ class BlobWebSocketModuleContentHandler final : public IWebSocketModuleContentHa void Register(int64_t socketID) noexcept; void Unregister(int64_t socketID) noexcept; - - winrt::array_view ResolveMessage(std::string &&blobId, int64_t offset, int64_t size) noexcept; - - void RemoveMessage(std::string &&blobId) noexcept; - - void StoreMessage(std::vector &&message, std::string &&blobId) noexcept; }; class BlobModuleUriHandler final : public IUriHandler { @@ -89,6 +102,7 @@ class BlobModuleResponseHandler final : public IResponseHandler { }; class BlobModule : public facebook::xplat::module::CxxModule { + std::shared_ptr m_blobPersistor; std::shared_ptr m_contentHandler; // Property bag high level reference. diff --git a/vnext/Shared/Modules/IBlobPersistor.h b/vnext/Shared/Modules/IBlobPersistor.h new file mode 100644 index 00000000000..2cc2a13b7af --- /dev/null +++ b/vnext/Shared/Modules/IBlobPersistor.h @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#pragma once + +// Windows API +#include + +// Standard Library +#include +#include + +namespace Microsoft::React { + +struct IBlobPersistor +{ + virtual winrt::array_view ResolveMessage(std::string &&blobId, int64_t offset, int64_t size) noexcept = 0; + + virtual void RemoveMessage(std::string &&blobId) noexcept = 0; + + virtual void StoreMessage(std::vector &&message, std::string &&blobId) noexcept = 0; +}; + +}//namespace diff --git a/vnext/Shared/Modules/IWebSocketModuleContentHandler.h b/vnext/Shared/Modules/IWebSocketModuleContentHandler.h index fb50015bcd2..97c06f158f9 100644 --- a/vnext/Shared/Modules/IWebSocketModuleContentHandler.h +++ b/vnext/Shared/Modules/IWebSocketModuleContentHandler.h @@ -10,9 +10,6 @@ #include #include -#include -#include - namespace Microsoft::React { /// diff --git a/vnext/Shared/Shared.vcxitems b/vnext/Shared/Shared.vcxitems index 3eacc3b842a..1486757c1b1 100644 --- a/vnext/Shared/Shared.vcxitems +++ b/vnext/Shared/Shared.vcxitems @@ -89,6 +89,7 @@ + diff --git a/vnext/Shared/Shared.vcxitems.filters b/vnext/Shared/Shared.vcxitems.filters index da93b5f6164..dd71377a288 100644 --- a/vnext/Shared/Shared.vcxitems.filters +++ b/vnext/Shared/Shared.vcxitems.filters @@ -441,6 +441,9 @@ Header Files\Modules + + Header Files\Modules + From 24e6e56891f9079720bd408ef1322c413365fb18 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Fri, 29 Apr 2022 00:52:46 -0700 Subject: [PATCH 40/91] clang format --- .../HttpResourceIntegrationTests.cpp | 2 +- vnext/Shared/Modules/BlobModule.cpp | 131 ++++++++++++++---- vnext/Shared/Modules/BlobModule.h | 26 +++- vnext/Shared/Modules/IBlobPersistor.h | 8 +- vnext/Shared/Modules/IRequestBodyHandler.h | 2 +- vnext/Shared/Modules/IResponseHandler.h | 2 +- 6 files changed, 131 insertions(+), 40 deletions(-) diff --git a/vnext/Desktop.IntegrationTests/HttpResourceIntegrationTests.cpp b/vnext/Desktop.IntegrationTests/HttpResourceIntegrationTests.cpp index 1a9e5aa6a26..cfb6bff7819 100644 --- a/vnext/Desktop.IntegrationTests/HttpResourceIntegrationTests.cpp +++ b/vnext/Desktop.IntegrationTests/HttpResourceIntegrationTests.cpp @@ -172,7 +172,7 @@ TEST_CLASS (HttpResourceIntegrationTest) { promise.set_value(); }); - resource->SendRequest("GET", "http://nonexistinghost", {}, {}, "text", false, 1000, false, {}, [](int64_t){}); + resource->SendRequest("GET", "http://nonexistinghost", {}, {}, "text", false, 1000, false, {}, [](int64_t) {}); promise.get_future().wait(); diff --git a/vnext/Shared/Modules/BlobModule.cpp b/vnext/Shared/Modules/BlobModule.cpp index e33bdc7ab72..c26266b6e4c 100644 --- a/vnext/Shared/Modules/BlobModule.cpp +++ b/vnext/Shared/Modules/BlobModule.cpp @@ -46,6 +46,9 @@ BlobModule::BlobModule(winrt::Windows::Foundation::IInspectable const &iProperti : m_sharedState{std::make_shared()}, m_blobPersistor{std::make_shared()}, m_contentHandler{std::make_shared(m_blobPersistor)}, + m_uriHandler{std::make_shared(m_blobPersistor)}, + m_requestBodyHandler{std::make_shared(m_blobPersistor)}, + m_responseHandler{std::make_shared(m_blobPersistor)}, m_iProperties{iProperties} { auto propId = ReactPropertyId>>{L"BlobModule.ContentHandler"}; @@ -122,14 +125,14 @@ std::vector BlobModule::getMethods() { [persistor = m_blobPersistor, weakState = weak_ptr(m_sharedState)](dynamic args) { auto parts = jsArgAsArray(args, 0); // Array auto blobId = jsArgAsString(args, 1); - vector buffer{}; + vector buffer{}; // TODO: build from parts. for (const auto &part : parts) { auto type = part["type"]; if (type == "blob") { auto blob = part["data"]; - auto bufferPart = persistor->ResolveMessage( - blob["blobId"].asString(), blob["offset"].asInt(), blob["size"].asInt()); + auto bufferPart = + persistor->ResolveMessage(blob["blobId"].asString(), blob["offset"].asInt(), blob["size"].asInt()); buffer.reserve(buffer.size() + bufferPart.size()); buffer.insert(buffer.end(), bufferPart.begin(), bufferPart.end()); } else if (type == "string") { @@ -190,16 +193,24 @@ void MemoryBlobPersistor::StoreMessage(vector &&message, string &&blobI m_blobs.insert_or_assign(std::move(blobId), std::move(message)); } +string MemoryBlobPersistor::StoreMessage(vector &&message) noexcept { + // substr(1, 36) strips curly braces from a GUID. + auto blobId = winrt::to_string(winrt::to_hstring(GuidHelper::CreateNewGuid())).substr(1, 36); + + scoped_lock lock{m_mutex}; + m_blobs.insert_or_assign(blobId, std::move(message)); + + return blobId; +} + #pragma endregion IBlobPersistor #pragma endregion MemoryBlobPersistor #pragma region BlobWebSocketModuleContentHandler -BlobWebSocketModuleContentHandler::BlobWebSocketModuleContentHandler( - shared_ptr blobPersistor) noexcept - : m_blobPersistor{blobPersistor} { -} +BlobWebSocketModuleContentHandler::BlobWebSocketModuleContentHandler(shared_ptr blobPersistor) noexcept + : m_blobPersistor{blobPersistor} {} #pragma region IWebSocketModuleContentHandler @@ -211,11 +222,7 @@ void BlobWebSocketModuleContentHandler::ProcessMessage(vector &&message auto blob = dynamic::object(); blob("offset", 0); blob("size", message.size()); - - // substr(1, 36) strips curly braces from a GUID. - string blobId = winrt::to_string(winrt::to_hstring(GuidHelper::CreateNewGuid())).substr(1, 36); - - m_blobPersistor->StoreMessage(std::move(message), std::move(blobId)); + blob("blobId", m_blobPersistor->StoreMessage(std::move(message))); params["data"] = std::move(blob); params["type"] = "blob"; @@ -237,6 +244,9 @@ void BlobWebSocketModuleContentHandler::Unregister(int64_t socketID) noexcept { #pragma region BlobModuleUriHandler +BlobModuleUriHandler::BlobModuleUriHandler(shared_ptr blobPersistor) noexcept + : m_blobPersistor{blobPersistor} {} + #pragma region IUriHandler bool BlobModuleUriHandler::Supports(string &uri, string &responseType) /*override*/ { @@ -246,43 +256,45 @@ bool BlobModuleUriHandler::Supports(string &uri, string &responseType) /*overrid } dynamic BlobModuleUriHandler::Fetch(string &uri) /*override*/ { - auto blob = dynamic{}; + auto data = vector{}; // getBytesFromUri - blob["blobId"] = string{}; // TODO: Store (See BlobWebSocketModuleContentHandler::StoreMessage) - blob["offset"] = 0; - blob["size"] = uri.size(); - blob["type"] = GetMimeTypeFromUri(uri); + auto blob = dynamic::object(); + blob("offset", 0); + blob("size", data.size()); + blob("type", GetMimeTypeFromUri(uri)); + blob("blobId", m_blobPersistor->StoreMessage(std::move(data))); // Needed for files - blob["name"] = GetNameFromUri(uri); - blob["lastModified"] = GetLastModifiedFromUri(uri); + blob("name", GetNameFromUri(uri)); + blob("lastModified", GetLastModifiedFromUri(uri)); return blob; } #pragma endregion IUriHandler -string BlobModuleUriHandler::GetMimeTypeFromUri(string& uri) noexcept { - //TODO: content resolver. - // See https://developer.android.com/reference/android/content/ContentResolver - // See https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/content/ContentResolver.java +string BlobModuleUriHandler::GetMimeTypeFromUri(string &uri) noexcept { + // TODO: content resolver. + // See https://developer.android.com/reference/android/content/ContentResolver + // See + // https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/content/ContentResolver.java return "blob"; } -string BlobModuleUriHandler::GetNameFromUri(string& uri) noexcept { - auto uriObj = Uri { winrt::to_hstring(uri) }; +string BlobModuleUriHandler::GetNameFromUri(string &uri) noexcept { + auto uriObj = Uri{winrt::to_hstring(uri)}; auto path = uriObj.Path(); if (L"file" == uriObj.SchemeName()) { return GetLastPathSegment(path); } - //TODO: Lookup "_display_name" + // TODO: Lookup "_display_name" return GetLastPathSegment(path); } -string BlobModuleUriHandler::GetLastPathSegment(winrt::hstring& path) noexcept { +string BlobModuleUriHandler::GetLastPathSegment(winrt::hstring &path) noexcept { auto start = path.size(); auto end = start; while (end > 0) { @@ -305,15 +317,74 @@ string BlobModuleUriHandler::GetLastPathSegment(winrt::hstring& path) noexcept { return winrt::to_string(path).substr(start, /*count*/ end - start); } -int64_t BlobModuleUriHandler::GetLastModifiedFromUri(string& uri) noexcept { - //TODO: Handle StorageFile URIs - // https://stackoverflow.com/questions/31860360 +int64_t BlobModuleUriHandler::GetLastModifiedFromUri(string &uri) noexcept { + // TODO: Handle StorageFile URIs + // https://stackoverflow.com/questions/31860360 return 0; } #pragma endregion BlobModuleUriHandler +#pragma region BlobModuleRequestBodyHandler + +BlobModuleRequestBodyHandler::BlobModuleRequestBodyHandler(shared_ptr blobPersistor) noexcept + : m_blobPersistor{blobPersistor} {} + +#pragma region IRequestBodyHandler + +bool BlobModuleRequestBodyHandler::Supports(dynamic &data) /*override*/ { + return !data.at("blob").empty(); +} + +dynamic BlobModuleRequestBodyHandler::ToRequestBody(dynamic &data, string &contentType) /*override*/ { + auto type = contentType; + if (!data["type"].asString().empty()) { + type = data["type"].asString(); + } + if (type.empty()) { + type = "application/octet-stream"; + } + + auto blob = data["blob"]; + auto blobId = blob["blobId"].asString(); + auto bytes = m_blobPersistor->ResolveMessage(std::move(blobId), blob["offset"].asInt(), blob["size"].asInt()); + + // TODO: create body from type and bytes + return {}; +} + +#pragma endregion IRequestBodyHandler + +#pragma endregion BlobModuleRequestBodyHandler + +#pragma region BlobModuleResponseHandler + +BlobModuleResponseHandler::BlobModuleResponseHandler(shared_ptr blobPersistor) noexcept + : m_blobPersistor{blobPersistor} {} + +#pragma region IResponseHandler + +bool BlobModuleResponseHandler::Supports(std::string &responseType) /*override*/ { + return blobURIScheme == responseType; +} + +dynamic BlobModuleResponseHandler::ToResponseData(dynamic &body) /*override*/ { + // TODO: get bytes from body + auto bytes = vector{}; + + auto blob = dynamic::object(); + blob("offset", 0); + blob("size", bytes.size()); + blob("blobId", m_blobPersistor->StoreMessage(std::move(bytes))); + + return blob; +} + +#pragma endregion IResponseHandler + +#pragma endregion BlobModuleResponseHandler + /*extern*/ const char *GetBlobModuleName() noexcept { return moduleName; } diff --git a/vnext/Shared/Modules/BlobModule.h b/vnext/Shared/Modules/BlobModule.h index eae1832ac85..433ce998356 100644 --- a/vnext/Shared/Modules/BlobModule.h +++ b/vnext/Shared/Modules/BlobModule.h @@ -24,8 +24,7 @@ namespace Microsoft::React { -class MemoryBlobPersistor final : public IBlobPersistor -{ +class MemoryBlobPersistor final : public IBlobPersistor { std::unordered_map> m_blobs; std::mutex m_mutex; @@ -38,6 +37,8 @@ class MemoryBlobPersistor final : public IBlobPersistor void StoreMessage(std::vector &&message, std::string &&blobId) noexcept override; + std::string StoreMessage(std::vector &&message) noexcept override; + #pragma endregion IBlobPersistor }; @@ -63,7 +64,11 @@ class BlobWebSocketModuleContentHandler final : public IWebSocketModuleContentHa }; class BlobModuleUriHandler final : public IUriHandler { + std::shared_ptr m_blobPersistor; + public: + BlobModuleUriHandler(std::shared_ptr blobPersistor) noexcept; + #pragma region IUriHandler bool Supports(std::string &uri, std::string &responseType) override; @@ -82,19 +87,29 @@ class BlobModuleUriHandler final : public IUriHandler { }; class BlobModuleRequestBodyHandler final : public IRequestBodyHandler { + std::shared_ptr m_blobPersistor; + + public: + BlobModuleRequestBodyHandler(std::shared_ptr blobPersistor) noexcept; + #pragma region IRequestBodyHandler bool Supports(folly::dynamic &data) override; - void * /*RequestBody*/ ToRequestBody(folly::dynamic &data, std::string &contentType) override; + folly::dynamic ToRequestBody(folly::dynamic &data, std::string &contentType) override; #pragma endregion IRequestBodyHandler }; class BlobModuleResponseHandler final : public IResponseHandler { + std::shared_ptr m_blobPersistor; + + public: + BlobModuleResponseHandler(std::shared_ptr blobPersistor) noexcept; + #pragma region IResponseHandler - bool Supports(std::string responseType) override; + bool Supports(std::string &responseType) override; folly::dynamic ToResponseData(folly::dynamic &body) override; @@ -104,6 +119,9 @@ class BlobModuleResponseHandler final : public IResponseHandler { class BlobModule : public facebook::xplat::module::CxxModule { std::shared_ptr m_blobPersistor; std::shared_ptr m_contentHandler; + std::shared_ptr m_uriHandler; + std::shared_ptr m_requestBodyHandler; + std::shared_ptr m_responseHandler; // Property bag high level reference. winrt::Windows::Foundation::IInspectable m_iProperties; diff --git a/vnext/Shared/Modules/IBlobPersistor.h b/vnext/Shared/Modules/IBlobPersistor.h index 2cc2a13b7af..e74146bc684 100644 --- a/vnext/Shared/Modules/IBlobPersistor.h +++ b/vnext/Shared/Modules/IBlobPersistor.h @@ -12,13 +12,15 @@ namespace Microsoft::React { -struct IBlobPersistor -{ +struct IBlobPersistor { virtual winrt::array_view ResolveMessage(std::string &&blobId, int64_t offset, int64_t size) noexcept = 0; virtual void RemoveMessage(std::string &&blobId) noexcept = 0; + // TODO: Keep only one variant for StoreMessage. virtual void StoreMessage(std::vector &&message, std::string &&blobId) noexcept = 0; + + virtual std::string StoreMessage(std::vector &&message) noexcept = 0; }; -}//namespace +} // namespace Microsoft::React diff --git a/vnext/Shared/Modules/IRequestBodyHandler.h b/vnext/Shared/Modules/IRequestBodyHandler.h index 5179fa68cb3..4b8369e0f98 100644 --- a/vnext/Shared/Modules/IRequestBodyHandler.h +++ b/vnext/Shared/Modules/IRequestBodyHandler.h @@ -23,7 +23,7 @@ struct IRequestBodyHandler { /// /// Returns the {@link RequestBody} for the JS body payload. /// - virtual void * /*RequestBody*/ ToRequestBody(folly::dynamic &data, std::string &contentType) = 0; + virtual folly::dynamic ToRequestBody(folly::dynamic &data, std::string &contentType) = 0; }; } // namespace Microsoft::React diff --git a/vnext/Shared/Modules/IResponseHandler.h b/vnext/Shared/Modules/IResponseHandler.h index add55bb95ff..7d062653c7b 100644 --- a/vnext/Shared/Modules/IResponseHandler.h +++ b/vnext/Shared/Modules/IResponseHandler.h @@ -15,7 +15,7 @@ struct IResponseHandler { /// /// Returns if the handler should be used for a response type. /// - virtual bool Supports(std::string responseType) = 0; + virtual bool Supports(std::string &responseType) = 0; /// /// Returns the JS body payload for the {@link ResponseBody}. From f03b745af9e86a3145ce96749dc5d44b51309b40 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Fri, 29 Apr 2022 23:32:29 -0700 Subject: [PATCH 41/91] Update yarn.lock --- yarn.lock | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/yarn.lock b/yarn.lock index e00e1aac945..f5d0bdada16 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1765,21 +1765,7 @@ logkitty "^0.7.1" slash "^3.0.0" -"@react-native-community/cli-platform-ios@^8.0.0-alpha.0": - version "8.0.0-alpha.3" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-ios/-/cli-platform-ios-8.0.0-alpha.3.tgz#251d3723774babc499e599e4f2667b1203f57407" - integrity sha512-V8129DhF0r4cFwkkQcShkYDtRCCPAhAexgYdbzqF42GMK6pQMIfWoMagkBH6QuGYT2FGsrWPGFa+XZi3YVv+Qg== - dependencies: - "@react-native-community/cli-tools" "^8.0.0-alpha.3" - chalk "^4.1.2" - execa "^1.0.0" - glob "^7.1.3" - js-yaml "^3.13.1" - lodash "^4.17.15" - ora "^5.4.1" - plist "^3.0.2" - -"@react-native-community/cli-platform-ios@^8.0.0-alpha.3": +"@react-native-community/cli-platform-ios@^8.0.0-alpha.0", "@react-native-community/cli-platform-ios@^8.0.0-alpha.3": version "8.0.0-alpha.3" resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-ios/-/cli-platform-ios-8.0.0-alpha.3.tgz#251d3723774babc499e599e4f2667b1203f57407" integrity sha512-V8129DhF0r4cFwkkQcShkYDtRCCPAhAexgYdbzqF42GMK6pQMIfWoMagkBH6QuGYT2FGsrWPGFa+XZi3YVv+Qg== @@ -1824,7 +1810,7 @@ serve-static "^1.13.1" ws "^7.5.1" -"@react-native-community/cli-tools@^8.0.0-alpha.0", "@react-native-community/cli-tools@^8.0.0-alpha.3": +"@react-native-community/cli-tools@^8.0.0-alpha.3": version "8.0.0-alpha.3" resolved "https://registry.yarnpkg.com/@react-native-community/cli-tools/-/cli-tools-8.0.0-alpha.3.tgz#0791c49eb11b39efc19a9670156056df6afddf7a" integrity sha512-laul2kNNygqZ9Y4jCy+cvBia8imb9Q9jGO00ObXGn1n7pvnPeS6JwbopYBI73Wk2yQrWaxcgrBnLFJ3LBXGm1w== From f5e1386bb0e35c7eedde69882e47e69f065bd405 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Sat, 30 Apr 2022 00:01:44 -0700 Subject: [PATCH 42/91] Update RctRootVieTagGen location --- vnext/Shared/Shared.vcxitems.filters | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vnext/Shared/Shared.vcxitems.filters b/vnext/Shared/Shared.vcxitems.filters index 555216d1f3c..f4e6afab308 100644 --- a/vnext/Shared/Shared.vcxitems.filters +++ b/vnext/Shared/Shared.vcxitems.filters @@ -145,7 +145,7 @@ Source Files\Networking - + From 314e8c2bdb7ae609f066e28eaad5f7a7f2d9f064 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Sat, 30 Apr 2022 00:40:38 -0700 Subject: [PATCH 43/91] Implement addNetworkingHandler --- vnext/Shared/Modules/BlobModule.cpp | 23 +++++++++++++++++------ vnext/Shared/Modules/BlobModule.h | 2 +- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/vnext/Shared/Modules/BlobModule.cpp b/vnext/Shared/Modules/BlobModule.cpp index c26266b6e4c..887dd6edff0 100644 --- a/vnext/Shared/Modules/BlobModule.cpp +++ b/vnext/Shared/Modules/BlobModule.cpp @@ -3,6 +3,7 @@ #include "BlobModule.h" +#include #include #include #include @@ -49,10 +50,10 @@ BlobModule::BlobModule(winrt::Windows::Foundation::IInspectable const &iProperti m_uriHandler{std::make_shared(m_blobPersistor)}, m_requestBodyHandler{std::make_shared(m_blobPersistor)}, m_responseHandler{std::make_shared(m_blobPersistor)}, - m_iProperties{iProperties} { + m_inspectableProperties{iProperties} { auto propId = ReactPropertyId>>{L"BlobModule.ContentHandler"}; - auto propBag = ReactPropertyBag{m_iProperties.try_as()}; + auto propBag = ReactPropertyBag{m_inspectableProperties.try_as()}; auto contentHandler = weak_ptr{m_contentHandler}; propBag.Set(propId, std::move(contentHandler)); @@ -73,11 +74,21 @@ std::map BlobModule::getConstants() { return {{"BLOB_URI_SCHEME", blobURIScheme}, {"BLOB_URI_HOST", {}}}; } -std::vector BlobModule::getMethods() { +vector BlobModule::getMethods() { return { {"addNetworkingHandler", - [](dynamic args) { - // TODO: Implement #6081 + [propBag = ReactPropertyBag{m_inspectableProperties.try_as()}, + uriHandler = m_uriHandler, + requestBodyHandler = m_requestBodyHandler, + responseHandler = m_responseHandler](dynamic args) { + auto propId = ReactPropertyId>>{L"HttpModule.Proxy"}; + + if (auto httpHandler = propBag.Get>>(propId).Value().lock()) { + httpHandler->AddUriHandler(uriHandler); + httpHandler->AddRequestBodyHandler(requestBodyHandler); + httpHandler->AddResponseHandler(responseHandler); + } + // TODO: else emit error? }}, {"addWebSocketHandler", @@ -96,7 +107,7 @@ std::vector BlobModule::getMethods() { {"sendOverSocket", [persistor = m_blobPersistor, - propBag = ReactPropertyBag{m_iProperties.try_as()}](dynamic args) { + propBag = ReactPropertyBag{m_inspectableProperties.try_as()}](dynamic args) { auto propId = ReactPropertyId>>{L"WebSocketModule.Proxy"}; auto wsProxy = propBag.Get(propId).Value().lock(); if (!wsProxy) { diff --git a/vnext/Shared/Modules/BlobModule.h b/vnext/Shared/Modules/BlobModule.h index 433ce998356..a9254d1cc9d 100644 --- a/vnext/Shared/Modules/BlobModule.h +++ b/vnext/Shared/Modules/BlobModule.h @@ -124,7 +124,7 @@ class BlobModule : public facebook::xplat::module::CxxModule { std::shared_ptr m_responseHandler; // Property bag high level reference. - winrt::Windows::Foundation::IInspectable m_iProperties; + winrt::Windows::Foundation::IInspectable m_inspectableProperties; public: enum class MethodId { From 1754a8f90ea518e54ffcbc718cffbff90a443c05 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Sat, 30 Apr 2022 02:35:32 -0700 Subject: [PATCH 44/91] Fix createFromParts buffer persistence --- vnext/Shared/Modules/BlobModule.cpp | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/vnext/Shared/Modules/BlobModule.cpp b/vnext/Shared/Modules/BlobModule.cpp index 887dd6edff0..82cb395b968 100644 --- a/vnext/Shared/Modules/BlobModule.cpp +++ b/vnext/Shared/Modules/BlobModule.cpp @@ -16,10 +16,14 @@ #include #include +// Standard Library +#include + using namespace facebook::xplat; using facebook::react::Instance; using folly::dynamic; +using std::queue; using std::scoped_lock; using std::shared_ptr; using std::string; @@ -131,40 +135,40 @@ vector BlobModule::getMethods() { {"createFromParts", // As of React Native 0.67, instance is set AFTER CxxModule::getMethods() is invoked. - // Directly use getInstance() once + // Use getInstance() directly once // https://github.com/facebook/react-native/commit/1d45b20b6c6ba66df0485cdb9be36463d96cf182 becomes available. [persistor = m_blobPersistor, weakState = weak_ptr(m_sharedState)](dynamic args) { auto parts = jsArgAsArray(args, 0); // Array auto blobId = jsArgAsString(args, 1); - vector buffer{}; // TODO: build from parts. + vector buffer{}; for (const auto &part : parts) { - auto type = part["type"]; - if (type == "blob") { + auto type = part["type"].asString(); + if ("blob" == type) { auto blob = part["data"]; auto bufferPart = persistor->ResolveMessage(blob["blobId"].asString(), blob["offset"].asInt(), blob["size"].asInt()); + buffer.reserve(buffer.size() + bufferPart.size()); buffer.insert(buffer.end(), bufferPart.begin(), bufferPart.end()); - } else if (type == "string") { + } else if ("string" == type) { auto data = part["data"].asString(); - auto bufferPart = vector(data.begin(), data.end()); - buffer.reserve(buffer.size() + bufferPart.size()); - buffer.insert(buffer.end(), bufferPart.begin(), bufferPart.end()); + buffer.reserve(buffer.size() + data.size()); + buffer.insert(buffer.end(), data.begin(), data.end()); } else { if (auto state = weakState.lock()) { if (auto instance = state->Module->getInstance().lock()) { - string message = "Invalid type for blob: " + type.asString(); + auto message = "Invalid type for blob: " + type; instance->callJSFunction( "RCTDeviceEventEmitter", "emit", dynamic::array("blobFailed", std::move(message))); } } return; } - - persistor->StoreMessage(std::move(buffer), std::move(blobId)); } + + persistor->StoreMessage(std::move(buffer), std::move(blobId)); }}, {"release", From 76dedef9a27db2b76696e6cfe8395504c0ed29a2 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Sun, 1 May 2022 14:25:41 -0700 Subject: [PATCH 45/91] Drop WebSocketModule s_sharedState in favor of property bag --- vnext/Shared/Modules/BlobModule.cpp | 6 +++-- vnext/Shared/Modules/WebSocketModule.cpp | 32 ++++++++++++++++-------- vnext/Shared/Modules/WebSocketModule.h | 6 +++++ 3 files changed, 31 insertions(+), 13 deletions(-) diff --git a/vnext/Shared/Modules/BlobModule.cpp b/vnext/Shared/Modules/BlobModule.cpp index 82cb395b968..6cab5b1cb11 100644 --- a/vnext/Shared/Modules/BlobModule.cpp +++ b/vnext/Shared/Modules/BlobModule.cpp @@ -251,8 +251,10 @@ void BlobWebSocketModuleContentHandler::Register(int64_t socketID) noexcept { void BlobWebSocketModuleContentHandler::Unregister(int64_t socketID) noexcept { scoped_lock lock{m_mutex}; - if (m_socketIds.find(socketID) != m_socketIds.end()) - m_socketIds.erase(socketID); + + auto itr = m_socketIds.find(socketID); + if (itr != m_socketIds.end()) + m_socketIds.erase(itr); } #pragma endregion BlobWebSocketModuleContentHandler diff --git a/vnext/Shared/Modules/WebSocketModule.cpp b/vnext/Shared/Modules/WebSocketModule.cpp index 89306f0152a..1122f5223a9 100644 --- a/vnext/Shared/Modules/WebSocketModule.cpp +++ b/vnext/Shared/Modules/WebSocketModule.cpp @@ -45,8 +45,6 @@ using Microsoft::React::Networking::IWebSocketResource; constexpr char moduleName[] = "WebSocketModule"; -weak_ptr s_sharedState; - static void SendEvent(weak_ptr weakInstance, string &&eventName, dynamic &&args) { if (auto instance = weakInstance.lock()) { instance->callJSFunction("RCTDeviceEventEmitter", "emit", dynamic::array(std::move(eventName), std::move(args))); @@ -159,17 +157,21 @@ namespace Microsoft::React { #pragma region WebSocketModule -WebSocketModule::WebSocketModule(winrt::Windows::Foundation::IInspectable const &iProperties) - : m_sharedState{std::make_shared()}, m_proxy{std::make_shared()} { +WebSocketModule::WebSocketModule(winrt::Windows::Foundation::IInspectable const &inspectableProperties) + : m_sharedState{std::make_shared()}, m_proxy{std::make_shared(inspectableProperties)} { m_sharedState->ResourceFactory = [](string &&url) { return IWebSocketResource::Make(); }; m_sharedState->Module = this; - m_sharedState->InspectableProps = iProperties; - s_sharedState = weak_ptr(m_sharedState); + m_sharedState->InspectableProps = inspectableProperties; - auto propId = ReactPropertyId>>{L"WebSocketModule.Proxy"}; auto propBag = ReactPropertyBag{m_sharedState->InspectableProps.try_as()}; + + auto proxyPropId = ReactPropertyId>>{L"WebSocketModule.Proxy"}; auto proxy = weak_ptr{m_proxy}; - propBag.Set(propId, std::move(proxy)); + propBag.Set(proxyPropId, std::move(proxy)); + + auto statePropId = ReactPropertyId>>{L"WebSocketModule.SharedState"}; + auto state = weak_ptr{m_sharedState}; + propBag.Set(statePropId, std::move(state)); } WebSocketModule::~WebSocketModule() noexcept /*override*/ { @@ -298,8 +300,16 @@ std::vector WebSocketModule::getMeth #pragma region WebSocketModuleProxy +WebSocketModuleProxy::WebSocketModuleProxy(IInspectable const& inspectableProperties) noexcept + : m_inspectableProps{inspectableProperties} { +} + void WebSocketModuleProxy::SendBinary(std::string &&base64String, int64_t id) noexcept /*override*/ { - weak_ptr weakWs = GetOrCreateWebSocket(id, {}, s_sharedState); + auto propBag = ReactPropertyBag{m_inspectableProps.try_as()}; + auto sharedPropId = ReactPropertyId>>{L"WebSocketModule.SharedState"}; + auto state = propBag.Get(sharedPropId).Value(); + + weak_ptr weakWs = GetOrCreateWebSocket(id, {}, std::move(state)); if (auto sharedWs = weakWs.lock()) { sharedWs->SendBinary(std::move(base64String)); } @@ -312,8 +322,8 @@ void WebSocketModuleProxy::SendBinary(std::string &&base64String, int64_t id) no } /*extern*/ std::unique_ptr CreateWebSocketModule( - IInspectable const &iProperties) noexcept { - if (auto properties = iProperties.try_as()) + IInspectable const &inspectableProperties) noexcept { + if (auto properties = inspectableProperties.try_as()) return std::make_unique(properties); return nullptr; diff --git a/vnext/Shared/Modules/WebSocketModule.h b/vnext/Shared/Modules/WebSocketModule.h index 4ca2423caa5..6d4b521de06 100644 --- a/vnext/Shared/Modules/WebSocketModule.h +++ b/vnext/Shared/Modules/WebSocketModule.h @@ -15,6 +15,12 @@ namespace Microsoft::React { class WebSocketModuleProxy final : public IWebSocketModuleProxy { + // Property bag high level reference. + winrt::Windows::Foundation::IInspectable m_inspectableProps; + +public: + WebSocketModuleProxy(winrt::Windows::Foundation::IInspectable const &inspectableProperties) noexcept; + #pragma region IWebSocketModuleProxy void SendBinary(std::string &&base64String, int64_t id) noexcept override; From b8bf1c3be4145fb2136b573a3e1d100d6821c2d5 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Sun, 1 May 2022 15:21:03 -0700 Subject: [PATCH 46/91] Disable back WebSocketBlob test --- vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp b/vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp index 64600cec1e5..482d563d09e 100644 --- a/vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp +++ b/vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp @@ -217,7 +217,7 @@ TEST_CLASS (RNTesterIntegrationTests) { } BEGIN_TEST_METHOD_ATTRIBUTE(WebSocketBlob) - // TEST_IGNORE() + TEST_IGNORE() END_TEST_METHOD_ATTRIBUTE() TEST_METHOD(WebSocketBlob) { auto result = m_runner.RunTest("IntegrationTests/WebSocketBlobTest", "WebSocketBlobTest"); From 0915349b6b1ef629457c750443cf21a98b88a4dd Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Sun, 1 May 2022 15:30:55 -0700 Subject: [PATCH 47/91] Rename iProperties to inspectableProperties --- vnext/Desktop.UnitTests/WebSocketModuleTest.cpp | 6 +++--- .../packages.lock.json | 2 +- vnext/Microsoft.ReactNative.Managed/packages.lock.json | 2 +- vnext/Shared/CreateModules.h | 4 ++-- vnext/Shared/Modules/BlobModule.cpp | 8 ++++---- vnext/Shared/Modules/BlobModule.h | 2 +- vnext/Shared/Modules/HttpModule.cpp | 4 ++-- vnext/Shared/Modules/HttpModule.h | 2 +- vnext/Shared/Modules/WebSocketModule.h | 2 +- vnext/Shared/OInstance.cpp | 4 ++-- 10 files changed, 18 insertions(+), 18 deletions(-) diff --git a/vnext/Desktop.UnitTests/WebSocketModuleTest.cpp b/vnext/Desktop.UnitTests/WebSocketModuleTest.cpp index c431e5b94af..295c1f117bf 100644 --- a/vnext/Desktop.UnitTests/WebSocketModuleTest.cpp +++ b/vnext/Desktop.UnitTests/WebSocketModuleTest.cpp @@ -26,7 +26,7 @@ TEST_CLASS (WebSocketModuleTest) { "connect", "close", "send", "sendBinary", "ping"}; TEST_METHOD(CreateModule) { - auto module = make_unique(nullptr /*iProperties*/); + auto module = make_unique(nullptr /*inspectableProperties*/); Assert::IsFalse(module == nullptr); Assert::AreEqual(string("WebSocketModule"), module->getName()); @@ -40,7 +40,7 @@ TEST_CLASS (WebSocketModuleTest) { } TEST_METHOD(ConnectEmptyUriFails) { - auto module = make_unique(nullptr /*iProperties*/); + auto module = make_unique(nullptr /*inspectableProperties*/); module->getMethods() .at(WebSocketModule::MethodId::Connect) @@ -70,7 +70,7 @@ TEST_CLASS (WebSocketModuleTest) { }; auto instance = CreateMockInstance(jsef); - auto module = make_unique(nullptr /*iProperties*/); + auto module = make_unique(nullptr /*inspectableProperties*/); module->setInstance(instance); module->SetResourceFactory([](const string &) { auto rc = make_shared(); diff --git a/vnext/Microsoft.ReactNative.Managed.UnitTests/packages.lock.json b/vnext/Microsoft.ReactNative.Managed.UnitTests/packages.lock.json index e11119cd75b..29155030c86 100644 --- a/vnext/Microsoft.ReactNative.Managed.UnitTests/packages.lock.json +++ b/vnext/Microsoft.ReactNative.Managed.UnitTests/packages.lock.json @@ -62,7 +62,7 @@ "Microsoft.NETCore.Platforms": { "type": "Transitive", "resolved": "2.1.0", - "contentHash": "ok+RPAtESz/9MUXeIEz6Lv5XAGQsaNmEYXMsgVALj4D7kqC8gveKWXWXbufLySR2fWrwZf8smyN5RmHu0e4BHA==" + "contentHash": "GmkKfoyerqmsHMn7OZj0AKpcBabD+GaafqphvX2Mw406IwiJRy1pKcKqdCfKJfYmkRyJ6+e+RaUylgdJoDa1jQ==" }, "Microsoft.NETCore.Targets": { "type": "Transitive", diff --git a/vnext/Microsoft.ReactNative.Managed/packages.lock.json b/vnext/Microsoft.ReactNative.Managed/packages.lock.json index 14d1ec0d868..04e54db679e 100644 --- a/vnext/Microsoft.ReactNative.Managed/packages.lock.json +++ b/vnext/Microsoft.ReactNative.Managed/packages.lock.json @@ -53,7 +53,7 @@ "Microsoft.NETCore.Platforms": { "type": "Transitive", "resolved": "2.1.0", - "contentHash": "ok+RPAtESz/9MUXeIEz6Lv5XAGQsaNmEYXMsgVALj4D7kqC8gveKWXWXbufLySR2fWrwZf8smyN5RmHu0e4BHA==" + "contentHash": "GmkKfoyerqmsHMn7OZj0AKpcBabD+GaafqphvX2Mw406IwiJRy1pKcKqdCfKJfYmkRyJ6+e+RaUylgdJoDa1jQ==" }, "Microsoft.SourceLink.Common": { "type": "Transitive", diff --git a/vnext/Shared/CreateModules.h b/vnext/Shared/CreateModules.h index 4a0f7ad12c5..6460022051e 100644 --- a/vnext/Shared/CreateModules.h +++ b/vnext/Shared/CreateModules.h @@ -39,10 +39,10 @@ extern std::unique_ptr CreateHttpModule() no extern const char *GetWebSocketModuleName() noexcept; extern std::unique_ptr CreateWebSocketModule( - winrt::Windows::Foundation::IInspectable const &iProperties) noexcept; + winrt::Windows::Foundation::IInspectable const &inspectableProperties) noexcept; extern const char *GetBlobModuleName() noexcept; extern std::unique_ptr CreateBlobModule( - winrt::Windows::Foundation::IInspectable const &iProperties) noexcept; + winrt::Windows::Foundation::IInspectable const &inspectableProperties) noexcept; } // namespace Microsoft::React diff --git a/vnext/Shared/Modules/BlobModule.cpp b/vnext/Shared/Modules/BlobModule.cpp index 6cab5b1cb11..fca7483a9d0 100644 --- a/vnext/Shared/Modules/BlobModule.cpp +++ b/vnext/Shared/Modules/BlobModule.cpp @@ -47,14 +47,14 @@ namespace Microsoft::React { #pragma region BlobModule -BlobModule::BlobModule(winrt::Windows::Foundation::IInspectable const &iProperties) noexcept +BlobModule::BlobModule(winrt::Windows::Foundation::IInspectable const &inspectableProperties) noexcept : m_sharedState{std::make_shared()}, m_blobPersistor{std::make_shared()}, m_contentHandler{std::make_shared(m_blobPersistor)}, m_uriHandler{std::make_shared(m_blobPersistor)}, m_requestBodyHandler{std::make_shared(m_blobPersistor)}, m_responseHandler{std::make_shared(m_blobPersistor)}, - m_inspectableProperties{iProperties} { + m_inspectableProperties{inspectableProperties} { auto propId = ReactPropertyId>>{L"BlobModule.ContentHandler"}; auto propBag = ReactPropertyBag{m_inspectableProperties.try_as()}; @@ -407,8 +407,8 @@ dynamic BlobModuleResponseHandler::ToResponseData(dynamic &body) /*override*/ { } /*extern*/ std::unique_ptr CreateBlobModule( - IInspectable const &iProperties) noexcept { - if (auto properties = iProperties.try_as()) + IInspectable const &inspectableProperties) noexcept { + if (auto properties = inspectableProperties.try_as()) return std::make_unique(properties); return nullptr; diff --git a/vnext/Shared/Modules/BlobModule.h b/vnext/Shared/Modules/BlobModule.h index a9254d1cc9d..fa7be62ea1e 100644 --- a/vnext/Shared/Modules/BlobModule.h +++ b/vnext/Shared/Modules/BlobModule.h @@ -137,7 +137,7 @@ class BlobModule : public facebook::xplat::module::CxxModule { SIZE = 6 }; - BlobModule(winrt::Windows::Foundation::IInspectable const &iProperties) noexcept; + BlobModule(winrt::Windows::Foundation::IInspectable const &inspectableProperties) noexcept; ~BlobModule() noexcept override; diff --git a/vnext/Shared/Modules/HttpModule.cpp b/vnext/Shared/Modules/HttpModule.cpp index 2e3829facc7..bddb4cf1c73 100644 --- a/vnext/Shared/Modules/HttpModule.cpp +++ b/vnext/Shared/Modules/HttpModule.cpp @@ -72,8 +72,8 @@ static shared_ptr CreateHttpResource(weak_ptr weakReact namespace Microsoft::React { -HttpModule::HttpModule(winrt::Windows::Foundation::IInspectable const &iProperties) noexcept - : m_holder{std::make_shared()}, m_inspectableProperties{iProperties} { +HttpModule::HttpModule(winrt::Windows::Foundation::IInspectable const &inspectableProperties) noexcept + : m_holder{std::make_shared()}, m_inspectableProperties{inspectableProperties} { m_holder->Module = this; auto propId = ReactPropertyId>>{L"HttpModule.Proxy"}; diff --git a/vnext/Shared/Modules/HttpModule.h b/vnext/Shared/Modules/HttpModule.h index 8d22f0c2d38..91bc394a897 100644 --- a/vnext/Shared/Modules/HttpModule.h +++ b/vnext/Shared/Modules/HttpModule.h @@ -35,7 +35,7 @@ class HttpModule : public facebook::xplat::module::CxxModule { public: enum MethodId { SendRequest = 0, AbortRequest = 1, ClearCookies = 2, LAST = ClearCookies }; - HttpModule(winrt::Windows::Foundation::IInspectable const &iProperties) noexcept; + HttpModule(winrt::Windows::Foundation::IInspectable const &inspectableProperties) noexcept; ~HttpModule() noexcept override; diff --git a/vnext/Shared/Modules/WebSocketModule.h b/vnext/Shared/Modules/WebSocketModule.h index 6d4b521de06..8d0739ba48a 100644 --- a/vnext/Shared/Modules/WebSocketModule.h +++ b/vnext/Shared/Modules/WebSocketModule.h @@ -36,7 +36,7 @@ class WebSocketModule : public facebook::xplat::module::CxxModule { public: enum MethodId { Connect = 0, Close = 1, Send = 2, SendBinary = 3, Ping = 4, SIZE = 5 }; - WebSocketModule(winrt::Windows::Foundation::IInspectable const &iProperties); + WebSocketModule(winrt::Windows::Foundation::IInspectable const &inspectableProperties); ~WebSocketModule() noexcept override; diff --git a/vnext/Shared/OInstance.cpp b/vnext/Shared/OInstance.cpp index 9130214ae0b..d4c010c6ad1 100644 --- a/vnext/Shared/OInstance.cpp +++ b/vnext/Shared/OInstance.cpp @@ -72,11 +72,11 @@ using winrt::Microsoft::ReactNative::ReactPropertyBagHelper; namespace Microsoft::React { /*extern*/ std::unique_ptr CreateHttpModule( - winrt::Windows::Foundation::IInspectable const &iProperties) noexcept { + winrt::Windows::Foundation::IInspectable const &inspectableProperties) noexcept { if (GetRuntimeOptionBool("Http.UseMonolithicModule")) { return std::make_unique(); } else { - return std::make_unique(iProperties); + return std::make_unique(inspectableProperties); } } From 2c839dc75af98db0053248c53c6070ec69f059f6 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Sun, 1 May 2022 16:02:21 -0700 Subject: [PATCH 48/91] Pass ReactContext properties to CreateHttpModule in InstanceWin --- .../Microsoft.ReactNative/Base/CoreNativeModules.cpp | 4 +++- vnext/Shared/CreateModules.h | 3 ++- vnext/Shared/Modules/WebSocketModule.cpp | 11 ++++++----- vnext/Shared/Modules/WebSocketModule.h | 2 +- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/vnext/Microsoft.ReactNative/Base/CoreNativeModules.cpp b/vnext/Microsoft.ReactNative/Base/CoreNativeModules.cpp index 4c1b23037f9..a2a2c9d3b36 100644 --- a/vnext/Microsoft.ReactNative/Base/CoreNativeModules.cpp +++ b/vnext/Microsoft.ReactNative/Base/CoreNativeModules.cpp @@ -46,7 +46,9 @@ std::vector GetCoreModules( std::vector modules; modules.emplace_back( - "Networking", []() { return Microsoft::React::CreateHttpModule(); }, jsMessageQueue); + "Networking", + [props = context->Properties()]() { return Microsoft::React::CreateHttpModule(props); }, + jsMessageQueue); modules.emplace_back( "Timing", diff --git a/vnext/Shared/CreateModules.h b/vnext/Shared/CreateModules.h index 6460022051e..9c8038dda5c 100644 --- a/vnext/Shared/CreateModules.h +++ b/vnext/Shared/CreateModules.h @@ -35,7 +35,8 @@ extern std::unique_ptr CreateTimingModule( namespace Microsoft::React { extern const char *GetHttpModuleName() noexcept; -extern std::unique_ptr CreateHttpModule() noexcept; +extern std::unique_ptr CreateHttpModule( + winrt::Windows::Foundation::IInspectable const &inspectableProperties) noexcept; extern const char *GetWebSocketModuleName() noexcept; extern std::unique_ptr CreateWebSocketModule( diff --git a/vnext/Shared/Modules/WebSocketModule.cpp b/vnext/Shared/Modules/WebSocketModule.cpp index 1122f5223a9..87b2475a136 100644 --- a/vnext/Shared/Modules/WebSocketModule.cpp +++ b/vnext/Shared/Modules/WebSocketModule.cpp @@ -158,7 +158,8 @@ namespace Microsoft::React { #pragma region WebSocketModule WebSocketModule::WebSocketModule(winrt::Windows::Foundation::IInspectable const &inspectableProperties) - : m_sharedState{std::make_shared()}, m_proxy{std::make_shared(inspectableProperties)} { + : m_sharedState{std::make_shared()}, + m_proxy{std::make_shared(inspectableProperties)} { m_sharedState->ResourceFactory = [](string &&url) { return IWebSocketResource::Make(); }; m_sharedState->Module = this; m_sharedState->InspectableProps = inspectableProperties; @@ -300,13 +301,13 @@ std::vector WebSocketModule::getMeth #pragma region WebSocketModuleProxy -WebSocketModuleProxy::WebSocketModuleProxy(IInspectable const& inspectableProperties) noexcept - : m_inspectableProps{inspectableProperties} { -} +WebSocketModuleProxy::WebSocketModuleProxy(IInspectable const &inspectableProperties) noexcept + : m_inspectableProps{inspectableProperties} {} void WebSocketModuleProxy::SendBinary(std::string &&base64String, int64_t id) noexcept /*override*/ { auto propBag = ReactPropertyBag{m_inspectableProps.try_as()}; - auto sharedPropId = ReactPropertyId>>{L"WebSocketModule.SharedState"}; + auto sharedPropId = + ReactPropertyId>>{L"WebSocketModule.SharedState"}; auto state = propBag.Get(sharedPropId).Value(); weak_ptr weakWs = GetOrCreateWebSocket(id, {}, std::move(state)); diff --git a/vnext/Shared/Modules/WebSocketModule.h b/vnext/Shared/Modules/WebSocketModule.h index 8d0739ba48a..5a10ac5990a 100644 --- a/vnext/Shared/Modules/WebSocketModule.h +++ b/vnext/Shared/Modules/WebSocketModule.h @@ -18,7 +18,7 @@ class WebSocketModuleProxy final : public IWebSocketModuleProxy { // Property bag high level reference. winrt::Windows::Foundation::IInspectable m_inspectableProps; -public: + public: WebSocketModuleProxy(winrt::Windows::Foundation::IInspectable const &inspectableProperties) noexcept; #pragma region IWebSocketModuleProxy From 01063bdeff3e7a1f724aed934d03bf934e3e92a6 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Sun, 1 May 2022 16:27:38 -0700 Subject: [PATCH 49/91] Remove WebSocketModule constructor from x86 DLL boundary --- vnext/Desktop.DLL/react-native-win32.x86.def | 1 - 1 file changed, 1 deletion(-) diff --git a/vnext/Desktop.DLL/react-native-win32.x86.def b/vnext/Desktop.DLL/react-native-win32.x86.def index 0365f31dd07..d899f933715 100644 --- a/vnext/Desktop.DLL/react-native-win32.x86.def +++ b/vnext/Desktop.DLL/react-native-win32.x86.def @@ -51,7 +51,6 @@ EXPORTS ?GetRuntimeOptionString@React@Microsoft@@YA?BV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@ABV34@@Z ?makeChakraRuntime@JSI@Microsoft@@YG?AV?$unique_ptr@VRuntime@jsi@facebook@@U?$default_delete@VRuntime@jsi@facebook@@@std@@@std@@$$QAUChakraRuntimeArgs@12@@Z ?CreateTimingModule@react@facebook@@YG?AV?$unique_ptr@VCxxModule@module@xplat@facebook@@U?$default_delete@VCxxModule@module@xplat@facebook@@@std@@@std@@ABV?$shared_ptr@VMessageQueueThread@react@facebook@@@4@@Z -??0WebSocketModule@React@Microsoft@@QAE@XZ ?Make@IHttpResource@Networking@React@Microsoft@@SG?AV?$shared_ptr@UIHttpResource@Networking@React@Microsoft@@@std@@XZ ??0NetworkingModule@React@Microsoft@@QAE@XZ ?MakeJSQueueThread@ReactNative@Microsoft@@YG?AV?$shared_ptr@VMessageQueueThread@react@facebook@@@std@@XZ From 8662a25e9f0fbd597fea2755637f3049f9916ab9 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Sun, 1 May 2022 16:33:24 -0700 Subject: [PATCH 50/91] yarn lint --- vnext/src/IntegrationTests/WebSocketBlobTest.js | 8 ++++---- .../websocket_integration_test_server_blob.js | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/vnext/src/IntegrationTests/WebSocketBlobTest.js b/vnext/src/IntegrationTests/WebSocketBlobTest.js index e0870109b00..2f975d719f4 100644 --- a/vnext/src/IntegrationTests/WebSocketBlobTest.js +++ b/vnext/src/IntegrationTests/WebSocketBlobTest.js @@ -63,7 +63,7 @@ class WebSocketBlobTest extends React.Component<{}, State> { _connect = () => { const socket = new WebSocket(this.state.url); socket.binaryType = 'blob'; - WS_EVENTS.forEach((ev) => socket.addEventListener(ev, this._onSocketEvent)); + WS_EVENTS.forEach(ev => socket.addEventListener(ev, this._onSocketEvent)); this.setState({ socket, socketState: socket.readyState, @@ -120,7 +120,7 @@ class WebSocketBlobTest extends React.Component<{}, State> { testConnect: () => void = () => { this._connect(); - this._waitFor(this._socketIsConnected, 5, (connectSucceeded) => { + this._waitFor(this._socketIsConnected, 5, connectSucceeded => { if (!connectSucceeded) { TestModule.markTestPassed(false); return; @@ -131,7 +131,7 @@ class WebSocketBlobTest extends React.Component<{}, State> { testSendAndReceive: () => void = () => { this._sendTestMessage(); - this._waitFor(this._receivedTestExpectedResponse, 5, (messageReceived) => { + this._waitFor(this._receivedTestExpectedResponse, 5, messageReceived => { if (!messageReceived) { TestModule.markTestPassed(false); return; @@ -142,7 +142,7 @@ class WebSocketBlobTest extends React.Component<{}, State> { testDisconnect: () => void = () => { this._disconnect(); - this._waitFor(this._socketIsDisconnected, 5, (disconnectSucceeded) => { + this._waitFor(this._socketIsDisconnected, 5, disconnectSucceeded => { TestModule.markTestPassed(disconnectSucceeded); }); }; diff --git a/vnext/src/IntegrationTests/websocket_integration_test_server_blob.js b/vnext/src/IntegrationTests/websocket_integration_test_server_blob.js index 7fb46a949d2..59e64f678bb 100644 --- a/vnext/src/IntegrationTests/websocket_integration_test_server_blob.js +++ b/vnext/src/IntegrationTests/websocket_integration_test_server_blob.js @@ -24,9 +24,9 @@ This will send each incoming message back, in binary form. `); const server = new WebSocket.Server({port: 5557}); -server.on('connection', (ws) => { +server.on('connection', ws => { ws.binaryType = 'blob'; - ws.on('message', (message) => { + ws.on('message', message => { console.log(message); ws.send([4, 5, 6, 7]); From c8a2d97a818f636ac82d396a24ad4925f4ca358e Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Mon, 2 May 2022 17:55:37 -0700 Subject: [PATCH 51/91] Update packages.lock --- .../Microsoft.ReactNative.Managed.UnitTests/packages.lock.json | 2 +- vnext/Microsoft.ReactNative.Managed/packages.lock.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/vnext/Microsoft.ReactNative.Managed.UnitTests/packages.lock.json b/vnext/Microsoft.ReactNative.Managed.UnitTests/packages.lock.json index 29155030c86..ddbef3311ae 100644 --- a/vnext/Microsoft.ReactNative.Managed.UnitTests/packages.lock.json +++ b/vnext/Microsoft.ReactNative.Managed.UnitTests/packages.lock.json @@ -72,7 +72,7 @@ "NETStandard.Library": { "type": "Transitive", "resolved": "2.0.3", - "contentHash": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==", + "contentHash": "548M6mnBSJWxsIlkQHfbzoYxpiYFXZZSL00p4GHYv8PkiqFBnnT68mW5mGEsA/ch9fDO9GkPgkFQpWiXZN7mAQ==", "dependencies": { "Microsoft.NETCore.Platforms": "1.1.0" } diff --git a/vnext/Microsoft.ReactNative.Managed/packages.lock.json b/vnext/Microsoft.ReactNative.Managed/packages.lock.json index 04e54db679e..7b897d0bd1e 100644 --- a/vnext/Microsoft.ReactNative.Managed/packages.lock.json +++ b/vnext/Microsoft.ReactNative.Managed/packages.lock.json @@ -63,7 +63,7 @@ "NETStandard.Library": { "type": "Transitive", "resolved": "2.0.3", - "contentHash": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==", + "contentHash": "548M6mnBSJWxsIlkQHfbzoYxpiYFXZZSL00p4GHYv8PkiqFBnnT68mW5mGEsA/ch9fDO9GkPgkFQpWiXZN7mAQ==", "dependencies": { "Microsoft.NETCore.Platforms": "1.1.0" } From 12252776c5d04bd226bd3324882fe8ce8b1f0781 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Mon, 2 May 2022 17:56:48 -0700 Subject: [PATCH 52/91] Make transitional property bag non-member --- vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp | 4 +++- vnext/Shared/OInstance.cpp | 6 ++---- vnext/Shared/OInstance.h | 6 ------ 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp b/vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp index 482d563d09e..57d98d1d317 100644 --- a/vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp +++ b/vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp @@ -188,10 +188,12 @@ TEST_CLASS (RNTesterIntegrationTests) { TEST_METHOD(WebSocket) { // Should behave the same as IntegrationTests/websocket_integration_test_server.js auto server = std::make_shared(5555, false /*useTLS*/); - server->SetMessageFactory([](std::string &&message) -> std::string { return message + "_response"; }); + server->SetMessageFactory([](string &&message) -> string { return message + "_response"; }); server->Start(); TestComponent("WebSocketTest"); + + server->Stop(); } BEGIN_TEST_METHOD_ATTRIBUTE(AccessibilityManager) diff --git a/vnext/Shared/OInstance.cpp b/vnext/Shared/OInstance.cpp index d4c010c6ad1..9f46c628f8e 100644 --- a/vnext/Shared/OInstance.cpp +++ b/vnext/Shared/OInstance.cpp @@ -246,8 +246,7 @@ InstanceImpl::InstanceImpl( m_jsBundleBasePath(std::move(jsBundleBasePath)), m_devSettings(std::move(devSettings)), m_devManager(std::move(devManager)), - m_innerInstance(std::move(instance)), - m_transitionalModuleProperties{ReactPropertyBagHelper::CreatePropertyBag()} { + m_innerInstance(std::move(instance)) { // Temp set the logmarker here facebook::react::ReactMarker::logTaggedMarker = logMarker; @@ -544,8 +543,7 @@ InstanceImpl::~InstanceImpl() { std::vector> InstanceImpl::GetDefaultNativeModules( std::shared_ptr nativeQueue) { std::vector> modules; - auto transitionalProps = m_transitionalModuleProperties; - auto propBag = winrt::Microsoft::ReactNative::ReactPropertyBag{transitionalProps}; + auto transitionalProps{ReactPropertyBagHelper::CreatePropertyBag()}; modules.push_back(std::make_unique( m_innerInstance, diff --git a/vnext/Shared/OInstance.h b/vnext/Shared/OInstance.h index 476a1f8302a..c6452311ff0 100644 --- a/vnext/Shared/OInstance.h +++ b/vnext/Shared/OInstance.h @@ -108,12 +108,6 @@ class InstanceImpl final : public InstanceWrapper, private ::std::enable_shared_ std::shared_ptr m_devManager; std::shared_ptr m_devSettings; bool m_isInError{false}; - - /// - /// Internal property bag meant to share arbitrary data between CxxModule instances. - /// This element should be deprecated once full ABI safety is achieved. - /// - winrt::Microsoft::ReactNative::IReactPropertyBag m_transitionalModuleProperties; }; } // namespace react From a5f8c2967e4c7d1fa74c6193e9d894cf8172b792 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Mon, 2 May 2022 18:45:29 -0700 Subject: [PATCH 53/91] Use blobURIScheme wherever possible --- vnext/Shared/Modules/BlobModule.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/vnext/Shared/Modules/BlobModule.cpp b/vnext/Shared/Modules/BlobModule.cpp index fca7483a9d0..0c3c25773c5 100644 --- a/vnext/Shared/Modules/BlobModule.cpp +++ b/vnext/Shared/Modules/BlobModule.cpp @@ -144,7 +144,7 @@ vector BlobModule::getMethods() { for (const auto &part : parts) { auto type = part["type"].asString(); - if ("blob" == type) { + if (blobURIScheme == type) { auto blob = part["data"]; auto bufferPart = persistor->ResolveMessage(blob["blobId"].asString(), blob["offset"].asInt(), blob["size"].asInt()); @@ -240,7 +240,7 @@ void BlobWebSocketModuleContentHandler::ProcessMessage(vector &&message blob("blobId", m_blobPersistor->StoreMessage(std::move(message))); params["data"] = std::move(blob); - params["type"] = "blob"; + params["type"] = blobURIScheme; } #pragma endregion IWebSocketModuleContentHandler @@ -269,7 +269,7 @@ BlobModuleUriHandler::BlobModuleUriHandler(shared_ptr blobPersis bool BlobModuleUriHandler::Supports(string &uri, string &responseType) /*override*/ { auto uriObj = Uri{winrt::to_hstring(uri)}; - return !(L"http" == uriObj.SchemeName() || L"https" == uriObj.SchemeName()) && "blob" == responseType; + return !(L"http" == uriObj.SchemeName() || L"https" == uriObj.SchemeName()) && blobURIScheme == responseType; } dynamic BlobModuleUriHandler::Fetch(string &uri) /*override*/ { @@ -296,7 +296,7 @@ string BlobModuleUriHandler::GetMimeTypeFromUri(string &uri) noexcept { // See // https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/content/ContentResolver.java - return "blob"; + return blobURIScheme; } string BlobModuleUriHandler::GetNameFromUri(string &uri) noexcept { @@ -351,7 +351,7 @@ BlobModuleRequestBodyHandler::BlobModuleRequestBodyHandler(shared_ptrResolveMessage(std::move(blobId), blob["offset"].asInt(), blob["size"].asInt()); From 82fe4751f4d958ab6da7091fde7f55a2a734bf35 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Tue, 3 May 2022 23:58:01 -0700 Subject: [PATCH 54/91] Pass request content as folly::dynaic. - Pass request ID directly from JavaScript layer. --- .../HttpOriginPolicyIntegrationTest.cpp | 13 +++--- .../HttpResourceIntegrationTests.cpp | 30 +++++++------- vnext/Shared/Modules/HttpModule.cpp | 31 ++------------ vnext/Shared/Modules/IBlobPersistor.h | 1 - vnext/Shared/Networking/IHttpResource.h | 41 ++++++++++++++++++- vnext/Shared/Networking/WinRTHttpResource.cpp | 40 +++++++++--------- vnext/Shared/Networking/WinRTHttpResource.h | 4 +- vnext/Shared/Networking/WinRTTypes.h | 5 ++- .../Network/RCTNetworkingWinShared.js | 7 ++++ 9 files changed, 98 insertions(+), 74 deletions(-) diff --git a/vnext/Desktop.IntegrationTests/HttpOriginPolicyIntegrationTest.cpp b/vnext/Desktop.IntegrationTests/HttpOriginPolicyIntegrationTest.cpp index 8844f2077c1..640383f71e6 100644 --- a/vnext/Desktop.IntegrationTests/HttpOriginPolicyIntegrationTest.cpp +++ b/vnext/Desktop.IntegrationTests/HttpOriginPolicyIntegrationTest.cpp @@ -142,13 +142,13 @@ TEST_CLASS(HttpOriginPolicyIntegrationTest) resource->SendRequest( string{http::to_string(clientArgs.Method).data()}, string{server1Args.Url}, + 0, /*requestId*/ std::move(clientArgs.RequestHeaders), - { IHttpResource::BodyData::Type::String, "REQUEST_CONTENT" }, + {}, /*data*/ "text", false, /*useIncrementalUpdates*/ 1000, /*timeout*/ clientArgs.WithCredentials, /*withCredentials*/ - {}, /*data*/ [](int64_t){} /*reactCallback*/ ); @@ -196,13 +196,13 @@ TEST_CLASS(HttpOriginPolicyIntegrationTest) resource->SendRequest( string{http::to_string(clientArgs.Method).data()}, string{serverArgs.Url}, + 0, /*requestId*/ std::move(clientArgs.RequestHeaders), - { IHttpResource::BodyData::Type::String, "REQUEST_CONTENT" }, + {}, /*data*/ "text", false, /*useIncrementalUpdates*/ 1000, /*timeout*/ clientArgs.WithCredentials, /*withCredentials*/ - {}, /*data*/ [](int64_t) {} /*reactCallback*/ ); @@ -292,15 +292,16 @@ TEST_CLASS(HttpOriginPolicyIntegrationTest) resource->SendRequest( "TRACE", url, + 0, /*requestId*/ { {"ValidHeader", "AnyValue"} }, - {} /*bodyData*/, + {}, /*data*/ + //{} /*bodyData*/, "text", false /*useIncrementalUpdates*/, 1000 /*timeout*/, false /*withCredentials*/, - {}, /*data*/ [](int64_t) {} /*callback*/ ); diff --git a/vnext/Desktop.IntegrationTests/HttpResourceIntegrationTests.cpp b/vnext/Desktop.IntegrationTests/HttpResourceIntegrationTests.cpp index cfb6bff7819..2d2e7dac901 100644 --- a/vnext/Desktop.IntegrationTests/HttpResourceIntegrationTests.cpp +++ b/vnext/Desktop.IntegrationTests/HttpResourceIntegrationTests.cpp @@ -73,13 +73,13 @@ TEST_CLASS (HttpResourceIntegrationTest) { resource->SendRequest( "GET", std::move(url), - {} /*header*/, - {} /*bodyData*/, + 0, /*requestId*/ + {}, /*header*/ + {}, /*data*/ "text", false, 1000 /*timeout*/, false /*withCredentials*/, - {}, /*data*/ [](int64_t) {}); // Synchronize response. @@ -129,18 +129,18 @@ TEST_CLASS (HttpResourceIntegrationTest) { resource->SendRequest( "GET", std::move(url), + 0, /*requestId*/ { {"Content-Type", "application/json"}, {"Content-Encoding", "ASCII"}, {"name3", "value3"}, {"name4", "value4"}, }, - {} /*bodyData*/, + {}, /*data*/ "text", false, 1000 /*timeout*/, false /*withCredentials*/, - {}, /*data*/ [](int64_t) {}); //clang-format on @@ -172,7 +172,7 @@ TEST_CLASS (HttpResourceIntegrationTest) { promise.set_value(); }); - resource->SendRequest("GET", "http://nonexistinghost", {}, {}, "text", false, 1000, false, {}, [](int64_t) {}); + resource->SendRequest("GET", "http://nonexistinghost", 0, {}, {}, "text", false, 1000, false, [](int64_t) {}); promise.get_future().wait(); @@ -240,24 +240,24 @@ TEST_CLASS (HttpResourceIntegrationTest) { resource->SendRequest( "OPTIONS", string{url}, - {} /*headers*/, - {} /*bodyData*/, + 0, /*requestId*/ + {}, /*headers*/ + {}, /*data*/ "text", false, 1000 /*timeout*/, false /*withCredentials*/, - {}, /*data*/ [](int64_t) {}); resource->SendRequest( "GET", std::move(url), - {} /*headers*/, - {} /*bodyData*/, + 0, /*requestId*/ + {}, /*headers*/ + {}, /*data*/ "text", false, 1000 /*timeout*/, false /*withCredentials*/, - {}, /*data*/ [](int64_t) {}); //clang-format on @@ -334,13 +334,13 @@ TEST_CLASS (HttpResourceIntegrationTest) { resource->SendRequest( "GET", std::move(url), - {} /*headers*/, - {} /*bodyData*/, + 0, /*requestId*/ + {}, /*headers*/ + {}, /*data*/ "text", false, /*useIncrementalUpdates*/ 1000 /*timeout*/, false /*withCredentials*/, - {}, /*data*/ [](int64_t) {}); //clang-format on diff --git a/vnext/Shared/Modules/HttpModule.cpp b/vnext/Shared/Modules/HttpModule.cpp index bddb4cf1c73..a5f6a9d23a1 100644 --- a/vnext/Shared/Modules/HttpModule.cpp +++ b/vnext/Shared/Modules/HttpModule.cpp @@ -117,32 +117,7 @@ std::vector HttpModule::getMethods() auto resource = holder->Module->m_resource; if (resource || (resource = CreateHttpResource(holder->Module->getInstance()))) { - IHttpResource::BodyData bodyData; auto params = facebook::xplat::jsArgAsObject(args, 0); - auto data = params["data"]; - auto stringData = data["string"]; - if (!stringData.empty()) - { - bodyData = {IHttpResource::BodyData::Type::String, stringData.getString()}; - } - else - { - auto base64Data = data["base64"]; - if (!base64Data.empty()) - { - bodyData = {IHttpResource::BodyData::Type::Base64, base64Data.getString()}; - } - else - { - auto uriData = data["uri"]; - if (!uriData.empty()) - { - bodyData = {IHttpResource::BodyData::Type::Uri, uriData.getString()}; - } - } - } - //TODO: Support FORM data - IHttpResource::Headers headers; for (auto& header : params["headers"].items()) { headers.emplace(header.first.getString(), header.second.getString()); @@ -151,13 +126,13 @@ std::vector HttpModule::getMethods() resource->SendRequest( params["method"].asString(), params["url"].asString(), + params["requestId"].asInt(), std::move(headers), - std::move(bodyData), + std::move(params["data"]), params["responseType"].asString(), params["incrementalUpdates"].asBool(), static_cast(params["timeout"].asDouble()), - false,//withCredentials, - std::move(data), + params["withCredentials"].asBool(), [cxxCallback = std::move(cxxCallback)](int64_t requestId) { cxxCallback({requestId}); } diff --git a/vnext/Shared/Modules/IBlobPersistor.h b/vnext/Shared/Modules/IBlobPersistor.h index e74146bc684..6d121049166 100644 --- a/vnext/Shared/Modules/IBlobPersistor.h +++ b/vnext/Shared/Modules/IBlobPersistor.h @@ -17,7 +17,6 @@ struct IBlobPersistor { virtual void RemoveMessage(std::string &&blobId) noexcept = 0; - // TODO: Keep only one variant for StoreMessage. virtual void StoreMessage(std::vector &&message, std::string &&blobId) noexcept = 0; virtual std::string StoreMessage(std::vector &&message) noexcept = 0; diff --git a/vnext/Shared/Networking/IHttpResource.h b/vnext/Shared/Networking/IHttpResource.h index 2f319231031..6b96530a698 100644 --- a/vnext/Shared/Networking/IHttpResource.h +++ b/vnext/Shared/Networking/IHttpResource.h @@ -17,6 +17,7 @@ namespace Microsoft::React::Networking { struct IHttpResource { typedef std::unordered_map Headers; + // TODO: Implement Form data struct BodyData { enum class Type : size_t { Empty, String, Base64, Uri, Form } Type = Type::Empty; std::string Data; @@ -32,16 +33,52 @@ struct IHttpResource { virtual ~IHttpResource() noexcept {} + /// + /// Initiates an HTTP request. + /// + /// + /// HTTP verb to send in de request. + /// GET | POST | PUT | DELETE | OPTIONS + /// + /// + /// Server/service remote endpoint to send the request to. + /// + /// + /// Request unique identifier. + /// + /// + /// HTTP request header map. + /// + /// + /// Dynamic map containing request payload. + /// The payload may be an empty request body or one of the following: + /// "string" - UTF-8 string payload + /// "base64" - Base64-encoded data string + /// "uri" - URI data reference + /// "form" - Form-encoded data + /// + /// + /// text | binary | blob + /// + /// + /// Response body to be retrieved in several iterations. + /// + /// + /// Request timeout in miliseconds. + /// + /// + /// Allow including credentials in request. + /// virtual void SendRequest( std::string &&method, std::string &&url, + int64_t requestId, Headers &&headers, - BodyData &&bodyData, + folly::dynamic &&data, std::string &&responseType, bool useIncrementalUpdates, int64_t timeout, bool withCredentials, - folly::dynamic &&data, // TODO: Drop usage of folly::dynamic once content marshalling is implemented. std::function &&callback) noexcept = 0; virtual void AbortRequest(int64_t requestId) noexcept = 0; diff --git a/vnext/Shared/Networking/WinRTHttpResource.cpp b/vnext/Shared/Networking/WinRTHttpResource.cpp index 8f60976de4c..d4b6c2cc751 100644 --- a/vnext/Shared/Networking/WinRTHttpResource.cpp +++ b/vnext/Shared/Networking/WinRTHttpResource.cpp @@ -59,16 +59,14 @@ WinRTHttpResource::WinRTHttpResource() noexcept : WinRTHttpResource(winrt::Windo void WinRTHttpResource::SendRequest( string &&method, string &&url, + int64_t requestId, Headers &&headers, - BodyData &&bodyData, + dynamic &&data, string &&responseType, bool useIncrementalUpdates, int64_t timeout, bool withCredentials, - dynamic &&data, std::function &&callback) noexcept /*override*/ { - auto requestId = ++s_lastRequestId; - // Enforce supported args assert(responseType == "text" || responseType == "base64"); @@ -85,7 +83,7 @@ void WinRTHttpResource::SendRequest( auto concreteArgs = args.as(); concreteArgs->RequestId = requestId; concreteArgs->Headers = std::move(headers); - concreteArgs->Body = std::move(bodyData); + concreteArgs->Data = std::move(data); concreteArgs->IncrementalUpdates = useIncrementalUpdates; concreteArgs->WithCredentials = withCredentials; concreteArgs->IsText = responseType == "text"; @@ -204,20 +202,24 @@ fire_and_forget WinRTHttpResource::PerformSendRequest(HttpRequestMessage &&reque } IHttpContent content{nullptr}; - if (BodyData::Type::String == coReqArgs->Body.Type) { - content = HttpStringContent{to_hstring(coReqArgs->Body.Data)}; - } else if (BodyData::Type::Base64 == coReqArgs->Body.Type) { - auto buffer = CryptographicBuffer::DecodeFromBase64String(to_hstring(coReqArgs->Body.Data)); - content = HttpBufferContent{buffer}; - } else if (BodyData::Type::Uri == coReqArgs->Body.Type) { - auto file = co_await StorageFile::GetFileFromApplicationUriAsync(Uri{to_hstring(coReqArgs->Body.Data)}); - auto stream = co_await file.OpenReadAsync(); - content = HttpStreamContent{stream}; - } else if (BodyData::Type::Form == coReqArgs->Body.Type) { - // #9535 - HTTP form data support - } else { - // BodyData::Type::Empty - // TODO: Error => unsupported?? + auto &data = coReqArgs->Data; + if (!data.isNull()) { + if (!data["string"].empty()) { + content = HttpStringContent{to_hstring(data["string"].asString())}; + } else if (!data["base64"].empty()) { + auto buffer = CryptographicBuffer::DecodeFromBase64String(to_hstring(data["base64"].asString())); + content = HttpBufferContent{std::move(buffer)}; + } else if (!data["uri"].empty()) { + auto file = co_await StorageFile::GetFileFromApplicationUriAsync(Uri{to_hstring(data["uri"].asString())}); + auto stream = co_await file.OpenReadAsync(); + content = HttpStreamContent{std::move(stream)}; + } else if (!data["form"].empty()) { + // #9535 - HTTP form data support + // winrt::Windows::Web::Http::HttpMultipartFormDataContent() + } else { + // Assume empty request body. + // content = HttpStringContent{L""}; + } } if (content != nullptr) { diff --git a/vnext/Shared/Networking/WinRTHttpResource.h b/vnext/Shared/Networking/WinRTHttpResource.h index d0be4ba13ad..2bc63912e4d 100644 --- a/vnext/Shared/Networking/WinRTHttpResource.h +++ b/vnext/Shared/Networking/WinRTHttpResource.h @@ -45,13 +45,13 @@ class WinRTHttpResource : public IHttpResource, public std::enable_shared_from_t void SendRequest( std::string &&method, std::string &&url, + int64_t requestId, Headers &&headers, - BodyData &&bodyData, + folly::dynamic &&data, std::string &&responseType, bool useIncrementalUpdates, int64_t timeout, bool withCredentials, - folly::dynamic &&data, std::function &&callback) noexcept override; void AbortRequest(int64_t requestId) noexcept override; void ClearCookies() noexcept override; diff --git a/vnext/Shared/Networking/WinRTTypes.h b/vnext/Shared/Networking/WinRTTypes.h index 3542776ed16..000bd2b9703 100644 --- a/vnext/Shared/Networking/WinRTTypes.h +++ b/vnext/Shared/Networking/WinRTTypes.h @@ -5,6 +5,9 @@ #include "IHttpResource.h" +// Folly +#include + // Windows API #include @@ -16,7 +19,7 @@ namespace Microsoft::React::Networking { struct RequestArgs : public winrt::implements { int64_t RequestId; IHttpResource::Headers Headers; - IHttpResource::BodyData Body; + folly::dynamic Data; bool IncrementalUpdates; bool WithCredentials; bool IsText; diff --git a/vnext/src/Libraries/Network/RCTNetworkingWinShared.js b/vnext/src/Libraries/Network/RCTNetworkingWinShared.js index cc73cb6e534..e60babf60b0 100644 --- a/vnext/src/Libraries/Network/RCTNetworkingWinShared.js +++ b/vnext/src/Libraries/Network/RCTNetworkingWinShared.js @@ -61,6 +61,11 @@ type RCTNetworkingEventDefinitions = $ReadOnly<{ ], }>; +let _requestId = 1; +function generateRequestId(): number { + return _requestId++; +} + const RCTNetworking = { addListener>( eventType: K, @@ -82,11 +87,13 @@ const RCTNetworking = { callback: (requestId: number) => void, withCredentials: boolean, ) { + const requestId = generateRequestId(); const body = convertRequestBody(data); RCTNetworkingNative.sendRequest( { method, url, + requestId, data: {...body, trackingName}, headers, responseType, From 63c5ee6b2ab70de23ab9e80dbc808e245d3f3132 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Wed, 4 May 2022 20:15:46 -0700 Subject: [PATCH 55/91] Use constexpr for folly indexes --- vnext/Shared/Modules/BlobModule.cpp | 77 +++++++++++-------- vnext/Shared/Networking/WinRTHttpResource.cpp | 3 - vnext/Shared/Networking/WinRTHttpResource.h | 2 - 3 files changed, 43 insertions(+), 39 deletions(-) diff --git a/vnext/Shared/Modules/BlobModule.cpp b/vnext/Shared/Modules/BlobModule.cpp index 0c3c25773c5..e3b0889919d 100644 --- a/vnext/Shared/Modules/BlobModule.cpp +++ b/vnext/Shared/Modules/BlobModule.cpp @@ -40,7 +40,12 @@ using winrt::Windows::Security::Cryptography::CryptographicBuffer; namespace { constexpr char moduleName[] = "BlobModule"; -constexpr char blobURIScheme[] = "blob"; +constexpr char blobKey[] = "blob"; +constexpr char blobIdKey[] = "blobId"; +constexpr char offsetKey[] = "offset"; +constexpr char sizeKey[] = "size"; +constexpr char typeKey[] = "type"; +constexpr char dataKey[] = "data"; } // namespace namespace Microsoft::React { @@ -75,7 +80,7 @@ string BlobModule::getName() { } std::map BlobModule::getConstants() { - return {{"BLOB_URI_SCHEME", blobURIScheme}, {"BLOB_URI_HOST", {}}}; + return {{"BLOB_URI_SCHEME", blobKey}, {"BLOB_URI_HOST", {}}}; } vector BlobModule::getMethods() { @@ -119,9 +124,9 @@ vector BlobModule::getMethods() { } auto blob = jsArgAsObject(args, 0); - auto blobId = blob["blobId"].getString(); - auto offset = blob["offset"].getInt(); - auto size = blob["size"].getInt(); + auto blobId = blob[blobIdKey].getString(); + auto offset = blob[offsetKey].getInt(); + auto size = blob[sizeKey].getInt(); auto socketID = jsArgAsInt(args, 1); auto data = persistor->ResolveMessage(std::move(blobId), offset, size); @@ -143,16 +148,16 @@ vector BlobModule::getMethods() { vector buffer{}; for (const auto &part : parts) { - auto type = part["type"].asString(); - if (blobURIScheme == type) { - auto blob = part["data"]; + auto type = part[typeKey].asString(); + if (blobKey == type) { + auto blob = part[dataKey]; auto bufferPart = - persistor->ResolveMessage(blob["blobId"].asString(), blob["offset"].asInt(), blob["size"].asInt()); + persistor->ResolveMessage(blob[blobIdKey].asString(), blob[offsetKey].asInt(), blob[sizeKey].asInt()); buffer.reserve(buffer.size() + bufferPart.size()); buffer.insert(buffer.end(), bufferPart.begin(), bufferPart.end()); } else if ("string" == type) { - auto data = part["data"].asString(); + auto data = part[dataKey].asString(); buffer.reserve(buffer.size() + data.size()); buffer.insert(buffer.end(), data.begin(), data.end()); @@ -230,17 +235,17 @@ BlobWebSocketModuleContentHandler::BlobWebSocketModuleContentHandler(shared_ptr< #pragma region IWebSocketModuleContentHandler void BlobWebSocketModuleContentHandler::ProcessMessage(string &&message, dynamic ¶ms) /*override*/ { - params["data"] = std::move(message); + params[dataKey] = std::move(message); } void BlobWebSocketModuleContentHandler::ProcessMessage(vector &&message, dynamic ¶ms) /*override*/ { auto blob = dynamic::object(); - blob("offset", 0); - blob("size", message.size()); - blob("blobId", m_blobPersistor->StoreMessage(std::move(message))); + blob(offsetKey, 0); + blob(sizeKey, message.size()); + blob(blobIdKey, m_blobPersistor->StoreMessage(std::move(message))); - params["data"] = std::move(blob); - params["type"] = blobURIScheme; + params[dataKey] = std::move(blob); + params[typeKey] = blobKey; } #pragma endregion IWebSocketModuleContentHandler @@ -269,17 +274,17 @@ BlobModuleUriHandler::BlobModuleUriHandler(shared_ptr blobPersis bool BlobModuleUriHandler::Supports(string &uri, string &responseType) /*override*/ { auto uriObj = Uri{winrt::to_hstring(uri)}; - return !(L"http" == uriObj.SchemeName() || L"https" == uriObj.SchemeName()) && blobURIScheme == responseType; + return !(L"http" == uriObj.SchemeName() || L"https" == uriObj.SchemeName()) && blobKey == responseType; } dynamic BlobModuleUriHandler::Fetch(string &uri) /*override*/ { auto data = vector{}; // getBytesFromUri auto blob = dynamic::object(); - blob("offset", 0); - blob("size", data.size()); - blob("type", GetMimeTypeFromUri(uri)); - blob("blobId", m_blobPersistor->StoreMessage(std::move(data))); + blob(offsetKey, 0); + blob(sizeKey, data.size()); + blob(typeKey, GetMimeTypeFromUri(uri)); + blob(blobIdKey, m_blobPersistor->StoreMessage(std::move(data))); // Needed for files blob("name", GetNameFromUri(uri)); @@ -296,7 +301,7 @@ string BlobModuleUriHandler::GetMimeTypeFromUri(string &uri) noexcept { // See // https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/content/ContentResolver.java - return blobURIScheme; + return blobIdKey; } string BlobModuleUriHandler::GetNameFromUri(string &uri) noexcept { @@ -351,24 +356,28 @@ BlobModuleRequestBodyHandler::BlobModuleRequestBodyHandler(shared_ptrResolveMessage(std::move(blobId), blob["offset"].asInt(), blob["size"].asInt()); + auto blob = data[blobKey]; + auto blobId = blob[blobIdKey].asString(); + auto bytes = m_blobPersistor->ResolveMessage(std::move(blobId), blob[offsetKey].asInt(), blob[sizeKey].asInt()); - // TODO: create body from type and bytes - return {}; + auto result = dynamic::object(); + result(typeKey, type); + result(sizeKey, bytes.size()); + result("bytes", dynamic(bytes.cbegin(), bytes.cend())); //TODO: Confirm key for blob payload. + + return result; } #pragma endregion IRequestBodyHandler @@ -383,7 +392,7 @@ BlobModuleResponseHandler::BlobModuleResponseHandler(shared_ptr #pragma region IResponseHandler bool BlobModuleResponseHandler::Supports(std::string &responseType) /*override*/ { - return blobURIScheme == responseType; + return blobKey == responseType; } dynamic BlobModuleResponseHandler::ToResponseData(dynamic &body) /*override*/ { @@ -391,9 +400,9 @@ dynamic BlobModuleResponseHandler::ToResponseData(dynamic &body) /*override*/ { auto bytes = vector{}; auto blob = dynamic::object(); - blob("offset", 0); - blob("size", bytes.size()); - blob("blobId", m_blobPersistor->StoreMessage(std::move(bytes))); + blob(offsetKey, 0); + blob(sizeKey, bytes.size()); + blob(blobIdKey, m_blobPersistor->StoreMessage(std::move(bytes))); return blob; } diff --git a/vnext/Shared/Networking/WinRTHttpResource.cpp b/vnext/Shared/Networking/WinRTHttpResource.cpp index d4b6c2cc751..7516efafe2a 100644 --- a/vnext/Shared/Networking/WinRTHttpResource.cpp +++ b/vnext/Shared/Networking/WinRTHttpResource.cpp @@ -47,9 +47,6 @@ namespace Microsoft::React::Networking { #pragma region WinRTHttpResource -// TODO: Check for multi-thread issues if there are multiple instances. -/*static*/ int64_t WinRTHttpResource::s_lastRequestId = 0; - WinRTHttpResource::WinRTHttpResource(IHttpClient &&client) noexcept : m_client{std::move(client)} {} WinRTHttpResource::WinRTHttpResource() noexcept : WinRTHttpResource(winrt::Windows::Web::Http::HttpClient()) {} diff --git a/vnext/Shared/Networking/WinRTHttpResource.h b/vnext/Shared/Networking/WinRTHttpResource.h index 2bc63912e4d..305adc67a63 100644 --- a/vnext/Shared/Networking/WinRTHttpResource.h +++ b/vnext/Shared/Networking/WinRTHttpResource.h @@ -16,8 +16,6 @@ namespace Microsoft::React::Networking { class WinRTHttpResource : public IHttpResource, public std::enable_shared_from_this { - static int64_t s_lastRequestId; - winrt::Windows::Web::Http::IHttpClient m_client; std::mutex m_mutex; std::unordered_map m_responses; From f3638a9cd23ebde60cbb8b6a117df700e3572427 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Thu, 5 May 2022 18:48:59 -0700 Subject: [PATCH 56/91] Implement GetMimeTypeFromUri --- vnext/Shared/Modules/BlobModule.cpp | 108 +++++++++++++++++++++++++--- 1 file changed, 99 insertions(+), 9 deletions(-) diff --git a/vnext/Shared/Modules/BlobModule.cpp b/vnext/Shared/Modules/BlobModule.cpp index e3b0889919d..10bade70ca2 100644 --- a/vnext/Shared/Modules/BlobModule.cpp +++ b/vnext/Shared/Modules/BlobModule.cpp @@ -17,7 +17,9 @@ #include // Standard Library +#include #include +#include using namespace facebook::xplat; @@ -46,6 +48,82 @@ constexpr char offsetKey[] = "offset"; constexpr char sizeKey[] = "size"; constexpr char typeKey[] = "type"; constexpr char dataKey[] = "data"; + +// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types +const static std::unordered_map extensionToMime = { + {".aac", "audio/aac"}, + {".abw", "application/x-abiword"}, + {".arc", "application/x-freearc"}, + {".avif", "image/avif"}, + {".avi", "video/x-msvideo"}, + {".azw", "application/vnd.amazon.ebook"}, + {".bin", "application/octet-stream"}, + {".bmp", "image/bmp"}, + {".bz", "application/x-bzip"}, + {".bz2", "application/x-bzip2"}, + {".cda", "application/x-cdf"}, + {".csh", "application/x-csh"}, + {".css", "text/css"}, + {".csv", "text/csv"}, + {".doc", "application/msword"}, + {".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"}, + {".eot", "application/vnd.ms-fontobject"}, + {".epub", "application/epub+zip"}, + {".gz", "application/gzip"}, + {".gif", "image/gif"}, + {".htm .html", "text/html"}, + {".ico", "image/vnd.microsoft.icon"}, + {".ics", "text/calendar"}, + {".jar", "application/java-archive"}, + {".jpeg .jpg", "image/jpeg"}, + {".js", "text/javascript"}, + {".json", "application/json"}, + {".jsonld", "application/ld+json"}, + {".mid .midi", "audio/midi audio/x-midi"}, + {".mjs", "text/javascript"}, + {".mp3", "audio/mpeg"}, + {".mp4", "video/mp4"}, + {".mpeg", "video/mpeg"}, + {".mpkg", "application/vnd.apple.installer+xml"}, + {".odp", "application/vnd.oasis.opendocument.presentation"}, + {".ods", "application/vnd.oasis.opendocument.spreadsheet"}, + {".odt", "application/vnd.oasis.opendocument.text"}, + {".oga", "audio/ogg"}, + {".ogv", "video/ogg"}, + {".ogx", "application/ogg"}, + {".opus", "audio/opus"}, + {".otf", "font/otf"}, + {".png", "image/png"}, + {".pdf", "application/pdf"}, + {".php", "application/x-httpd-php"}, + {".ppt", "application/vnd.ms-powerpoint"}, + {".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"}, + {".rar", "application/vnd.rar"}, + {".rtf", "application/rtf"}, + {".sh", "application/x-sh"}, + {".svg", "image/svg+xml"}, + {".swf", "application/x-shockwave-flash"}, + {".tar", "application/x-tar"}, + {".tif .tiff", "image/tiff"}, + {".ts", "video/mp2t"}, + {".ttf", "font/ttf"}, + {".txt", "text/plain"}, + {".vsd", "application/vnd.visio"}, + {".wav", "audio/wav"}, + {".weba", "audio/webm"}, + {".webm", "video/webm"}, + {".webp", "image/webp"}, + {".woff", "font/woff"}, + {".woff2", "font/woff2"}, + {".xhtml", "application/xhtml+xml"}, + {".xls", "application/vnd.ms-excel"}, + {".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}, + {".xml", "application/xml"}, + {".xul", "application/vnd.mozilla.xul+xml"}, + {".zip", "application/zip"}, + {".3gp", "video/3gpp; audio/3gpp if it doesn't contain video"}, + {".3g2", "video/3gpp2; audio/3gpp2 if it doesn't contain video"}, + {".7z", "application/x-7z-compressed"}}; // extensionToMime } // namespace namespace Microsoft::React { @@ -295,13 +373,24 @@ dynamic BlobModuleUriHandler::Fetch(string &uri) /*override*/ { #pragma endregion IUriHandler +/// +/// See +/// https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/content/ContentResolver.java +/// string BlobModuleUriHandler::GetMimeTypeFromUri(string &uri) noexcept { - // TODO: content resolver. - // See https://developer.android.com/reference/android/content/ContentResolver - // See - // https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/content/ContentResolver.java + string result{}; + + auto uriPath = Uri{winrt::to_hstring(uri)}.Path(); + auto lastSegment = GetLastPathSegment(uriPath); + auto path = std::filesystem::path{lastSegment}; + if (path.has_extension()) { + auto entry = extensionToMime.find(path.extension().string()); + if (entry != extensionToMime.cend()) { + result = (*entry).second; + } + } - return blobIdKey; + return result; } string BlobModuleUriHandler::GetNameFromUri(string &uri) noexcept { @@ -340,10 +429,11 @@ string BlobModuleUriHandler::GetLastPathSegment(winrt::hstring &path) noexcept { } int64_t BlobModuleUriHandler::GetLastModifiedFromUri(string &uri) noexcept { - // TODO: Handle StorageFile URIs - // https://stackoverflow.com/questions/31860360 + int64_t result{0}; + + // TODO: Use Windows.Storage, std::filesystem, or alternatives. - return 0; + return result; } #pragma endregion BlobModuleUriHandler @@ -375,7 +465,7 @@ dynamic BlobModuleRequestBodyHandler::ToRequestBody(dynamic &data, string &conte auto result = dynamic::object(); result(typeKey, type); result(sizeKey, bytes.size()); - result("bytes", dynamic(bytes.cbegin(), bytes.cend())); //TODO: Confirm key for blob payload. + result("bytes", dynamic(bytes.cbegin(), bytes.cend())); // TODO: Confirm key for blob payload. return result; } From f2211910eb470f9181ebeda475a6c725017ce555 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Fri, 6 May 2022 22:57:09 -0700 Subject: [PATCH 57/91] Finish BlobModule handler implementations. --- vnext/Shared/Modules/BlobModule.cpp | 47 ++++++++++++++++------ vnext/Shared/Modules/BlobModule.h | 10 ++++- vnext/Shared/Modules/IRequestBodyHandler.h | 23 +++++++++++ vnext/Shared/Modules/IResponseHandler.h | 3 +- vnext/Shared/Modules/IUriHandler.h | 2 +- 5 files changed, 70 insertions(+), 15 deletions(-) diff --git a/vnext/Shared/Modules/BlobModule.cpp b/vnext/Shared/Modules/BlobModule.cpp index 10bade70ca2..c29544f3ca4 100644 --- a/vnext/Shared/Modules/BlobModule.cpp +++ b/vnext/Shared/Modules/BlobModule.cpp @@ -17,7 +17,9 @@ #include // Standard Library +#include #include +#include #include #include @@ -40,6 +42,8 @@ using winrt::Windows::Foundation::IInspectable; using winrt::Windows::Foundation::Uri; using winrt::Windows::Security::Cryptography::CryptographicBuffer; +namespace fs = std::filesystem; + namespace { constexpr char moduleName[] = "BlobModule"; constexpr char blobKey[] = "blob"; @@ -356,7 +360,7 @@ bool BlobModuleUriHandler::Supports(string &uri, string &responseType) /*overrid } dynamic BlobModuleUriHandler::Fetch(string &uri) /*override*/ { - auto data = vector{}; // getBytesFromUri + auto data = GetBytesFromUri(uri); auto blob = dynamic::object(); blob(offsetKey, 0); @@ -373,6 +377,18 @@ dynamic BlobModuleUriHandler::Fetch(string &uri) /*override*/ { #pragma endregion IUriHandler +// TODO: Avoid double allocation (malloc() + vector{}}. +vector BlobModuleUriHandler::GetBytesFromUri(string &uri) { + auto path = fs::weakly_canonical(uri); + auto size = fs::file_size(path); + auto sharedBuff = shared_ptr{malloc(static_cast(size)), free}; + auto inStream = std::ifstream{path, std::ios::binary}; + inStream.read(static_cast(sharedBuff.get()), size); + auto asBytePtr = static_cast(sharedBuff.get()); + + return vector{asBytePtr, asBytePtr + static_cast(size)}; +} + /// /// See /// https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/content/ContentResolver.java @@ -429,9 +445,19 @@ string BlobModuleUriHandler::GetLastPathSegment(winrt::hstring &path) noexcept { } int64_t BlobModuleUriHandler::GetLastModifiedFromUri(string &uri) noexcept { - int64_t result{0}; - - // TODO: Use Windows.Storage, std::filesystem, or alternatives. + int64_t result; + + try { + auto path = fs::weakly_canonical(uri); + auto lastModified = fs::last_write_time(path); + auto timeSince = lastModified.time_since_epoch(); + auto duration = std::chrono::duration_cast(timeSince); + auto count = duration.count(); + + result = static_cast(count); + } catch (const fs::filesystem_error &) { + result = 0; + } return result; } @@ -465,7 +491,7 @@ dynamic BlobModuleRequestBodyHandler::ToRequestBody(dynamic &data, string &conte auto result = dynamic::object(); result(typeKey, type); result(sizeKey, bytes.size()); - result("bytes", dynamic(bytes.cbegin(), bytes.cend())); // TODO: Confirm key for blob payload. + result("bytes", dynamic(bytes.cbegin(), bytes.cend())); return result; } @@ -481,18 +507,15 @@ BlobModuleResponseHandler::BlobModuleResponseHandler(shared_ptr #pragma region IResponseHandler -bool BlobModuleResponseHandler::Supports(std::string &responseType) /*override*/ { +bool BlobModuleResponseHandler::Supports(string &responseType) /*override*/ { return blobKey == responseType; } -dynamic BlobModuleResponseHandler::ToResponseData(dynamic &body) /*override*/ { - // TODO: get bytes from body - auto bytes = vector{}; - +dynamic BlobModuleResponseHandler::ToResponseData(vector &&content) /*override*/ { auto blob = dynamic::object(); blob(offsetKey, 0); - blob(sizeKey, bytes.size()); - blob(blobIdKey, m_blobPersistor->StoreMessage(std::move(bytes))); + blob(sizeKey, content.size()); + blob(blobIdKey, m_blobPersistor->StoreMessage(std::move(content))); return blob; } diff --git a/vnext/Shared/Modules/BlobModule.h b/vnext/Shared/Modules/BlobModule.h index fa7be62ea1e..05efa0083ef 100644 --- a/vnext/Shared/Modules/BlobModule.h +++ b/vnext/Shared/Modules/BlobModule.h @@ -77,12 +77,20 @@ class BlobModuleUriHandler final : public IUriHandler { #pragma endregion IUriHandler + /// + /// May throw std::filesystem::filesystem_error. + /// + std::vector GetBytesFromUri(std::string &uri); + std::string GetMimeTypeFromUri(std::string &uri) noexcept; std::string GetNameFromUri(std::string &uri) noexcept; std::string GetLastPathSegment(winrt::hstring &path) noexcept; + /// + /// Last modified time in miliseconds + /// int64_t GetLastModifiedFromUri(std::string &uri) noexcept; }; @@ -111,7 +119,7 @@ class BlobModuleResponseHandler final : public IResponseHandler { bool Supports(std::string &responseType) override; - folly::dynamic ToResponseData(folly::dynamic &body) override; + folly::dynamic ToResponseData(std::vector &&content) override; #pragma endregion IResponseHandler }; diff --git a/vnext/Shared/Modules/IRequestBodyHandler.h b/vnext/Shared/Modules/IRequestBodyHandler.h index 4b8369e0f98..34fe0d3405c 100644 --- a/vnext/Shared/Modules/IRequestBodyHandler.h +++ b/vnext/Shared/Modules/IRequestBodyHandler.h @@ -18,11 +18,34 @@ struct IRequestBodyHandler { /// /// Returns if the handler should be used for a JS body payload. /// + /// + /// folly object potentially containing a blob reference. + /// "blob" - folly object holding blob metadata. Optional. + /// + /// + /// true - contains a blob reference. + /// false - does not contain a blob reference. + /// virtual bool Supports(folly::dynamic &data) = 0; /// /// Returns the {@link RequestBody} for the JS body payload. /// + /// + /// Incoming folly object containing the blob metadada. + /// Structure: + /// "blob" - folly object info + /// "blobId" - Blob unique identifier + /// "offset" - Start index to read the blob + /// "size" - Amount of bytes to read from the blob + /// + /// + /// folly::dynamic object with the following entries: + /// "type" - Request content type + /// "size" - Amount of bytes + /// "bytes" - Raw body content + /// NOTE: This is an arbitrary key. Pending non-folly structured object to model request body. + /// virtual folly::dynamic ToRequestBody(folly::dynamic &data, std::string &contentType) = 0; }; diff --git a/vnext/Shared/Modules/IResponseHandler.h b/vnext/Shared/Modules/IResponseHandler.h index 7d062653c7b..abbc78f7e60 100644 --- a/vnext/Shared/Modules/IResponseHandler.h +++ b/vnext/Shared/Modules/IResponseHandler.h @@ -5,6 +5,7 @@ // Standard Library #include +#include namespace Microsoft::React { @@ -20,7 +21,7 @@ struct IResponseHandler { /// /// Returns the JS body payload for the {@link ResponseBody}. /// - virtual folly::dynamic ToResponseData(folly::dynamic &body) = 0; + virtual folly::dynamic ToResponseData(std::vector &&content) = 0; }; } // namespace Microsoft::React diff --git a/vnext/Shared/Modules/IUriHandler.h b/vnext/Shared/Modules/IUriHandler.h index 7bb8b44ebe0..84f06bbae3d 100644 --- a/vnext/Shared/Modules/IUriHandler.h +++ b/vnext/Shared/Modules/IUriHandler.h @@ -17,7 +17,7 @@ namespace Microsoft::React { /// struct IUriHandler { /// - /// Returns if the handler should be used for an URI. + /// Returns whether the handler should be used for a given URI. /// virtual bool Supports(std::string &uri, std::string &responseType) = 0; From f7b3172bef4065f4f8d6ffddd43c7fe7918abf86 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Fri, 6 May 2022 23:11:52 -0700 Subject: [PATCH 58/91] Remove unused includes --- vnext/Shared/Modules/BlobModule.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/vnext/Shared/Modules/BlobModule.cpp b/vnext/Shared/Modules/BlobModule.cpp index c29544f3ca4..c0a9c12b659 100644 --- a/vnext/Shared/Modules/BlobModule.cpp +++ b/vnext/Shared/Modules/BlobModule.cpp @@ -9,7 +9,6 @@ #include // React Native -#include #include // Windows API @@ -20,14 +19,11 @@ #include #include #include -#include #include using namespace facebook::xplat; -using facebook::react::Instance; using folly::dynamic; -using std::queue; using std::scoped_lock; using std::shared_ptr; using std::string; From ec7ba12ace10d7c6fbc2233d1f591989a991108b Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Sat, 7 May 2022 00:03:38 -0700 Subject: [PATCH 59/91] Ensure HttpModule::m_resource is set --- vnext/Shared/Modules/HttpModule.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vnext/Shared/Modules/HttpModule.cpp b/vnext/Shared/Modules/HttpModule.cpp index a5f6a9d23a1..d72dd6f8794 100644 --- a/vnext/Shared/Modules/HttpModule.cpp +++ b/vnext/Shared/Modules/HttpModule.cpp @@ -115,7 +115,7 @@ std::vector HttpModule::getMethods() } auto resource = holder->Module->m_resource; - if (resource || (resource = CreateHttpResource(holder->Module->getInstance()))) + if (resource || (resource = holder->Module->m_resource = CreateHttpResource(holder->Module->getInstance()))) { auto params = facebook::xplat::jsArgAsObject(args, 0); IHttpResource::Headers headers; @@ -151,7 +151,7 @@ std::vector HttpModule::getMethods() } auto resource = holder->Module->m_resource; - if (resource || (resource = CreateHttpResource(holder->Module->getInstance()))) + if (resource || (resource = holder->Module->m_resource = CreateHttpResource(holder->Module->getInstance()))) { resource->AbortRequest(facebook::xplat::jsArgAsInt(args, 0)); } @@ -168,7 +168,7 @@ std::vector HttpModule::getMethods() } auto resource = holder->Module->m_resource; - if (resource || (resource = CreateHttpResource(holder->Module->getInstance()))) + if (resource || (resource = holder->Module->m_resource = CreateHttpResource(holder->Module->getInstance()))) { resource->ClearCookies(); } From 5d804489f320191a1ffdeb4d64b12b489b540b89 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Sat, 7 May 2022 00:44:20 -0700 Subject: [PATCH 60/91] clang format --- vnext/Shared/Modules/CxxModuleUtilities.cpp | 19 +++++++++++++++++++ vnext/Shared/Modules/CxxModuleUtilities.h | 20 ++++++++++++++++++++ vnext/Shared/Modules/HttpModule.cpp | 8 ++------ vnext/Shared/Modules/WebSocketModule.cpp | 8 ++------ vnext/Shared/Shared.vcxitems | 2 ++ vnext/Shared/Shared.vcxitems.filters | 6 ++++++ 6 files changed, 51 insertions(+), 12 deletions(-) create mode 100644 vnext/Shared/Modules/CxxModuleUtilities.cpp create mode 100644 vnext/Shared/Modules/CxxModuleUtilities.h diff --git a/vnext/Shared/Modules/CxxModuleUtilities.cpp b/vnext/Shared/Modules/CxxModuleUtilities.cpp new file mode 100644 index 00000000000..34f03456493 --- /dev/null +++ b/vnext/Shared/Modules/CxxModuleUtilities.cpp @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "CxxModuleUtilities.h" + +using facebook::react::Instance; +using folly::dynamic; +using std::string; +using std::weak_ptr; + +namespace Microsoft::React::Modules { + +void SendEvent(weak_ptr weakReactInstance, string &&eventName, dynamic &&args) { + if (auto instance = weakReactInstance.lock()) { + instance->callJSFunction("RCTDeviceEventEmitter", "emit", dynamic::array(std::move(eventName), std::move(args))); + } +} + +} // namespace diff --git a/vnext/Shared/Modules/CxxModuleUtilities.h b/vnext/Shared/Modules/CxxModuleUtilities.h new file mode 100644 index 00000000000..fc7c875f276 --- /dev/null +++ b/vnext/Shared/Modules/CxxModuleUtilities.h @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#pragma once + +// Folly +#include + +// React Native +#include + +// Standard Library +#include +#include + +namespace Microsoft::React::Modules { + +void SendEvent(std::weak_ptr weakReactInstance, std::string &&eventName, folly::dynamic &&args); + +} // namespace diff --git a/vnext/Shared/Modules/HttpModule.cpp b/vnext/Shared/Modules/HttpModule.cpp index d72dd6f8794..69a406cb766 100644 --- a/vnext/Shared/Modules/HttpModule.cpp +++ b/vnext/Shared/Modules/HttpModule.cpp @@ -5,6 +5,7 @@ #include "HttpModule.h" +#include #include // React Native @@ -24,16 +25,11 @@ using winrt::Windows::Foundation::IInspectable; namespace { +using Microsoft::React::Modules::SendEvent; using Microsoft::React::Networking::IHttpResource; constexpr char moduleName[] = "Networking"; -static void SendEvent(weak_ptr weakReactInstance, string &&eventName, dynamic &&args) { - if (auto instance = weakReactInstance.lock()) { - instance->callJSFunction("RCTDeviceEventEmitter", "emit", dynamic::array(std::move(eventName), std::move(args))); - } -} - static shared_ptr CreateHttpResource(weak_ptr weakReactInstance) { auto resource = IHttpResource::Make(); diff --git a/vnext/Shared/Modules/WebSocketModule.cpp b/vnext/Shared/Modules/WebSocketModule.cpp index 87b2475a136..8bc02781ec2 100644 --- a/vnext/Shared/Modules/WebSocketModule.cpp +++ b/vnext/Shared/Modules/WebSocketModule.cpp @@ -5,6 +5,7 @@ #include +#include #include #include #include "Unicode.h" @@ -41,16 +42,11 @@ using winrt::Windows::Security::Cryptography::CryptographicBuffer; namespace { using Microsoft::React::IWebSocketModuleProxy; using Microsoft::React::WebSocketModule; +using Microsoft::React::Modules::SendEvent; using Microsoft::React::Networking::IWebSocketResource; constexpr char moduleName[] = "WebSocketModule"; -static void SendEvent(weak_ptr weakInstance, string &&eventName, dynamic &&args) { - if (auto instance = weakInstance.lock()) { - instance->callJSFunction("RCTDeviceEventEmitter", "emit", dynamic::array(std::move(eventName), std::move(args))); - } -} - static shared_ptr GetOrCreateWebSocket(int64_t id, string &&url, weak_ptr weakState) { auto state = weakState.lock(); diff --git a/vnext/Shared/Shared.vcxitems b/vnext/Shared/Shared.vcxitems index 1ff8ce29f37..7c250862f1c 100644 --- a/vnext/Shared/Shared.vcxitems +++ b/vnext/Shared/Shared.vcxitems @@ -45,6 +45,7 @@ true + @@ -89,6 +90,7 @@ + diff --git a/vnext/Shared/Shared.vcxitems.filters b/vnext/Shared/Shared.vcxitems.filters index f4e6afab308..6d524ab27cf 100644 --- a/vnext/Shared/Shared.vcxitems.filters +++ b/vnext/Shared/Shared.vcxitems.filters @@ -146,6 +146,9 @@ Source Files\Networking + + Source Files\Modules + @@ -445,6 +448,9 @@ Header Files\Modules + + Header Files\Modules + From e2dcd66c63c8f18e2aaa0188fee250936fc7c36b Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Sat, 7 May 2022 00:59:58 -0700 Subject: [PATCH 61/91] clang format --- vnext/Shared/Modules/CxxModuleUtilities.cpp | 2 +- vnext/Shared/Modules/CxxModuleUtilities.h | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/vnext/Shared/Modules/CxxModuleUtilities.cpp b/vnext/Shared/Modules/CxxModuleUtilities.cpp index 34f03456493..45c48dc7405 100644 --- a/vnext/Shared/Modules/CxxModuleUtilities.cpp +++ b/vnext/Shared/Modules/CxxModuleUtilities.cpp @@ -16,4 +16,4 @@ void SendEvent(weak_ptr weakReactInstance, string &&eventName, dynamic } } -} // namespace +} // namespace Microsoft::React::Modules diff --git a/vnext/Shared/Modules/CxxModuleUtilities.h b/vnext/Shared/Modules/CxxModuleUtilities.h index fc7c875f276..00c869a953e 100644 --- a/vnext/Shared/Modules/CxxModuleUtilities.h +++ b/vnext/Shared/Modules/CxxModuleUtilities.h @@ -15,6 +15,9 @@ namespace Microsoft::React::Modules { -void SendEvent(std::weak_ptr weakReactInstance, std::string &&eventName, folly::dynamic &&args); +void SendEvent( + std::weak_ptr weakReactInstance, + std::string &&eventName, + folly::dynamic &&args); -} // namespace +} // namespace Microsoft::React::Modules From 3f3608136a4b1bb7b49371d5c15a07960d87aa2f Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Sat, 7 May 2022 19:05:46 -0700 Subject: [PATCH 62/91] Allow blob responseType --- vnext/Shared/Networking/WinRTHttpResource.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vnext/Shared/Networking/WinRTHttpResource.cpp b/vnext/Shared/Networking/WinRTHttpResource.cpp index 7516efafe2a..0163fb91b9e 100644 --- a/vnext/Shared/Networking/WinRTHttpResource.cpp +++ b/vnext/Shared/Networking/WinRTHttpResource.cpp @@ -65,7 +65,7 @@ void WinRTHttpResource::SendRequest( bool withCredentials, std::function &&callback) noexcept /*override*/ { // Enforce supported args - assert(responseType == "text" || responseType == "base64"); + assert(responseType == "text" || responseType == "base64" | responseType == "blob"); if (callback) { callback(requestId); From e84c398e79170398f9710a81cf99745db510bb29 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Sat, 7 May 2022 19:10:13 -0700 Subject: [PATCH 63/91] Use winrt::to_hstring instead of Utf8ToUtf16 --- vnext/Shared/Modules/WebSocketModule.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/vnext/Shared/Modules/WebSocketModule.cpp b/vnext/Shared/Modules/WebSocketModule.cpp index 8bc02781ec2..e9951e04a85 100644 --- a/vnext/Shared/Modules/WebSocketModule.cpp +++ b/vnext/Shared/Modules/WebSocketModule.cpp @@ -8,7 +8,6 @@ #include #include #include -#include "Unicode.h" // React Native #include @@ -25,8 +24,6 @@ using namespace facebook::xplat; using facebook::react::Instance; using folly::dynamic; -using Microsoft::Common::Unicode::Utf8ToUtf16; - using std::shared_ptr; using std::string; using std::weak_ptr; @@ -118,7 +115,7 @@ GetOrCreateWebSocket(int64_t id, string &&url, weak_ptr arr; CryptographicBuffer::CopyToByteArray(buffer, arr); auto data = std::vector(arr.begin(), arr.end()); @@ -214,7 +211,7 @@ std::vector WebSocketModule::getMeth const auto& headersDynamic = optionsDynamic["headers"]; for (const auto& header : headersDynamic.items()) { - options.emplace(Utf8ToUtf16(header.first.getString()), header.second.getString()); + options.emplace(winrt::to_hstring(header.first.getString()), header.second.getString()); } } From c991d16155f23366117e2d56d4aa07e1d4270212 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Sun, 8 May 2022 00:11:52 -0700 Subject: [PATCH 64/91] Pass inspectableProperties down to WinRTHttpResource --- vnext/Shared/Modules/HttpModule.cpp | 16 +++++++------ vnext/Shared/Networking/IHttpResource.h | 5 ++++ vnext/Shared/Networking/WinRTHttpResource.cpp | 24 +++++++++++++++++-- vnext/Shared/Networking/WinRTHttpResource.h | 5 ++++ 4 files changed, 41 insertions(+), 9 deletions(-) diff --git a/vnext/Shared/Modules/HttpModule.cpp b/vnext/Shared/Modules/HttpModule.cpp index 69a406cb766..73b73d3fa47 100644 --- a/vnext/Shared/Modules/HttpModule.cpp +++ b/vnext/Shared/Modules/HttpModule.cpp @@ -30,8 +30,10 @@ using Microsoft::React::Networking::IHttpResource; constexpr char moduleName[] = "Networking"; -static shared_ptr CreateHttpResource(weak_ptr weakReactInstance) { - auto resource = IHttpResource::Make(); +static shared_ptr CreateHttpResource( + weak_ptr weakReactInstance, + IInspectable &inspectableProperties) { + auto resource = IHttpResource::Make(inspectableProperties); resource->SetOnResponse([weakReactInstance](int64_t requestId, IHttpResource::Response &&response) { dynamic headers = dynamic::object(); @@ -45,7 +47,7 @@ static shared_ptr CreateHttpResource(weak_ptr weakReact SendEvent(weakReactInstance, "didReceiveNetworkResponse", std::move(args)); }); - resource->SetOnData([weakReactInstance](int64_t requestId, std::string &&responseData) { + resource->SetOnData([weakReactInstance](int64_t requestId, string &&responseData) { dynamic args = dynamic::array(requestId, std::move(responseData)); SendEvent(weakReactInstance, "didReceiveNetworkData", std::move(args)); @@ -68,7 +70,7 @@ static shared_ptr CreateHttpResource(weak_ptr weakReact namespace Microsoft::React { -HttpModule::HttpModule(winrt::Windows::Foundation::IInspectable const &inspectableProperties) noexcept +HttpModule::HttpModule(IInspectable const &inspectableProperties) noexcept : m_holder{std::make_shared()}, m_inspectableProperties{inspectableProperties} { m_holder->Module = this; @@ -111,7 +113,7 @@ std::vector HttpModule::getMethods() } auto resource = holder->Module->m_resource; - if (resource || (resource = holder->Module->m_resource = CreateHttpResource(holder->Module->getInstance()))) + if (resource || (resource = holder->Module->m_resource = CreateHttpResource(holder->Module->getInstance(), holder->Module->m_inspectableProperties))) { auto params = facebook::xplat::jsArgAsObject(args, 0); IHttpResource::Headers headers; @@ -147,7 +149,7 @@ std::vector HttpModule::getMethods() } auto resource = holder->Module->m_resource; - if (resource || (resource = holder->Module->m_resource = CreateHttpResource(holder->Module->getInstance()))) + if (resource || (resource = holder->Module->m_resource = CreateHttpResource(holder->Module->getInstance(), holder->Module->m_inspectableProperties))) { resource->AbortRequest(facebook::xplat::jsArgAsInt(args, 0)); } @@ -164,7 +166,7 @@ std::vector HttpModule::getMethods() } auto resource = holder->Module->m_resource; - if (resource || (resource = holder->Module->m_resource = CreateHttpResource(holder->Module->getInstance()))) + if (resource || (resource = holder->Module->m_resource = CreateHttpResource(holder->Module->getInstance(), holder->Module->m_inspectableProperties))) { resource->ClearCookies(); } diff --git a/vnext/Shared/Networking/IHttpResource.h b/vnext/Shared/Networking/IHttpResource.h index 6b96530a698..40e1efacf91 100644 --- a/vnext/Shared/Networking/IHttpResource.h +++ b/vnext/Shared/Networking/IHttpResource.h @@ -6,6 +6,9 @@ // Folly #include +// Windows API +#include + // Standard Library #include #include @@ -31,6 +34,8 @@ struct IHttpResource { static std::shared_ptr Make() noexcept; + static std::shared_ptr Make(winrt::Windows::Foundation::IInspectable &inspectableProperties) noexcept; + virtual ~IHttpResource() noexcept {} /// diff --git a/vnext/Shared/Networking/WinRTHttpResource.cpp b/vnext/Shared/Networking/WinRTHttpResource.cpp index 0163fb91b9e..b0ba8213ceb 100644 --- a/vnext/Shared/Networking/WinRTHttpResource.cpp +++ b/vnext/Shared/Networking/WinRTHttpResource.cpp @@ -47,9 +47,13 @@ namespace Microsoft::React::Networking { #pragma region WinRTHttpResource -WinRTHttpResource::WinRTHttpResource(IHttpClient &&client) noexcept : m_client{std::move(client)} {} +WinRTHttpResource::WinRTHttpResource(IHttpClient &&client, IInspectable inspectableProperties) noexcept + : m_client{std::move(client)}, m_inspectableProperties{inspectableProperties} {} -WinRTHttpResource::WinRTHttpResource() noexcept : WinRTHttpResource(winrt::Windows::Web::Http::HttpClient()) {} +WinRTHttpResource::WinRTHttpResource(IHttpClient &&client) noexcept + : WinRTHttpResource(std::move(client), IInspectable{nullptr}) {} + +WinRTHttpResource::WinRTHttpResource() noexcept : WinRTHttpResource(winrt::Windows::Web::Http::HttpClient{}) {} #pragma region IHttpResource @@ -331,6 +335,22 @@ fire_and_forget WinRTHttpResource::PerformSendRequest(HttpRequestMessage &&reque #pragma region IHttpResource +/*static*/ shared_ptr IHttpResource::Make( + winrt::Windows::Foundation::IInspectable &inspectableProperties) noexcept { + using winrt::Windows::Web::Http::HttpClient; + + if (static_cast(GetRuntimeOptionInt("Http.OriginPolicy")) == OriginPolicy::None) { + return std::make_shared(HttpClient{}, inspectableProperties); + } else { + auto globalOrigin = GetRuntimeOptionString("Http.GlobalOrigin"); + OriginPolicyHttpFilter::SetStaticOrigin(std::move(globalOrigin)); + auto opFilter = winrt::make(); + auto client = HttpClient{opFilter}; + + return std::make_shared(std::move(client), inspectableProperties); + } +} + /*static*/ shared_ptr IHttpResource::Make() noexcept { if (static_cast(GetRuntimeOptionInt("Http.OriginPolicy")) == OriginPolicy::None) { return std::make_shared(); diff --git a/vnext/Shared/Networking/WinRTHttpResource.h b/vnext/Shared/Networking/WinRTHttpResource.h index 305adc67a63..b17b787e0cb 100644 --- a/vnext/Shared/Networking/WinRTHttpResource.h +++ b/vnext/Shared/Networking/WinRTHttpResource.h @@ -19,6 +19,7 @@ class WinRTHttpResource : public IHttpResource, public std::enable_shared_from_t winrt::Windows::Web::Http::IHttpClient m_client; std::mutex m_mutex; std::unordered_map m_responses; + winrt::Windows::Foundation::IInspectable m_inspectableProperties; std::function m_onRequest; std::function m_onResponse; @@ -38,6 +39,10 @@ class WinRTHttpResource : public IHttpResource, public std::enable_shared_from_t WinRTHttpResource(winrt::Windows::Web::Http::IHttpClient &&client) noexcept; + WinRTHttpResource( + winrt::Windows::Web::Http::IHttpClient &&client, + winrt::Windows::Foundation::IInspectable inspectableProperties) noexcept; + #pragma region IHttpResource void SendRequest( From 05d7f3f3a06292d8a4ee42ed47c98521fb965241 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Sun, 8 May 2022 00:47:25 -0700 Subject: [PATCH 65/91] Implement IHttpModuleProxy via WinRTHttpResource --- vnext/Shared/Modules/HttpModule.cpp | 5 -- vnext/Shared/Modules/HttpModule.h | 17 ------ vnext/Shared/Networking/WinRTHttpResource.cpp | 56 +++++++++++++------ vnext/Shared/Networking/WinRTHttpResource.h | 29 +++++++--- 4 files changed, 60 insertions(+), 47 deletions(-) diff --git a/vnext/Shared/Modules/HttpModule.cpp b/vnext/Shared/Modules/HttpModule.cpp index 73b73d3fa47..af6a8e63bf6 100644 --- a/vnext/Shared/Modules/HttpModule.cpp +++ b/vnext/Shared/Modules/HttpModule.cpp @@ -73,11 +73,6 @@ namespace Microsoft::React { HttpModule::HttpModule(IInspectable const &inspectableProperties) noexcept : m_holder{std::make_shared()}, m_inspectableProperties{inspectableProperties} { m_holder->Module = this; - - auto propId = ReactPropertyId>>{L"HttpModule.Proxy"}; - auto propBag = ReactPropertyBag{m_inspectableProperties.try_as()}; - auto contentHandler = weak_ptr{m_proxy}; - propBag.Set(propId, std::move(contentHandler)); } HttpModule::~HttpModule() noexcept /*override*/ { diff --git a/vnext/Shared/Modules/HttpModule.h b/vnext/Shared/Modules/HttpModule.h index 91bc394a897..6df762f7771 100644 --- a/vnext/Shared/Modules/HttpModule.h +++ b/vnext/Shared/Modules/HttpModule.h @@ -15,18 +15,6 @@ namespace Microsoft::React { -class HttpModuleProxy final : public IHttpModuleProxy { -#pragma region IHttpModuleProxy - - void AddUriHandler(std::shared_ptr uriHandler) noexcept override; - - void AddRequestBodyHandler(std::shared_ptr requestBodyHandler) noexcept override; - - void AddResponseHandler(std::shared_ptr responseHandler) noexcept override; - -#pragma endregion IHttpModuleProxy -}; - /// /// Realizes NativeModules projection. /// See src\Libraries\Network\RCTNetworkingWinShared.js @@ -67,11 +55,6 @@ class HttpModule : public facebook::xplat::module::CxxModule { std::shared_ptr m_resource; std::shared_ptr m_holder; - /// - /// Exposes a subset of the module's methods. - /// - std::shared_ptr m_proxy; - // Property bag high level reference. winrt::Windows::Foundation::IInspectable m_inspectableProperties; }; diff --git a/vnext/Shared/Networking/WinRTHttpResource.cpp b/vnext/Shared/Networking/WinRTHttpResource.cpp index b0ba8213ceb..a666ab9dec2 100644 --- a/vnext/Shared/Networking/WinRTHttpResource.cpp +++ b/vnext/Shared/Networking/WinRTHttpResource.cpp @@ -4,6 +4,7 @@ #include "WinRTHttpResource.h" #include +#include #include #include #include @@ -23,6 +24,7 @@ using std::function; using std::scoped_lock; using std::shared_ptr; using std::string; +using std::weak_ptr; using winrt::fire_and_forget; using winrt::hresult_error; @@ -47,11 +49,7 @@ namespace Microsoft::React::Networking { #pragma region WinRTHttpResource -WinRTHttpResource::WinRTHttpResource(IHttpClient &&client, IInspectable inspectableProperties) noexcept - : m_client{std::move(client)}, m_inspectableProperties{inspectableProperties} {} - -WinRTHttpResource::WinRTHttpResource(IHttpClient &&client) noexcept - : WinRTHttpResource(std::move(client), IInspectable{nullptr}) {} +WinRTHttpResource::WinRTHttpResource(IHttpClient &&client) noexcept : m_client{std::move(client)} {} WinRTHttpResource::WinRTHttpResource() noexcept : WinRTHttpResource(winrt::Windows::Web::Http::HttpClient{}) {} @@ -329,39 +327,63 @@ fire_and_forget WinRTHttpResource::PerformSendRequest(HttpRequestMessage &&reque } self->UntrackResponse(coReqArgs->RequestId); +} // PerformSendRequest + +#pragma region IHttpModuleProxy + +void WinRTHttpResource::AddUriHandler(shared_ptr uriHandler) noexcept /*override*/ +{ + m_uriHandler = weak_ptr(uriHandler); +} + +void WinRTHttpResource::AddRequestBodyHandler(shared_ptr requestBodyHandler) noexcept /*override*/ +{ + m_requestBodyHandler = weak_ptr(requestBodyHandler); } +void WinRTHttpResource::AddResponseHandler(shared_ptr responseHandler) noexcept /*override*/ +{ + m_responseHandler = weak_ptr(responseHandler); +} + +#pragma endregion IHttpModuleProxy + #pragma endregion WinRTHttpResource #pragma region IHttpResource /*static*/ shared_ptr IHttpResource::Make( winrt::Windows::Foundation::IInspectable &inspectableProperties) noexcept { + using namespace winrt::Microsoft::ReactNative; using winrt::Windows::Web::Http::HttpClient; + shared_ptr result; + if (static_cast(GetRuntimeOptionInt("Http.OriginPolicy")) == OriginPolicy::None) { - return std::make_shared(HttpClient{}, inspectableProperties); + result = std::make_shared(); } else { auto globalOrigin = GetRuntimeOptionString("Http.GlobalOrigin"); OriginPolicyHttpFilter::SetStaticOrigin(std::move(globalOrigin)); auto opFilter = winrt::make(); auto client = HttpClient{opFilter}; - return std::make_shared(std::move(client), inspectableProperties); + result = std::make_shared(std::move(client)); } + + // Register resource as HTTP module proxy. + if (inspectableProperties) { + auto propId = ReactPropertyId>>{L"HttpModule.Proxy"}; + auto propBag = ReactPropertyBag{inspectableProperties.try_as()}; + auto moduleProxy = weak_ptr{result}; + propBag.Set(propId, std::move(moduleProxy)); + } + + return result; } /*static*/ shared_ptr IHttpResource::Make() noexcept { - if (static_cast(GetRuntimeOptionInt("Http.OriginPolicy")) == OriginPolicy::None) { - return std::make_shared(); - } else { - auto globalOrigin = GetRuntimeOptionString("Http.GlobalOrigin"); - OriginPolicyHttpFilter::SetStaticOrigin(std::move(globalOrigin)); - auto opFilter = winrt::make(); - auto client = winrt::Windows::Web::Http::HttpClient{opFilter}; - - return std::make_shared(std::move(client)); - } + auto inspectableProperties = IInspectable{nullptr}; + return Make(inspectableProperties); } #pragma endregion IHttpResource diff --git a/vnext/Shared/Networking/WinRTHttpResource.h b/vnext/Shared/Networking/WinRTHttpResource.h index b17b787e0cb..208fda837ec 100644 --- a/vnext/Shared/Networking/WinRTHttpResource.h +++ b/vnext/Shared/Networking/WinRTHttpResource.h @@ -5,6 +5,7 @@ #include "IHttpResource.h" +#include #include "WinRTTypes.h" // Windows API @@ -15,17 +16,23 @@ namespace Microsoft::React::Networking { -class WinRTHttpResource : public IHttpResource, public std::enable_shared_from_this { +class WinRTHttpResource : public IHttpResource, + public IHttpModuleProxy, + public std::enable_shared_from_this { winrt::Windows::Web::Http::IHttpClient m_client; std::mutex m_mutex; std::unordered_map m_responses; - winrt::Windows::Foundation::IInspectable m_inspectableProperties; std::function m_onRequest; std::function m_onResponse; std::function m_onData; std::function m_onError; + // Used for IHttpModuleProxy + std::weak_ptr m_uriHandler; + std::weak_ptr m_requestBodyHandler; + std::weak_ptr m_responseHandler; + void TrackResponse(int64_t requestId, ResponseOperation response) noexcept; void UntrackResponse(int64_t requestId) noexcept; @@ -39,10 +46,6 @@ class WinRTHttpResource : public IHttpResource, public std::enable_shared_from_t WinRTHttpResource(winrt::Windows::Web::Http::IHttpClient &&client) noexcept; - WinRTHttpResource( - winrt::Windows::Web::Http::IHttpClient &&client, - winrt::Windows::Foundation::IInspectable inspectableProperties) noexcept; - #pragma region IHttpResource void SendRequest( @@ -59,13 +62,23 @@ class WinRTHttpResource : public IHttpResource, public std::enable_shared_from_t void AbortRequest(int64_t requestId) noexcept override; void ClearCookies() noexcept override; -#pragma endregion IHttpResource - void SetOnRequest(std::function &&handler) noexcept override; void SetOnResponse(std::function &&handler) noexcept override; void SetOnData(std::function &&handler) noexcept override; void SetOnError(std::function &&handler) noexcept override; + +#pragma endregion IHttpResource + +#pragma region IHttpModuleProxy + + void AddUriHandler(std::shared_ptr uriHandler) noexcept override; + + void AddRequestBodyHandler(std::shared_ptr requestBodyHandler) noexcept override; + + void AddResponseHandler(std::shared_ptr responseHandler) noexcept override; + +#pragma endregion IHttpModuleProxy }; } // namespace Microsoft::React::Networking From a9d1f4201ba81923988d16fe869cb87ba32ab86c Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Sun, 8 May 2022 02:04:50 -0700 Subject: [PATCH 66/91] Consume URI handler - IHttpResource - Rename SetOnRequest to SetOnRequestSuccess - Declare SetOnBlobData to pass complex (non-string) response data --- vnext/Shared/Modules/HttpModule.cpp | 26 ++++++++++---- vnext/Shared/Modules/IUriHandler.h | 7 ++++ vnext/Shared/Networking/IHttpResource.h | 4 ++- vnext/Shared/Networking/WinRTHttpResource.cpp | 35 ++++++++++++++++--- vnext/Shared/Networking/WinRTHttpResource.h | 6 ++-- vnext/Shared/Networking/WinRTTypes.h | 2 +- 6 files changed, 65 insertions(+), 15 deletions(-) diff --git a/vnext/Shared/Modules/HttpModule.cpp b/vnext/Shared/Modules/HttpModule.cpp index af6a8e63bf6..14173585e05 100644 --- a/vnext/Shared/Modules/HttpModule.cpp +++ b/vnext/Shared/Modules/HttpModule.cpp @@ -30,11 +30,23 @@ using Microsoft::React::Networking::IHttpResource; constexpr char moduleName[] = "Networking"; +// React event names +constexpr char completedResponse[] = "didCompleteNetworkResponse"; +constexpr char receivedResponse[] = "didReceiveNetworkResponse"; +constexpr char receivedData[] = "didReceiveNetworkData"; +constexpr char receivedDataProgress[] = "didReceiveNetworkDataProgress"; + static shared_ptr CreateHttpResource( weak_ptr weakReactInstance, IInspectable &inspectableProperties) { auto resource = IHttpResource::Make(inspectableProperties); + resource->SetOnRequestSuccess([weakReactInstance](int64_t requestId) { + auto args = dynamic::array(requestId); + + SendEvent(weakReactInstance, completedResponse, std::move(args)); + }); + resource->SetOnResponse([weakReactInstance](int64_t requestId, IHttpResource::Response &&response) { dynamic headers = dynamic::object(); for (auto &header : response.Headers) { @@ -44,23 +56,25 @@ static shared_ptr CreateHttpResource( // TODO: Test response content. dynamic args = dynamic::array(requestId, response.StatusCode, headers, response.Url); - SendEvent(weakReactInstance, "didReceiveNetworkResponse", std::move(args)); + SendEvent(weakReactInstance, receivedResponse, std::move(args)); }); resource->SetOnData([weakReactInstance](int64_t requestId, string &&responseData) { - dynamic args = dynamic::array(requestId, std::move(responseData)); - - SendEvent(weakReactInstance, "didReceiveNetworkData", std::move(args)); + SendEvent(weakReactInstance, receivedData, dynamic::array(requestId, std::move(responseData))); // TODO: Move into separate method IF not executed right after onData() - SendEvent(weakReactInstance, "didCompleteNetworkResponse", dynamic::array(requestId)); + SendEvent(weakReactInstance, completedResponse, dynamic::array(requestId)); + }); + + resource->SetOnBlobData([weakReactInstance](int64_t requestId, dynamic &&responseData) { + SendEvent(weakReactInstance, receivedData, dynamic::array(requestId, std::move(responseData))); }); resource->SetOnError([weakReactInstance](int64_t requestId, string &&message) { dynamic args = dynamic::array(requestId, std::move(message)); // TODO: isTimeout errorArgs.push_back(true); - SendEvent(weakReactInstance, "didCompleteNetworkResponse", std::move(args)); + SendEvent(weakReactInstance, completedResponse, std::move(args)); }); return resource; diff --git a/vnext/Shared/Modules/IUriHandler.h b/vnext/Shared/Modules/IUriHandler.h index 84f06bbae3d..fa96c251cef 100644 --- a/vnext/Shared/Modules/IUriHandler.h +++ b/vnext/Shared/Modules/IUriHandler.h @@ -24,6 +24,13 @@ struct IUriHandler { /// /// Fetch the URI and return the JS body payload. /// + /// + /// Blob representation in a dynamic object with the folliwing structure: + /// "blobId" - Blob unique identifier + /// "offset" - Blob segment starting offset + /// "size" - Number of bytes fetched from blob + /// "name" - File name obtained from the URI + /// "lastModified - Last write to local file in milliseconds virtual folly::dynamic Fetch(std::string &uri) = 0; }; diff --git a/vnext/Shared/Networking/IHttpResource.h b/vnext/Shared/Networking/IHttpResource.h index 40e1efacf91..c5a1498d6ce 100644 --- a/vnext/Shared/Networking/IHttpResource.h +++ b/vnext/Shared/Networking/IHttpResource.h @@ -89,9 +89,11 @@ struct IHttpResource { virtual void ClearCookies() noexcept = 0; - virtual void SetOnRequest(std::function &&handler) noexcept = 0; + virtual void SetOnRequestSuccess(std::function &&handler) noexcept = 0; virtual void SetOnResponse(std::function &&handler) noexcept = 0; virtual void SetOnData(std::function &&handler) noexcept = 0; + virtual void SetOnBlobData( + std::function &&handler) noexcept = 0; virtual void SetOnError( std::function &&handler) noexcept = 0; }; diff --git a/vnext/Shared/Networking/WinRTHttpResource.cpp b/vnext/Shared/Networking/WinRTHttpResource.cpp index a666ab9dec2..3e81941b3c6 100644 --- a/vnext/Shared/Networking/WinRTHttpResource.cpp +++ b/vnext/Shared/Networking/WinRTHttpResource.cpp @@ -85,7 +85,7 @@ void WinRTHttpResource::SendRequest( concreteArgs->Data = std::move(data); concreteArgs->IncrementalUpdates = useIncrementalUpdates; concreteArgs->WithCredentials = withCredentials; - concreteArgs->IsText = responseType == "text"; + concreteArgs->ResponseType = std::move(responseType); concreteArgs->Timeout = timeout; PerformSendRequest(std::move(request), args); @@ -126,8 +126,8 @@ void WinRTHttpResource::ClearCookies() noexcept /*override*/ { // NOT IMPLEMENTED } -void WinRTHttpResource::SetOnRequest(function &&handler) noexcept /*override*/ { - m_onRequest = std::move(handler); +void WinRTHttpResource::SetOnRequestSuccess(function &&handler) noexcept /*override*/ { + m_onRequestSuccess = std::move(handler); } void WinRTHttpResource::SetOnResponse(function &&handler) noexcept @@ -140,6 +140,12 @@ void WinRTHttpResource::SetOnData(function &&handler) noexcept +/*override*/ +{ + m_onBlobData = std::move(handler); +} + void WinRTHttpResource::SetOnError(function &&handler) noexcept /*override*/ { m_onError = std::move(handler); @@ -167,6 +173,24 @@ fire_and_forget WinRTHttpResource::PerformSendRequest(HttpRequestMessage &&reque // Ensure background thread co_await winrt::resume_background(); + // If URI handler is available, it takes over request processing. + if (auto uriHandler = self->m_uriHandler.lock()) { + auto uri = winrt::to_string(coRequest.RequestUri().ToString()); + try { + if (uriHandler->Supports(uri, coReqArgs->ResponseType)) { + auto blob = uriHandler->Fetch(uri); + if (self->m_onBlobData && self->m_onRequestSuccess) + co_return self->m_onBlobData(coReqArgs->RequestId, std::move(blob)); + } + } catch (const hresult_error &e) { + if (self->m_onError) + co_return self->m_onError(coReqArgs->RequestId, Utilities::HResultToString(e)); + } catch (const std::exception &e) { + if (self->m_onError) + co_return self->m_onError(coReqArgs->RequestId, e.what()); + } + } + HttpMediaTypeHeaderValue contentType{nullptr}; string contentEncoding; string contentLength; @@ -282,7 +306,8 @@ fire_and_forget WinRTHttpResource::PerformSendRequest(HttpRequestMessage &&reque auto inputStream = co_await response.Content().ReadAsInputStreamAsync(); auto reader = DataReader{inputStream}; - if (coReqArgs->IsText) { + auto isText = coReqArgs->ResponseType == "text"; + if (isText) { reader.UnicodeEncoding(UnicodeEncoding::Utf8); } @@ -290,7 +315,7 @@ fire_and_forget WinRTHttpResource::PerformSendRequest(HttpRequestMessage &&reque co_await reader.LoadAsync(10 * 1024 * 1024); auto length = reader.UnconsumedBufferLength(); - if (coReqArgs->IsText) { + if (isText) { std::vector data(length); reader.ReadBytes(data); string responseData = string(Common::Utilities::CheckedReinterpretCast(data.data()), data.size()); diff --git a/vnext/Shared/Networking/WinRTHttpResource.h b/vnext/Shared/Networking/WinRTHttpResource.h index 208fda837ec..cfa3fb856ed 100644 --- a/vnext/Shared/Networking/WinRTHttpResource.h +++ b/vnext/Shared/Networking/WinRTHttpResource.h @@ -23,9 +23,10 @@ class WinRTHttpResource : public IHttpResource, std::mutex m_mutex; std::unordered_map m_responses; - std::function m_onRequest; + std::function m_onRequestSuccess; std::function m_onResponse; std::function m_onData; + std::function m_onBlobData; std::function m_onError; // Used for IHttpModuleProxy @@ -62,9 +63,10 @@ class WinRTHttpResource : public IHttpResource, void AbortRequest(int64_t requestId) noexcept override; void ClearCookies() noexcept override; - void SetOnRequest(std::function &&handler) noexcept override; + void SetOnRequestSuccess(std::function &&handler) noexcept override; void SetOnResponse(std::function &&handler) noexcept override; void SetOnData(std::function &&handler) noexcept override; + void SetOnBlobData(std::function &&handler) noexcept override; void SetOnError(std::function &&handler) noexcept override; diff --git a/vnext/Shared/Networking/WinRTTypes.h b/vnext/Shared/Networking/WinRTTypes.h index 000bd2b9703..d0bbafc27a0 100644 --- a/vnext/Shared/Networking/WinRTTypes.h +++ b/vnext/Shared/Networking/WinRTTypes.h @@ -22,7 +22,7 @@ struct RequestArgs : public winrt::implements Date: Sun, 8 May 2022 03:10:47 -0700 Subject: [PATCH 67/91] Consume IRequestBodyHandler --- vnext/Shared/Networking/WinRTHttpResource.cpp | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/vnext/Shared/Networking/WinRTHttpResource.cpp b/vnext/Shared/Networking/WinRTHttpResource.cpp index 3e81941b3c6..049b8fe0883 100644 --- a/vnext/Shared/Networking/WinRTHttpResource.cpp +++ b/vnext/Shared/Networking/WinRTHttpResource.cpp @@ -227,7 +227,20 @@ fire_and_forget WinRTHttpResource::PerformSendRequest(HttpRequestMessage &&reque IHttpContent content{nullptr}; auto &data = coReqArgs->Data; if (!data.isNull()) { - if (!data["string"].empty()) { + if (auto bodyHandler = self->m_requestBodyHandler.lock()) { + if (bodyHandler->Supports(data)) { + auto contentTypeString = contentType ? winrt::to_string(contentType.ToString()) : ""; + auto blob = bodyHandler->ToRequestBody(data, contentTypeString); + auto bytes = blob["bytes"]; + auto byteVector = std::vector(bytes.size()); + for (auto &byte : bytes) { + byteVector.push_back(static_cast(byte.asInt())); + } + auto view = winrt::array_view{byteVector}; + auto buffer = CryptographicBuffer::CreateFromByteArray(view); + content = HttpBufferContent{std::move(buffer)}; + } + } else if (!data["string"].empty()) { content = HttpStringContent{to_hstring(data["string"].asString())}; } else if (!data["base64"].empty()) { auto buffer = CryptographicBuffer::DecodeFromBase64String(to_hstring(data["base64"].asString())); @@ -252,12 +265,11 @@ fire_and_forget WinRTHttpResource::PerformSendRequest(HttpRequestMessage &&reque } if (!contentEncoding.empty()) { if (!content.Headers().ContentEncoding().TryParseAdd(to_hstring(contentEncoding))) { - if (m_onError) { - m_onError(coReqArgs->RequestId, "Failed to parse Content-Encoding"); - } - co_return; + if (self->m_onError) + co_return self->m_onError(coReqArgs->RequestId, "Failed to parse Content-Encoding"); } } + if (!contentLength.empty()) { const auto contentLengthHeader = _atoi64(contentLength.c_str()); content.Headers().ContentLength(contentLengthHeader); From f8fe4c0466af1425e84514a7610df6e1c1a9cda6 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Sun, 8 May 2022 03:38:42 -0700 Subject: [PATCH 68/91] Consume IResponseHandler --- vnext/Shared/Networking/WinRTHttpResource.cpp | 33 +++++++++++++++---- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/vnext/Shared/Networking/WinRTHttpResource.cpp b/vnext/Shared/Networking/WinRTHttpResource.cpp index 049b8fe0883..41b53b0e7ab 100644 --- a/vnext/Shared/Networking/WinRTHttpResource.cpp +++ b/vnext/Shared/Networking/WinRTHttpResource.cpp @@ -24,6 +24,7 @@ using std::function; using std::scoped_lock; using std::shared_ptr; using std::string; +using std::vector; using std::weak_ptr; using winrt::fire_and_forget; @@ -179,8 +180,12 @@ fire_and_forget WinRTHttpResource::PerformSendRequest(HttpRequestMessage &&reque try { if (uriHandler->Supports(uri, coReqArgs->ResponseType)) { auto blob = uriHandler->Fetch(uri); - if (self->m_onBlobData && self->m_onRequestSuccess) - co_return self->m_onBlobData(coReqArgs->RequestId, std::move(blob)); + if (self->m_onBlobData && self->m_onRequestSuccess) { + self->m_onBlobData(coReqArgs->RequestId, std::move(blob)); + self->m_onRequestSuccess(coReqArgs->RequestId); + } + + co_return; } } catch (const hresult_error &e) { if (self->m_onError) @@ -232,7 +237,7 @@ fire_and_forget WinRTHttpResource::PerformSendRequest(HttpRequestMessage &&reque auto contentTypeString = contentType ? winrt::to_string(contentType.ToString()) : ""; auto blob = bodyHandler->ToRequestBody(data, contentTypeString); auto bytes = blob["bytes"]; - auto byteVector = std::vector(bytes.size()); + auto byteVector = vector(bytes.size()); for (auto &byte : bytes) { byteVector.push_back(static_cast(byte.asInt())); } @@ -295,7 +300,7 @@ fire_and_forget WinRTHttpResource::PerformSendRequest(HttpRequestMessage &&reque auto response = sendRequestOp.GetResults(); if (response) { if (self->m_onResponse) { - string url = to_string(response.RequestMessage().RequestUri().AbsoluteUri()); + auto url = to_string(response.RequestMessage().RequestUri().AbsoluteUri()); // Gather headers for both the response content and the response itself // See Invoke-WebRequest PowerShell cmdlet or Chromium response handling @@ -318,6 +323,22 @@ fire_and_forget WinRTHttpResource::PerformSendRequest(HttpRequestMessage &&reque auto inputStream = co_await response.Content().ReadAsInputStreamAsync(); auto reader = DataReader{inputStream}; + // Let response handler take over, if set + if (auto responseHandler = self->m_responseHandler.lock()) { + if (responseHandler->Supports(coReqArgs->ResponseType)) { + auto bytes = vector(reader.UnconsumedBufferLength()); + reader.ReadBytes(bytes); + auto blob = responseHandler->ToResponseData(std::move(bytes)); + + if (self->m_onBlobData && self->m_onRequestSuccess) { + self->m_onBlobData(coReqArgs->RequestId, std::move(blob)); + self->m_onRequestSuccess(coReqArgs->RequestId); + } + + co_return; + } + } + auto isText = coReqArgs->ResponseType == "text"; if (isText) { reader.UnicodeEncoding(UnicodeEncoding::Utf8); @@ -328,9 +349,9 @@ fire_and_forget WinRTHttpResource::PerformSendRequest(HttpRequestMessage &&reque auto length = reader.UnconsumedBufferLength(); if (isText) { - std::vector data(length); + auto data = vector(length); reader.ReadBytes(data); - string responseData = string(Common::Utilities::CheckedReinterpretCast(data.data()), data.size()); + auto responseData = string(Common::Utilities::CheckedReinterpretCast(data.data()), data.size()); if (self->m_onData) { self->m_onData(coReqArgs->RequestId, std::move(responseData)); From d1f3b23523bf404b64e5d01a863d9209ea812f90 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Sun, 8 May 2022 19:18:29 -0700 Subject: [PATCH 69/91] Ensure properties exist in bag before using value --- vnext/Shared/Modules/BlobModule.cpp | 15 ++++++++++----- vnext/Shared/Modules/WebSocketModule.cpp | 11 ++++++----- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/vnext/Shared/Modules/BlobModule.cpp b/vnext/Shared/Modules/BlobModule.cpp index c0a9c12b659..ee97cbaf774 100644 --- a/vnext/Shared/Modules/BlobModule.cpp +++ b/vnext/Shared/Modules/BlobModule.cpp @@ -170,10 +170,12 @@ vector BlobModule::getMethods() { responseHandler = m_responseHandler](dynamic args) { auto propId = ReactPropertyId>>{L"HttpModule.Proxy"}; - if (auto httpHandler = propBag.Get>>(propId).Value().lock()) { - httpHandler->AddUriHandler(uriHandler); - httpHandler->AddRequestBodyHandler(requestBodyHandler); - httpHandler->AddResponseHandler(responseHandler); + if (auto prop = propBag.Get(propId)) { + if (auto httpHandler = prop.Value().lock()) { + httpHandler->AddUriHandler(uriHandler); + httpHandler->AddRequestBodyHandler(requestBodyHandler); + httpHandler->AddResponseHandler(responseHandler); + } } // TODO: else emit error? }}, @@ -196,7 +198,10 @@ vector BlobModule::getMethods() { [persistor = m_blobPersistor, propBag = ReactPropertyBag{m_inspectableProperties.try_as()}](dynamic args) { auto propId = ReactPropertyId>>{L"WebSocketModule.Proxy"}; - auto wsProxy = propBag.Get(propId).Value().lock(); + shared_ptr wsProxy; + if (auto prop = propBag.Get(propId)) { + wsProxy = prop.Value().lock(); + } if (!wsProxy) { return; } diff --git a/vnext/Shared/Modules/WebSocketModule.cpp b/vnext/Shared/Modules/WebSocketModule.cpp index e9951e04a85..2ccb180e26e 100644 --- a/vnext/Shared/Modules/WebSocketModule.cpp +++ b/vnext/Shared/Modules/WebSocketModule.cpp @@ -106,14 +106,13 @@ GetOrCreateWebSocket(int64_t id, string &&url, weak_ptr contentHandler; auto propId = ReactPropertyId>>{ L"BlobModule.ContentHandler"}; - auto contentHandler = propBag.Get(propId).Value().lock(); + if (auto prop = propBag.Get(propId)) + contentHandler = prop.Value().lock(); - if (!contentHandler) { - args["data"] = message; - } else { + if (contentHandler) { if (isBinary) { auto buffer = CryptographicBuffer::DecodeFromBase64String(winrt::to_hstring(message)); winrt::com_array arr; @@ -124,6 +123,8 @@ GetOrCreateWebSocket(int64_t id, string &&url, weak_ptrProcessMessage(string{message}, args); } + } else { + args["data"] = message; } SendEvent(weakInstance, "websocketMessage", std::move(args)); From 57139dac89298658aa5a1a23debf723b0642cad3 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Sun, 8 May 2022 20:58:33 -0700 Subject: [PATCH 70/91] Update packages lock --- .../Microsoft.ReactNative.Managed.UnitTests/packages.lock.json | 2 +- vnext/Microsoft.ReactNative.Managed/packages.lock.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/vnext/Microsoft.ReactNative.Managed.UnitTests/packages.lock.json b/vnext/Microsoft.ReactNative.Managed.UnitTests/packages.lock.json index ddbef3311ae..29155030c86 100644 --- a/vnext/Microsoft.ReactNative.Managed.UnitTests/packages.lock.json +++ b/vnext/Microsoft.ReactNative.Managed.UnitTests/packages.lock.json @@ -72,7 +72,7 @@ "NETStandard.Library": { "type": "Transitive", "resolved": "2.0.3", - "contentHash": "548M6mnBSJWxsIlkQHfbzoYxpiYFXZZSL00p4GHYv8PkiqFBnnT68mW5mGEsA/ch9fDO9GkPgkFQpWiXZN7mAQ==", + "contentHash": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==", "dependencies": { "Microsoft.NETCore.Platforms": "1.1.0" } diff --git a/vnext/Microsoft.ReactNative.Managed/packages.lock.json b/vnext/Microsoft.ReactNative.Managed/packages.lock.json index 7b897d0bd1e..04e54db679e 100644 --- a/vnext/Microsoft.ReactNative.Managed/packages.lock.json +++ b/vnext/Microsoft.ReactNative.Managed/packages.lock.json @@ -63,7 +63,7 @@ "NETStandard.Library": { "type": "Transitive", "resolved": "2.0.3", - "contentHash": "548M6mnBSJWxsIlkQHfbzoYxpiYFXZZSL00p4GHYv8PkiqFBnnT68mW5mGEsA/ch9fDO9GkPgkFQpWiXZN7mAQ==", + "contentHash": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==", "dependencies": { "Microsoft.NETCore.Platforms": "1.1.0" } From 11a3576fea0e3d3731a53b2a263be1355b01779e Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Sun, 8 May 2022 21:14:29 -0700 Subject: [PATCH 71/91] Add missing call to Modules::SendEvent --- vnext/Shared/Modules/BlobModule.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/vnext/Shared/Modules/BlobModule.cpp b/vnext/Shared/Modules/BlobModule.cpp index ee97cbaf774..8c8bf8e0800 100644 --- a/vnext/Shared/Modules/BlobModule.cpp +++ b/vnext/Shared/Modules/BlobModule.cpp @@ -3,6 +3,7 @@ #include "BlobModule.h" +#include #include #include #include @@ -246,11 +247,8 @@ vector BlobModule::getMethods() { buffer.insert(buffer.end(), data.begin(), data.end()); } else { if (auto state = weakState.lock()) { - if (auto instance = state->Module->getInstance().lock()) { - auto message = "Invalid type for blob: " + type; - instance->callJSFunction( - "RCTDeviceEventEmitter", "emit", dynamic::array("blobFailed", std::move(message))); - } + auto message = "Invalid type for blob: " + type; + Modules::SendEvent(state->Module->getInstance(), "blobFailed", std::move(message)); } return; } From 5a96be7308ea592f328aaebd829dbbafea990155 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Mon, 9 May 2022 17:19:27 -0700 Subject: [PATCH 72/91] Fix Shared filters --- vnext/Shared/Shared.vcxitems.filters | 1 - 1 file changed, 1 deletion(-) diff --git a/vnext/Shared/Shared.vcxitems.filters b/vnext/Shared/Shared.vcxitems.filters index 0b3683b1156..6d524ab27cf 100644 --- a/vnext/Shared/Shared.vcxitems.filters +++ b/vnext/Shared/Shared.vcxitems.filters @@ -146,7 +146,6 @@ Source Files\Networking - Source Files\Modules From 2d4728f06e7b3737270b6c0813ea6b4fa63010b6 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Sat, 14 May 2022 22:27:33 -0700 Subject: [PATCH 73/91] Rename SetOnBlobData to SetOnData (different args) --- vnext/Shared/Modules/HttpModule.cpp | 6 ++++-- vnext/Shared/Networking/IHttpResource.h | 2 +- vnext/Shared/Networking/WinRTHttpResource.cpp | 12 ++++++------ vnext/Shared/Networking/WinRTHttpResource.h | 4 ++-- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/vnext/Shared/Modules/HttpModule.cpp b/vnext/Shared/Modules/HttpModule.cpp index 14173585e05..3635c25fef8 100644 --- a/vnext/Shared/Modules/HttpModule.cpp +++ b/vnext/Shared/Modules/HttpModule.cpp @@ -66,9 +66,11 @@ static shared_ptr CreateHttpResource( SendEvent(weakReactInstance, completedResponse, dynamic::array(requestId)); }); - resource->SetOnBlobData([weakReactInstance](int64_t requestId, dynamic &&responseData) { + // Explicitly declaring function type to avoid type inference ambiguity. + std::function onDataDynamic = [weakReactInstance](int64_t requestId, dynamic &&responseData) { SendEvent(weakReactInstance, receivedData, dynamic::array(requestId, std::move(responseData))); - }); + }; + resource->SetOnData(std::move(onDataDynamic)); resource->SetOnError([weakReactInstance](int64_t requestId, string &&message) { dynamic args = dynamic::array(requestId, std::move(message)); diff --git a/vnext/Shared/Networking/IHttpResource.h b/vnext/Shared/Networking/IHttpResource.h index c5a1498d6ce..c27f754d80c 100644 --- a/vnext/Shared/Networking/IHttpResource.h +++ b/vnext/Shared/Networking/IHttpResource.h @@ -92,7 +92,7 @@ struct IHttpResource { virtual void SetOnRequestSuccess(std::function &&handler) noexcept = 0; virtual void SetOnResponse(std::function &&handler) noexcept = 0; virtual void SetOnData(std::function &&handler) noexcept = 0; - virtual void SetOnBlobData( + virtual void SetOnData( std::function &&handler) noexcept = 0; virtual void SetOnError( std::function &&handler) noexcept = 0; diff --git a/vnext/Shared/Networking/WinRTHttpResource.cpp b/vnext/Shared/Networking/WinRTHttpResource.cpp index 41b53b0e7ab..504bd15f727 100644 --- a/vnext/Shared/Networking/WinRTHttpResource.cpp +++ b/vnext/Shared/Networking/WinRTHttpResource.cpp @@ -141,10 +141,10 @@ void WinRTHttpResource::SetOnData(function &&handler) noexcept +void WinRTHttpResource::SetOnData(function &&handler) noexcept /*override*/ { - m_onBlobData = std::move(handler); + m_onDataDynamic = std::move(handler); } void WinRTHttpResource::SetOnError(function &&handler) noexcept @@ -180,8 +180,8 @@ fire_and_forget WinRTHttpResource::PerformSendRequest(HttpRequestMessage &&reque try { if (uriHandler->Supports(uri, coReqArgs->ResponseType)) { auto blob = uriHandler->Fetch(uri); - if (self->m_onBlobData && self->m_onRequestSuccess) { - self->m_onBlobData(coReqArgs->RequestId, std::move(blob)); + if (self->m_onDataDynamic && self->m_onRequestSuccess) { + self->m_onDataDynamic(coReqArgs->RequestId, std::move(blob)); self->m_onRequestSuccess(coReqArgs->RequestId); } @@ -330,8 +330,8 @@ fire_and_forget WinRTHttpResource::PerformSendRequest(HttpRequestMessage &&reque reader.ReadBytes(bytes); auto blob = responseHandler->ToResponseData(std::move(bytes)); - if (self->m_onBlobData && self->m_onRequestSuccess) { - self->m_onBlobData(coReqArgs->RequestId, std::move(blob)); + if (self->m_onDataDynamic && self->m_onRequestSuccess) { + self->m_onDataDynamic(coReqArgs->RequestId, std::move(blob)); self->m_onRequestSuccess(coReqArgs->RequestId); } diff --git a/vnext/Shared/Networking/WinRTHttpResource.h b/vnext/Shared/Networking/WinRTHttpResource.h index cfa3fb856ed..38d030f8264 100644 --- a/vnext/Shared/Networking/WinRTHttpResource.h +++ b/vnext/Shared/Networking/WinRTHttpResource.h @@ -26,7 +26,7 @@ class WinRTHttpResource : public IHttpResource, std::function m_onRequestSuccess; std::function m_onResponse; std::function m_onData; - std::function m_onBlobData; + std::function m_onDataDynamic; std::function m_onError; // Used for IHttpModuleProxy @@ -66,7 +66,7 @@ class WinRTHttpResource : public IHttpResource, void SetOnRequestSuccess(std::function &&handler) noexcept override; void SetOnResponse(std::function &&handler) noexcept override; void SetOnData(std::function &&handler) noexcept override; - void SetOnBlobData(std::function &&handler) noexcept override; + void SetOnData(std::function &&handler) noexcept override; void SetOnError(std::function &&handler) noexcept override; From d70bf0ab9e3848381004028c2e6b1209600c6948 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Sun, 15 May 2022 00:17:25 -0700 Subject: [PATCH 74/91] Correctly retrieve blob slices --- vnext/Shared/Modules/BlobModule.cpp | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/vnext/Shared/Modules/BlobModule.cpp b/vnext/Shared/Modules/BlobModule.cpp index 8c8bf8e0800..bcee14b74fe 100644 --- a/vnext/Shared/Modules/BlobModule.cpp +++ b/vnext/Shared/Modules/BlobModule.cpp @@ -275,11 +275,25 @@ vector BlobModule::getMethods() { #pragma region IBlobPersistor winrt::array_view MemoryBlobPersistor::ResolveMessage(string &&blobId, int64_t offset, int64_t size) noexcept { + if (offset < 0 || size < 1) + return {}; + scoped_lock lock{m_mutex}; - auto &data = m_blobs.at(std::move(blobId)); + auto dataItr = m_blobs.find(std::move(blobId)); + if (dataItr != m_blobs.cend()) { + auto &bytes = (*dataItr).second; + + auto endBound = static_cast(offset + size); + // Out of bounds. + if (endBound > bytes.size()) + return {}; + + return winrt::array_view(bytes.data() + offset, bytes.data() + endBound ); + } - return winrt::array_view{data}; + // Not found. + return {}; } void MemoryBlobPersistor::RemoveMessage(string &&blobId) noexcept { From 14ddf67f81c2980cad45c36a4b98d7eff73c6258 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Sun, 15 May 2022 00:17:40 -0700 Subject: [PATCH 75/91] Correctly retrieve blob slices --- vnext/Shared/Modules/BlobModule.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vnext/Shared/Modules/BlobModule.cpp b/vnext/Shared/Modules/BlobModule.cpp index bcee14b74fe..7f577770890 100644 --- a/vnext/Shared/Modules/BlobModule.cpp +++ b/vnext/Shared/Modules/BlobModule.cpp @@ -289,7 +289,7 @@ winrt::array_view MemoryBlobPersistor::ResolveMessage(string &&blobId, if (endBound > bytes.size()) return {}; - return winrt::array_view(bytes.data() + offset, bytes.data() + endBound ); + return winrt::array_view(bytes.data() + offset, bytes.data() + endBound); } // Not found. From 2a65a00578e44f7ca8b9dd585b8c95f6fc888bd8 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Sun, 15 May 2022 00:30:38 -0700 Subject: [PATCH 76/91] Clang format --- vnext/Shared/Modules/HttpModule.cpp | 3 ++- vnext/Shared/Networking/IHttpResource.h | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/vnext/Shared/Modules/HttpModule.cpp b/vnext/Shared/Modules/HttpModule.cpp index 3635c25fef8..6d8896613f2 100644 --- a/vnext/Shared/Modules/HttpModule.cpp +++ b/vnext/Shared/Modules/HttpModule.cpp @@ -67,7 +67,8 @@ static shared_ptr CreateHttpResource( }); // Explicitly declaring function type to avoid type inference ambiguity. - std::function onDataDynamic = [weakReactInstance](int64_t requestId, dynamic &&responseData) { + std::function onDataDynamic = [weakReactInstance]( + int64_t requestId, dynamic &&responseData) { SendEvent(weakReactInstance, receivedData, dynamic::array(requestId, std::move(responseData))); }; resource->SetOnData(std::move(onDataDynamic)); diff --git a/vnext/Shared/Networking/IHttpResource.h b/vnext/Shared/Networking/IHttpResource.h index c27f754d80c..b3d75b2a300 100644 --- a/vnext/Shared/Networking/IHttpResource.h +++ b/vnext/Shared/Networking/IHttpResource.h @@ -92,8 +92,7 @@ struct IHttpResource { virtual void SetOnRequestSuccess(std::function &&handler) noexcept = 0; virtual void SetOnResponse(std::function &&handler) noexcept = 0; virtual void SetOnData(std::function &&handler) noexcept = 0; - virtual void SetOnData( - std::function &&handler) noexcept = 0; + virtual void SetOnData(std::function &&handler) noexcept = 0; virtual void SetOnError( std::function &&handler) noexcept = 0; }; From 698d0731390b989f03fe48ac382a762b39fd11df Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Wed, 18 May 2022 16:24:56 -0700 Subject: [PATCH 77/91] Update project filters --- .../Microsoft.ReactNative.Cxx.vcxitems.filters | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vnext/Microsoft.ReactNative.Cxx/Microsoft.ReactNative.Cxx.vcxitems.filters b/vnext/Microsoft.ReactNative.Cxx/Microsoft.ReactNative.Cxx.vcxitems.filters index 38880c96c7c..c8626e0d355 100644 --- a/vnext/Microsoft.ReactNative.Cxx/Microsoft.ReactNative.Cxx.vcxitems.filters +++ b/vnext/Microsoft.ReactNative.Cxx/Microsoft.ReactNative.Cxx.vcxitems.filters @@ -27,7 +27,7 @@ JSI - + From e8166bcea44243058198c02ede86fa1a934fa43a Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Wed, 18 May 2022 16:37:37 -0700 Subject: [PATCH 78/91] Drop BlobModuleUriHandler --- vnext/Shared/Modules/BlobModule.cpp | 121 ------------------ vnext/Shared/Modules/BlobModule.h | 33 ----- vnext/Shared/Modules/HttpModule.h | 2 - vnext/Shared/Modules/IHttpModuleProxy.h | 1 + vnext/Shared/Networking/WinRTHttpResource.cpp | 2 +- 5 files changed, 2 insertions(+), 157 deletions(-) diff --git a/vnext/Shared/Modules/BlobModule.cpp b/vnext/Shared/Modules/BlobModule.cpp index 7f577770890..be0868bb1f8 100644 --- a/vnext/Shared/Modules/BlobModule.cpp +++ b/vnext/Shared/Modules/BlobModule.cpp @@ -135,7 +135,6 @@ BlobModule::BlobModule(winrt::Windows::Foundation::IInspectable const &inspectab : m_sharedState{std::make_shared()}, m_blobPersistor{std::make_shared()}, m_contentHandler{std::make_shared(m_blobPersistor)}, - m_uriHandler{std::make_shared(m_blobPersistor)}, m_requestBodyHandler{std::make_shared(m_blobPersistor)}, m_responseHandler{std::make_shared(m_blobPersistor)}, m_inspectableProperties{inspectableProperties} { @@ -166,14 +165,12 @@ vector BlobModule::getMethods() { return { {"addNetworkingHandler", [propBag = ReactPropertyBag{m_inspectableProperties.try_as()}, - uriHandler = m_uriHandler, requestBodyHandler = m_requestBodyHandler, responseHandler = m_responseHandler](dynamic args) { auto propId = ReactPropertyId>>{L"HttpModule.Proxy"}; if (auto prop = propBag.Get(propId)) { if (auto httpHandler = prop.Value().lock()) { - httpHandler->AddUriHandler(uriHandler); httpHandler->AddRequestBodyHandler(requestBodyHandler); httpHandler->AddResponseHandler(responseHandler); } @@ -359,124 +356,6 @@ void BlobWebSocketModuleContentHandler::Unregister(int64_t socketID) noexcept { #pragma endregion BlobWebSocketModuleContentHandler -#pragma region BlobModuleUriHandler - -BlobModuleUriHandler::BlobModuleUriHandler(shared_ptr blobPersistor) noexcept - : m_blobPersistor{blobPersistor} {} - -#pragma region IUriHandler - -bool BlobModuleUriHandler::Supports(string &uri, string &responseType) /*override*/ { - auto uriObj = Uri{winrt::to_hstring(uri)}; - - return !(L"http" == uriObj.SchemeName() || L"https" == uriObj.SchemeName()) && blobKey == responseType; -} - -dynamic BlobModuleUriHandler::Fetch(string &uri) /*override*/ { - auto data = GetBytesFromUri(uri); - - auto blob = dynamic::object(); - blob(offsetKey, 0); - blob(sizeKey, data.size()); - blob(typeKey, GetMimeTypeFromUri(uri)); - blob(blobIdKey, m_blobPersistor->StoreMessage(std::move(data))); - - // Needed for files - blob("name", GetNameFromUri(uri)); - blob("lastModified", GetLastModifiedFromUri(uri)); - - return blob; -} - -#pragma endregion IUriHandler - -// TODO: Avoid double allocation (malloc() + vector{}}. -vector BlobModuleUriHandler::GetBytesFromUri(string &uri) { - auto path = fs::weakly_canonical(uri); - auto size = fs::file_size(path); - auto sharedBuff = shared_ptr{malloc(static_cast(size)), free}; - auto inStream = std::ifstream{path, std::ios::binary}; - inStream.read(static_cast(sharedBuff.get()), size); - auto asBytePtr = static_cast(sharedBuff.get()); - - return vector{asBytePtr, asBytePtr + static_cast(size)}; -} - -/// -/// See -/// https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/content/ContentResolver.java -/// -string BlobModuleUriHandler::GetMimeTypeFromUri(string &uri) noexcept { - string result{}; - - auto uriPath = Uri{winrt::to_hstring(uri)}.Path(); - auto lastSegment = GetLastPathSegment(uriPath); - auto path = std::filesystem::path{lastSegment}; - if (path.has_extension()) { - auto entry = extensionToMime.find(path.extension().string()); - if (entry != extensionToMime.cend()) { - result = (*entry).second; - } - } - - return result; -} - -string BlobModuleUriHandler::GetNameFromUri(string &uri) noexcept { - auto uriObj = Uri{winrt::to_hstring(uri)}; - auto path = uriObj.Path(); - if (L"file" == uriObj.SchemeName()) { - return GetLastPathSegment(path); - } - - // TODO: Lookup "_display_name" - - return GetLastPathSegment(path); -} - -string BlobModuleUriHandler::GetLastPathSegment(winrt::hstring &path) noexcept { - auto start = path.size(); - auto end = start; - while (end > 0) { - if (path[end - 1] != '/') { - start = end - 1; - break; - } else { - end--; - } - } - - // No name characters found - if (start >= end) - return {}; - - while (start > 0 && path[start - 1] != '/') { - start--; - } - - return winrt::to_string(path).substr(start, /*count*/ end - start); -} - -int64_t BlobModuleUriHandler::GetLastModifiedFromUri(string &uri) noexcept { - int64_t result; - - try { - auto path = fs::weakly_canonical(uri); - auto lastModified = fs::last_write_time(path); - auto timeSince = lastModified.time_since_epoch(); - auto duration = std::chrono::duration_cast(timeSince); - auto count = duration.count(); - - result = static_cast(count); - } catch (const fs::filesystem_error &) { - result = 0; - } - - return result; -} - -#pragma endregion BlobModuleUriHandler - #pragma region BlobModuleRequestBodyHandler BlobModuleRequestBodyHandler::BlobModuleRequestBodyHandler(shared_ptr blobPersistor) noexcept diff --git a/vnext/Shared/Modules/BlobModule.h b/vnext/Shared/Modules/BlobModule.h index 05efa0083ef..2820f0d8875 100644 --- a/vnext/Shared/Modules/BlobModule.h +++ b/vnext/Shared/Modules/BlobModule.h @@ -6,7 +6,6 @@ #include #include #include -#include #include // React Native @@ -63,37 +62,6 @@ class BlobWebSocketModuleContentHandler final : public IWebSocketModuleContentHa void Unregister(int64_t socketID) noexcept; }; -class BlobModuleUriHandler final : public IUriHandler { - std::shared_ptr m_blobPersistor; - - public: - BlobModuleUriHandler(std::shared_ptr blobPersistor) noexcept; - -#pragma region IUriHandler - - bool Supports(std::string &uri, std::string &responseType) override; - - folly::dynamic Fetch(std::string &uri) override; - -#pragma endregion IUriHandler - - /// - /// May throw std::filesystem::filesystem_error. - /// - std::vector GetBytesFromUri(std::string &uri); - - std::string GetMimeTypeFromUri(std::string &uri) noexcept; - - std::string GetNameFromUri(std::string &uri) noexcept; - - std::string GetLastPathSegment(winrt::hstring &path) noexcept; - - /// - /// Last modified time in miliseconds - /// - int64_t GetLastModifiedFromUri(std::string &uri) noexcept; -}; - class BlobModuleRequestBodyHandler final : public IRequestBodyHandler { std::shared_ptr m_blobPersistor; @@ -127,7 +95,6 @@ class BlobModuleResponseHandler final : public IResponseHandler { class BlobModule : public facebook::xplat::module::CxxModule { std::shared_ptr m_blobPersistor; std::shared_ptr m_contentHandler; - std::shared_ptr m_uriHandler; std::shared_ptr m_requestBodyHandler; std::shared_ptr m_responseHandler; diff --git a/vnext/Shared/Modules/HttpModule.h b/vnext/Shared/Modules/HttpModule.h index 6df762f7771..eaab7a6d9dc 100644 --- a/vnext/Shared/Modules/HttpModule.h +++ b/vnext/Shared/Modules/HttpModule.h @@ -3,8 +3,6 @@ #pragma once -#include -#include #include // React Native diff --git a/vnext/Shared/Modules/IHttpModuleProxy.h b/vnext/Shared/Modules/IHttpModuleProxy.h index c0f3c5030fd..194d9171916 100644 --- a/vnext/Shared/Modules/IHttpModuleProxy.h +++ b/vnext/Shared/Modules/IHttpModuleProxy.h @@ -19,6 +19,7 @@ namespace Microsoft::React { struct IHttpModuleProxy { virtual ~IHttpModuleProxy() noexcept {} + //TODO: Implement custom URI handlers. virtual void AddUriHandler(std::shared_ptr uriHandler) noexcept = 0; virtual void AddRequestBodyHandler(std::shared_ptr requestBodyHandler) noexcept = 0; diff --git a/vnext/Shared/Networking/WinRTHttpResource.cpp b/vnext/Shared/Networking/WinRTHttpResource.cpp index 504bd15f727..23fb83dd0b1 100644 --- a/vnext/Shared/Networking/WinRTHttpResource.cpp +++ b/vnext/Shared/Networking/WinRTHttpResource.cpp @@ -391,7 +391,7 @@ fire_and_forget WinRTHttpResource::PerformSendRequest(HttpRequestMessage &&reque void WinRTHttpResource::AddUriHandler(shared_ptr uriHandler) noexcept /*override*/ { - m_uriHandler = weak_ptr(uriHandler); + //TODO: Implement custom URI handling. } void WinRTHttpResource::AddRequestBodyHandler(shared_ptr requestBodyHandler) noexcept /*override*/ From 62346c0d65b959e27d235015231d09a985eff56b Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Thu, 19 May 2022 18:18:15 -0700 Subject: [PATCH 79/91] Continue handling requests when not blob-supported --- vnext/Shared/Modules/BlobModule.cpp | 4 +- vnext/Shared/Modules/HttpModule.cpp | 73 ++++++++++--------- vnext/Shared/Modules/HttpModule.h | 1 + vnext/Shared/Networking/IHttpResource.h | 2 +- vnext/Shared/Networking/WinRTHttpResource.cpp | 27 ++++--- 5 files changed, 57 insertions(+), 50 deletions(-) diff --git a/vnext/Shared/Modules/BlobModule.cpp b/vnext/Shared/Modules/BlobModule.cpp index be0868bb1f8..4f4130d2929 100644 --- a/vnext/Shared/Modules/BlobModule.cpp +++ b/vnext/Shared/Modules/BlobModule.cpp @@ -364,7 +364,9 @@ BlobModuleRequestBodyHandler::BlobModuleRequestBodyHandler(shared_ptr CreateHttpResource( +static void SetUpHttpResource( + shared_ptr resource, weak_ptr weakReactInstance, IInspectable &inspectableProperties) { - auto resource = IHttpResource::Make(inspectableProperties); resource->SetOnRequestSuccess([weakReactInstance](int64_t requestId) { auto args = dynamic::array(requestId); @@ -79,8 +79,6 @@ static shared_ptr CreateHttpResource( SendEvent(weakReactInstance, completedResponse, std::move(args)); }); - - return resource; } } // namespace @@ -88,7 +86,9 @@ static shared_ptr CreateHttpResource( namespace Microsoft::React { HttpModule::HttpModule(IInspectable const &inspectableProperties) noexcept - : m_holder{std::make_shared()}, m_inspectableProperties{inspectableProperties} { + : m_holder{std::make_shared()}, + m_inspectableProperties{inspectableProperties}, + m_resource{IHttpResource::Make(inspectableProperties)} { m_holder->Module = this; } @@ -109,10 +109,6 @@ std::map HttpModule::getConstants() { // clang-format off std::vector HttpModule::getMethods() { - auto weakHolder = weak_ptr(m_holder); - auto holder = weakHolder.lock(); - auto weakReactInstance = weak_ptr(holder->Module->getInstance()); - return { { @@ -125,29 +121,32 @@ std::vector HttpModule::getMethods() } auto resource = holder->Module->m_resource; - if (resource || (resource = holder->Module->m_resource = CreateHttpResource(holder->Module->getInstance(), holder->Module->m_inspectableProperties))) + if (!holder->Module->m_isResourceSetup) { - auto params = facebook::xplat::jsArgAsObject(args, 0); - IHttpResource::Headers headers; - for (auto& header : params["headers"].items()) { - headers.emplace(header.first.getString(), header.second.getString()); - } + SetUpHttpResource(resource, holder->Module->getInstance(), holder->Module->m_inspectableProperties); + holder->Module->m_isResourceSetup = true; + } - resource->SendRequest( - params["method"].asString(), - params["url"].asString(), - params["requestId"].asInt(), - std::move(headers), - std::move(params["data"]), - params["responseType"].asString(), - params["incrementalUpdates"].asBool(), - static_cast(params["timeout"].asDouble()), - params["withCredentials"].asBool(), - [cxxCallback = std::move(cxxCallback)](int64_t requestId) { - cxxCallback({requestId}); - } - ); - } // If resource available + auto params = facebook::xplat::jsArgAsObject(args, 0); + IHttpResource::Headers headers; + for (auto& header : params["headers"].items()) { + headers.emplace(header.first.getString(), header.second.getString()); + } + + resource->SendRequest( + params["method"].asString(), + params["url"].asString(), + params["requestId"].asInt(), + std::move(headers), + std::move(params["data"]), + params["responseType"].asString(), + params["incrementalUpdates"].asBool(), + static_cast(params["timeout"].asDouble()), + params["withCredentials"].asBool(), + [cxxCallback = std::move(cxxCallback)](int64_t requestId) { + cxxCallback({requestId}); + } + ); } }, { @@ -161,10 +160,13 @@ std::vector HttpModule::getMethods() } auto resource = holder->Module->m_resource; - if (resource || (resource = holder->Module->m_resource = CreateHttpResource(holder->Module->getInstance(), holder->Module->m_inspectableProperties))) + if (!holder->Module->m_isResourceSetup) { - resource->AbortRequest(facebook::xplat::jsArgAsInt(args, 0)); + SetUpHttpResource(resource, holder->Module->getInstance(), holder->Module->m_inspectableProperties); + holder->Module->m_isResourceSetup = true; } + + resource->AbortRequest(facebook::xplat::jsArgAsInt(args, 0)); } }, { @@ -178,10 +180,13 @@ std::vector HttpModule::getMethods() } auto resource = holder->Module->m_resource; - if (resource || (resource = holder->Module->m_resource = CreateHttpResource(holder->Module->getInstance(), holder->Module->m_inspectableProperties))) + if (!holder->Module->m_isResourceSetup) { - resource->ClearCookies(); + SetUpHttpResource(resource, holder->Module->getInstance(), holder->Module->m_inspectableProperties); + holder->Module->m_isResourceSetup = true; } + + resource->ClearCookies(); } } }; diff --git a/vnext/Shared/Modules/HttpModule.h b/vnext/Shared/Modules/HttpModule.h index eaab7a6d9dc..103818c03fe 100644 --- a/vnext/Shared/Modules/HttpModule.h +++ b/vnext/Shared/Modules/HttpModule.h @@ -52,6 +52,7 @@ class HttpModule : public facebook::xplat::module::CxxModule { std::shared_ptr m_resource; std::shared_ptr m_holder; + bool m_isResourceSetup{false}; // Property bag high level reference. winrt::Windows::Foundation::IInspectable m_inspectableProperties; diff --git a/vnext/Shared/Networking/IHttpResource.h b/vnext/Shared/Networking/IHttpResource.h index b3d75b2a300..c513855bace 100644 --- a/vnext/Shared/Networking/IHttpResource.h +++ b/vnext/Shared/Networking/IHttpResource.h @@ -34,7 +34,7 @@ struct IHttpResource { static std::shared_ptr Make() noexcept; - static std::shared_ptr Make(winrt::Windows::Foundation::IInspectable &inspectableProperties) noexcept; + static std::shared_ptr Make(winrt::Windows::Foundation::IInspectable const &inspectableProperties) noexcept; virtual ~IHttpResource() noexcept {} diff --git a/vnext/Shared/Networking/WinRTHttpResource.cpp b/vnext/Shared/Networking/WinRTHttpResource.cpp index 23fb83dd0b1..9996905d764 100644 --- a/vnext/Shared/Networking/WinRTHttpResource.cpp +++ b/vnext/Shared/Networking/WinRTHttpResource.cpp @@ -232,19 +232,18 @@ fire_and_forget WinRTHttpResource::PerformSendRequest(HttpRequestMessage &&reque IHttpContent content{nullptr}; auto &data = coReqArgs->Data; if (!data.isNull()) { - if (auto bodyHandler = self->m_requestBodyHandler.lock()) { - if (bodyHandler->Supports(data)) { - auto contentTypeString = contentType ? winrt::to_string(contentType.ToString()) : ""; - auto blob = bodyHandler->ToRequestBody(data, contentTypeString); - auto bytes = blob["bytes"]; - auto byteVector = vector(bytes.size()); - for (auto &byte : bytes) { - byteVector.push_back(static_cast(byte.asInt())); - } - auto view = winrt::array_view{byteVector}; - auto buffer = CryptographicBuffer::CreateFromByteArray(view); - content = HttpBufferContent{std::move(buffer)}; + auto bodyHandler = self->m_requestBodyHandler.lock(); + if (bodyHandler && bodyHandler->Supports(data)) { + auto contentTypeString = contentType ? winrt::to_string(contentType.ToString()) : ""; + auto blob = bodyHandler->ToRequestBody(data, contentTypeString); + auto bytes = blob["bytes"]; + auto byteVector = vector(bytes.size()); + for (auto &byte : bytes) { + byteVector.push_back(static_cast(byte.asInt())); } + auto view = winrt::array_view{byteVector}; + auto buffer = CryptographicBuffer::CreateFromByteArray(view); + content = HttpBufferContent{std::move(buffer)}; } else if (!data["string"].empty()) { content = HttpStringContent{to_hstring(data["string"].asString())}; } else if (!data["base64"].empty()) { @@ -389,7 +388,7 @@ fire_and_forget WinRTHttpResource::PerformSendRequest(HttpRequestMessage &&reque #pragma region IHttpModuleProxy -void WinRTHttpResource::AddUriHandler(shared_ptr uriHandler) noexcept /*override*/ +void WinRTHttpResource::AddUriHandler(shared_ptr /*uriHandler*/) noexcept /*override*/ { //TODO: Implement custom URI handling. } @@ -411,7 +410,7 @@ void WinRTHttpResource::AddResponseHandler(shared_ptr response #pragma region IHttpResource /*static*/ shared_ptr IHttpResource::Make( - winrt::Windows::Foundation::IInspectable &inspectableProperties) noexcept { + winrt::Windows::Foundation::IInspectable const &inspectableProperties) noexcept { using namespace winrt::Microsoft::ReactNative; using winrt::Windows::Web::Http::HttpClient; From 842ed4b6ef73471a94eaf2b861ba6b4c611dc9ac Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Thu, 19 May 2022 18:18:33 -0700 Subject: [PATCH 80/91] Add BlobTest --- .../RNTesterIntegrationTests.cpp | 8 ++ vnext/src/IntegrationTests/BlobTest.js | 85 +++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 vnext/src/IntegrationTests/BlobTest.js diff --git a/vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp b/vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp index 57d98d1d317..af3bced8313 100644 --- a/vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp +++ b/vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp @@ -226,5 +226,13 @@ TEST_CLASS (RNTesterIntegrationTests) { Assert::AreEqual(TestStatus::Passed, result.Status, result.Message.c_str()); } + BEGIN_TEST_METHOD_ATTRIBUTE(Blob) + TEST_IGNORE() + END_TEST_METHOD_ATTRIBUTE() + TEST_METHOD(Blob) { + auto result = m_runner.RunTest("IntegrationTests/BlobTest", "BlobTest"); + Assert::AreEqual(TestStatus::Passed, result.Status, result.Message.c_str()); + } + #pragma endregion Extended Tests }; diff --git a/vnext/src/IntegrationTests/BlobTest.js b/vnext/src/IntegrationTests/BlobTest.js new file mode 100644 index 00000000000..4e740687372 --- /dev/null +++ b/vnext/src/IntegrationTests/BlobTest.js @@ -0,0 +1,85 @@ +/** + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + * @format + * @flow + */ +'use strict'; + +const React = require('react'); +const ReactNative = require('react-native'); + +const {AppRegistry, View} = ReactNative; + +const {TestModule, BlobModule} = ReactNative.NativeModules; + +type State = { + statusCode: number, + xhr: XMLHttpRequest, +}; + +class BlobTest extends React.Component<{...}, State> { + state: State = { + statusCode: 0, + xhr: new XMLHttpRequest(), + }; + + _get = () => { + this.state.xhr.onloadend = () => { + this.setState({ + statusCode: this.state.xhr.status, + }); + }; + this.state.xhr.open( + 'GET', + //'https://www.facebook.com/favicon.ico?r=1&t=1652923053846', + 'http://localhost:666/hello.txt', + ); + this.state.xhr.setRequestHeader('Accept-Encoding', 'utf-8'); + this.state.xhr.responseType = "blob"; + this.state.xhr.send(); + }; + + _getSucceeded = () => { + console.log( + `_getSucceeded [${this.state.statusCode}],[${this.state.xhr.response}]`, + ); + return ( + this.state.statusCode === 200 && + this.state.xhr.response !== null + ); + }; + + _waitFor = (condition: any, timeout: any, callback: any) => { + let remaining = timeout; + const timeoutFunction = function () { + if (condition()) { + callback(true); + return; + } + remaining--; + if (remaining === 0) { + callback(false); + } else { + setTimeout(timeoutFunction, 1000); + } + }; + setTimeout(timeoutFunction, 1000); + }; + + componentDidMount() { + this._get(); + this._waitFor(this._getSucceeded, 5, doneSucceeded => { + console.log(`Suc: [${doneSucceeded}]`); + TestModule.markTestPassed(doneSucceeded); + }); + } + + render(): React.Node { + return ; + } +} + +AppRegistry.registerComponent('BlobTest', () => BlobTest); + +module.exports = BlobTest; From 367e4cccab12603303871e29e616df7f7b55439e Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Thu, 19 May 2022 19:40:33 -0700 Subject: [PATCH 81/91] Update packages.lock.json --- .../packages.lock.json | 59 +++++++++++++++++- .../packages.lock.json | 61 ++++++++++++++++++- 2 files changed, 115 insertions(+), 5 deletions(-) diff --git a/vnext/Microsoft.ReactNative.Managed.UnitTests/packages.lock.json b/vnext/Microsoft.ReactNative.Managed.UnitTests/packages.lock.json index 29155030c86..0f5ab5eac44 100644 --- a/vnext/Microsoft.ReactNative.Managed.UnitTests/packages.lock.json +++ b/vnext/Microsoft.ReactNative.Managed.UnitTests/packages.lock.json @@ -38,6 +38,11 @@ "resolved": "2.1.2", "contentHash": "DXTDuBumPC4oo9KKZMt5zgOuLdfUjqcsLRLyqeubaIxfl7ZBfk8wfsKRWYd1m5aCL3ekifW5pwT3rwuB2mDdLw==" }, + "boost": { + "type": "Transitive", + "resolved": "1.76.0", + "contentHash": "p+w3YvNdXL8Cu9Fzrmexssu0tZbWxuf6ywsQqHjDlKFE5ojXHof1HIyMC3zDLfLnh80dIeFcEUAuR2Asg/XHRA==" + }, "Microsoft.Net.Native.Compiler": { "type": "Transitive", "resolved": "2.2.7-rel-27913-00", @@ -69,10 +74,25 @@ "resolved": "1.0.1", "contentHash": "rkn+fKobF/cbWfnnfBOQHKVKIOpxMZBvlSHkqDWgBpwGDcLRduvs3D9OLGeV6GWGvVwNlVi2CBbTjuPmtHvyNw==" }, + "Microsoft.UI.Xaml": { + "type": "Transitive", + "resolved": "2.7.0", + "contentHash": "dB4im13tfmMgL/V3Ei+3kD2rUF+/lTxAmR4gjJ45l577eljHfdo/KUrxpq/3I1Vp6e5GCDG1evDaEGuDxypLMg==" + }, + "Microsoft.Windows.CppWinRT": { + "type": "Transitive", + "resolved": "2.0.211028.7", + "contentHash": "JBGI0c3WLoU6aYJRy9Qo0MLDQfObEp+d4nrhR95iyzf7+HOgjRunHDp/6eGFREd7xq3OI1mll9ecJrMfzBvlyg==" + }, + "Microsoft.Windows.SDK.BuildTools": { + "type": "Transitive", + "resolved": "10.0.22000.194", + "contentHash": "4L0P3zqut466SIqT3VBeLTNUQTxCBDOrTRymRuROCRJKazcK7ibLz9yAO1nKWRt50ttCj39oAa2Iuz9ZTDmLlg==" + }, "NETStandard.Library": { "type": "Transitive", "resolved": "2.0.3", - "contentHash": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==", + "contentHash": "548M6mnBSJWxsIlkQHfbzoYxpiYFXZZSL00p4GHYv8PkiqFBnnT68mW5mGEsA/ch9fDO9GkPgkFQpWiXZN7mAQ==", "dependencies": { "Microsoft.NETCore.Platforms": "1.1.0" } @@ -82,6 +102,11 @@ "resolved": "9.0.1", "contentHash": "U82mHQSKaIk+lpSVCbWYKNavmNH1i5xrExDEquU1i6I5pV6UMOqRnJRSlKO3cMPfcpp0RgDY+8jUXHdQ4IfXvw==" }, + "ReactNative.Hermes.Windows": { + "type": "Transitive", + "resolved": "0.11.0-ms.6", + "contentHash": "WAVLsSZBV4p/3hNC3W67su7xu3f/ZMSKxu0ON7g2GaKRbkJmH0Qyif1IlzcJwtvR48kuOdfgPu7Bgtz3AY+gqg==" + }, "runtime.win10-arm.Microsoft.Net.Native.Compiler": { "type": "Transitive", "resolved": "2.2.7-rel-27913-00", @@ -268,15 +293,45 @@ "System.Runtime": "4.1.0" } }, - "microsoft.reactnative": { + "common": { + "type": "Project" + }, + "fmt": { "type": "Project" }, + "folly": { + "type": "Project", + "dependencies": { + "boost": "1.76.0", + "fmt": "1.0.0" + } + }, + "microsoft.reactnative": { + "type": "Project", + "dependencies": { + "Common": "1.0.0", + "Folly": "1.0.0", + "Microsoft.UI.Xaml": "2.7.0", + "Microsoft.Windows.CppWinRT": "2.0.211028.7", + "Microsoft.Windows.SDK.BuildTools": "10.0.22000.194", + "ReactCommon": "1.0.0", + "ReactNative.Hermes.Windows": "0.11.0-ms.6", + "boost": "1.76.0" + } + }, "microsoft.reactnative.managed": { "type": "Project", "dependencies": { "Microsoft.NETCore.UniversalWindowsPlatform": "6.2.9", "Microsoft.ReactNative": "1.0.0" } + }, + "reactcommon": { + "type": "Project", + "dependencies": { + "Folly": "1.0.0", + "boost": "1.76.0" + } } }, "UAP,Version=v10.0.16299/win10-arm": { diff --git a/vnext/Microsoft.ReactNative.Managed/packages.lock.json b/vnext/Microsoft.ReactNative.Managed/packages.lock.json index 14d1ec0d868..decd73cbdce 100644 --- a/vnext/Microsoft.ReactNative.Managed/packages.lock.json +++ b/vnext/Microsoft.ReactNative.Managed/packages.lock.json @@ -24,6 +24,11 @@ "Microsoft.SourceLink.Common": "1.0.0" } }, + "boost": { + "type": "Transitive", + "resolved": "1.76.0", + "contentHash": "p+w3YvNdXL8Cu9Fzrmexssu0tZbWxuf6ywsQqHjDlKFE5ojXHof1HIyMC3zDLfLnh80dIeFcEUAuR2Asg/XHRA==" + }, "Microsoft.Build.Tasks.Git": { "type": "Transitive", "resolved": "1.0.0", @@ -53,21 +58,41 @@ "Microsoft.NETCore.Platforms": { "type": "Transitive", "resolved": "2.1.0", - "contentHash": "ok+RPAtESz/9MUXeIEz6Lv5XAGQsaNmEYXMsgVALj4D7kqC8gveKWXWXbufLySR2fWrwZf8smyN5RmHu0e4BHA==" + "contentHash": "GmkKfoyerqmsHMn7OZj0AKpcBabD+GaafqphvX2Mw406IwiJRy1pKcKqdCfKJfYmkRyJ6+e+RaUylgdJoDa1jQ==" }, "Microsoft.SourceLink.Common": { "type": "Transitive", "resolved": "1.0.0", "contentHash": "G8DuQY8/DK5NN+3jm5wcMcd9QYD90UV7MiLmdljSJixi3U/vNaeBKmmXUqI4DJCOeWizIUEh4ALhSt58mR+5eg==" }, + "Microsoft.UI.Xaml": { + "type": "Transitive", + "resolved": "2.7.0", + "contentHash": "dB4im13tfmMgL/V3Ei+3kD2rUF+/lTxAmR4gjJ45l577eljHfdo/KUrxpq/3I1Vp6e5GCDG1evDaEGuDxypLMg==" + }, + "Microsoft.Windows.CppWinRT": { + "type": "Transitive", + "resolved": "2.0.211028.7", + "contentHash": "JBGI0c3WLoU6aYJRy9Qo0MLDQfObEp+d4nrhR95iyzf7+HOgjRunHDp/6eGFREd7xq3OI1mll9ecJrMfzBvlyg==" + }, + "Microsoft.Windows.SDK.BuildTools": { + "type": "Transitive", + "resolved": "10.0.22000.194", + "contentHash": "4L0P3zqut466SIqT3VBeLTNUQTxCBDOrTRymRuROCRJKazcK7ibLz9yAO1nKWRt50ttCj39oAa2Iuz9ZTDmLlg==" + }, "NETStandard.Library": { "type": "Transitive", "resolved": "2.0.3", - "contentHash": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==", + "contentHash": "548M6mnBSJWxsIlkQHfbzoYxpiYFXZZSL00p4GHYv8PkiqFBnnT68mW5mGEsA/ch9fDO9GkPgkFQpWiXZN7mAQ==", "dependencies": { "Microsoft.NETCore.Platforms": "1.1.0" } }, + "ReactNative.Hermes.Windows": { + "type": "Transitive", + "resolved": "0.11.0-ms.6", + "contentHash": "WAVLsSZBV4p/3hNC3W67su7xu3f/ZMSKxu0ON7g2GaKRbkJmH0Qyif1IlzcJwtvR48kuOdfgPu7Bgtz3AY+gqg==" + }, "runtime.win10-arm.Microsoft.Net.Native.Compiler": { "type": "Transitive", "resolved": "2.2.7-rel-27913-00", @@ -135,8 +160,38 @@ "resolved": "2.2.9", "contentHash": "qF6RRZKaflI+LR1YODNyWYjq5YoX8IJ2wx5y8O+AW2xO+1t/Q6Mm+jQ38zJbWnmXbrcOqUYofn7Y3/KC6lTLBQ==" }, - "microsoft.reactnative": { + "common": { "type": "Project" + }, + "fmt": { + "type": "Project" + }, + "folly": { + "type": "Project", + "dependencies": { + "boost": "1.76.0", + "fmt": "1.0.0" + } + }, + "microsoft.reactnative": { + "type": "Project", + "dependencies": { + "Common": "1.0.0", + "Folly": "1.0.0", + "Microsoft.UI.Xaml": "2.7.0", + "Microsoft.Windows.CppWinRT": "2.0.211028.7", + "Microsoft.Windows.SDK.BuildTools": "10.0.22000.194", + "ReactCommon": "1.0.0", + "ReactNative.Hermes.Windows": "0.11.0-ms.6", + "boost": "1.76.0" + } + }, + "reactcommon": { + "type": "Project", + "dependencies": { + "Folly": "1.0.0", + "boost": "1.76.0" + } } }, "UAP,Version=v10.0.16299/win10-arm": { From 0e10755e29f2a4285d13132eae167966c6cc02ee Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Thu, 19 May 2022 20:12:16 -0700 Subject: [PATCH 82/91] Define FileReaderModule --- vnext/Shared/CreateModules.h | 3 ++ vnext/Shared/Modules/FileReaderModule.cpp | 65 +++++++++++++++++++++++ vnext/Shared/Modules/FileReaderModule.h | 65 +++++++++++++++++++++++ vnext/Shared/OInstance.cpp | 7 +++ vnext/Shared/Shared.vcxitems | 2 + vnext/Shared/Shared.vcxitems.filters | 6 +++ 6 files changed, 148 insertions(+) create mode 100644 vnext/Shared/Modules/FileReaderModule.cpp create mode 100644 vnext/Shared/Modules/FileReaderModule.h diff --git a/vnext/Shared/CreateModules.h b/vnext/Shared/CreateModules.h index 9c8038dda5c..34b2c88d298 100644 --- a/vnext/Shared/CreateModules.h +++ b/vnext/Shared/CreateModules.h @@ -46,4 +46,7 @@ extern const char *GetBlobModuleName() noexcept; extern std::unique_ptr CreateBlobModule( winrt::Windows::Foundation::IInspectable const &inspectableProperties) noexcept; +extern const char *GetFileReaderModuleName() noexcept; +extern std::unique_ptr CreateFileReaderModule() noexcept; + } // namespace Microsoft::React diff --git a/vnext/Shared/Modules/FileReaderModule.cpp b/vnext/Shared/Modules/FileReaderModule.cpp new file mode 100644 index 00000000000..b6b0deb3d20 --- /dev/null +++ b/vnext/Shared/Modules/FileReaderModule.cpp @@ -0,0 +1,65 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "FileReaderModule.h" + +using namespace facebook::xplat; + +using folly::dynamic; +using std::string; + +namespace { +constexpr char moduleName[] = "FileReaderModule"; +} //namespace + +namespace Microsoft::React { + +#pragma region FileReaderModule + +FileReaderModule::FileReaderModule() noexcept +{ +} + +FileReaderModule::~FileReaderModule() noexcept /*override*/ +{ +} + +#pragma region CxxModule + +string FileReaderModule::getName() +{ + return moduleName; +} + +std::map FileReaderModule::getConstants() +{ + return {}; +} + +std::vector FileReaderModule::getMethods() +{ + return + { + { + "readAsDataURL", [](dynamic args) {} + }, + { + "readAsText", [](dynamic args) {} + } + }; +} + +#pragma endregion CxxModule + +#pragma endregion FileReaderModule + +/*extern*/ const char* GetFileReaderModuleName() noexcept +{ + return moduleName; +} + +/*extern*/ std::unique_ptr CreateFileReaderModule() noexcept { + return std::make_unique(); +} + +}//namespace diff --git a/vnext/Shared/Modules/FileReaderModule.h b/vnext/Shared/Modules/FileReaderModule.h new file mode 100644 index 00000000000..a2b132ffa58 --- /dev/null +++ b/vnext/Shared/Modules/FileReaderModule.h @@ -0,0 +1,65 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#pragma once + +// React Native +#include + +// Folly +#include + +// Standard Library +#include +#include +#include + +namespace Microsoft::React { + +class FileReaderModule : public facebook::xplat::module::CxxModule { + public: + enum class MethodId { + ReadAsDataURL = 0, + ReadAsText = 1, + SIZE = 2 + }; + + FileReaderModule() noexcept; + + ~FileReaderModule() noexcept override; + + //struct SharedState { + // /// + // /// Keeps a raw reference to the module object to lazily retrieve the React Instance as needed. + // /// + // CxxModule *Module{nullptr}; + //}; + +#pragma region CxxModule + + /// + /// + /// + std::string getName() override; + + /// + /// + /// + std::map getConstants() override; + + /// + /// + /// + /// See See react-native/Libraries/WebSocket/WebSocket.js + std::vector getMethods() override; + +#pragma endregion CxxModule + + private: + /// + /// Keeps members that can be accessed threads other than this module's owner accessible. + /// + //std::shared_ptr m_sharedState; +}; + +} // namespace Microsoft::React diff --git a/vnext/Shared/OInstance.cpp b/vnext/Shared/OInstance.cpp index d093f0321e2..01303698497 100644 --- a/vnext/Shared/OInstance.cpp +++ b/vnext/Shared/OInstance.cpp @@ -624,6 +624,13 @@ std::vector> InstanceImpl::GetDefaultNativeModules [transitionalProps]() { return Microsoft::React::CreateBlobModule(transitionalProps); }, nativeQueue)); + modules.push_back( + std::make_unique( + m_innerInstance, + Microsoft::React::GetFileReaderModuleName(), + []() { return Microsoft::React::CreateFileReaderModule(); }, + nativeQueue)); + return modules; } diff --git a/vnext/Shared/Shared.vcxitems b/vnext/Shared/Shared.vcxitems index 47f2cc7ebb5..6041031a617 100644 --- a/vnext/Shared/Shared.vcxitems +++ b/vnext/Shared/Shared.vcxitems @@ -47,6 +47,7 @@ + @@ -91,6 +92,7 @@ + diff --git a/vnext/Shared/Shared.vcxitems.filters b/vnext/Shared/Shared.vcxitems.filters index 6d524ab27cf..3976b01efbf 100644 --- a/vnext/Shared/Shared.vcxitems.filters +++ b/vnext/Shared/Shared.vcxitems.filters @@ -149,6 +149,9 @@ Source Files\Modules + + Source Files\Modules + @@ -451,6 +454,9 @@ Header Files\Modules + + Header Files\Modules + From 9fd02364ef8ff1516519206168945efeb1ba3071 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Fri, 20 May 2022 01:40:56 -0700 Subject: [PATCH 83/91] Implement FileReaderModule --- vnext/Shared/CreateModules.h | 3 +- vnext/Shared/Modules/BlobModule.cpp | 12 ++- vnext/Shared/Modules/FileReaderModule.cpp | 109 ++++++++++++++++++++-- vnext/Shared/Modules/FileReaderModule.h | 10 +- vnext/Shared/OInstance.cpp | 2 +- 5 files changed, 118 insertions(+), 18 deletions(-) diff --git a/vnext/Shared/CreateModules.h b/vnext/Shared/CreateModules.h index 34b2c88d298..535f26e0273 100644 --- a/vnext/Shared/CreateModules.h +++ b/vnext/Shared/CreateModules.h @@ -47,6 +47,7 @@ extern std::unique_ptr CreateBlobModule( winrt::Windows::Foundation::IInspectable const &inspectableProperties) noexcept; extern const char *GetFileReaderModuleName() noexcept; -extern std::unique_ptr CreateFileReaderModule() noexcept; +extern std::unique_ptr CreateFileReaderModule( + winrt::Windows::Foundation::IInspectable const &inspectableProperties) noexcept; } // namespace Microsoft::React diff --git a/vnext/Shared/Modules/BlobModule.cpp b/vnext/Shared/Modules/BlobModule.cpp index 4f4130d2929..6ea43004635 100644 --- a/vnext/Shared/Modules/BlobModule.cpp +++ b/vnext/Shared/Modules/BlobModule.cpp @@ -20,7 +20,6 @@ #include #include #include -#include using namespace facebook::xplat; @@ -138,11 +137,16 @@ BlobModule::BlobModule(winrt::Windows::Foundation::IInspectable const &inspectab m_requestBodyHandler{std::make_shared(m_blobPersistor)}, m_responseHandler{std::make_shared(m_blobPersistor)}, m_inspectableProperties{inspectableProperties} { - auto propId = - ReactPropertyId>>{L"BlobModule.ContentHandler"}; auto propBag = ReactPropertyBag{m_inspectableProperties.try_as()}; + + auto contentHandlerPropId = + ReactPropertyId>>{L"BlobModule.ContentHandler"}; auto contentHandler = weak_ptr{m_contentHandler}; - propBag.Set(propId, std::move(contentHandler)); + propBag.Set(contentHandlerPropId, std::move(contentHandler)); + + auto blobPersistorPropId = ReactPropertyId>>{L"Blob.Persistor"}; + auto blobPersistor = weak_ptr{m_blobPersistor}; + propBag.Set(blobPersistorPropId, std::move(blobPersistor)); m_sharedState->Module = this; } diff --git a/vnext/Shared/Modules/FileReaderModule.cpp b/vnext/Shared/Modules/FileReaderModule.cpp index b6b0deb3d20..45a1d267c1f 100644 --- a/vnext/Shared/Modules/FileReaderModule.cpp +++ b/vnext/Shared/Modules/FileReaderModule.cpp @@ -3,10 +3,29 @@ #include "FileReaderModule.h" +#include + +// Boost Library +#include +#include +#include + +// React Native +#include + +// Windows API +#include + using namespace facebook::xplat; using folly::dynamic; using std::string; +using std::weak_ptr; +using winrt::Microsoft::ReactNative::IReactPropertyBag; +using winrt::Microsoft::ReactNative::ReactNonAbiValue; +using winrt::Microsoft::ReactNative::ReactPropertyBag; +using winrt::Microsoft::ReactNative::ReactPropertyId; +using winrt::Windows::Foundation::IInspectable; namespace { constexpr char moduleName[] = "FileReaderModule"; @@ -16,9 +35,8 @@ namespace Microsoft::React { #pragma region FileReaderModule -FileReaderModule::FileReaderModule() noexcept -{ -} +FileReaderModule::FileReaderModule(weak_ptr weakBlobPersistor) noexcept + : m_weakBlobPersistor{weakBlobPersistor} {} FileReaderModule::~FileReaderModule() noexcept /*override*/ { @@ -41,10 +59,76 @@ std::vector FileReaderModule::getMethods() return { { - "readAsDataURL", [](dynamic args) {} + /// + /// + /// Array of arguments passed from the JavaScript layer. + /// [0] - dynamic blob object { blobId, offset, size[, type] } + /// + /// + "readAsDataURL", + [blobPersistor = m_weakBlobPersistor.lock()](dynamic args, Callback resolve, Callback reject) { + if (!blobPersistor) { + return reject({"Could not get BlobModule from ReactApplicationContext"}); + } + + auto blob = jsArgAsObject(args, 0); + + auto blobId = blob["blobId"].asString(); + auto offset = blob["offset"].asInt(); + auto size = blob["size"].asInt(); + + // TODO: Make ResolveMessage throw + auto bytes = blobPersistor->ResolveMessage(std::move(blobId), offset, size); + + auto result = string{"data:"}; + auto typeItr = blob.find("type"); + if (typeItr == blob.items().end()) { + result += "application/octet-stream"; + } else { + result += (*typeItr).second.asString(); + } + result += ";base64,"; + + // https://www.boost.org/doc/libs/1_76_0/libs/serialization/doc/dataflow.html + using namespace boost::archive::iterators; + typedef base64_from_binary> encode_base64; + std::ostringstream oss; + std::copy(encode_base64(bytes.cbegin()), encode_base64(bytes.cend()), ostream_iterator(oss)); + result += oss.str(); + + resolve({std::move(result)}); + } }, { - "readAsText", [](dynamic args) {} + /// + /// + /// Array of arguments passed from the JavaScript layer. + /// [0] - dynamic blob object { blobId, offset, size } + /// [1] - string encoding + /// + /// + "readAsText", + [blobPersistor = m_weakBlobPersistor.lock()](dynamic args, Callback resolve, Callback reject) { + if (!blobPersistor) { + return reject({"Could not get BlobModule from ReactApplicationContext"}); + } + + auto blob = jsArgAsObject(args, 0); + auto encoding = jsArgAsString(args, 1);//Default: "UTF-8" + + auto blobId = blob["blobId"].asString(); + auto offset = blob["offset"].asInt(); + auto size = blob["size"].asInt(); + + //TODO: Make ResolveMessage throw + auto bytes = blobPersistor->ResolveMessage(std::move(blobId), offset, size); + + //TODO: Handle non-UTF8 encodings + // See https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/charset/Charset.html + auto result = string{bytes.cbegin(), bytes.cend()}; + + resolve({std::move(result)}); + } } }; } @@ -58,8 +142,19 @@ std::vector FileReaderModule::getMethods() return moduleName; } -/*extern*/ std::unique_ptr CreateFileReaderModule() noexcept { - return std::make_unique(); +/*extern*/ std::unique_ptr CreateFileReaderModule( + IInspectable const &inspectableProperties) noexcept { + + auto propId = ReactPropertyId>>{L"Blob.Persistor"}; + auto propBag = ReactPropertyBag{inspectableProperties.try_as()}; + + if (auto prop = propBag.Get(propId)) { + auto weakBlobPersistor = prop.Value(); + + return std::make_unique(weakBlobPersistor); + } + + return nullptr; } }//namespace diff --git a/vnext/Shared/Modules/FileReaderModule.h b/vnext/Shared/Modules/FileReaderModule.h index a2b132ffa58..8ec6421ed3a 100644 --- a/vnext/Shared/Modules/FileReaderModule.h +++ b/vnext/Shared/Modules/FileReaderModule.h @@ -3,6 +3,8 @@ #pragma once +#include "IBlobPersistor.h" + // React Native #include @@ -11,6 +13,7 @@ // Standard Library #include +#include #include #include @@ -24,7 +27,7 @@ class FileReaderModule : public facebook::xplat::module::CxxModule { SIZE = 2 }; - FileReaderModule() noexcept; + FileReaderModule(std::weak_ptr weakBlobPersistor) noexcept; ~FileReaderModule() noexcept override; @@ -56,10 +59,7 @@ class FileReaderModule : public facebook::xplat::module::CxxModule { #pragma endregion CxxModule private: - /// - /// Keeps members that can be accessed threads other than this module's owner accessible. - /// - //std::shared_ptr m_sharedState; + std::weak_ptr m_weakBlobPersistor; }; } // namespace Microsoft::React diff --git a/vnext/Shared/OInstance.cpp b/vnext/Shared/OInstance.cpp index 01303698497..1de359ec4fa 100644 --- a/vnext/Shared/OInstance.cpp +++ b/vnext/Shared/OInstance.cpp @@ -628,7 +628,7 @@ std::vector> InstanceImpl::GetDefaultNativeModules std::make_unique( m_innerInstance, Microsoft::React::GetFileReaderModuleName(), - []() { return Microsoft::React::CreateFileReaderModule(); }, + [transitionalProps]() { return Microsoft::React::CreateFileReaderModule(transitionalProps); }, nativeQueue)); return modules; From a73a4fe704f77b06d725a191ed2467c96e0cc15f Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Fri, 20 May 2022 02:48:59 -0700 Subject: [PATCH 84/91] Complete BlobTest --- vnext/Shared/Modules/FileReaderModule.cpp | 72 ++++------ vnext/Shared/Modules/FileReaderModule.h | 15 +- vnext/Shared/Modules/HttpModule.cpp | 1 - vnext/Shared/Modules/IHttpModuleProxy.h | 2 +- vnext/Shared/Networking/IHttpResource.h | 3 +- vnext/Shared/Networking/WinRTHttpResource.cpp | 7 +- vnext/Shared/OInstance.cpp | 3 +- vnext/overrides.json | 4 + vnext/src/IntegrationTests/BlobTest.js | 134 ++++++++++++++++-- 9 files changed, 169 insertions(+), 72 deletions(-) diff --git a/vnext/Shared/Modules/FileReaderModule.cpp b/vnext/Shared/Modules/FileReaderModule.cpp index 45a1d267c1f..1dc763f8016 100644 --- a/vnext/Shared/Modules/FileReaderModule.cpp +++ b/vnext/Shared/Modules/FileReaderModule.cpp @@ -29,7 +29,7 @@ using winrt::Windows::Foundation::IInspectable; namespace { constexpr char moduleName[] = "FileReaderModule"; -} //namespace +} // namespace namespace Microsoft::React { @@ -39,33 +39,27 @@ FileReaderModule::FileReaderModule(weak_ptr weakBlobPersistor) n : m_weakBlobPersistor{weakBlobPersistor} {} FileReaderModule::~FileReaderModule() noexcept /*override*/ -{ -} +{} #pragma region CxxModule -string FileReaderModule::getName() -{ +string FileReaderModule::getName() { return moduleName; } -std::map FileReaderModule::getConstants() -{ +std::map FileReaderModule::getConstants() { return {}; } -std::vector FileReaderModule::getMethods() -{ - return - { - { - /// - /// - /// Array of arguments passed from the JavaScript layer. - /// [0] - dynamic blob object { blobId, offset, size[, type] } - /// - /// - "readAsDataURL", +std::vector FileReaderModule::getMethods() { + return { + {/// + /// + /// Array of arguments passed from the JavaScript layer. + /// [0] - dynamic blob object { blobId, offset, size[, type] } + /// + /// + "readAsDataURL", [blobPersistor = m_weakBlobPersistor.lock()](dynamic args, Callback resolve, Callback reject) { if (!blobPersistor) { return reject({"Could not get BlobModule from ReactApplicationContext"}); @@ -97,54 +91,48 @@ std::vector FileReaderModule::getMethods() result += oss.str(); resolve({std::move(result)}); - } - }, - { - /// - /// - /// Array of arguments passed from the JavaScript layer. - /// [0] - dynamic blob object { blobId, offset, size } - /// [1] - string encoding - /// - /// - "readAsText", + }}, + {/// + /// + /// Array of arguments passed from the JavaScript layer. + /// [0] - dynamic blob object { blobId, offset, size } + /// [1] - string encoding + /// + /// + "readAsText", [blobPersistor = m_weakBlobPersistor.lock()](dynamic args, Callback resolve, Callback reject) { if (!blobPersistor) { return reject({"Could not get BlobModule from ReactApplicationContext"}); } auto blob = jsArgAsObject(args, 0); - auto encoding = jsArgAsString(args, 1);//Default: "UTF-8" + auto encoding = jsArgAsString(args, 1); // Default: "UTF-8" auto blobId = blob["blobId"].asString(); auto offset = blob["offset"].asInt(); auto size = blob["size"].asInt(); - //TODO: Make ResolveMessage throw + // TODO: Make ResolveMessage throw auto bytes = blobPersistor->ResolveMessage(std::move(blobId), offset, size); - //TODO: Handle non-UTF8 encodings - // See https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/charset/Charset.html + // TODO: Handle non-UTF8 encodings + // See https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/charset/Charset.html auto result = string{bytes.cbegin(), bytes.cend()}; resolve({std::move(result)}); - } - } - }; + }}}; } #pragma endregion CxxModule #pragma endregion FileReaderModule -/*extern*/ const char* GetFileReaderModuleName() noexcept -{ +/*extern*/ const char *GetFileReaderModuleName() noexcept { return moduleName; } /*extern*/ std::unique_ptr CreateFileReaderModule( - IInspectable const &inspectableProperties) noexcept { - + IInspectable const &inspectableProperties) noexcept { auto propId = ReactPropertyId>>{L"Blob.Persistor"}; auto propBag = ReactPropertyBag{inspectableProperties.try_as()}; @@ -157,4 +145,4 @@ std::vector FileReaderModule::getMethods() return nullptr; } -}//namespace +} // namespace Microsoft::React diff --git a/vnext/Shared/Modules/FileReaderModule.h b/vnext/Shared/Modules/FileReaderModule.h index 8ec6421ed3a..bde28680a45 100644 --- a/vnext/Shared/Modules/FileReaderModule.h +++ b/vnext/Shared/Modules/FileReaderModule.h @@ -21,23 +21,12 @@ namespace Microsoft::React { class FileReaderModule : public facebook::xplat::module::CxxModule { public: - enum class MethodId { - ReadAsDataURL = 0, - ReadAsText = 1, - SIZE = 2 - }; + enum class MethodId { ReadAsDataURL = 0, ReadAsText = 1, SIZE = 2 }; FileReaderModule(std::weak_ptr weakBlobPersistor) noexcept; ~FileReaderModule() noexcept override; - //struct SharedState { - // /// - // /// Keeps a raw reference to the module object to lazily retrieve the React Instance as needed. - // /// - // CxxModule *Module{nullptr}; - //}; - #pragma region CxxModule /// @@ -59,7 +48,7 @@ class FileReaderModule : public facebook::xplat::module::CxxModule { #pragma endregion CxxModule private: - std::weak_ptr m_weakBlobPersistor; + std::weak_ptr m_weakBlobPersistor; }; } // namespace Microsoft::React diff --git a/vnext/Shared/Modules/HttpModule.cpp b/vnext/Shared/Modules/HttpModule.cpp index 2f827db04c0..0c9f2947af2 100644 --- a/vnext/Shared/Modules/HttpModule.cpp +++ b/vnext/Shared/Modules/HttpModule.cpp @@ -40,7 +40,6 @@ static void SetUpHttpResource( shared_ptr resource, weak_ptr weakReactInstance, IInspectable &inspectableProperties) { - resource->SetOnRequestSuccess([weakReactInstance](int64_t requestId) { auto args = dynamic::array(requestId); diff --git a/vnext/Shared/Modules/IHttpModuleProxy.h b/vnext/Shared/Modules/IHttpModuleProxy.h index 194d9171916..0c0f3c9b381 100644 --- a/vnext/Shared/Modules/IHttpModuleProxy.h +++ b/vnext/Shared/Modules/IHttpModuleProxy.h @@ -19,7 +19,7 @@ namespace Microsoft::React { struct IHttpModuleProxy { virtual ~IHttpModuleProxy() noexcept {} - //TODO: Implement custom URI handlers. + // TODO: Implement custom URI handlers. virtual void AddUriHandler(std::shared_ptr uriHandler) noexcept = 0; virtual void AddRequestBodyHandler(std::shared_ptr requestBodyHandler) noexcept = 0; diff --git a/vnext/Shared/Networking/IHttpResource.h b/vnext/Shared/Networking/IHttpResource.h index c513855bace..7a8434e9f6f 100644 --- a/vnext/Shared/Networking/IHttpResource.h +++ b/vnext/Shared/Networking/IHttpResource.h @@ -34,7 +34,8 @@ struct IHttpResource { static std::shared_ptr Make() noexcept; - static std::shared_ptr Make(winrt::Windows::Foundation::IInspectable const &inspectableProperties) noexcept; + static std::shared_ptr Make( + winrt::Windows::Foundation::IInspectable const &inspectableProperties) noexcept; virtual ~IHttpResource() noexcept {} diff --git a/vnext/Shared/Networking/WinRTHttpResource.cpp b/vnext/Shared/Networking/WinRTHttpResource.cpp index 9996905d764..fee66e29c4a 100644 --- a/vnext/Shared/Networking/WinRTHttpResource.cpp +++ b/vnext/Shared/Networking/WinRTHttpResource.cpp @@ -322,6 +322,9 @@ fire_and_forget WinRTHttpResource::PerformSendRequest(HttpRequestMessage &&reque auto inputStream = co_await response.Content().ReadAsInputStreamAsync(); auto reader = DataReader{inputStream}; + // #9510 - 10mb limit on fetch + co_await reader.LoadAsync(10 * 1024 * 1024); + // Let response handler take over, if set if (auto responseHandler = self->m_responseHandler.lock()) { if (responseHandler->Supports(coReqArgs->ResponseType)) { @@ -343,8 +346,6 @@ fire_and_forget WinRTHttpResource::PerformSendRequest(HttpRequestMessage &&reque reader.UnicodeEncoding(UnicodeEncoding::Utf8); } - // #9510 - 10mb limit on fetch - co_await reader.LoadAsync(10 * 1024 * 1024); auto length = reader.UnconsumedBufferLength(); if (isText) { @@ -390,7 +391,7 @@ fire_and_forget WinRTHttpResource::PerformSendRequest(HttpRequestMessage &&reque void WinRTHttpResource::AddUriHandler(shared_ptr /*uriHandler*/) noexcept /*override*/ { - //TODO: Implement custom URI handling. + // TODO: Implement custom URI handling. } void WinRTHttpResource::AddRequestBodyHandler(shared_ptr requestBodyHandler) noexcept /*override*/ diff --git a/vnext/Shared/OInstance.cpp b/vnext/Shared/OInstance.cpp index 1de359ec4fa..fc40263f557 100644 --- a/vnext/Shared/OInstance.cpp +++ b/vnext/Shared/OInstance.cpp @@ -624,8 +624,7 @@ std::vector> InstanceImpl::GetDefaultNativeModules [transitionalProps]() { return Microsoft::React::CreateBlobModule(transitionalProps); }, nativeQueue)); - modules.push_back( - std::make_unique( + modules.push_back(std::make_unique( m_innerInstance, Microsoft::React::GetFileReaderModuleName(), [transitionalProps]() { return Microsoft::React::CreateFileReaderModule(transitionalProps); }, diff --git a/vnext/overrides.json b/vnext/overrides.json index 604b2ebed5b..4bdfbe0af30 100644 --- a/vnext/overrides.json +++ b/vnext/overrides.json @@ -115,6 +115,10 @@ "baseFile": "index.js", "baseHash": "2a0bd511c691be2ac3da45a3c77250aaf55414e1" }, + { + "type": "platform", + "file": "src/IntegrationTests/BlobTest.js" + }, { "type": "platform", "file": "src/IntegrationTests/DummyTest.js" diff --git a/vnext/src/IntegrationTests/BlobTest.js b/vnext/src/IntegrationTests/BlobTest.js index 4e740687372..6e731792832 100644 --- a/vnext/src/IntegrationTests/BlobTest.js +++ b/vnext/src/IntegrationTests/BlobTest.js @@ -11,17 +11,134 @@ const ReactNative = require('react-native'); const {AppRegistry, View} = ReactNative; -const {TestModule, BlobModule} = ReactNative.NativeModules; +const {TestModule} = ReactNative.NativeModules; type State = { statusCode: number, xhr: XMLHttpRequest, + expected : String, }; class BlobTest extends React.Component<{...}, State> { state: State = { statusCode: 0, xhr: new XMLHttpRequest(), + expected : // https://www.facebook.com/favicon.ico + 'data:application/octet-stream;base64,' + + 'AAABAAIAEBAAAAEAIABoBAAAJgAAACAgAAABACAAqBAAAI4EAAAoAAAAEAAAACAA' + + 'AAABACAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOFl' + + 'BiviZgKP4WYB1f//////////4WUA1eJmAI/hawYrAAAAAAAAAAAAAAAAAAAAAAAA' + + 'AAAAAAAA/4ArBuNpA5TkawP942kC/+NpAv///////////+NpAv/jaQL/5GoD/eNp' + + 'A5T/gCsGAAAAAAAAAAAAAAAA/4ArBuVvBL3lbgT/5W4E/+VuBP/lbgT/////////' + + '///lbgT/5W4E/+VuBP/lbgT/5W8Evf+AKwYAAAAAAAAAAOlzBZTncwX/53MF/+dz' + + 'Bf/ncwX/53MF////////////53MF/+dzBf/ncwb/53MF/+dzBv/pcweUAAAAAO19' + + 'DCvpeAf96HcH/+l4B//odwf/6XgH/+l4B////////////+h3B//odwf/6XgH/+h3' + + 'B//peAf/6nkH/e19DCvrfQmP630J/+t9Cf/rfAn/630J/+t8Cf/rfAn/////////' + + '///rfQn/630J/+p8CP/rfQn/6nwI/+p8CP/rfQuP7YEL1e2BCv/tgQr/7IEK/+2B' + + 'Cv/////////////////////////////////uiRj/7IEK/+2CCv/tggr/7YIM1e6G' + + 'DfPvhgz/74YM/++HDP/vhgz/////////////////////////////////8Zw4/++G' + + 'DP/uhgz/7oYM/+6GDPPwiw7z8IsN//CLDf/wiw3/8IsN//CLDf/wiw3/////////' + + '///wig3/8IoN//CLDf/wig3/8IsN//CLDf/xjA/z85EQ1fOQD//zkA//85AP//OQ' + + 'D//zkA//85AP////////////8o8P//KPD//ykA//8o8P//KQD//ykA//85EQ1fSU' + + 'EI/1lRH/9ZUR//SUEP/1lRH/9JQQ//SUEP/+9uz///////jDev/0mRz/9ZUR//SV' + + 'Ef/1lRH/9ZUR//SUEI/5mhgr95kS/faZEv/2mRL/9pkS//aZEv/2mRL//Nqo////' + + '//////////////rLhv/3mhL/9pkS//eZEv35mhgrAAAAAPifFZT4nhT/+Z8U//ie' + + 'FP/5nxT/+Z8U//ikI//83a3//vjw//78+f/7yX3/+J4T//ieFP/4nxWUAAAAAAAA' + + 'AAD/qisG+6MWvfqjFf/6oxX/+qMV//qjFf/6oxX/+qMV//qjFf/6oxX/+qIV//qj' + + 'Ff/7oxa9/6orBgAAAAAAAAAAAAAAAP+qKwb9qRiU/agW/fyoF//8qBf//agX//yo' + + 'F//9qBf//agX//2oF/39qRiU/6orBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP+y' + + 'Hiv/rRmP/6wZ1f+tGPP/rBjz/64Z1f+vGY//sh4rAAAAAAAAAAAAAAAAAAAAAPAP' + + 'AADAAwAAgAEAAIABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAB' + + 'AACAAQAAwAMAAPAPAAAoAAAAIAAAAEAAAAABACAAAAAAAAAQAAAAAAAAAAAAAAAA' + + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP+A' + + 'AAbiZQRH4GMAlf//////////////////////////4GQAv+BjAJXiZQBH/4AABgAA' + + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAOpqCxjiZgKW4WYB8eJmAf/hZQH/////////' + + '///////////////////hZgH/4mYB/+FmAf/iZwHx4mYClupqCxgAAAAAAAAAAAAA' + + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9t' + + 'JAfkagSC42kC9ONoAv/jaAL/4mgC/+NoAv///////////////////////////+Jo' + + 'Af/iaAL/4mgB/+JoAv/iaAL/42kC9ORqBIL/bSQHAAAAAAAAAAAAAAAAAAAAAAAA' + + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADqagsY5GoEx+NqA//jagP/42oD/+Nq' + + 'A//kawP/42oD////////////////////////////42oD/+RrA//jagP/5GsD/+Rr' + + 'A//kawP/5GsD/+RsBMfqdQsYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + + 'AAAAAAAA6HEGLeVuBOPlbQT/5GwD/+VtBP/kbAP/5GwD/+VtBP/kbAP/////////' + + '///////////////////kbAP/5G0D/+RsA//kbQP/5G0D/+RtA//kbQP/5G0D/+Ru' + + 'A+PocQYtAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOp1CxjmcAbj5W8E/+Vv' + + 'BP/lbwT/5W8E/+VvBP/lbwT/5W8E/+VvBP///////////////////////////+Vv' + + 'BP/mcAX/5W8E/+ZwBf/mcAX/5W8E/+ZwBf/lbwT/5W8E/+ZwBuPqdQsYAAAAAAAA' + + 'AAAAAAAAAAAAAAAAAAD/kiQH53IFx+ZyBf/mcQX/5nEF/+ZxBf/mcQX/5nEF/+Zx' + + 'Bf/ncgX/5nEF////////////////////////////53IG/+ZxBf/ncgb/5nEF/+Zx' + + 'Bf/mcgX/5nEF/+ZyBf/mcgX/5nEF/+dyBcf/kiQHAAAAAAAAAAAAAAAAAAAAAOd2' + + 'CILodAb/53QG/+h0Bv/odAb/6HQG/+h0Bv/odAb/6HQG/+d0Bv/odAb/////////' + + '///////////////////ndAX/53QG/+d0Bf/ndAb/53QG/+h1Bv/ndAb/6HUG/+h1' + + 'Bv/ndAX/6HUG/+d2BoIAAAAAAAAAAAAAAADqgAsY6HYG9Oh2B//odgb/6HYH/+h2' + + 'B//odgb/6HYH/+h2Bv/odgb/6HYH/+h2Bv///////////////////////////+l3' + + 'B//odgb/6XcH/+h2Bv/odgb/6HYH/+h2Bv/odgf/6HYH/+h2Bv/odgf/6HYG9OqA' + + 'CxgAAAAAAAAAAOt6CZbpeQj/6nkI/+l5CP/qeQj/6nkI/+l5B//qeQj/6XkH/+l5' + + 'B//peQf/6XkH////////////////////////////6XkI/+l5B//peQj/6XkH/+l5' + + 'B//peQj/6XkH/+l5CP/peQj/6XkH/+l5CP/peQf/63oJlgAAAAD/gCsG7H0K8et8' + + 'Cf/qewj/63wJ/+p7CP/qewj/6nsI/+p7CP/qewj/6nsI/+t8Cf/qewj/////////' + + '///////////////////qewj/6nwJ/+p7CP/qfAn/6nwJ/+p7CP/qfAn/6nsI/+p7' + + 'CP/rfAn/6nsI/+t8Cf/sfQrx/4ArBu2BC0frfQn/630J/+t+Cf/rfQn/634J/+t+' + + 'Cf/rfgn/634J////////////////////////////////////////////////////' + + '///////////////////zs27/634J/+x+Cf/rfgn/634J/+t+Cf/rfgn/634J/+t+' + + 'Cf/tgQtH7IAKleyACv/sgAr/7IAK/+yACv/sgAr/7IAK/+yACv/sgAr/////////' + + '//////////////////////////////////////////////////////////////XC' + + 'iv/sgAr/7IAK/+yACv/sgAr/7IAJ/+yACv/sgAn/7IAJ/+yACpXugwu/7YML/+2D' + + 'C//tggr/7YML/+2CCv/tggr/7YIK/+2CCv//////////////////////////////' + + '////////////////////////////////////////+NKn/+2DC//tggr/7YML/+2D' + + 'C//tgwv/7YML/+2DC//tgwv/7oMLv++GDNnuhQv/7oUL/+6FC//uhQv/7oUL/+6F' + + 'C//vhQz/7oUL////////////////////////////////////////////////////' + + '///////////////////64cT/7oUL/+6FC//uhQv/7oUL/+6EC//uhQv/7oQL/+6E' + + 'C//vhgzZ74gO8++IDP/viAz/74cM/++IDP/vhwz/74cM/++HDP/vhwz/////////' + + '//////////////////////////////////////////////////////////////3w' + + '4f/viA3/74cM/++IDf/viA3/74cM/++IDf/vhwz/74cM/++HDfPwiw7z8IoN//CK' + + 'Df/wig3/8IoN//CKDf/wig3/8IkN//CKDf/wiQ3/8IkN//CKDf/wiQ3/////////' + + '///////////////////wiQ3/8IoN//CJDf/wig3/8IoN//CJDf/wig3/8IkN//CJ' + + 'Df/wiQ3/8IkN//CJDf/wiQ3/8IsO8/KNDtnxjA7/8YwO//GMDf/xjA7/8YwN//GM' + + 'Df/xjA3/8YwN//GMDf/xjA3/8YwO//GMDf////////////////////////////GM' + + 'Dv/xjA7/8YwO//GMDv/xjA7/8YwO//GMDv/xjA7/8YwO//GMDv/xjA7/8YwO//GM' + + 'Dv/yjQ7Z8o8Pv/KPD//yjw//8o8P//KPD//yjw//8o8P//KPD//yjw//8o8P//KP' + + 'D//yjg7/8o8P////////////////////////////8Y4O//KODv/xjg7/8o4O//KO' + + 'Dv/yjg7/8o4O//KODv/yjg7/8o8P//KODv/yjw//8o8P//OQEL/zkQ+V85EP//OR' + + 'D//zkQ//85EP//ORD//zkQ//85EP//ORD//zkQ//85EP//OREP/zkQ///vr0////' + + '///////////////////0myb/85EQ//ORD//zkRD/85EQ//ORD//zkRD/85EP//OR' + + 'D//zkQ//85EP//ORD//zkQ//85EPlfSXEkf0kxD/9JMQ//SUEP/0kxD/9JQQ//SU' + + 'EP/zkxD/9JQQ//OTEP/zkxD/9JQQ//OTEP/86tD///////////////////////rV' + + 'ov/1nSb/85MQ//STEP/0kxD/9JMQ//STEP/0kxD/9JMQ//SUEP/0kxD/9JQQ//SU' + + 'EP/0kxJH/6orBvWWEvH1lhH/9ZYR//WWEf/1lhH/9ZYR//WWEf/1lhH/9ZYR//WW' + + 'Ef/1lhH/9ZYR//vZq///////////////////////////////////////////////' + + '///1lhH/9ZYR//WWEf/1lhH/9ZYR//WWEf/1lhH/9ZYS8f+qKwYAAAAA95kTlvaY' + + 'Ev/2mBH/9pgS//aYEf/2mBH/9ZgR//aYEf/1mBH/9ZgR//aYEv/1mBH/+LFN////' + + '//////////////////////////////////////////////aYEv/1mBH/9pgS//aY' + + 'Ev/1mBH/9pgS//WYEf/3mRGWAAAAAAAAAAD/nxUY+JwU9PebE//3mxP/95sT//eb' + + 'E//3mxP/95sT//ebE//3mxP/95oS//ebE//3mhL//OK7////////////////////' + + '////////////////////////95sT//ebE//3mxP/95sT//aaEv/3mxP/95sT9P+f' + + 'FRgAAAAAAAAAAAAAAAD5nxSC+J0T//idE//4nRP/+J0T//ecE//4nRP/95wT//ec' + + 'E//4nRP/95wT//idE//4pSf//efF////////////////////////////////////' + + '///4nRP/950T//idE//4nRP/+J0T//idE//5nxSCAAAAAAAAAAAAAAAAAAAAAP+2' + + 'JAf6oBXH+aAU//mgFP/5oBT/+J8U//mgFP/4nxT/+J8U//mfFP/4nxT/+Z8U//mf' + + 'FP/5oRf/+86H//7w2v/+/Pj//v36//758f/+8+P//evQ//mgFP/5nxT/+aAU//mg' + + 'FP/4nxT/+qAVx/+2JAcAAAAAAAAAAAAAAAAAAAAAAAAAAP+qFRj7oxXj+aEU//mh' + + 'FP/6ohX/+aEU//qiFf/6ohX/+qIV//qiFf/6ohX/+qIV//mhFP/6ohX/+aEU//mh' + + 'FP/6ohX/+aEU//qiFf/6ohX/+aEU//qiFf/5oRT/+aEU//ujFeP/qhUYAAAAAAAA' + + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP+mFi78pBXj+6QV//ukFv/7pBX/+6QW//uk' + + 'Fv/6pBX/+6QW//qkFf/6pBX/+6QW//qkFf/7pBb/+6QW//ulFv/7pBb/+6UW//ul' + + 'Fv/7pBX/+6UW//ukFf/8pBXj/6QXLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + + 'AAAAAAAAAAAAAP+qIBj8qBfH/KcW//ynF//8pxb//KcW//ynFv/8pxb//KcW//yn' + + 'Fv/7phb//KcW//umFv/7phb/+6YW//umFv/7phb/+6YW//ynFv/7phb//KgXx/+q' + + 'IBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP+2' + + 'JAf9qxiC/akY9PypF//8qRf//KgX//ypF//8qBf//KgX//2pF//8qBf//akX//2p' + + 'F//9qRf//akX//2pF//9qRf//qkY9P2rGIL/tiQHAAAAAAAAAAAAAAAAAAAAAAAA' + + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/tSAY/60alv+s' + + 'GPH+rBj//qwY//6sGP/+rBj//asY//6sGP/9qxj//asY//2rF//9qxj//qsX8f2s' + + 'GJb/tSAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/9UrBv+wGUf/rxqV/68Zv/+v' + + 'Gtn/rhnz/64Z8/+vGtn/rxm//68alf+wGUf/1SsGAAAAAAAAAAAAAAAAAAAAAAAA' + + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/AA///AAD//AAAP/gAAB/wAAAP4AAAB8AA' + + 'AAPAAAADgAAAAYAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + + 'AAAAAAAAAAAAAAAAAACAAAABgAAAAcAAAAPAAAAD4AAAB/AAAA/4AAAf/AAAP/8A' + + 'AP//wAP/', }; _get = () => { @@ -32,8 +149,7 @@ class BlobTest extends React.Component<{...}, State> { }; this.state.xhr.open( 'GET', - //'https://www.facebook.com/favicon.ico?r=1&t=1652923053846', - 'http://localhost:666/hello.txt', + 'https://www.facebook.com/favicon.ico', ); this.state.xhr.setRequestHeader('Accept-Encoding', 'utf-8'); this.state.xhr.responseType = "blob"; @@ -41,9 +157,6 @@ class BlobTest extends React.Component<{...}, State> { }; _getSucceeded = () => { - console.log( - `_getSucceeded [${this.state.statusCode}],[${this.state.xhr.response}]`, - ); return ( this.state.statusCode === 200 && this.state.xhr.response !== null @@ -69,9 +182,12 @@ class BlobTest extends React.Component<{...}, State> { componentDidMount() { this._get(); - this._waitFor(this._getSucceeded, 5, doneSucceeded => { - console.log(`Suc: [${doneSucceeded}]`); - TestModule.markTestPassed(doneSucceeded); + this._waitFor(this._getSucceeded, 6, doneSucceeded => { + let reader = new FileReader(); + reader.readAsDataURL(this.state.xhr.response); + reader.onload = () => { + TestModule.markTestPassed(doneSucceeded && this.state.expected === reader.result); + }; }); } From 35bec9eb52c46961b0b4003bf51d76e023d90ff1 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Fri, 20 May 2022 03:32:33 -0700 Subject: [PATCH 85/91] Make IBlobPersistor::ResolveMessage throw std::invalid_argument --- vnext/Shared/Modules/BlobModule.cpp | 29 +++++++++++++++---- vnext/Shared/Modules/BlobModule.h | 2 +- vnext/Shared/Modules/FileReaderModule.cpp | 16 +++++++--- vnext/Shared/Modules/IBlobPersistor.h | 7 ++++- vnext/Shared/Networking/WinRTHttpResource.cpp | 10 ++++++- 5 files changed, 51 insertions(+), 13 deletions(-) diff --git a/vnext/Shared/Modules/BlobModule.cpp b/vnext/Shared/Modules/BlobModule.cpp index 6ea43004635..34322366c84 100644 --- a/vnext/Shared/Modules/BlobModule.cpp +++ b/vnext/Shared/Modules/BlobModule.cpp @@ -197,7 +197,8 @@ vector BlobModule::getMethods() { }}, {"sendOverSocket", - [persistor = m_blobPersistor, + [weakState = weak_ptr(m_sharedState), + persistor = m_blobPersistor, propBag = ReactPropertyBag{m_inspectableProperties.try_as()}](dynamic args) { auto propId = ReactPropertyId>>{L"WebSocketModule.Proxy"}; shared_ptr wsProxy; @@ -214,7 +215,15 @@ vector BlobModule::getMethods() { auto size = blob[sizeKey].getInt(); auto socketID = jsArgAsInt(args, 1); - auto data = persistor->ResolveMessage(std::move(blobId), offset, size); + winrt::array_view data; + try { + data = persistor->ResolveMessage(std::move(blobId), offset, size); + } catch (const std::invalid_argument &e) { + if (auto sharedState = weakState.lock()) { + Modules::SendEvent(sharedState->Module->getInstance(), "blobFailed", e.what()); + } + return; + } auto buffer = CryptographicBuffer::CreateFromByteArray(data); auto winrtString = CryptographicBuffer::EncodeToBase64String(std::move(buffer)); @@ -236,8 +245,16 @@ vector BlobModule::getMethods() { auto type = part[typeKey].asString(); if (blobKey == type) { auto blob = part[dataKey]; - auto bufferPart = - persistor->ResolveMessage(blob[blobIdKey].asString(), blob[offsetKey].asInt(), blob[sizeKey].asInt()); + winrt::array_view bufferPart; + try { + bufferPart = persistor->ResolveMessage( + blob[blobIdKey].asString(), blob[offsetKey].asInt(), blob[sizeKey].asInt()); + } catch (const std::invalid_argument &e) { + if (auto sharedState = weakState.lock()) { + Modules::SendEvent(sharedState->Module->getInstance(), "blobFailed", e.what()); + } + return; + } buffer.reserve(buffer.size() + bufferPart.size()); buffer.insert(buffer.end(), bufferPart.begin(), bufferPart.end()); @@ -275,7 +292,7 @@ vector BlobModule::getMethods() { #pragma region IBlobPersistor -winrt::array_view MemoryBlobPersistor::ResolveMessage(string &&blobId, int64_t offset, int64_t size) noexcept { +winrt::array_view MemoryBlobPersistor::ResolveMessage(string &&blobId, int64_t offset, int64_t size) { if (offset < 0 || size < 1) return {}; @@ -294,7 +311,7 @@ winrt::array_view MemoryBlobPersistor::ResolveMessage(string &&blobId, } // Not found. - return {}; + throw std::invalid_argument("Blob object not found"); } void MemoryBlobPersistor::RemoveMessage(string &&blobId) noexcept { diff --git a/vnext/Shared/Modules/BlobModule.h b/vnext/Shared/Modules/BlobModule.h index 2820f0d8875..06e9b0b4695 100644 --- a/vnext/Shared/Modules/BlobModule.h +++ b/vnext/Shared/Modules/BlobModule.h @@ -30,7 +30,7 @@ class MemoryBlobPersistor final : public IBlobPersistor { public: #pragma region IBlobPersistor - winrt::array_view ResolveMessage(std::string &&blobId, int64_t offset, int64_t size) noexcept override; + winrt::array_view ResolveMessage(std::string &&blobId, int64_t offset, int64_t size) override; void RemoveMessage(std::string &&blobId) noexcept override; diff --git a/vnext/Shared/Modules/FileReaderModule.cpp b/vnext/Shared/Modules/FileReaderModule.cpp index 1dc763f8016..b56ec9f6bf6 100644 --- a/vnext/Shared/Modules/FileReaderModule.cpp +++ b/vnext/Shared/Modules/FileReaderModule.cpp @@ -71,8 +71,12 @@ std::vector FileReaderModule::getMethods() { auto offset = blob["offset"].asInt(); auto size = blob["size"].asInt(); - // TODO: Make ResolveMessage throw - auto bytes = blobPersistor->ResolveMessage(std::move(blobId), offset, size); + winrt::array_view bytes; + try { + bytes = blobPersistor->ResolveMessage(std::move(blobId), offset, size); + } catch (const std::invalid_argument &e) { + return reject({e.what()}); + } auto result = string{"data:"}; auto typeItr = blob.find("type"); @@ -112,8 +116,12 @@ std::vector FileReaderModule::getMethods() { auto offset = blob["offset"].asInt(); auto size = blob["size"].asInt(); - // TODO: Make ResolveMessage throw - auto bytes = blobPersistor->ResolveMessage(std::move(blobId), offset, size); + winrt::array_view bytes; + try { + bytes = blobPersistor->ResolveMessage(std::move(blobId), offset, size); + } catch (const std::invalid_argument &e) { + return reject({e.what()}); + } // TODO: Handle non-UTF8 encodings // See https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/charset/Charset.html diff --git a/vnext/Shared/Modules/IBlobPersistor.h b/vnext/Shared/Modules/IBlobPersistor.h index 6d121049166..b1035fc461e 100644 --- a/vnext/Shared/Modules/IBlobPersistor.h +++ b/vnext/Shared/Modules/IBlobPersistor.h @@ -13,7 +13,12 @@ namespace Microsoft::React { struct IBlobPersistor { - virtual winrt::array_view ResolveMessage(std::string &&blobId, int64_t offset, int64_t size) noexcept = 0; + /// + /// + /// When an entry for blobId cannot be found. + /// + /// + virtual winrt::array_view ResolveMessage(std::string &&blobId, int64_t offset, int64_t size) = 0; virtual void RemoveMessage(std::string &&blobId) noexcept = 0; diff --git a/vnext/Shared/Networking/WinRTHttpResource.cpp b/vnext/Shared/Networking/WinRTHttpResource.cpp index fee66e29c4a..9f54bcf414f 100644 --- a/vnext/Shared/Networking/WinRTHttpResource.cpp +++ b/vnext/Shared/Networking/WinRTHttpResource.cpp @@ -235,7 +235,15 @@ fire_and_forget WinRTHttpResource::PerformSendRequest(HttpRequestMessage &&reque auto bodyHandler = self->m_requestBodyHandler.lock(); if (bodyHandler && bodyHandler->Supports(data)) { auto contentTypeString = contentType ? winrt::to_string(contentType.ToString()) : ""; - auto blob = bodyHandler->ToRequestBody(data, contentTypeString); + dynamic blob; + try { + blob = bodyHandler->ToRequestBody(data, contentTypeString); + } catch (const std::invalid_argument &e) { + if (self->m_onError) { + self->m_onError(coReqArgs->RequestId, e.what()); + } + co_return; + } auto bytes = blob["bytes"]; auto byteVector = vector(bytes.size()); for (auto &byte : bytes) { From 40ee5cf0c60bbbcd8820a882c0ccadc9bebcc15f Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Fri, 20 May 2022 03:40:36 -0700 Subject: [PATCH 86/91] Fail on Content-Encoding parsing even if no error handler --- vnext/Shared/Networking/WinRTHttpResource.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/vnext/Shared/Networking/WinRTHttpResource.cpp b/vnext/Shared/Networking/WinRTHttpResource.cpp index 9f54bcf414f..76fdee79377 100644 --- a/vnext/Shared/Networking/WinRTHttpResource.cpp +++ b/vnext/Shared/Networking/WinRTHttpResource.cpp @@ -278,7 +278,9 @@ fire_and_forget WinRTHttpResource::PerformSendRequest(HttpRequestMessage &&reque if (!contentEncoding.empty()) { if (!content.Headers().ContentEncoding().TryParseAdd(to_hstring(contentEncoding))) { if (self->m_onError) - co_return self->m_onError(coReqArgs->RequestId, "Failed to parse Content-Encoding"); + self->m_onError(coReqArgs->RequestId, "Failed to parse Content-Encoding"); + + co_return; } } From eceaea40dc5d145396603cf032f208cf1420b9bc Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Fri, 20 May 2022 12:58:57 -0700 Subject: [PATCH 87/91] Remove MIME mappings. Currently unused --- vnext/Shared/Modules/BlobModule.cpp | 76 ----------------------------- 1 file changed, 76 deletions(-) diff --git a/vnext/Shared/Modules/BlobModule.cpp b/vnext/Shared/Modules/BlobModule.cpp index 34322366c84..0cbfbc6abbc 100644 --- a/vnext/Shared/Modules/BlobModule.cpp +++ b/vnext/Shared/Modules/BlobModule.cpp @@ -48,82 +48,6 @@ constexpr char offsetKey[] = "offset"; constexpr char sizeKey[] = "size"; constexpr char typeKey[] = "type"; constexpr char dataKey[] = "data"; - -// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types -const static std::unordered_map extensionToMime = { - {".aac", "audio/aac"}, - {".abw", "application/x-abiword"}, - {".arc", "application/x-freearc"}, - {".avif", "image/avif"}, - {".avi", "video/x-msvideo"}, - {".azw", "application/vnd.amazon.ebook"}, - {".bin", "application/octet-stream"}, - {".bmp", "image/bmp"}, - {".bz", "application/x-bzip"}, - {".bz2", "application/x-bzip2"}, - {".cda", "application/x-cdf"}, - {".csh", "application/x-csh"}, - {".css", "text/css"}, - {".csv", "text/csv"}, - {".doc", "application/msword"}, - {".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"}, - {".eot", "application/vnd.ms-fontobject"}, - {".epub", "application/epub+zip"}, - {".gz", "application/gzip"}, - {".gif", "image/gif"}, - {".htm .html", "text/html"}, - {".ico", "image/vnd.microsoft.icon"}, - {".ics", "text/calendar"}, - {".jar", "application/java-archive"}, - {".jpeg .jpg", "image/jpeg"}, - {".js", "text/javascript"}, - {".json", "application/json"}, - {".jsonld", "application/ld+json"}, - {".mid .midi", "audio/midi audio/x-midi"}, - {".mjs", "text/javascript"}, - {".mp3", "audio/mpeg"}, - {".mp4", "video/mp4"}, - {".mpeg", "video/mpeg"}, - {".mpkg", "application/vnd.apple.installer+xml"}, - {".odp", "application/vnd.oasis.opendocument.presentation"}, - {".ods", "application/vnd.oasis.opendocument.spreadsheet"}, - {".odt", "application/vnd.oasis.opendocument.text"}, - {".oga", "audio/ogg"}, - {".ogv", "video/ogg"}, - {".ogx", "application/ogg"}, - {".opus", "audio/opus"}, - {".otf", "font/otf"}, - {".png", "image/png"}, - {".pdf", "application/pdf"}, - {".php", "application/x-httpd-php"}, - {".ppt", "application/vnd.ms-powerpoint"}, - {".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"}, - {".rar", "application/vnd.rar"}, - {".rtf", "application/rtf"}, - {".sh", "application/x-sh"}, - {".svg", "image/svg+xml"}, - {".swf", "application/x-shockwave-flash"}, - {".tar", "application/x-tar"}, - {".tif .tiff", "image/tiff"}, - {".ts", "video/mp2t"}, - {".ttf", "font/ttf"}, - {".txt", "text/plain"}, - {".vsd", "application/vnd.visio"}, - {".wav", "audio/wav"}, - {".weba", "audio/webm"}, - {".webm", "video/webm"}, - {".webp", "image/webp"}, - {".woff", "font/woff"}, - {".woff2", "font/woff2"}, - {".xhtml", "application/xhtml+xml"}, - {".xls", "application/vnd.ms-excel"}, - {".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}, - {".xml", "application/xml"}, - {".xul", "application/vnd.mozilla.xul+xml"}, - {".zip", "application/zip"}, - {".3gp", "video/3gpp; audio/3gpp if it doesn't contain video"}, - {".3g2", "video/3gpp2; audio/3gpp2 if it doesn't contain video"}, - {".7z", "application/x-7z-compressed"}}; // extensionToMime } // namespace namespace Microsoft::React { From a68a0aa3bbfe056179b1b6a5b332b3d8c0ce753c Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Fri, 20 May 2022 13:26:02 -0700 Subject: [PATCH 88/91] MemoryBlobPersistor::ResolveMessage throw on out of bounds --- vnext/Shared/Modules/BlobModule.cpp | 26 +++++++++++------------ vnext/Shared/Modules/FileReaderModule.cpp | 8 +++---- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/vnext/Shared/Modules/BlobModule.cpp b/vnext/Shared/Modules/BlobModule.cpp index 0cbfbc6abbc..b03b2e44269 100644 --- a/vnext/Shared/Modules/BlobModule.cpp +++ b/vnext/Shared/Modules/BlobModule.cpp @@ -142,7 +142,7 @@ vector BlobModule::getMethods() { winrt::array_view data; try { data = persistor->ResolveMessage(std::move(blobId), offset, size); - } catch (const std::invalid_argument &e) { + } catch (const std::exception &e) { if (auto sharedState = weakState.lock()) { Modules::SendEvent(sharedState->Module->getInstance(), "blobFailed", e.what()); } @@ -173,7 +173,7 @@ vector BlobModule::getMethods() { try { bufferPart = persistor->ResolveMessage( blob[blobIdKey].asString(), blob[offsetKey].asInt(), blob[sizeKey].asInt()); - } catch (const std::invalid_argument &e) { + } catch (const std::exception &e) { if (auto sharedState = weakState.lock()) { Modules::SendEvent(sharedState->Module->getInstance(), "blobFailed", e.what()); } @@ -217,25 +217,23 @@ vector BlobModule::getMethods() { #pragma region IBlobPersistor winrt::array_view MemoryBlobPersistor::ResolveMessage(string &&blobId, int64_t offset, int64_t size) { - if (offset < 0 || size < 1) + if (size < 1) return {}; scoped_lock lock{m_mutex}; auto dataItr = m_blobs.find(std::move(blobId)); - if (dataItr != m_blobs.cend()) { - auto &bytes = (*dataItr).second; - - auto endBound = static_cast(offset + size); - // Out of bounds. - if (endBound > bytes.size()) - return {}; + // Not found. + if (dataItr == m_blobs.cend()) + throw std::invalid_argument("Blob object not found"); - return winrt::array_view(bytes.data() + offset, bytes.data() + endBound); - } + auto &bytes = (*dataItr).second; + auto endBound = static_cast(offset + size); + // Out of bounds. + if (endBound > bytes.size() || offset >= static_cast(bytes.size()) || offset < 0) + throw std::out_of_range("Offset or size out of range"); - // Not found. - throw std::invalid_argument("Blob object not found"); + return winrt::array_view(bytes.data() + offset, bytes.data() + endBound); } void MemoryBlobPersistor::RemoveMessage(string &&blobId) noexcept { diff --git a/vnext/Shared/Modules/FileReaderModule.cpp b/vnext/Shared/Modules/FileReaderModule.cpp index b56ec9f6bf6..36942f7f8c2 100644 --- a/vnext/Shared/Modules/FileReaderModule.cpp +++ b/vnext/Shared/Modules/FileReaderModule.cpp @@ -74,7 +74,7 @@ std::vector FileReaderModule::getMethods() { winrt::array_view bytes; try { bytes = blobPersistor->ResolveMessage(std::move(blobId), offset, size); - } catch (const std::invalid_argument &e) { + } catch (const std::exception &e) { return reject({e.what()}); } @@ -119,12 +119,12 @@ std::vector FileReaderModule::getMethods() { winrt::array_view bytes; try { bytes = blobPersistor->ResolveMessage(std::move(blobId), offset, size); - } catch (const std::invalid_argument &e) { + } catch (const std::exception &e) { return reject({e.what()}); } - // TODO: Handle non-UTF8 encodings - // See https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/charset/Charset.html + // #9982 - Handle non-UTF8 encodings + // See https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/charset/Charset.html auto result = string{bytes.cbegin(), bytes.cend()}; resolve({std::move(result)}); From ffcdfb8e58a7be4d8ae0d6c9a7600c7914a4b781 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Fri, 20 May 2022 14:17:49 -0700 Subject: [PATCH 89/91] lint --- vnext/src/IntegrationTests/BlobTest.js | 250 ++++++++++++------------- 1 file changed, 124 insertions(+), 126 deletions(-) diff --git a/vnext/src/IntegrationTests/BlobTest.js b/vnext/src/IntegrationTests/BlobTest.js index 6e731792832..a9a87a365d5 100644 --- a/vnext/src/IntegrationTests/BlobTest.js +++ b/vnext/src/IntegrationTests/BlobTest.js @@ -16,129 +16,131 @@ const {TestModule} = ReactNative.NativeModules; type State = { statusCode: number, xhr: XMLHttpRequest, - expected : String, + expected: String, }; class BlobTest extends React.Component<{...}, State> { state: State = { statusCode: 0, xhr: new XMLHttpRequest(), - expected : // https://www.facebook.com/favicon.ico + // https://www.facebook.com/favicon.ico + expected: new String( 'data:application/octet-stream;base64,' + - 'AAABAAIAEBAAAAEAIABoBAAAJgAAACAgAAABACAAqBAAAI4EAAAoAAAAEAAAACAA' + - 'AAABACAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOFl' + - 'BiviZgKP4WYB1f//////////4WUA1eJmAI/hawYrAAAAAAAAAAAAAAAAAAAAAAAA' + - 'AAAAAAAA/4ArBuNpA5TkawP942kC/+NpAv///////////+NpAv/jaQL/5GoD/eNp' + - 'A5T/gCsGAAAAAAAAAAAAAAAA/4ArBuVvBL3lbgT/5W4E/+VuBP/lbgT/////////' + - '///lbgT/5W4E/+VuBP/lbgT/5W8Evf+AKwYAAAAAAAAAAOlzBZTncwX/53MF/+dz' + - 'Bf/ncwX/53MF////////////53MF/+dzBf/ncwb/53MF/+dzBv/pcweUAAAAAO19' + - 'DCvpeAf96HcH/+l4B//odwf/6XgH/+l4B////////////+h3B//odwf/6XgH/+h3' + - 'B//peAf/6nkH/e19DCvrfQmP630J/+t9Cf/rfAn/630J/+t8Cf/rfAn/////////' + - '///rfQn/630J/+p8CP/rfQn/6nwI/+p8CP/rfQuP7YEL1e2BCv/tgQr/7IEK/+2B' + - 'Cv/////////////////////////////////uiRj/7IEK/+2CCv/tggr/7YIM1e6G' + - 'DfPvhgz/74YM/++HDP/vhgz/////////////////////////////////8Zw4/++G' + - 'DP/uhgz/7oYM/+6GDPPwiw7z8IsN//CLDf/wiw3/8IsN//CLDf/wiw3/////////' + - '///wig3/8IoN//CLDf/wig3/8IsN//CLDf/xjA/z85EQ1fOQD//zkA//85AP//OQ' + - 'D//zkA//85AP////////////8o8P//KPD//ykA//8o8P//KQD//ykA//85EQ1fSU' + - 'EI/1lRH/9ZUR//SUEP/1lRH/9JQQ//SUEP/+9uz///////jDev/0mRz/9ZUR//SV' + - 'Ef/1lRH/9ZUR//SUEI/5mhgr95kS/faZEv/2mRL/9pkS//aZEv/2mRL//Nqo////' + - '//////////////rLhv/3mhL/9pkS//eZEv35mhgrAAAAAPifFZT4nhT/+Z8U//ie' + - 'FP/5nxT/+Z8U//ikI//83a3//vjw//78+f/7yX3/+J4T//ieFP/4nxWUAAAAAAAA' + - 'AAD/qisG+6MWvfqjFf/6oxX/+qMV//qjFf/6oxX/+qMV//qjFf/6oxX/+qIV//qj' + - 'Ff/7oxa9/6orBgAAAAAAAAAAAAAAAP+qKwb9qRiU/agW/fyoF//8qBf//agX//yo' + - 'F//9qBf//agX//2oF/39qRiU/6orBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP+y' + - 'Hiv/rRmP/6wZ1f+tGPP/rBjz/64Z1f+vGY//sh4rAAAAAAAAAAAAAAAAAAAAAPAP' + - 'AADAAwAAgAEAAIABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAB' + - 'AACAAQAAwAMAAPAPAAAoAAAAIAAAAEAAAAABACAAAAAAAAAQAAAAAAAAAAAAAAAA' + - 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP+A' + - 'AAbiZQRH4GMAlf//////////////////////////4GQAv+BjAJXiZQBH/4AABgAA' + - 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + - 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAOpqCxjiZgKW4WYB8eJmAf/hZQH/////////' + - '///////////////////hZgH/4mYB/+FmAf/iZwHx4mYClupqCxgAAAAAAAAAAAAA' + - 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9t' + - 'JAfkagSC42kC9ONoAv/jaAL/4mgC/+NoAv///////////////////////////+Jo' + - 'Af/iaAL/4mgB/+JoAv/iaAL/42kC9ORqBIL/bSQHAAAAAAAAAAAAAAAAAAAAAAAA' + - 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADqagsY5GoEx+NqA//jagP/42oD/+Nq' + - 'A//kawP/42oD////////////////////////////42oD/+RrA//jagP/5GsD/+Rr' + - 'A//kawP/5GsD/+RsBMfqdQsYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + - 'AAAAAAAA6HEGLeVuBOPlbQT/5GwD/+VtBP/kbAP/5GwD/+VtBP/kbAP/////////' + - '///////////////////kbAP/5G0D/+RsA//kbQP/5G0D/+RtA//kbQP/5G0D/+Ru' + - 'A+PocQYtAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOp1CxjmcAbj5W8E/+Vv' + - 'BP/lbwT/5W8E/+VvBP/lbwT/5W8E/+VvBP///////////////////////////+Vv' + - 'BP/mcAX/5W8E/+ZwBf/mcAX/5W8E/+ZwBf/lbwT/5W8E/+ZwBuPqdQsYAAAAAAAA' + - 'AAAAAAAAAAAAAAAAAAD/kiQH53IFx+ZyBf/mcQX/5nEF/+ZxBf/mcQX/5nEF/+Zx' + - 'Bf/ncgX/5nEF////////////////////////////53IG/+ZxBf/ncgb/5nEF/+Zx' + - 'Bf/mcgX/5nEF/+ZyBf/mcgX/5nEF/+dyBcf/kiQHAAAAAAAAAAAAAAAAAAAAAOd2' + - 'CILodAb/53QG/+h0Bv/odAb/6HQG/+h0Bv/odAb/6HQG/+d0Bv/odAb/////////' + - '///////////////////ndAX/53QG/+d0Bf/ndAb/53QG/+h1Bv/ndAb/6HUG/+h1' + - 'Bv/ndAX/6HUG/+d2BoIAAAAAAAAAAAAAAADqgAsY6HYG9Oh2B//odgb/6HYH/+h2' + - 'B//odgb/6HYH/+h2Bv/odgb/6HYH/+h2Bv///////////////////////////+l3' + - 'B//odgb/6XcH/+h2Bv/odgb/6HYH/+h2Bv/odgf/6HYH/+h2Bv/odgf/6HYG9OqA' + - 'CxgAAAAAAAAAAOt6CZbpeQj/6nkI/+l5CP/qeQj/6nkI/+l5B//qeQj/6XkH/+l5' + - 'B//peQf/6XkH////////////////////////////6XkI/+l5B//peQj/6XkH/+l5' + - 'B//peQj/6XkH/+l5CP/peQj/6XkH/+l5CP/peQf/63oJlgAAAAD/gCsG7H0K8et8' + - 'Cf/qewj/63wJ/+p7CP/qewj/6nsI/+p7CP/qewj/6nsI/+t8Cf/qewj/////////' + - '///////////////////qewj/6nwJ/+p7CP/qfAn/6nwJ/+p7CP/qfAn/6nsI/+p7' + - 'CP/rfAn/6nsI/+t8Cf/sfQrx/4ArBu2BC0frfQn/630J/+t+Cf/rfQn/634J/+t+' + - 'Cf/rfgn/634J////////////////////////////////////////////////////' + - '///////////////////zs27/634J/+x+Cf/rfgn/634J/+t+Cf/rfgn/634J/+t+' + - 'Cf/tgQtH7IAKleyACv/sgAr/7IAK/+yACv/sgAr/7IAK/+yACv/sgAr/////////' + - '//////////////////////////////////////////////////////////////XC' + - 'iv/sgAr/7IAK/+yACv/sgAr/7IAJ/+yACv/sgAn/7IAJ/+yACpXugwu/7YML/+2D' + - 'C//tggr/7YML/+2CCv/tggr/7YIK/+2CCv//////////////////////////////' + - '////////////////////////////////////////+NKn/+2DC//tggr/7YML/+2D' + - 'C//tgwv/7YML/+2DC//tgwv/7oMLv++GDNnuhQv/7oUL/+6FC//uhQv/7oUL/+6F' + - 'C//vhQz/7oUL////////////////////////////////////////////////////' + - '///////////////////64cT/7oUL/+6FC//uhQv/7oUL/+6EC//uhQv/7oQL/+6E' + - 'C//vhgzZ74gO8++IDP/viAz/74cM/++IDP/vhwz/74cM/++HDP/vhwz/////////' + - '//////////////////////////////////////////////////////////////3w' + - '4f/viA3/74cM/++IDf/viA3/74cM/++IDf/vhwz/74cM/++HDfPwiw7z8IoN//CK' + - 'Df/wig3/8IoN//CKDf/wig3/8IkN//CKDf/wiQ3/8IkN//CKDf/wiQ3/////////' + - '///////////////////wiQ3/8IoN//CJDf/wig3/8IoN//CJDf/wig3/8IkN//CJ' + - 'Df/wiQ3/8IkN//CJDf/wiQ3/8IsO8/KNDtnxjA7/8YwO//GMDf/xjA7/8YwN//GM' + - 'Df/xjA3/8YwN//GMDf/xjA3/8YwO//GMDf////////////////////////////GM' + - 'Dv/xjA7/8YwO//GMDv/xjA7/8YwO//GMDv/xjA7/8YwO//GMDv/xjA7/8YwO//GM' + - 'Dv/yjQ7Z8o8Pv/KPD//yjw//8o8P//KPD//yjw//8o8P//KPD//yjw//8o8P//KP' + - 'D//yjg7/8o8P////////////////////////////8Y4O//KODv/xjg7/8o4O//KO' + - 'Dv/yjg7/8o4O//KODv/yjg7/8o8P//KODv/yjw//8o8P//OQEL/zkQ+V85EP//OR' + - 'D//zkQ//85EP//ORD//zkQ//85EP//ORD//zkQ//85EP//OREP/zkQ///vr0////' + - '///////////////////0myb/85EQ//ORD//zkRD/85EQ//ORD//zkRD/85EP//OR' + - 'D//zkQ//85EP//ORD//zkQ//85EPlfSXEkf0kxD/9JMQ//SUEP/0kxD/9JQQ//SU' + - 'EP/zkxD/9JQQ//OTEP/zkxD/9JQQ//OTEP/86tD///////////////////////rV' + - 'ov/1nSb/85MQ//STEP/0kxD/9JMQ//STEP/0kxD/9JMQ//SUEP/0kxD/9JQQ//SU' + - 'EP/0kxJH/6orBvWWEvH1lhH/9ZYR//WWEf/1lhH/9ZYR//WWEf/1lhH/9ZYR//WW' + - 'Ef/1lhH/9ZYR//vZq///////////////////////////////////////////////' + - '///1lhH/9ZYR//WWEf/1lhH/9ZYR//WWEf/1lhH/9ZYS8f+qKwYAAAAA95kTlvaY' + - 'Ev/2mBH/9pgS//aYEf/2mBH/9ZgR//aYEf/1mBH/9ZgR//aYEv/1mBH/+LFN////' + - '//////////////////////////////////////////////aYEv/1mBH/9pgS//aY' + - 'Ev/1mBH/9pgS//WYEf/3mRGWAAAAAAAAAAD/nxUY+JwU9PebE//3mxP/95sT//eb' + - 'E//3mxP/95sT//ebE//3mxP/95oS//ebE//3mhL//OK7////////////////////' + - '////////////////////////95sT//ebE//3mxP/95sT//aaEv/3mxP/95sT9P+f' + - 'FRgAAAAAAAAAAAAAAAD5nxSC+J0T//idE//4nRP/+J0T//ecE//4nRP/95wT//ec' + - 'E//4nRP/95wT//idE//4pSf//efF////////////////////////////////////' + - '///4nRP/950T//idE//4nRP/+J0T//idE//5nxSCAAAAAAAAAAAAAAAAAAAAAP+2' + - 'JAf6oBXH+aAU//mgFP/5oBT/+J8U//mgFP/4nxT/+J8U//mfFP/4nxT/+Z8U//mf' + - 'FP/5oRf/+86H//7w2v/+/Pj//v36//758f/+8+P//evQ//mgFP/5nxT/+aAU//mg' + - 'FP/4nxT/+qAVx/+2JAcAAAAAAAAAAAAAAAAAAAAAAAAAAP+qFRj7oxXj+aEU//mh' + - 'FP/6ohX/+aEU//qiFf/6ohX/+qIV//qiFf/6ohX/+qIV//mhFP/6ohX/+aEU//mh' + - 'FP/6ohX/+aEU//qiFf/6ohX/+aEU//qiFf/5oRT/+aEU//ujFeP/qhUYAAAAAAAA' + - 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP+mFi78pBXj+6QV//ukFv/7pBX/+6QW//uk' + - 'Fv/6pBX/+6QW//qkFf/6pBX/+6QW//qkFf/7pBb/+6QW//ulFv/7pBb/+6UW//ul' + - 'Fv/7pBX/+6UW//ukFf/8pBXj/6QXLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + - 'AAAAAAAAAAAAAP+qIBj8qBfH/KcW//ynF//8pxb//KcW//ynFv/8pxb//KcW//yn' + - 'Fv/7phb//KcW//umFv/7phb/+6YW//umFv/7phb/+6YW//ynFv/7phb//KgXx/+q' + - 'IBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP+2' + - 'JAf9qxiC/akY9PypF//8qRf//KgX//ypF//8qBf//KgX//2pF//8qBf//akX//2p' + - 'F//9qRf//akX//2pF//9qRf//qkY9P2rGIL/tiQHAAAAAAAAAAAAAAAAAAAAAAAA' + - 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/tSAY/60alv+s' + - 'GPH+rBj//qwY//6sGP/+rBj//asY//6sGP/9qxj//asY//2rF//9qxj//qsX8f2s' + - 'GJb/tSAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + - 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/9UrBv+wGUf/rxqV/68Zv/+v' + - 'Gtn/rhnz/64Z8/+vGtn/rxm//68alf+wGUf/1SsGAAAAAAAAAAAAAAAAAAAAAAAA' + - 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/AA///AAD//AAAP/gAAB/wAAAP4AAAB8AA' + - 'AAPAAAADgAAAAYAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + - 'AAAAAAAAAAAAAAAAAACAAAABgAAAAcAAAAPAAAAD4AAAB/AAAA/4AAAf/AAAP/8A' + - 'AP//wAP/', + 'AAABAAIAEBAAAAEAIABoBAAAJgAAACAgAAABACAAqBAAAI4EAAAoAAAAEAAAACAA' + + 'AAABACAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOFl' + + 'BiviZgKP4WYB1f//////////4WUA1eJmAI/hawYrAAAAAAAAAAAAAAAAAAAAAAAA' + + 'AAAAAAAA/4ArBuNpA5TkawP942kC/+NpAv///////////+NpAv/jaQL/5GoD/eNp' + + 'A5T/gCsGAAAAAAAAAAAAAAAA/4ArBuVvBL3lbgT/5W4E/+VuBP/lbgT/////////' + + '///lbgT/5W4E/+VuBP/lbgT/5W8Evf+AKwYAAAAAAAAAAOlzBZTncwX/53MF/+dz' + + 'Bf/ncwX/53MF////////////53MF/+dzBf/ncwb/53MF/+dzBv/pcweUAAAAAO19' + + 'DCvpeAf96HcH/+l4B//odwf/6XgH/+l4B////////////+h3B//odwf/6XgH/+h3' + + 'B//peAf/6nkH/e19DCvrfQmP630J/+t9Cf/rfAn/630J/+t8Cf/rfAn/////////' + + '///rfQn/630J/+p8CP/rfQn/6nwI/+p8CP/rfQuP7YEL1e2BCv/tgQr/7IEK/+2B' + + 'Cv/////////////////////////////////uiRj/7IEK/+2CCv/tggr/7YIM1e6G' + + 'DfPvhgz/74YM/++HDP/vhgz/////////////////////////////////8Zw4/++G' + + 'DP/uhgz/7oYM/+6GDPPwiw7z8IsN//CLDf/wiw3/8IsN//CLDf/wiw3/////////' + + '///wig3/8IoN//CLDf/wig3/8IsN//CLDf/xjA/z85EQ1fOQD//zkA//85AP//OQ' + + 'D//zkA//85AP////////////8o8P//KPD//ykA//8o8P//KQD//ykA//85EQ1fSU' + + 'EI/1lRH/9ZUR//SUEP/1lRH/9JQQ//SUEP/+9uz///////jDev/0mRz/9ZUR//SV' + + 'Ef/1lRH/9ZUR//SUEI/5mhgr95kS/faZEv/2mRL/9pkS//aZEv/2mRL//Nqo////' + + '//////////////rLhv/3mhL/9pkS//eZEv35mhgrAAAAAPifFZT4nhT/+Z8U//ie' + + 'FP/5nxT/+Z8U//ikI//83a3//vjw//78+f/7yX3/+J4T//ieFP/4nxWUAAAAAAAA' + + 'AAD/qisG+6MWvfqjFf/6oxX/+qMV//qjFf/6oxX/+qMV//qjFf/6oxX/+qIV//qj' + + 'Ff/7oxa9/6orBgAAAAAAAAAAAAAAAP+qKwb9qRiU/agW/fyoF//8qBf//agX//yo' + + 'F//9qBf//agX//2oF/39qRiU/6orBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP+y' + + 'Hiv/rRmP/6wZ1f+tGPP/rBjz/64Z1f+vGY//sh4rAAAAAAAAAAAAAAAAAAAAAPAP' + + 'AADAAwAAgAEAAIABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAB' + + 'AACAAQAAwAMAAPAPAAAoAAAAIAAAAEAAAAABACAAAAAAAAAQAAAAAAAAAAAAAAAA' + + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP+A' + + 'AAbiZQRH4GMAlf//////////////////////////4GQAv+BjAJXiZQBH/4AABgAA' + + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAOpqCxjiZgKW4WYB8eJmAf/hZQH/////////' + + '///////////////////hZgH/4mYB/+FmAf/iZwHx4mYClupqCxgAAAAAAAAAAAAA' + + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9t' + + 'JAfkagSC42kC9ONoAv/jaAL/4mgC/+NoAv///////////////////////////+Jo' + + 'Af/iaAL/4mgB/+JoAv/iaAL/42kC9ORqBIL/bSQHAAAAAAAAAAAAAAAAAAAAAAAA' + + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADqagsY5GoEx+NqA//jagP/42oD/+Nq' + + 'A//kawP/42oD////////////////////////////42oD/+RrA//jagP/5GsD/+Rr' + + 'A//kawP/5GsD/+RsBMfqdQsYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + + 'AAAAAAAA6HEGLeVuBOPlbQT/5GwD/+VtBP/kbAP/5GwD/+VtBP/kbAP/////////' + + '///////////////////kbAP/5G0D/+RsA//kbQP/5G0D/+RtA//kbQP/5G0D/+Ru' + + 'A+PocQYtAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOp1CxjmcAbj5W8E/+Vv' + + 'BP/lbwT/5W8E/+VvBP/lbwT/5W8E/+VvBP///////////////////////////+Vv' + + 'BP/mcAX/5W8E/+ZwBf/mcAX/5W8E/+ZwBf/lbwT/5W8E/+ZwBuPqdQsYAAAAAAAA' + + 'AAAAAAAAAAAAAAAAAAD/kiQH53IFx+ZyBf/mcQX/5nEF/+ZxBf/mcQX/5nEF/+Zx' + + 'Bf/ncgX/5nEF////////////////////////////53IG/+ZxBf/ncgb/5nEF/+Zx' + + 'Bf/mcgX/5nEF/+ZyBf/mcgX/5nEF/+dyBcf/kiQHAAAAAAAAAAAAAAAAAAAAAOd2' + + 'CILodAb/53QG/+h0Bv/odAb/6HQG/+h0Bv/odAb/6HQG/+d0Bv/odAb/////////' + + '///////////////////ndAX/53QG/+d0Bf/ndAb/53QG/+h1Bv/ndAb/6HUG/+h1' + + 'Bv/ndAX/6HUG/+d2BoIAAAAAAAAAAAAAAADqgAsY6HYG9Oh2B//odgb/6HYH/+h2' + + 'B//odgb/6HYH/+h2Bv/odgb/6HYH/+h2Bv///////////////////////////+l3' + + 'B//odgb/6XcH/+h2Bv/odgb/6HYH/+h2Bv/odgf/6HYH/+h2Bv/odgf/6HYG9OqA' + + 'CxgAAAAAAAAAAOt6CZbpeQj/6nkI/+l5CP/qeQj/6nkI/+l5B//qeQj/6XkH/+l5' + + 'B//peQf/6XkH////////////////////////////6XkI/+l5B//peQj/6XkH/+l5' + + 'B//peQj/6XkH/+l5CP/peQj/6XkH/+l5CP/peQf/63oJlgAAAAD/gCsG7H0K8et8' + + 'Cf/qewj/63wJ/+p7CP/qewj/6nsI/+p7CP/qewj/6nsI/+t8Cf/qewj/////////' + + '///////////////////qewj/6nwJ/+p7CP/qfAn/6nwJ/+p7CP/qfAn/6nsI/+p7' + + 'CP/rfAn/6nsI/+t8Cf/sfQrx/4ArBu2BC0frfQn/630J/+t+Cf/rfQn/634J/+t+' + + 'Cf/rfgn/634J////////////////////////////////////////////////////' + + '///////////////////zs27/634J/+x+Cf/rfgn/634J/+t+Cf/rfgn/634J/+t+' + + 'Cf/tgQtH7IAKleyACv/sgAr/7IAK/+yACv/sgAr/7IAK/+yACv/sgAr/////////' + + '//////////////////////////////////////////////////////////////XC' + + 'iv/sgAr/7IAK/+yACv/sgAr/7IAJ/+yACv/sgAn/7IAJ/+yACpXugwu/7YML/+2D' + + 'C//tggr/7YML/+2CCv/tggr/7YIK/+2CCv//////////////////////////////' + + '////////////////////////////////////////+NKn/+2DC//tggr/7YML/+2D' + + 'C//tgwv/7YML/+2DC//tgwv/7oMLv++GDNnuhQv/7oUL/+6FC//uhQv/7oUL/+6F' + + 'C//vhQz/7oUL////////////////////////////////////////////////////' + + '///////////////////64cT/7oUL/+6FC//uhQv/7oUL/+6EC//uhQv/7oQL/+6E' + + 'C//vhgzZ74gO8++IDP/viAz/74cM/++IDP/vhwz/74cM/++HDP/vhwz/////////' + + '//////////////////////////////////////////////////////////////3w' + + '4f/viA3/74cM/++IDf/viA3/74cM/++IDf/vhwz/74cM/++HDfPwiw7z8IoN//CK' + + 'Df/wig3/8IoN//CKDf/wig3/8IkN//CKDf/wiQ3/8IkN//CKDf/wiQ3/////////' + + '///////////////////wiQ3/8IoN//CJDf/wig3/8IoN//CJDf/wig3/8IkN//CJ' + + 'Df/wiQ3/8IkN//CJDf/wiQ3/8IsO8/KNDtnxjA7/8YwO//GMDf/xjA7/8YwN//GM' + + 'Df/xjA3/8YwN//GMDf/xjA3/8YwO//GMDf////////////////////////////GM' + + 'Dv/xjA7/8YwO//GMDv/xjA7/8YwO//GMDv/xjA7/8YwO//GMDv/xjA7/8YwO//GM' + + 'Dv/yjQ7Z8o8Pv/KPD//yjw//8o8P//KPD//yjw//8o8P//KPD//yjw//8o8P//KP' + + 'D//yjg7/8o8P////////////////////////////8Y4O//KODv/xjg7/8o4O//KO' + + 'Dv/yjg7/8o4O//KODv/yjg7/8o8P//KODv/yjw//8o8P//OQEL/zkQ+V85EP//OR' + + 'D//zkQ//85EP//ORD//zkQ//85EP//ORD//zkQ//85EP//OREP/zkQ///vr0////' + + '///////////////////0myb/85EQ//ORD//zkRD/85EQ//ORD//zkRD/85EP//OR' + + 'D//zkQ//85EP//ORD//zkQ//85EPlfSXEkf0kxD/9JMQ//SUEP/0kxD/9JQQ//SU' + + 'EP/zkxD/9JQQ//OTEP/zkxD/9JQQ//OTEP/86tD///////////////////////rV' + + 'ov/1nSb/85MQ//STEP/0kxD/9JMQ//STEP/0kxD/9JMQ//SUEP/0kxD/9JQQ//SU' + + 'EP/0kxJH/6orBvWWEvH1lhH/9ZYR//WWEf/1lhH/9ZYR//WWEf/1lhH/9ZYR//WW' + + 'Ef/1lhH/9ZYR//vZq///////////////////////////////////////////////' + + '///1lhH/9ZYR//WWEf/1lhH/9ZYR//WWEf/1lhH/9ZYS8f+qKwYAAAAA95kTlvaY' + + 'Ev/2mBH/9pgS//aYEf/2mBH/9ZgR//aYEf/1mBH/9ZgR//aYEv/1mBH/+LFN////' + + '//////////////////////////////////////////////aYEv/1mBH/9pgS//aY' + + 'Ev/1mBH/9pgS//WYEf/3mRGWAAAAAAAAAAD/nxUY+JwU9PebE//3mxP/95sT//eb' + + 'E//3mxP/95sT//ebE//3mxP/95oS//ebE//3mhL//OK7////////////////////' + + '////////////////////////95sT//ebE//3mxP/95sT//aaEv/3mxP/95sT9P+f' + + 'FRgAAAAAAAAAAAAAAAD5nxSC+J0T//idE//4nRP/+J0T//ecE//4nRP/95wT//ec' + + 'E//4nRP/95wT//idE//4pSf//efF////////////////////////////////////' + + '///4nRP/950T//idE//4nRP/+J0T//idE//5nxSCAAAAAAAAAAAAAAAAAAAAAP+2' + + 'JAf6oBXH+aAU//mgFP/5oBT/+J8U//mgFP/4nxT/+J8U//mfFP/4nxT/+Z8U//mf' + + 'FP/5oRf/+86H//7w2v/+/Pj//v36//758f/+8+P//evQ//mgFP/5nxT/+aAU//mg' + + 'FP/4nxT/+qAVx/+2JAcAAAAAAAAAAAAAAAAAAAAAAAAAAP+qFRj7oxXj+aEU//mh' + + 'FP/6ohX/+aEU//qiFf/6ohX/+qIV//qiFf/6ohX/+qIV//mhFP/6ohX/+aEU//mh' + + 'FP/6ohX/+aEU//qiFf/6ohX/+aEU//qiFf/5oRT/+aEU//ujFeP/qhUYAAAAAAAA' + + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP+mFi78pBXj+6QV//ukFv/7pBX/+6QW//uk' + + 'Fv/6pBX/+6QW//qkFf/6pBX/+6QW//qkFf/7pBb/+6QW//ulFv/7pBb/+6UW//ul' + + 'Fv/7pBX/+6UW//ukFf/8pBXj/6QXLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + + 'AAAAAAAAAAAAAP+qIBj8qBfH/KcW//ynF//8pxb//KcW//ynFv/8pxb//KcW//yn' + + 'Fv/7phb//KcW//umFv/7phb/+6YW//umFv/7phb/+6YW//ynFv/7phb//KgXx/+q' + + 'IBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP+2' + + 'JAf9qxiC/akY9PypF//8qRf//KgX//ypF//8qBf//KgX//2pF//8qBf//akX//2p' + + 'F//9qRf//akX//2pF//9qRf//qkY9P2rGIL/tiQHAAAAAAAAAAAAAAAAAAAAAAAA' + + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/tSAY/60alv+s' + + 'GPH+rBj//qwY//6sGP/+rBj//asY//6sGP/9qxj//asY//2rF//9qxj//qsX8f2s' + + 'GJb/tSAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/9UrBv+wGUf/rxqV/68Zv/+v' + + 'Gtn/rhnz/64Z8/+vGtn/rxm//68alf+wGUf/1SsGAAAAAAAAAAAAAAAAAAAAAAAA' + + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/AA///AAD//AAAP/gAAB/wAAAP4AAAB8AA' + + 'AAPAAAADgAAAAYAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + + 'AAAAAAAAAAAAAAAAAACAAAABgAAAAcAAAAPAAAAD4AAAB/AAAA/4AAAf/AAAP/8A' + + 'AP//wAP/', + ), }; _get = () => { @@ -147,20 +149,14 @@ class BlobTest extends React.Component<{...}, State> { statusCode: this.state.xhr.status, }); }; - this.state.xhr.open( - 'GET', - 'https://www.facebook.com/favicon.ico', - ); + this.state.xhr.open('GET', 'https://www.facebook.com/favicon.ico'); this.state.xhr.setRequestHeader('Accept-Encoding', 'utf-8'); - this.state.xhr.responseType = "blob"; + this.state.xhr.responseType = 'blob'; this.state.xhr.send(); }; _getSucceeded = () => { - return ( - this.state.statusCode === 200 && - this.state.xhr.response !== null - ); + return this.state.statusCode === 200 && this.state.xhr.response !== null; }; _waitFor = (condition: any, timeout: any, callback: any) => { @@ -186,7 +182,9 @@ class BlobTest extends React.Component<{...}, State> { let reader = new FileReader(); reader.readAsDataURL(this.state.xhr.response); reader.onload = () => { - TestModule.markTestPassed(doneSucceeded && this.state.expected === reader.result); + TestModule.markTestPassed( + doneSucceeded && this.state.expected === reader.result, + ); }; }); } From 958e66ae02395ac9f9bb36fdded3de2b37f3f4e7 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Fri, 20 May 2022 14:18:28 -0700 Subject: [PATCH 90/91] Enable BlobTest by default --- vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp b/vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp index af3bced8313..ea1183a6a96 100644 --- a/vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp +++ b/vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp @@ -227,7 +227,6 @@ TEST_CLASS (RNTesterIntegrationTests) { } BEGIN_TEST_METHOD_ATTRIBUTE(Blob) - TEST_IGNORE() END_TEST_METHOD_ATTRIBUTE() TEST_METHOD(Blob) { auto result = m_runner.RunTest("IntegrationTests/BlobTest", "BlobTest"); From aa70c68773d4cc5130f7027ddec96c5fd4a87abc Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Fri, 20 May 2022 15:11:28 -0700 Subject: [PATCH 91/91] Disable Blob test in CI (may hang) --- .ado/jobs/desktop.yml | 1 + vnext/Shared/Modules/FileReaderModule.cpp | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.ado/jobs/desktop.yml b/.ado/jobs/desktop.yml index 86ba196b7f9..cb5b4af022a 100644 --- a/.ado/jobs/desktop.yml +++ b/.ado/jobs/desktop.yml @@ -63,6 +63,7 @@ jobs: - name: Desktop.IntegrationTests.Filter value: > (FullyQualifiedName!=RNTesterIntegrationTests::AsyncStorage)& + (FullyQualifiedName!=RNTesterIntegrationTests::Blob)& (FullyQualifiedName!=RNTesterIntegrationTests::IntegrationTestHarness)& (FullyQualifiedName!=WebSocketResourcePerformanceTest::ProcessThreadsPerResource)& (FullyQualifiedName!=Microsoft::React::Test::HttpOriginPolicyIntegrationTest) diff --git a/vnext/Shared/Modules/FileReaderModule.cpp b/vnext/Shared/Modules/FileReaderModule.cpp index 36942f7f8c2..a23328c0e15 100644 --- a/vnext/Shared/Modules/FileReaderModule.cpp +++ b/vnext/Shared/Modules/FileReaderModule.cpp @@ -62,7 +62,7 @@ std::vector FileReaderModule::getMethods() { "readAsDataURL", [blobPersistor = m_weakBlobPersistor.lock()](dynamic args, Callback resolve, Callback reject) { if (!blobPersistor) { - return reject({"Could not get BlobModule from ReactApplicationContext"}); + return reject({"Could not find Blob persistor"}); } auto blob = jsArgAsObject(args, 0); @@ -106,7 +106,7 @@ std::vector FileReaderModule::getMethods() { "readAsText", [blobPersistor = m_weakBlobPersistor.lock()](dynamic args, Callback resolve, Callback reject) { if (!blobPersistor) { - return reject({"Could not get BlobModule from ReactApplicationContext"}); + return reject({"Could not find Blob persistor"}); } auto blob = jsArgAsObject(args, 0);