react-native-windows 0.67.8 → 0.67.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.json CHANGED
@@ -1,6 +1,48 @@
1
1
  {
2
2
  "name": "react-native-windows",
3
3
  "entries": [
4
+ {
5
+ "date": "Mon, 02 May 2022 15:10:43 GMT",
6
+ "tag": "react-native-windows_v0.67.9",
7
+ "version": "0.67.9",
8
+ "comments": {
9
+ "patch": [
10
+ {
11
+ "author": "53799235+ZihanChen-MSFT@users.noreply.github.com",
12
+ "package": "react-native-windows",
13
+ "commit": "9458dad1646fa2a8a5afea7383133eeca144fb88",
14
+ "comment": "Fix clang-check errors in NativeModules.h"
15
+ },
16
+ {
17
+ "author": "vmorozov@microsoft.com",
18
+ "package": "react-native-windows",
19
+ "commit": "38c0547235927e51c08bd17b08b2f6be878fd0a5",
20
+ "comment": "Fix JSI for Node-API and update V8 to 0.65.11"
21
+ },
22
+ {
23
+ "author": "julio.rocha@microsoft.com",
24
+ "package": "react-native-windows",
25
+ "commit": "e3ef52d578991181c00a6487c902ce4c31cc702a",
26
+ "comment": "Handle abrupt WebSocket connection interruption (#9829)"
27
+ }
28
+ ]
29
+ }
30
+ },
31
+ {
32
+ "date": "Mon, 25 Apr 2022 15:11:36 GMT",
33
+ "tag": "react-native-windows_v0.67.8",
34
+ "version": "0.67.8",
35
+ "comments": {
36
+ "patch": [
37
+ {
38
+ "author": "vmorozov@microsoft.com",
39
+ "package": "react-native-windows",
40
+ "commit": "not available",
41
+ "comment": "Fix Sequential DispatchQueue deadlock on shutdown"
42
+ }
43
+ ]
44
+ }
45
+ },
4
46
  {
5
47
  "date": "Mon, 25 Apr 2022 15:11:22 GMT",
6
48
  "tag": "react-native-windows_v0.67.8",
package/CHANGELOG.md CHANGED
@@ -1,17 +1,35 @@
1
1
  # Change Log - react-native-windows
2
2
 
3
- This log was last generated on Mon, 25 Apr 2022 15:11:22 GMT and should not be manually modified.
3
+ This log was last generated on Mon, 02 May 2022 15:10:43 GMT and should not be manually modified.
4
4
 
5
5
  <!-- Start content -->
6
6
 
7
- ## 0.67.8
7
+ ## 0.67.9
8
8
 
9
- Mon, 25 Apr 2022 15:11:22 GMT
9
+ Mon, 02 May 2022 15:10:43 GMT
10
10
 
11
11
  ### Patches
12
12
 
13
- - Fix Sequential DispatchQueue deadlock on shutdown (vmorozov@microsoft.com)
13
+ - Fix clang-check errors in NativeModules.h (53799235+ZihanChen-MSFT@users.noreply.github.com)
14
+ - Fix JSI for Node-API and update V8 to 0.65.11 (vmorozov@microsoft.com)
15
+ - Handle abrupt WebSocket connection interruption (#9829) (julio.rocha@microsoft.com)
14
16
 
17
+ ## 0.67.8
18
+
19
+ Mon, 25 Apr 2022 15:11:36 GMT
20
+
21
+ ### Patches
22
+
23
+ - Fix Sequential DispatchQueue deadlock on shutdown (vmorozov@microsoft.com)
24
+
25
+ ## 0.67.8
26
+
27
+ Mon, 25 Apr 2022 15:11:22 GMT
28
+
29
+ ### Patches
30
+
31
+ - Fix Sequential DispatchQueue deadlock on shutdown (vmorozov@microsoft.com)
32
+
15
33
  ## 0.67.7
16
34
 
17
35
  Mon, 04 Apr 2022 15:13:28 GMT
@@ -13,7 +13,6 @@
13
13
  #include <Modules/ClipboardModule.h>
14
14
  #include <Modules/LinkingManagerModule.h>
15
15
  #include <Modules/NativeUIManager.h>
16
- #include <Modules/NetworkingModule.h>
17
16
  #include <Modules/PaperUIManagerModule.h>
18
17
  #include <Threading/MessageQueueThreadFactory.h>
19
18
 
@@ -50,9 +49,7 @@ std::vector<facebook::react::NativeModuleDescription> GetCoreModules(
50
49
  std::vector<facebook::react::NativeModuleDescription> modules;
51
50
 
52
51
  modules.emplace_back(
53
- Microsoft::React::NetworkingModule::Name,
54
- []() { return std::make_unique<Microsoft::React::NetworkingModule>(); },
55
- jsMessageQueue);
52
+ "Networking", []() { return Microsoft::React::CreateHttpModule(); }, jsMessageQueue);
56
53
 
57
54
  modules.emplace_back(
58
55
  "Timing",
@@ -8,5 +8,5 @@
8
8
  <package id="Microsoft.Windows.CppWinRT" version="2.0.210312.4" targetFramework="native" />
9
9
  <package id="Microsoft.WinUI" version="3.0.0-preview4.210210.4" targetFramework="native" />
10
10
  <package id="ReactNative.Hermes.Windows" version="0.10.0-ms.1" targetFramework="native" />
11
- <!-- package id="ReactNative.V8Jsi.Windows.UWP" version="0.65.5" targetFramework="native" / -->
11
+ <!-- package id="ReactNative.V8Jsi.Windows.UWP" version="0.65.11" targetFramework="native" / -->
12
12
  </packages>
@@ -190,7 +190,7 @@ struct NapiJsiRuntime : facebook::jsi::Runtime {
190
190
  facebook::jsi::Function createFunctionFromHostFunction(
191
191
  const facebook::jsi::PropNameID &name,
192
192
  unsigned int paramCount,
193
- facebook::jsi::HostFunctionType type) override;
193
+ facebook::jsi::HostFunctionType func) override;
194
194
  facebook::jsi::Value call(
195
195
  const facebook::jsi::Function &func,
196
196
  const facebook::jsi::Value &jsThis,
@@ -475,7 +475,7 @@ struct NapiJsiRuntime : facebook::jsi::Runtime {
475
475
  void SetProperty(napi_value object, napi_value propertyId, napi_value value, napi_property_attributes attrs) const;
476
476
  napi_value CreateArray(size_t length) const;
477
477
  void SetElement(napi_value array, uint32_t index, napi_value value) const;
478
- static napi_value JsiHostFunctionCallback(napi_env env, napi_callback_info info) noexcept;
478
+ static napi_value __cdecl JsiHostFunctionCallback(napi_env env, napi_callback_info info) noexcept;
479
479
  napi_value CreateExternalFunction(napi_value name, int32_t paramCount, napi_callback callback, void *callbackData);
480
480
  napi_value CreateExternalObject(void *data, napi_finalize finalizeCallback) const;
481
481
  template <typename T>
@@ -1702,7 +1702,7 @@ void NapiJsiRuntime::SetElement(napi_value array, uint32_t index, napi_value val
1702
1702
  }
1703
1703
 
1704
1704
  // The NAPI external function callback used for the JSI host function implementation.
1705
- /*static*/ napi_value NapiJsiRuntime::JsiHostFunctionCallback(napi_env env, napi_callback_info info) noexcept {
1705
+ /*static*/ napi_value __cdecl NapiJsiRuntime::JsiHostFunctionCallback(napi_env env, napi_callback_info info) noexcept {
1706
1706
  HostFunctionWrapper *hostFuncWrapper{};
1707
1707
  size_t argc{};
1708
1708
  CHECK_NAPI_ELSE_CRASH(
@@ -1750,15 +1750,11 @@ napi_value NapiJsiRuntime::CreateExternalObject(void *data, napi_finalize finali
1750
1750
  // Wraps up std::unique_ptr as an external object.
1751
1751
  template <typename T>
1752
1752
  napi_value NapiJsiRuntime::CreateExternalObject(unique_ptr<T> &&data) const {
1753
- napi_value object =
1754
- CreateExternalObject(data.get(), [](napi_env /*env*/, void *dataToDestroy, void * /*finalizerHint*/) {
1755
- // We wrap dataToDestroy in a unique_ptr to avoid calling delete explicitly.
1756
- if (std::is_array<T>::value) {
1757
- delete[] static_cast<T *>(dataToDestroy);
1758
- } else {
1759
- delete static_cast<T *>(dataToDestroy);
1760
- }
1761
- });
1753
+ napi_finalize finalize = [](napi_env /*env*/, void *dataToDestroy, void * /*finalizerHint*/) {
1754
+ // We wrap dataToDestroy in a unique_ptr to avoid calling delete explicitly.
1755
+ unique_ptr<T> dataDeleter{static_cast<T *>(dataToDestroy)};
1756
+ };
1757
+ napi_value object = CreateExternalObject(data.get(), finalize);
1762
1758
 
1763
1759
  // We only call data.release() after the CreateExternalObject succeeds.
1764
1760
  // Otherwise, when CreateExternalObject fails and an exception is thrown,
@@ -1807,7 +1803,7 @@ napi_value NapiJsiRuntime::GetHostObjectProxyHandler() {
1807
1803
  // Sets Proxy trap method as a pointer to NapiJsiRuntime instance method.
1808
1804
  template <napi_value (NapiJsiRuntime::*trapMethod)(span<napi_value>), size_t argCount>
1809
1805
  void NapiJsiRuntime::SetProxyTrap(napi_value handler, napi_value propertyName) {
1810
- auto proxyTrap = [](napi_env env, napi_callback_info info) noexcept {
1806
+ napi_callback proxyTrap = [](napi_env env, napi_callback_info info) noexcept {
1811
1807
  NapiJsiRuntime *runtime{};
1812
1808
  napi_value args[argCount]{};
1813
1809
  size_t actualArgCount{argCount};
@@ -2,6 +2,8 @@
2
2
  // Licensed under the MIT License.
3
3
 
4
4
  #pragma once
5
+ #ifndef MICROSOFT_REACTNATIVE_JSI_NODEAPIJSIRUNTIME
6
+ #define MICROSOFT_REACTNATIVE_JSI_NODEAPIJSIRUNTIME
5
7
 
6
8
  // JSI
7
9
  #include <js_native_ext_api.h>
@@ -19,3 +21,5 @@ namespace Microsoft::JSI {
19
21
  std::unique_ptr<facebook::jsi::Runtime> __cdecl MakeNodeApiJsiRuntime(napi_env env) noexcept;
20
22
 
21
23
  } // namespace Microsoft::JSI
24
+
25
+ #endif // MICROSOFT_REACTNATIVE_JSI_NODEAPIJSIRUNTIME
@@ -1091,7 +1091,7 @@ template <class TModule, int I, class TMethodSpec>
1091
1091
  struct ReactMethodVerifier {
1092
1092
  static constexpr bool Verify() noexcept {
1093
1093
  ReactMethodVerifier verifier{};
1094
- ReactMemberInfoIterator<TModule>{}.GetMemberInfo<I>(verifier);
1094
+ ReactMemberInfoIterator<TModule>{}.template GetMemberInfo<I>(verifier);
1095
1095
  return verifier.m_result;
1096
1096
  }
1097
1097
 
@@ -1109,7 +1109,7 @@ template <class TModule, int I, class TMethodSpec>
1109
1109
  struct ReactSyncMethodVerifier {
1110
1110
  static constexpr bool Verify() noexcept {
1111
1111
  ReactSyncMethodVerifier verifier{};
1112
- ReactMemberInfoIterator<TModule>{}.GetMemberInfo<I>(verifier);
1112
+ ReactMemberInfoIterator<TModule>{}.template GetMemberInfo<I>(verifier);
1113
1113
  return verifier.m_result;
1114
1114
  }
1115
1115
 
@@ -10,10 +10,10 @@
10
10
  -->
11
11
  <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
12
12
  <PropertyGroup>
13
- <ReactNativeWindowsVersion>0.67.8</ReactNativeWindowsVersion>
13
+ <ReactNativeWindowsVersion>0.67.9</ReactNativeWindowsVersion>
14
14
  <ReactNativeWindowsMajor>0</ReactNativeWindowsMajor>
15
15
  <ReactNativeWindowsMinor>67</ReactNativeWindowsMinor>
16
- <ReactNativeWindowsPatch>8</ReactNativeWindowsPatch>
16
+ <ReactNativeWindowsPatch>9</ReactNativeWindowsPatch>
17
17
  <ReactNativeWindowsCanary>false</ReactNativeWindowsCanary>
18
18
  </PropertyGroup>
19
19
  </Project>
@@ -16,7 +16,7 @@
16
16
  <EnableDevServerHBCBundles Condition="'$(EnableDevServerHBCBundles)' == ''">false</EnableDevServerHBCBundles>
17
17
 
18
18
  <UseV8 Condition="'$(UseV8)' == ''">false</UseV8>
19
- <V8Version Condition="'$(V8Version)' == ''">0.65.5</V8Version>
19
+ <V8Version Condition="'$(V8Version)' == ''">0.65.11</V8Version>
20
20
  <V8PackageName>ReactNative.V8Jsi.Windows</V8PackageName>
21
21
  <V8PackageName Condition="'$(V8AppPlatform)' != 'win32'">$(V8PackageName).UWP</V8PackageName>
22
22
  <V8Package>$(SolutionDir)packages\$(V8PackageName).$(V8Version)</V8Package>
@@ -32,6 +32,10 @@ extern std::unique_ptr<facebook::xplat::module::CxxModule> CreateAsyncStorageMod
32
32
 
33
33
  namespace Microsoft::React {
34
34
 
35
+ extern const char *GetHttpModuleName() noexcept;
36
+ extern std::unique_ptr<facebook::xplat::module::CxxModule> CreateHttpModule() noexcept;
37
+
38
+ extern const char *GetWebSocketModuleName() noexcept;
35
39
  extern std::unique_ptr<facebook::xplat::module::CxxModule> CreateWebSocketModule() noexcept;
36
40
 
37
41
  } // namespace Microsoft::React
@@ -3,32 +3,51 @@
3
3
 
4
4
  #pragma once
5
5
 
6
- #include <Folly/dynamic.h>
6
+ // Standard Library
7
+ #include <functional>
8
+ #include <memory>
9
+ #include <string>
10
+ #include <unordered_map>
7
11
 
8
12
  namespace Microsoft::React {
9
13
 
10
14
  struct IHttpResource {
11
- typedef std::map<std::string, std::string> Headers;
15
+ typedef std::unordered_map<std::string, std::string> Headers;
12
16
 
13
- static std::unique_ptr<IHttpResource> Make() noexcept;
17
+ struct BodyData {
18
+ enum class Type : size_t { Empty, String, Base64, Uri, Form } Type = Type::Empty;
19
+ std::string Data;
20
+ };
21
+
22
+ struct Response {
23
+ int64_t StatusCode;
24
+ Headers Headers;
25
+ std::string Url;
26
+ };
27
+
28
+ static std::shared_ptr<IHttpResource> Make() noexcept;
14
29
 
15
30
  virtual ~IHttpResource() noexcept {}
16
31
 
17
32
  virtual void SendRequest(
18
- const std::string &method,
19
- const std::string &url,
20
- const Headers &headers,
21
- folly::dynamic bodyData, // ISS:2365799 - Make non-folly.
22
- const std::string &responseType,
33
+ std::string &&method,
34
+ std::string &&url,
35
+ Headers &&headers,
36
+ BodyData &&bodyData,
37
+ std::string &&responseType,
23
38
  bool useIncrementalUpdates,
24
- std::int64_t timeout,
39
+ int64_t timeout,
40
+ bool withCredentials,
25
41
  std::function<void(int64_t)> &&callback) noexcept = 0;
26
- virtual void AbortRequest() noexcept = 0;
42
+ virtual void AbortRequest(int64_t requestId) noexcept = 0;
43
+
27
44
  virtual void ClearCookies() noexcept = 0;
28
45
 
29
- virtual void SetOnRequest(std::function<void()> &&handler) noexcept = 0;
30
- virtual void SetOnResponse(std::function<void(const std::string &)> &&handler) noexcept = 0;
31
- virtual void SetOnError(std::function<void(const std::string &)> &&handler) noexcept = 0;
46
+ virtual void SetOnRequest(std::function<void(int64_t requestId)> &&handler) noexcept = 0; // TODO: Keep???
47
+ virtual void SetOnResponse(std::function<void(int64_t requestId, Response &&response)> &&handler) noexcept = 0;
48
+ virtual void SetOnData(std::function<void(int64_t requestId, std::string &&responseData)> &&handler) noexcept = 0;
49
+ virtual void SetOnError(
50
+ std::function<void(int64_t requestId, std::string &&errorMessage /*, bool isTimeout*/)> &&handler) noexcept = 0;
32
51
  };
33
52
 
34
53
  } // namespace Microsoft::React
@@ -71,7 +71,7 @@ struct IWebSocketResource {
71
71
 
72
72
  struct Error {
73
73
  std::string Message;
74
- const ErrorType Type;
74
+ ErrorType Type;
75
75
  };
76
76
 
77
77
  #pragma endregion Inner types
@@ -22,7 +22,7 @@ class NapiJsiV8RuntimeHolder : public Microsoft::JSI::RuntimeHolderLazyInit {
22
22
  std::unique_ptr<facebook::jsi::PreparedScriptStore> &&preparedScriptStore) noexcept;
23
23
 
24
24
  private:
25
- static void ScheduleTaskCallback(
25
+ static void __cdecl ScheduleTaskCallback(
26
26
  napi_env env,
27
27
  napi_ext_task_callback taskCb,
28
28
  void *taskData,
@@ -0,0 +1,198 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT License.
3
+
4
+ #include "pch.h"
5
+
6
+ #include "HttpModule.h"
7
+
8
+ // React Native
9
+ #include <cxxreact/Instance.h>
10
+ #include <cxxreact/JsArgumentHelpers.h>
11
+
12
+ using facebook::react::Instance;
13
+ using folly::dynamic;
14
+ using std::shared_ptr;
15
+ using std::string;
16
+ using std::weak_ptr;
17
+
18
+ namespace {
19
+
20
+ using Microsoft::React::IHttpResource;
21
+
22
+ constexpr char moduleName[] = "Networking";
23
+
24
+ static void SendEvent(weak_ptr<Instance> weakReactInstance, string &&eventName, dynamic &&args) {
25
+ if (auto instance = weakReactInstance.lock()) {
26
+ instance->callJSFunction("RCTDeviceEventEmitter", "emit", dynamic::array(std::move(eventName), std::move(args)));
27
+ }
28
+ }
29
+
30
+ static shared_ptr<IHttpResource> CreateHttpResource(weak_ptr<Instance> weakReactInstance) {
31
+ auto resource = IHttpResource::Make();
32
+
33
+ resource->SetOnResponse([weakReactInstance](int64_t requestId, IHttpResource::Response &&response) {
34
+ dynamic headers = dynamic::object();
35
+ for (auto &header : response.Headers) {
36
+ headers[header.first] = header.second;
37
+ }
38
+
39
+ // TODO: Test response content.
40
+ dynamic args = dynamic::array(requestId, response.StatusCode, headers, response.Url);
41
+
42
+ SendEvent(weakReactInstance, "didReceiveNetworkResponse", std::move(args));
43
+ });
44
+
45
+ resource->SetOnData([weakReactInstance](int64_t requestId, std::string &&responseData) {
46
+ dynamic args = dynamic::array(requestId, std::move(responseData));
47
+
48
+ SendEvent(weakReactInstance, "didReceiveNetworkData", std::move(args));
49
+
50
+ // TODO: Move into separate method IF not executed right after onData()
51
+ SendEvent(weakReactInstance, "didCompleteNetworkResponse", dynamic::array(requestId));
52
+ });
53
+
54
+ resource->SetOnError([weakReactInstance](int64_t requestId, string &&message) {
55
+ dynamic args = dynamic::array(requestId, std::move(message));
56
+ // TODO: isTimeout errorArgs.push_back(true);
57
+
58
+ SendEvent(weakReactInstance, "didCompleteNetworkResponse", std::move(args));
59
+ });
60
+
61
+ return resource;
62
+ }
63
+
64
+ } // namespace
65
+
66
+ namespace Microsoft::React {
67
+
68
+ HttpModule::HttpModule() noexcept : m_holder{std::make_shared<ModuleHolder>()} {
69
+ m_holder->Module = this;
70
+ }
71
+
72
+ HttpModule::~HttpModule() noexcept /*override*/ {
73
+ m_holder->Module = nullptr;
74
+ }
75
+
76
+ #pragma region CxxModule
77
+
78
+ string HttpModule::getName() /*override*/ {
79
+ return moduleName;
80
+ }
81
+
82
+ std::map<string, dynamic> HttpModule::getConstants() {
83
+ return {};
84
+ }
85
+
86
+ // clang-format off
87
+ std::vector<facebook::xplat::module::CxxModule::Method> HttpModule::getMethods() {
88
+
89
+ auto weakHolder = weak_ptr<ModuleHolder>(m_holder);
90
+ auto holder = weakHolder.lock();
91
+ auto weakReactInstance = weak_ptr<Instance>(holder->Module->getInstance());
92
+
93
+ return
94
+ {
95
+ {
96
+ "sendRequest",
97
+ [weakHolder = weak_ptr<ModuleHolder>(m_holder)](dynamic args, Callback cxxCallback)
98
+ {
99
+ auto holder = weakHolder.lock();
100
+ if (!holder) {
101
+ return;
102
+ }
103
+
104
+ auto resource = holder->Module->m_resource;
105
+ if (resource || (resource = CreateHttpResource(holder->Module->getInstance())))
106
+ {
107
+ IHttpResource::BodyData bodyData;
108
+ auto params = facebook::xplat::jsArgAsObject(args, 0);
109
+ auto data = params["data"];
110
+ auto stringData = data["string"];
111
+ if (!stringData.empty())
112
+ {
113
+ bodyData = {IHttpResource::BodyData::Type::String, stringData.getString()};
114
+ }
115
+ else
116
+ {
117
+ auto base64Data = data["base64"];
118
+ if (!base64Data.empty())
119
+ {
120
+ bodyData = {IHttpResource::BodyData::Type::Base64, base64Data.getString()};
121
+ }
122
+ else
123
+ {
124
+ auto uriData = data["uri"];
125
+ if (!uriData.empty())
126
+ {
127
+ bodyData = {IHttpResource::BodyData::Type::Uri, uriData.getString()};
128
+ }
129
+ }
130
+ }
131
+ //TODO: Support FORM data
132
+
133
+ IHttpResource::Headers headers;
134
+ for (auto& header : params["headers"].items()) {
135
+ headers.emplace(header.first.getString(), header.second.getString());
136
+ }
137
+
138
+ resource->SendRequest(
139
+ params["method"].asString(),
140
+ params["url"].asString(),
141
+ std::move(headers),
142
+ std::move(bodyData),
143
+ params["responseType"].asString(),
144
+ params["incrementalUpdates"].asBool(),
145
+ static_cast<int64_t>(params["timeout"].asDouble()),
146
+ false,//withCredentials,
147
+ [cxxCallback = std::move(cxxCallback)](int64_t requestId) {
148
+ cxxCallback({requestId});
149
+ }
150
+ );
151
+ } // If resource available
152
+ }
153
+ },
154
+ {
155
+ "abortRequest",
156
+ [weakHolder = weak_ptr<ModuleHolder>(m_holder)](dynamic args)
157
+ {
158
+ auto holder = weakHolder.lock();
159
+ if (!holder)
160
+ {
161
+ return;
162
+ }
163
+
164
+ auto resource = holder->Module->m_resource;
165
+ if (resource || (resource = CreateHttpResource(holder->Module->getInstance())))
166
+ {
167
+ resource->AbortRequest(facebook::xplat::jsArgAsInt(args, 0));
168
+ }
169
+ }
170
+ },
171
+ {
172
+ "clearCookies",
173
+ [weakHolder = weak_ptr<ModuleHolder>(m_holder)](dynamic args)
174
+ {
175
+ auto holder = weakHolder.lock();
176
+ if (!holder)
177
+ {
178
+ return;
179
+ }
180
+
181
+ auto resource = holder->Module->m_resource;
182
+ if (resource || (resource = CreateHttpResource(holder->Module->getInstance())))
183
+ {
184
+ resource->ClearCookies();
185
+ }
186
+ }
187
+ }
188
+ };
189
+ }
190
+ // clang-format on
191
+
192
+ #pragma endregion CxxModule
193
+
194
+ /*extern*/ const char *GetHttpModuleName() noexcept {
195
+ return moduleName;
196
+ }
197
+
198
+ } // namespace Microsoft::React
@@ -0,0 +1,53 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT License.
3
+
4
+ #pragma once
5
+
6
+ #include "IHttpResource.h"
7
+
8
+ // React Native
9
+ #include <cxxreact/CxxModule.h>
10
+
11
+ namespace Microsoft::React {
12
+
13
+ ///
14
+ /// Realizes <c>NativeModules</c> projection.
15
+ /// <remarks>See src\Libraries\Network\RCTNetworkingWinShared.js</remarks>
16
+ ///
17
+ class HttpModule : public facebook::xplat::module::CxxModule {
18
+ public:
19
+ enum MethodId { SendRequest = 0, AbortRequest = 1, ClearCookies = 2, LAST = ClearCookies };
20
+
21
+ HttpModule() noexcept;
22
+
23
+ ~HttpModule() noexcept override;
24
+
25
+ #pragma region CxxModule
26
+
27
+ /// <summary>
28
+ /// <see cref="facebook::xplat::module::CxxModule::getName" />
29
+ /// </summary>
30
+ std::string getName() override;
31
+
32
+ /// <summary>
33
+ /// <see cref="facebook::xplat::module::CxxModule::getConstants" />
34
+ /// </summary>
35
+ std::map<std::string, folly::dynamic> getConstants() override;
36
+
37
+ /// <summary>
38
+ /// <see cref="facebook::xplat::module::CxxModule::getMethods" />
39
+ /// </summary>
40
+ /// <remarks>See See react-native/Libraries/WebSocket/WebSocket.js</remarks>
41
+ std::vector<Method> getMethods() override;
42
+
43
+ #pragma endregion CxxModule
44
+
45
+ private:
46
+ struct ModuleHolder {
47
+ HttpModule *Module{nullptr};
48
+ };
49
+
50
+ std::shared_ptr<IHttpResource> m_resource;
51
+ std::shared_ptr<ModuleHolder> m_holder;
52
+ };
53
+ } // namespace Microsoft::React
@@ -246,6 +246,10 @@ std::vector<facebook::xplat::module::CxxModule::Method> WebSocketModule::getMeth
246
246
  } // getMethods
247
247
  // clang-format on
248
248
 
249
+ /*extern*/ const char *GetWebSocketModuleName() noexcept {
250
+ return moduleName;
251
+ }
252
+
249
253
  /*extern*/ std::unique_ptr<facebook::xplat::module::CxxModule> CreateWebSocketModule() noexcept {
250
254
  return std::make_unique<WebSocketModule>();
251
255
  }
@@ -26,6 +26,8 @@
26
26
  #include <cxxreact/ModuleRegistry.h>
27
27
 
28
28
  #include <Modules/ExceptionsManagerModule.h>
29
+ #include <Modules/HttpModule.h>
30
+ #include <Modules/NetworkingModule.h>
29
31
  #include <Modules/PlatformConstantsModule.h>
30
32
  #include <Modules/SourceCodeModule.h>
31
33
  #include <Modules/StatusBarManagerModule.h>
@@ -146,6 +148,18 @@ using namespace Microsoft::JSI;
146
148
 
147
149
  using std::make_shared;
148
150
 
151
+ namespace Microsoft::React {
152
+
153
+ /*extern*/ std::unique_ptr<facebook::xplat::module::CxxModule> CreateHttpModule() noexcept {
154
+ if (GetRuntimeOptionBool("Http.UseMonolithicModule")) {
155
+ return std::make_unique<NetworkingModule>();
156
+ } else {
157
+ return std::make_unique<HttpModule>();
158
+ }
159
+ }
160
+
161
+ } // namespace Microsoft::React
162
+
149
163
  namespace facebook {
150
164
  namespace react {
151
165
 
@@ -613,7 +627,13 @@ std::vector<std::unique_ptr<NativeModule>> InstanceImpl::GetDefaultNativeModules
613
627
 
614
628
  modules.push_back(std::make_unique<CxxNativeModule>(
615
629
  m_innerInstance,
616
- "WebSocketModule",
630
+ Microsoft::React::GetHttpModuleName(),
631
+ [nativeQueue]() -> std::unique_ptr<xplat::module::CxxModule> { return Microsoft::React::CreateHttpModule(); },
632
+ nativeQueue));
633
+
634
+ modules.push_back(std::make_unique<CxxNativeModule>(
635
+ m_innerInstance,
636
+ Microsoft::React::GetWebSocketModuleName(),
617
637
  [nativeQueue]() -> std::unique_ptr<xplat::module::CxxModule> {
618
638
  return Microsoft::React::CreateWebSocketModule();
619
639
  },
@@ -46,6 +46,7 @@
46
46
  <ExcludedFromBuild Condition="'$(ApplicationType)' == ''">true</ExcludedFromBuild>
47
47
  </ClCompile>
48
48
  <ClCompile Include="$(MSBuildThisFileDirectory)Modules\ExceptionsManagerModule.cpp" />
49
+ <ClCompile Include="$(MSBuildThisFileDirectory)Modules\HttpModule.cpp" />
49
50
  <ClCompile Include="$(MSBuildThisFileDirectory)Modules\I18nModule.cpp" />
50
51
  <ClCompile Include="$(MSBuildThisFileDirectory)Modules\NetworkingModule.cpp" />
51
52
  <ClCompile Include="$(MSBuildThisFileDirectory)Modules\PlatformConstantsModule.cpp" />
@@ -61,9 +62,11 @@
61
62
  <ClCompile Include="$(MSBuildThisFileDirectory)tracing\tracing.cpp" />
62
63
  <ClCompile Include="$(MSBuildThisFileDirectory)TurboModuleManager.cpp" />
63
64
  <ClCompile Include="$(MSBuildThisFileDirectory)Utils.cpp" />
65
+ <ClCompile Include="$(MSBuildThisFileDirectory)Utils\WinRTConversions.cpp" />
64
66
  <ClCompile Include="$(MSBuildThisFileDirectory)V8JSIRuntimeHolder.cpp">
65
67
  <ExcludedFromBuild Condition="'$(UseV8)' != 'true'">true</ExcludedFromBuild>
66
68
  </ClCompile>
69
+ <ClCompile Include="$(MSBuildThisFileDirectory)WinRTHttpResource.cpp" />
67
70
  <ClCompile Include="$(MSBuildThisFileDirectory)WinRTWebSocketResource.cpp" />
68
71
  </ItemGroup>
69
72
  <ItemGroup>
@@ -83,6 +86,7 @@
83
86
  <ClInclude Include="$(MSBuildThisFileDirectory)JSI\NapiJsiV8RuntimeHolder.h" />
84
87
  <ClInclude Include="$(MSBuildThisFileDirectory)JSI\RuntimeHolder.h" />
85
88
  <ClInclude Include="$(MSBuildThisFileDirectory)JSI\ScriptStore.h" />
89
+ <ClInclude Include="$(MSBuildThisFileDirectory)Modules\HttpModule.h" />
86
90
  <ClInclude Include="$(MSBuildThisFileDirectory)Modules\NetworkingModule.h" />
87
91
  <ClInclude Include="$(MSBuildThisFileDirectory)RuntimeOptions.h" />
88
92
  <ClInclude Include="$(MSBuildThisFileDirectory)Modules\AsyncStorageModuleWin32.h" />
@@ -130,8 +134,10 @@
130
134
  <ClInclude Include="$(MSBuildThisFileDirectory)TurboModuleRegistry.h" />
131
135
  <ClInclude Include="$(MSBuildThisFileDirectory)Utils.h" />
132
136
  <ClInclude Include="$(MSBuildThisFileDirectory)Utils\CppWinrtLessExceptions.h" />
137
+ <ClInclude Include="$(MSBuildThisFileDirectory)Utils\WinRTConversions.h" />
133
138
  <ClInclude Include="$(MSBuildThisFileDirectory)V8JSIRuntimeHolder.h" />
134
139
  <ClInclude Include="$(MSBuildThisFileDirectory)WebSocketJSExecutorFactory.h" />
140
+ <ClInclude Include="$(MSBuildThisFileDirectory)WinRTHttpResource.h" />
135
141
  <ClInclude Include="$(MSBuildThisFileDirectory)WinRTWebSocketResource.h" />
136
142
  </ItemGroup>
137
143
  <ItemGroup>
@@ -133,6 +133,15 @@
133
133
  <ClCompile Include="$(MSBuildThisFileDirectory)HermesShim.cpp">
134
134
  <Filter>Source Files</Filter>
135
135
  </ClCompile>
136
+ <ClCompile Include="$(MSBuildThisFileDirectory)Modules\HttpModule.cpp">
137
+ <Filter>Source Files\Modules</Filter>
138
+ </ClCompile>
139
+ <ClCompile Include="$(MSBuildThisFileDirectory)WinRTHttpResource.cpp">
140
+ <Filter>Source Files</Filter>
141
+ </ClCompile>
142
+ <ClCompile Include="$(MSBuildThisFileDirectory)Utils\WinRTConversions.cpp">
143
+ <Filter>Source Files\Utils</Filter>
144
+ </ClCompile>
136
145
  </ItemGroup>
137
146
  <ItemGroup>
138
147
  <Filter Include="Source Files">
@@ -183,6 +192,9 @@
183
192
  <Filter Include="Source Files\JSI">
184
193
  <UniqueIdentifier>{1a3ad55f-1297-41b3-ba2a-0f819e69270c}</UniqueIdentifier>
185
194
  </Filter>
195
+ <Filter Include="Source Files\Utils">
196
+ <UniqueIdentifier>{e78de2f1-a7e5-4a81-b69b-4a1f7fa91cde}</UniqueIdentifier>
197
+ </Filter>
186
198
  </ItemGroup>
187
199
  <ItemGroup>
188
200
  <ClInclude Include="$(MSBuildThisFileDirectory)AsyncStorage\StorageFileIO.h">
@@ -381,6 +393,15 @@
381
393
  <ClInclude Include="$(MSBuildThisFileDirectory)HermesShim.h">
382
394
  <Filter>Header Files</Filter>
383
395
  </ClInclude>
396
+ <ClInclude Include="$(MSBuildThisFileDirectory)Modules\HttpModule.h">
397
+ <Filter>Header Files\Modules</Filter>
398
+ </ClInclude>
399
+ <ClInclude Include="$(MSBuildThisFileDirectory)WinRTHttpResource.h">
400
+ <Filter>Header Files</Filter>
401
+ </ClInclude>
402
+ <ClInclude Include="$(MSBuildThisFileDirectory)Utils\WinRTConversions.h">
403
+ <Filter>Header Files\Utils</Filter>
404
+ </ClInclude>
384
405
  </ItemGroup>
385
406
  <ItemGroup>
386
407
  <None Include="$(MSBuildThisFileDirectory)tracing\rnw.wprp">
@@ -0,0 +1,22 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT License.
3
+
4
+ #include "WinRTConversions.h"
5
+
6
+ // Standard Library
7
+ #include <sstream>
8
+
9
+ namespace Microsoft::React::Utilities {
10
+
11
+ std::string HResultToString(winrt::hresult_error const &e) {
12
+ std::stringstream stream;
13
+ stream << "[0x" << std::hex << e.code() << "] " << winrt::to_string(e.message());
14
+
15
+ return stream.str();
16
+ }
17
+
18
+ std::string HResultToString(winrt::hresult &&result) {
19
+ return HResultToString(winrt::hresult_error(std::move(result), winrt::hresult_error::from_abi));
20
+ }
21
+
22
+ } // namespace Microsoft::React::Utilities
@@ -0,0 +1,15 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT License.
3
+
4
+ #pragma once
5
+
6
+ // Windows API
7
+ #include <winrt/base.h>
8
+
9
+ namespace Microsoft::React::Utilities {
10
+
11
+ std::string HResultToString(winrt::hresult_error const &e);
12
+
13
+ std::string HResultToString(winrt::hresult &&result);
14
+
15
+ } // namespace Microsoft::React::Utilities
@@ -0,0 +1,317 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT License.
3
+
4
+ #include "WinRTHttpResource.h"
5
+
6
+ #include <Utils/CppWinrtLessExceptions.h>
7
+ #include <Utils/WinRTConversions.h>
8
+ #include <utilities.h>
9
+
10
+ // Windows API
11
+ #include <winrt/Windows.Security.Cryptography.h>
12
+ #include <winrt/Windows.Storage.Streams.h>
13
+ #include <winrt/Windows.Web.Http.Headers.h>
14
+
15
+ using std::function;
16
+ using std::scoped_lock;
17
+ using std::shared_ptr;
18
+ using std::string;
19
+
20
+ using winrt::fire_and_forget;
21
+ using winrt::hresult_error;
22
+ using winrt::to_hstring;
23
+ using winrt::to_string;
24
+ using winrt::Windows::Foundation::Uri;
25
+ using winrt::Windows::Security::Cryptography::CryptographicBuffer;
26
+ using winrt::Windows::Storage::StorageFile;
27
+ using winrt::Windows::Storage::Streams::DataReader;
28
+ using winrt::Windows::Storage::Streams::UnicodeEncoding;
29
+ using winrt::Windows::Web::Http::HttpBufferContent;
30
+ using winrt::Windows::Web::Http::HttpMethod;
31
+ using winrt::Windows::Web::Http::HttpRequestMessage;
32
+ using winrt::Windows::Web::Http::HttpStreamContent;
33
+ using winrt::Windows::Web::Http::HttpStringContent;
34
+ using winrt::Windows::Web::Http::IHttpClient;
35
+ using winrt::Windows::Web::Http::IHttpContent;
36
+ using winrt::Windows::Web::Http::Headers::HttpMediaTypeHeaderValue;
37
+
38
+ namespace Microsoft::React {
39
+
40
+ #pragma region WinRTHttpResource
41
+
42
+ // TODO: Check for multi-thread issues if there are multiple instances.
43
+ /*static*/ int64_t WinRTHttpResource::s_lastRequestId = 0;
44
+
45
+ WinRTHttpResource::WinRTHttpResource(IHttpClient &&client) noexcept : m_client{std::move(client)} {}
46
+
47
+ WinRTHttpResource::WinRTHttpResource() noexcept : WinRTHttpResource(winrt::Windows::Web::Http::HttpClient()) {}
48
+
49
+ #pragma region IHttpResource
50
+
51
+ void WinRTHttpResource::SendRequest(
52
+ string &&method,
53
+ string &&url,
54
+ Headers &&headers,
55
+ BodyData &&bodyData,
56
+ string &&responseType,
57
+ bool useIncrementalUpdates,
58
+ int64_t timeout,
59
+ bool withCredentials,
60
+ std::function<void(int64_t)> &&callback) noexcept /*override*/ {
61
+ auto requestId = ++s_lastRequestId;
62
+
63
+ // Enforce supported args
64
+ assert(responseType == "text" || responseType == "base64");
65
+
66
+ if (callback) {
67
+ callback(requestId);
68
+ }
69
+
70
+ try {
71
+ HttpMethod httpMethod{to_hstring(std::move(method))};
72
+ Uri uri{to_hstring(std::move(url))};
73
+ HttpRequestMessage request{httpMethod, uri};
74
+ HttpMediaTypeHeaderValue contentType{nullptr};
75
+ string contentEncoding;
76
+ string contentLength;
77
+
78
+ // Headers are generally case-insensitive
79
+ // https://www.ietf.org/rfc/rfc2616.txt section 4.2
80
+ for (auto &header : headers) {
81
+ if (_stricmp(header.first.c_str(), "content-type") == 0) {
82
+ bool success = HttpMediaTypeHeaderValue::TryParse(to_hstring(header.second), contentType);
83
+ if (!success && m_onError) {
84
+ return m_onError(requestId, "Failed to parse Content-Type");
85
+ }
86
+ } else if (_stricmp(header.first.c_str(), "content-encoding") == 0) {
87
+ contentEncoding = header.second;
88
+ } else if (_stricmp(header.first.c_str(), "content-length") == 0) {
89
+ contentLength = header.second;
90
+ } else if (_stricmp(header.first.c_str(), "authorization") == 0) {
91
+ bool success =
92
+ request.Headers().TryAppendWithoutValidation(to_hstring(header.first), to_hstring(header.second));
93
+ if (!success && m_onError) {
94
+ return m_onError(requestId, "Failed to append Authorization");
95
+ }
96
+ } else {
97
+ request.Headers().Append(to_hstring(header.first), to_hstring(header.second));
98
+ }
99
+ }
100
+
101
+ IHttpContent content{nullptr};
102
+ if (BodyData::Type::String == bodyData.Type) {
103
+ content = HttpStringContent{to_hstring(bodyData.Data)};
104
+ } else if (BodyData::Type::Base64 == bodyData.Type) {
105
+ auto buffer = CryptographicBuffer::DecodeFromBase64String(to_hstring(bodyData.Data));
106
+ content = HttpBufferContent{buffer};
107
+ } else if (BodyData::Type::Uri == bodyData.Type) {
108
+ auto file = StorageFile::GetFileFromApplicationUriAsync(Uri{to_hstring(bodyData.Data)}).get();
109
+ auto stream = file.OpenReadAsync().get();
110
+ content = HttpStreamContent{stream};
111
+ } else if (BodyData::Type::Form == bodyData.Type) {
112
+ // TODO: Add support
113
+ } else {
114
+ // BodyData::Type::Empty
115
+ // TODO: Error 'cause unsupported??
116
+ }
117
+
118
+ if (content != nullptr) {
119
+ // Attach content headers
120
+ if (contentType) {
121
+ content.Headers().ContentType(contentType);
122
+ }
123
+ if (!contentEncoding.empty()) {
124
+ if (!content.Headers().ContentEncoding().TryParseAdd(to_hstring(contentEncoding))) {
125
+ if (m_onError) {
126
+ m_onError(requestId, "Failed to parse Content-Encoding");
127
+ }
128
+ return;
129
+ }
130
+ }
131
+ if (!contentLength.empty()) {
132
+ const auto contentLengthHeader = _atoi64(contentLength.c_str()); // TODO: Alternatives to _atoi64?
133
+ content.Headers().ContentLength(contentLengthHeader);
134
+ }
135
+
136
+ request.Content(content);
137
+ }
138
+
139
+ PerformSendRequest(requestId, std::move(request), responseType == "text");
140
+ } catch (std::exception const &e) {
141
+ if (m_onError) {
142
+ m_onError(requestId, e.what());
143
+ }
144
+ } catch (hresult_error const &e) {
145
+ if (m_onError) {
146
+ m_onError(requestId, Utilities::HResultToString(e));
147
+ }
148
+ } catch (...) {
149
+ m_onError(requestId, "Unidentified error sending HTTP request");
150
+ }
151
+ }
152
+
153
+ void WinRTHttpResource::AbortRequest(int64_t requestId) noexcept /*override*/ {
154
+ ResponseType request{nullptr};
155
+
156
+ {
157
+ scoped_lock lock{m_mutex};
158
+ auto iter = m_responses.find(requestId);
159
+ if (iter == std::end(m_responses)) {
160
+ return;
161
+ }
162
+ request = iter->second;
163
+ }
164
+
165
+ try {
166
+ request.Cancel();
167
+ } catch (hresult_error const &e) {
168
+ m_onError(requestId, Utilities::HResultToString(e));
169
+ }
170
+ }
171
+
172
+ void WinRTHttpResource::ClearCookies() noexcept /*override*/ {
173
+ assert(false);
174
+ // NOT IMPLEMENTED
175
+ }
176
+
177
+ void WinRTHttpResource::SetOnRequest(function<void(int64_t requestId)> &&handler) noexcept /*override*/ {
178
+ m_onRequest = std::move(handler);
179
+ }
180
+
181
+ void WinRTHttpResource::SetOnResponse(function<void(int64_t requestId, Response &&response)> &&handler) noexcept
182
+ /*override*/ {
183
+ m_onResponse = std::move(handler);
184
+ }
185
+
186
+ void WinRTHttpResource::SetOnData(function<void(int64_t requestId, std::string &&responseData)> &&handler) noexcept
187
+ /*override*/ {
188
+ m_onData = std::move(handler);
189
+ }
190
+
191
+ void WinRTHttpResource::SetOnError(function<void(int64_t requestId, string &&errorMessage)> &&handler) noexcept
192
+ /*override*/ {
193
+ m_onError = std::move(handler);
194
+ }
195
+
196
+ #pragma endregion IHttpResource
197
+
198
+ void WinRTHttpResource::TrackResponse(int64_t requestId, ResponseType response) noexcept {
199
+ scoped_lock lock{m_mutex};
200
+ m_responses[requestId] = response;
201
+ }
202
+
203
+ void WinRTHttpResource::UntrackResponse(int64_t requestId) noexcept {
204
+ scoped_lock lock{m_mutex};
205
+ m_responses.erase(requestId);
206
+ }
207
+
208
+ fire_and_forget
209
+ WinRTHttpResource::PerformSendRequest(int64_t requestId, HttpRequestMessage &&request, bool textResponse) noexcept {
210
+ // Keep references after coroutine suspension.
211
+ auto self = shared_from_this();
212
+ auto coRequest = std::move(request);
213
+
214
+ // Ensure background thread
215
+ co_await winrt::resume_background();
216
+
217
+ try {
218
+ auto sendRequestOp = self->m_client.SendRequestAsync(coRequest);
219
+
220
+ self->TrackResponse(requestId, sendRequestOp);
221
+
222
+ co_await lessthrow_await_adapter<ResponseType>{sendRequestOp};
223
+ auto result = sendRequestOp.ErrorCode();
224
+ if (result < 0) {
225
+ if (self->m_onError) {
226
+ self->m_onError(requestId, Utilities::HResultToString(std::move(result)));
227
+ }
228
+ self->UntrackResponse(requestId);
229
+ co_return;
230
+ }
231
+
232
+ auto response = sendRequestOp.GetResults();
233
+ if (response) {
234
+ if (self->m_onResponse) {
235
+ Headers headers;
236
+
237
+ // Gather headers for both the response content and the response itself
238
+ // See Invoke-WebRequest PowerShell cmdlet or Chromium response handling
239
+ for (auto header : response.Headers()) {
240
+ headers.emplace(to_string(header.Key()), to_string(header.Value()));
241
+ }
242
+ for (auto header : response.Content().Headers()) {
243
+ headers.emplace(to_string(header.Key()), to_string(header.Value()));
244
+ }
245
+ string url = to_string(response.RequestMessage().RequestUri().AbsoluteUri());
246
+ self->m_onResponse(
247
+ requestId, {static_cast<int32_t>(response.StatusCode()), std::move(headers), std::move(url)});
248
+ }
249
+ }
250
+
251
+ // TODO: Incremental updates?
252
+ if (response && response.Content()) {
253
+ auto inputStream = co_await response.Content().ReadAsInputStreamAsync();
254
+ auto reader = DataReader{inputStream};
255
+
256
+ if (textResponse) {
257
+ reader.UnicodeEncoding(UnicodeEncoding::Utf8);
258
+ }
259
+
260
+ // Only support response sizes up to 10MB.
261
+ // TODO: WHY????
262
+ co_await reader.LoadAsync(10 * 1024 * 1024);
263
+ auto length = reader.UnconsumedBufferLength();
264
+
265
+ if (textResponse) {
266
+ std::vector<uint8_t> data(length);
267
+ reader.ReadBytes(data);
268
+ string responseData = string(Common::Utilities::CheckedReinterpretCast<char *>(data.data()), data.size());
269
+
270
+ if (self->m_onData) {
271
+ self->m_onData(requestId, std::move(responseData));
272
+ }
273
+ } else {
274
+ auto buffer = reader.ReadBuffer(length);
275
+ auto data = CryptographicBuffer::EncodeToBase64String(buffer);
276
+ auto responseData = to_string(std::wstring_view(data));
277
+
278
+ if (self->m_onData) {
279
+ self->m_onData(requestId, std::move(responseData));
280
+ }
281
+ }
282
+ } else {
283
+ if (self->m_onError) {
284
+ self->m_onError(requestId, response == nullptr ? "request failed" : "No response content");
285
+ }
286
+ }
287
+ } catch (std::exception const &e) {
288
+ if (self->m_onError) {
289
+ self->m_onError(requestId, e.what());
290
+ }
291
+ } catch (hresult_error const &e) {
292
+ if (self->m_onError) {
293
+ self->m_onError(requestId, Utilities::HResultToString(e));
294
+ }
295
+ } catch (...) {
296
+ if (self->m_onError) {
297
+ self->m_onError(requestId, "Unhandled exception during request");
298
+ }
299
+ }
300
+
301
+ self->UntrackResponse(requestId);
302
+
303
+ // TODO: keep? See https://devblogs.microsoft.com/oldnewthing/?p=106160
304
+ co_return;
305
+ }
306
+
307
+ #pragma endregion WinRTHttpResource
308
+
309
+ #pragma region IHttpResource
310
+
311
+ /*static*/ shared_ptr<IHttpResource> IHttpResource::Make() noexcept {
312
+ return std::make_shared<WinRTHttpResource>();
313
+ }
314
+
315
+ #pragma endregion IHttpResource
316
+
317
+ } // namespace Microsoft::React
@@ -0,0 +1,71 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT License.
3
+
4
+ #pragma once
5
+
6
+ #include <IHttpResource.h>
7
+
8
+ // Windows API
9
+ #include <winrt/Windows.Web.Http.h>
10
+
11
+ // Standard Library
12
+ #include <mutex>
13
+
14
+ namespace Microsoft::React {
15
+
16
+ class WinRTHttpResource : public IHttpResource, public std::enable_shared_from_this<WinRTHttpResource> {
17
+ typedef winrt::Windows::Foundation::IAsyncOperationWithProgress<
18
+ winrt::Windows::Web::Http::HttpResponseMessage,
19
+ winrt::Windows::Web::Http::HttpProgress>
20
+ ResponseType;
21
+
22
+ static int64_t s_lastRequestId;
23
+
24
+ winrt::Windows::Web::Http::IHttpClient m_client;
25
+ std::mutex m_mutex;
26
+ std::unordered_map<int64_t, ResponseType> m_responses;
27
+
28
+ std::function<void(int64_t requestId)> m_onRequest;
29
+ std::function<void(int64_t requestId, Response &&response)> m_onResponse;
30
+ std::function<void(int64_t requestId, std::string &&responseData)> m_onData;
31
+ std::function<void(int64_t requestId, std::string &&errorMessage /*, bool isTimeout*/)> m_onError;
32
+
33
+ void TrackResponse(int64_t requestId, ResponseType response) noexcept;
34
+
35
+ void UntrackResponse(int64_t requestId) noexcept;
36
+
37
+ winrt::fire_and_forget PerformSendRequest(
38
+ int64_t requestId,
39
+ winrt::Windows::Web::Http::HttpRequestMessage &&request,
40
+ bool textResponse) noexcept;
41
+
42
+ public:
43
+ WinRTHttpResource() noexcept;
44
+
45
+ WinRTHttpResource(winrt::Windows::Web::Http::IHttpClient &&client) noexcept;
46
+
47
+ #pragma region IHttpResource
48
+
49
+ void SendRequest(
50
+ std::string &&method,
51
+ std::string &&url,
52
+ Headers &&headers,
53
+ BodyData &&bodyData,
54
+ std::string &&responseType,
55
+ bool useIncrementalUpdates,
56
+ int64_t timeout,
57
+ bool withCredentials,
58
+ std::function<void(int64_t)> &&callback) noexcept override;
59
+ void AbortRequest(int64_t requestId) noexcept override;
60
+ void ClearCookies() noexcept override;
61
+
62
+ #pragma endregion IHttpResource
63
+
64
+ void SetOnRequest(std::function<void(int64_t requestId)> &&handler) noexcept override;
65
+ void SetOnResponse(std::function<void(int64_t requestId, Response &&response)> &&handler) noexcept override;
66
+ void SetOnData(std::function<void(int64_t requestId, std::string &&responseData)> &&handler) noexcept override;
67
+ void SetOnError(std::function<void(int64_t requestId, std::string &&errorMessage /*, bool isTimeout*/)>
68
+ &&handler) noexcept override;
69
+ };
70
+
71
+ } // namespace Microsoft::React
@@ -5,6 +5,10 @@
5
5
 
6
6
  #include <Utilities.h>
7
7
  #include <Utils/CppWinrtLessExceptions.h>
8
+ #include <Utils/WinRTConversions.h>
9
+
10
+ // MSO
11
+ #include <dispatchQueue/dispatchQueue.h>
8
12
 
9
13
  // Windows API
10
14
  #include <winrt/Windows.Foundation.Collections.h>
@@ -44,6 +48,7 @@ using winrt::Windows::Storage::Streams::IDataWriter;
44
48
  using winrt::Windows::Storage::Streams::UnicodeEncoding;
45
49
 
46
50
  namespace {
51
+
47
52
  ///
48
53
  /// Implements an awaiter for Mso::DispatchQueue
49
54
  ///
@@ -72,17 +77,6 @@ auto resume_in_queue(const Mso::DispatchQueue &queue) noexcept {
72
77
  return awaitable{queue};
73
78
  } // resume_in_queue
74
79
 
75
- string HResultToString(hresult_error const &e) {
76
- std::stringstream stream;
77
- stream << "[0x" << std::hex << e.code() << "] " << winrt::to_string(e.message());
78
-
79
- return stream.str();
80
- }
81
-
82
- string HResultToString(hresult &&result) {
83
- return HResultToString(hresult_error(std::move(result), hresult_error::from_abi));
84
- }
85
-
86
80
  } // namespace
87
81
 
88
82
  namespace Microsoft::React {
@@ -134,12 +128,12 @@ IAsyncAction WinRTWebSocketResource::PerformConnect(Uri &&uri) noexcept {
134
128
  }
135
129
  } else {
136
130
  if (self->m_errorHandler) {
137
- self->m_errorHandler({HResultToString(std::move(result)), ErrorType::Connection});
131
+ self->m_errorHandler({Utilities::HResultToString(std::move(result)), ErrorType::Connection});
138
132
  }
139
133
  }
140
134
  } catch (hresult_error const &e) {
141
135
  if (self->m_errorHandler) {
142
- self->m_errorHandler({HResultToString(e), ErrorType::Connection});
136
+ self->m_errorHandler({Utilities::HResultToString(e), ErrorType::Connection});
143
137
  }
144
138
  }
145
139
 
@@ -180,12 +174,12 @@ fire_and_forget WinRTWebSocketResource::PerformPing() noexcept {
180
174
  }
181
175
  } else {
182
176
  if (self->m_errorHandler) {
183
- self->m_errorHandler({HResultToString(std::move(result)), ErrorType::Ping});
177
+ self->m_errorHandler({Utilities::HResultToString(std::move(result)), ErrorType::Ping});
184
178
  }
185
179
  }
186
180
  } catch (hresult_error const &e) {
187
181
  if (self->m_errorHandler) {
188
- self->m_errorHandler({HResultToString(e), ErrorType::Ping});
182
+ self->m_errorHandler({Utilities::HResultToString(e), ErrorType::Ping});
189
183
  }
190
184
  }
191
185
  }
@@ -246,7 +240,7 @@ fire_and_forget WinRTWebSocketResource::PerformWrite(string &&message, bool isBi
246
240
  }
247
241
  } else {
248
242
  if (self->m_errorHandler) {
249
- self->m_errorHandler({HResultToString(std::move(result)), ErrorType::Send});
243
+ self->m_errorHandler({Utilities::HResultToString(std::move(result)), ErrorType::Send});
250
244
  }
251
245
  }
252
246
  } catch (std::exception const &e) {
@@ -256,7 +250,7 @@ fire_and_forget WinRTWebSocketResource::PerformWrite(string &&message, bool isBi
256
250
  } catch (hresult_error const &e) {
257
251
  // TODO: Remove after fixing unit tests exceptions.
258
252
  if (self->m_errorHandler) {
259
- self->m_errorHandler({HResultToString(e), ErrorType::Ping});
253
+ self->m_errorHandler({Utilities::HResultToString(e), ErrorType::Ping});
260
254
  }
261
255
  }
262
256
  }
@@ -278,7 +272,7 @@ fire_and_forget WinRTWebSocketResource::PerformClose() noexcept {
278
272
  }
279
273
  } catch (hresult_error const &e) {
280
274
  if (m_errorHandler) {
281
- m_errorHandler({HResultToString(e), ErrorType::Close});
275
+ m_errorHandler({Utilities::HResultToString(e), ErrorType::Close});
282
276
  }
283
277
  }
284
278
 
@@ -322,7 +316,22 @@ void WinRTWebSocketResource::Connect(string &&url, const Protocols &protocols, c
322
316
  }
323
317
  } catch (hresult_error const &e) {
324
318
  if (self->m_errorHandler) {
325
- self->m_errorHandler({HResultToString(e), ErrorType::Receive});
319
+ string errorMessage;
320
+ ErrorType errorType;
321
+ // See
322
+ // https://docs.microsoft.com/uwp/api/windows.networking.sockets.messagewebsocketmessagereceivedeventargs.getdatareader?view=winrt-19041#remarks
323
+ if (e.code() == WININET_E_CONNECTION_ABORTED) {
324
+ errorMessage = "[0x80072EFE] Underlying TCP connection suddenly terminated";
325
+ errorType = ErrorType::Connection;
326
+ self->m_errorHandler({errorMessage, errorType});
327
+
328
+ // Note: We are not clear whether all read-related errors should close the socket.
329
+ self->Close(CloseCode::BadPayload, std::move(errorMessage));
330
+ } else {
331
+ errorMessage = Utilities::HResultToString(e);
332
+ errorType = ErrorType::Receive;
333
+ self->m_errorHandler({errorMessage, errorType});
334
+ }
326
335
  }
327
336
  }
328
337
  });
@@ -346,7 +355,7 @@ void WinRTWebSocketResource::Connect(string &&url, const Protocols &protocols, c
346
355
  uri = Uri{winrt::to_hstring(url)};
347
356
  } catch (hresult_error const &e) {
348
357
  if (m_errorHandler) {
349
- m_errorHandler({HResultToString(e), ErrorType::Connection});
358
+ m_errorHandler({Utilities::HResultToString(e), ErrorType::Connection});
350
359
  }
351
360
 
352
361
  // Abort - Mark connection as concluded.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-windows",
3
- "version": "0.67.8",
3
+ "version": "0.67.9",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",