diff --git a/.ado/windows-vs-pr.yml b/.ado/windows-vs-pr.yml index bdaf87afcb5..dc7028158a6 100644 --- a/.ado/windows-vs-pr.yml +++ b/.ado/windows-vs-pr.yml @@ -389,13 +389,9 @@ jobs: variables: #5059 - Disable failing or intermittent tests (IntegrationTestHarness,AsyncStorage,WebSocket,Logging). #5265 - WebSocketModuleIntegrationTest::WebSocketModule_Ping fails for Release - #5412 - Disable XHRSample due to intermittedly crashing the test process Desktop.IntegrationTests.Filter: > - (FullyQualifiedName!=RNTesterIntegrationTests::AsyncStorage)& - (FullyQualifiedName!=RNTesterIntegrationTests::Dummy)& (FullyQualifiedName!=RNTesterIntegrationTests::IntegrationTestHarness)& (FullyQualifiedName!=RNTesterIntegrationTests::WebSocket)& - (FullyQualifiedName!=RNTesterIntegrationTests::XHRSample)& (FullyQualifiedName!~WebSocketModule_)& (FullyQualifiedName!=WebSocketResourcePerformanceTest::ProcessThreadsPerResource) diff --git a/change/react-native-windows-2020-09-25-01-35-29-consolidate-http.json b/change/react-native-windows-2020-09-25-01-35-29-consolidate-http.json new file mode 100644 index 00000000000..22f42fd97db --- /dev/null +++ b/change/react-native-windows-2020-09-25-01-35-29-consolidate-http.json @@ -0,0 +1,8 @@ +{ + "type": "prerelease", + "comment": "Use WinRT HTTP module", + "packageName": "react-native-windows", + "email": "julio.rocha@microsoft.com", + "dependentChangeType": "patch", + "date": "2020-09-25T08:35:29.737Z" +} diff --git a/vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp b/vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp index 5ae6ee31190..3d0251caafd 100644 --- a/vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp +++ b/vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp @@ -88,6 +88,8 @@ TEST_CLASS (RNTesterIntegrationTests) { Assert::AreEqual(L"This is from console.error", result.Message.c_str()); } + BEGIN_TEST_METHOD_ATTRIBUTE(XHRSample) + END_TEST_METHOD_ATTRIBUTE() TEST_METHOD(XHRSample) { auto result = m_runner.RunTest("IntegrationTests/XHRTest", "XHRTest"); Assert::AreEqual(TestStatus::Passed, result.Status, result.Message.c_str()); diff --git a/vnext/Desktop/Modules/NetworkingModule.cpp b/vnext/Desktop/Modules/NetworkingModule.cpp deleted file mode 100644 index 38c73cae128..00000000000 --- a/vnext/Desktop/Modules/NetworkingModule.cpp +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "pch.h" - -#include "NetworkingModule.h" - -#include -#include - -using folly::dynamic; -using std::map; -using std::move; -using std::string; -using std::unique_ptr; -using std::vector; - -namespace Microsoft::React { - -#pragma region NetworkingModule static members - -const char *NetworkingModule::name = "Networking"; - -int64_t NetworkingModule::s_lastRequestId = 0; - -#pragma endregion - -NetworkingModule::NetworkingModule() {} - -IHttpResource *NetworkingModule::GetResource(int64_t requestId) noexcept { - IHttpResource *ptr = nullptr; - if (m_resources.find(requestId) == std::end(m_resources)) { - auto rc = IHttpResource::Make(); - rc->SetOnError([this, requestId](const string &message) { - // ISS:2306365 - Deal with timeout conditions. - OnRequestError(requestId, move(message), false /*isTimeOut*/); - }); - rc->SetOnRequest([this, requestId]() { OnRequestSuccess(requestId); }); - rc->SetOnResponse([this, requestId](const string &message) { OnDataReceived(requestId, message); }); - - ptr = rc.get(); - m_resources.emplace(requestId, move(rc)); - } else { - ptr = m_resources.at(requestId).get(); - } - - return ptr; -} - -void NetworkingModule::OnDataReceived(int64_t requestId, const string &data) noexcept { - SendEvent("didReceiveNetworkData", dynamic::array(requestId, data)); -} - -void NetworkingModule::OnRequestError(int64_t requestId, const string &error, bool isTimeOut) noexcept { - SendEvent("didCompleteNetworkResponse", dynamic::array(requestId, error, isTimeOut)); -} - -void NetworkingModule::OnRequestSuccess(int64_t requestId) noexcept { - SendEvent("didCompleteNetworkResponse", dynamic::array(requestId)); -} - -void NetworkingModule::OnResponseReceived( - int64_t requestId, - int64_t statusCode, - const dynamic &headers, - const string &url) noexcept { - SendEvent("didReceiveNetworkResponse", dynamic::array(requestId, statusCode, headers, url)); -} - -void NetworkingModule::SendEvent(string &&eventName, dynamic &¶meters) { - auto instance = this->getInstance().lock(); - if (instance) - instance->callJSFunction("RCTDeviceEventEmitter", "emit", dynamic::array(move(eventName), move(parameters))); -} - -#pragma region CxxModule members - -string NetworkingModule::getName() { - return NetworkingModule::name; -} - -map NetworkingModule::getConstants() { - return {}; -} - -vector NetworkingModule::getMethods() { - return {Method( - "sendRequest", - [this](dynamic args, Callback cb) noexcept { - auto params = facebook::xplat::jsArgAsObject(args, 0); - - auto headers = map(); - auto follyHeaders = params["headers"]; - if (!follyHeaders.empty()) { - for (auto &header : follyHeaders.items()) { - headers[header.first.getString()] = header.second.getString(); - } - } - - int64_t requestId = 0; // ISS:2306365 - Manage requestIds. - GetResource(requestId)->SendRequest( - params["method"].asString(), - params["url"].asString(), - headers, - params["data"], - params["responseType"].asString(), - params["incrementalUpdates"].asBool(), - params["timeout"].asInt(), - [](int64_t) {} // ISS:2306365 - Convert from 'cb' parameter. - ); - // ISS:2306365 - Callback? - }), - Method( - "abortRequest", - [this](dynamic args) noexcept { - int64_t requestId = facebook::xplat::jsArgAsInt(args, 0); - GetResource(requestId)->AbortRequest(); - }), - Method("clearCookies", [this](dynamic args) noexcept { - int64_t requestId = facebook::xplat::jsArgAsInt(args, 0); - GetResource(requestId)->ClearCookies(); - })}; -} - -#pragma endregion CxxModule members - -} // namespace Microsoft::React diff --git a/vnext/Desktop/Modules/NetworkingModule.h b/vnext/Desktop/Modules/NetworkingModule.h deleted file mode 100644 index ae426d0d545..00000000000 --- a/vnext/Desktop/Modules/NetworkingModule.h +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include -#include - -namespace Microsoft::React { -// NetworkingModule -// provides the 'Networking' native module backing RCTNetworking.js -class NetworkingModule : public facebook::xplat::module::CxxModule { - static int64_t s_lastRequestId; - std::unordered_map> m_resources; - - IHttpResource *GetResource(int64_t requestId) noexcept; - void OnDataReceived(int64_t requestId, const std::string &data) noexcept; - void OnRequestError(int64_t requestId, const std::string &error, bool isTimeOut) noexcept; - void OnRequestSuccess(int64_t requestId) noexcept; - void OnResponseReceived( - int64_t requestId, - int64_t statusCode, - const folly::dynamic &headers, - const std::string &url) noexcept; - void SendEvent(std::string &&eventName, folly::dynamic &¶meters); - - public: - NetworkingModule(); - -#pragma region CxxModule members - - std::string getName() override; - std::map getConstants() override; - std::vector getMethods() override; - -#pragma endregion CxxModule members - - static const char *name; -}; - -} // namespace Microsoft::React diff --git a/vnext/Desktop/React.Windows.Desktop.vcxproj b/vnext/Desktop/React.Windows.Desktop.vcxproj index 607956bdc9c..0932e655dd3 100644 --- a/vnext/Desktop/React.Windows.Desktop.vcxproj +++ b/vnext/Desktop/React.Windows.Desktop.vcxproj @@ -219,7 +219,6 @@ - @@ -284,7 +283,6 @@ - diff --git a/vnext/Desktop/React.Windows.Desktop.vcxproj.filters b/vnext/Desktop/React.Windows.Desktop.vcxproj.filters index 24338eb9a51..a2d46af90e7 100644 --- a/vnext/Desktop/React.Windows.Desktop.vcxproj.filters +++ b/vnext/Desktop/React.Windows.Desktop.vcxproj.filters @@ -129,9 +129,6 @@ Generated Files - - Source Files\Modules - Source Files\Modules @@ -195,9 +192,6 @@ Header Files - - Header Files\Modules - Header Files\Modules diff --git a/vnext/Microsoft.ReactNative.sln b/vnext/Microsoft.ReactNative.sln index ce2079d9c90..60deb5bb4fd 100644 --- a/vnext/Microsoft.ReactNative.sln +++ b/vnext/Microsoft.ReactNative.sln @@ -138,7 +138,6 @@ Global Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems*{da8b35b3-da00-4b02-bde6-6a397b3fd46b}*SharedItemsImports = 9 include\Include.vcxitems*{ef074ba1-2d54-4d49-a28e-5e040b47cd2e}*SharedItemsImports = 9 Chakra\Chakra.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4 - JSI\Shared\JSI.Shared.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4 Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4 Mso\Mso.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4 Shared\Shared.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4 diff --git a/vnext/Microsoft.ReactNative/Base/CoreNativeModules.cpp b/vnext/Microsoft.ReactNative/Base/CoreNativeModules.cpp index 49b494935d0..9874a649aac 100644 --- a/vnext/Microsoft.ReactNative/Base/CoreNativeModules.cpp +++ b/vnext/Microsoft.ReactNative/Base/CoreNativeModules.cpp @@ -67,7 +67,10 @@ std::vector GetCoreModules( []() { return std::make_unique(); }, jsMessageQueue); - modules.emplace_back(NetworkingModule::Name, []() { return std::make_unique(); }, jsMessageQueue); + modules.emplace_back( + Microsoft::React::NetworkingModule::Name, + []() { return std::make_unique(); }, + jsMessageQueue); modules.emplace_back( "Timing", diff --git a/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj b/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj index fa2dad678f0..20df6fad06f 100644 --- a/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj +++ b/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj @@ -241,7 +241,6 @@ - @@ -440,7 +439,6 @@ - diff --git a/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj.filters b/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj.filters index 7f35fda8aaf..783eaa86f84 100644 --- a/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj.filters +++ b/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj.filters @@ -167,9 +167,6 @@ Modules - - Modules - Modules @@ -518,9 +515,6 @@ Modules - - Modules - Modules diff --git a/vnext/ReactWindows-Desktop.sln b/vnext/ReactWindows-Desktop.sln index 8bbd3a4e298..ed3cbd4c376 100644 --- a/vnext/ReactWindows-Desktop.sln +++ b/vnext/ReactWindows-Desktop.sln @@ -113,7 +113,6 @@ Global Shared\Shared.vcxitems*{2049dbe9-8d13-42c9-ae4b-413ae38fffd0}*SharedItemsImports = 9 Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems*{44dced9b-9c4c-48fe-8545-0930192bbc16}*SharedItemsImports = 4 Mso\Mso.vcxitems*{44dced9b-9c4c-48fe-8545-0930192bbc16}*SharedItemsImports = 4 - Chakra\Chakra.vcxitems*{6f354505-fe3a-4bd2-a9a6-d12bbf37a85c}*SharedItemsImports = 4 Mso\Mso.vcxitems*{84e05bfa-cbaf-4f0d-bfb6-4ce85742a57e}*SharedItemsImports = 9 Chakra\Chakra.vcxitems*{95048601-c3dc-475f-adf8-7c0c764c10d5}*SharedItemsImports = 4 Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems*{95048601-c3dc-475f-adf8-7c0c764c10d5}*SharedItemsImports = 4 diff --git a/vnext/Microsoft.ReactNative/Modules/NetworkingModule.cpp b/vnext/Shared/Modules/NetworkingModule.cpp similarity index 99% rename from vnext/Microsoft.ReactNative/Modules/NetworkingModule.cpp rename to vnext/Shared/Modules/NetworkingModule.cpp index ffe46b49907..6dee50ec5b8 100644 --- a/vnext/Microsoft.ReactNative/Modules/NetworkingModule.cpp +++ b/vnext/Shared/Modules/NetworkingModule.cpp @@ -4,6 +4,7 @@ #include "pch.h" #include +#include #include #include #include @@ -25,7 +26,7 @@ #pragma optimize("", off) #endif -namespace react::uwp { +namespace Microsoft::React { // // NetworkingModule::NetworkingHelper @@ -423,4 +424,4 @@ auto NetworkingModule::getMethods() -> std::vector { }; } -} // namespace react::uwp +} // namespace Microsoft::React diff --git a/vnext/Microsoft.ReactNative/Modules/NetworkingModule.h b/vnext/Shared/Modules/NetworkingModule.h similarity index 92% rename from vnext/Microsoft.ReactNative/Modules/NetworkingModule.h rename to vnext/Shared/Modules/NetworkingModule.h index 7fbde471e4c..fc7a891649b 100644 --- a/vnext/Microsoft.ReactNative/Modules/NetworkingModule.h +++ b/vnext/Shared/Modules/NetworkingModule.h @@ -11,7 +11,7 @@ #include #include -namespace react::uwp { +namespace Microsoft::React { class NetworkingModule : public facebook::xplat::module::CxxModule { public: @@ -31,4 +31,4 @@ class NetworkingModule : public facebook::xplat::module::CxxModule { std::shared_ptr m_networking; }; -} // namespace react::uwp +} // namespace Microsoft::React diff --git a/vnext/Shared/Shared.vcxitems b/vnext/Shared/Shared.vcxitems index 45c3a1624eb..5b74ea707ad 100644 --- a/vnext/Shared/Shared.vcxitems +++ b/vnext/Shared/Shared.vcxitems @@ -41,6 +41,7 @@ + @@ -64,6 +65,7 @@ + diff --git a/vnext/Shared/Shared.vcxitems.filters b/vnext/Shared/Shared.vcxitems.filters index b9c58bf92f7..1b1f45cacb8 100644 --- a/vnext/Shared/Shared.vcxitems.filters +++ b/vnext/Shared/Shared.vcxitems.filters @@ -107,6 +107,9 @@ Source Files\Executors + + Source Files\Modules + @@ -306,6 +309,9 @@ Header Files + + Header Files\Modules + @@ -321,4 +327,4 @@ Source Files\etw - + \ No newline at end of file diff --git a/vnext/src/IntegrationTests/XHRTest.js b/vnext/src/IntegrationTests/XHRTest.js index 50da4a85dec..0eef07eba8d 100644 --- a/vnext/src/IntegrationTests/XHRTest.js +++ b/vnext/src/IntegrationTests/XHRTest.js @@ -9,7 +9,7 @@ const React = require('react'); const ReactNative = require('react-native'); -const {Alert, AppRegistry, View} = ReactNative; +const {AppRegistry, View} = ReactNative; const {TestModule} = ReactNative.NativeModules; @@ -31,76 +31,80 @@ class XHRTest extends React.Component<{}, Object> { xhr: ?XMLHttpRequest = null; _download = () => { - let xhr; - if (this.xhr) { - xhr = this.xhr; - xhr.abort(); - } else { - xhr = this.xhr = new XMLHttpRequest(); - } - - const onreadystatechange = () => { - if (xhr.readyState === xhr.HEADERS_RECEIVED) { - const contentLength = parseInt( - xhr.getResponseHeader('Content-Length'), - 10, - ); - this.setState({ - contentLength, - responseLength: 0, - }); - } else if (xhr.readyState === xhr.LOADING && xhr.response) { - this.setState({ - responseLength: xhr.response.length, - }); + return new Promise((resolve, reject) => { + let xhr; + if (this.xhr) { + xhr = this.xhr; + xhr.abort(); + } else { + xhr = this.xhr = new XMLHttpRequest(); } - }; - if (this.state.readystateHandler) { - xhr.onreadystatechange = onreadystatechange; - } - //ISS:2306365 - Uncomment when native module is complete. - //if (this.state.progressHandler) { - // xhr.onprogress = onprogress; - //} - if (this.state.arraybuffer) { - xhr.responseType = 'arraybuffer'; - } - xhr.onload = () => { - this.setState({downloading: false}); - if (xhr.status === 200) { - let responseType = `Response is a string, ${ - xhr.response.length - } characters long.`; - if (xhr.response instanceof ArrayBuffer) { - responseType = `Response is an ArrayBuffer, ${ - xhr.response.byteLength - } bytes long.`; + const onreadystatechange = () => { + if (xhr.readyState === xhr.HEADERS_RECEIVED) { + const contentLength = parseInt( + xhr.getResponseHeader('Content-Length'), + 10, + ); + this.setState({ + contentLength, + responseLength: 0, + }); + } else if (xhr.readyState === xhr.LOADING && xhr.response) { + this.setState({ + responseLength: xhr.response.length, + }); } - Alert.alert('Download complete!', responseType); - } else if (xhr.status !== 0) { - Alert.alert( - 'Error', - `Server returned HTTP status of ${xhr.status}: ${xhr.responseText}`, - ); - } else { - Alert.alert('Error', xhr.responseText); + }; + + if (this.state.readystateHandler) { + xhr.onreadystatechange = onreadystatechange; + } + //ISS:2306365 - Uncomment when native module is complete. + //if (this.state.progressHandler) { + // xhr.onprogress = onprogress; + //} + if (this.state.arraybuffer) { + xhr.responseType = 'arraybuffer'; } - }; - xhr.open('GET', 'http://aleph.gutenberg.org/cache/epub/100/pg100.txt.utf8'); - // Avoid gzip so we can actually show progress - xhr.setRequestHeader('Accept-Encoding', ''); - xhr.send(); + xhr.onload = () => { + this.setState({downloading: false}); + if (xhr.status === 200) { + let responseType = `Response is a string, ${ + xhr.response.length + } characters long.`; + if (xhr.response instanceof ArrayBuffer) { + responseType = `Response is an ArrayBuffer, ${ + xhr.response.byteLength + } bytes long.`; + } + console.log('Download complete!', responseType); + } else if (xhr.status !== 0) { + console.error( + 'Error', + `Server returned HTTP status of ${xhr.status}: ${xhr.responseText}`, + ); + } else { + console.error('Error', xhr.responseText); + } + }; + xhr.open('GET', 'https://en.wikipedia.org/favicon.ico'); + // Avoid gzip so we can actually show progress + xhr.setRequestHeader('Accept-Encoding', ''); + xhr.send(); - this.setState({downloading: true}); + this.setState({downloading: true}); + }); }; componentDidMount() { - try { - this._download(); - } catch (e) { - console.error(e); - } + this._download().then( + function(data) {}, + function(e) { + console.log(e); + TestModule.markTestPassed(false); + }, + ); TestModule.markTestPassed(true); }