react-native-windows 0.66.17 → 0.66.18
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 +22 -1
- package/CHANGELOG.md +14 -5
- package/Microsoft.ReactNative/Base/CoreNativeModules.cpp +1 -4
- package/Microsoft.ReactNative.Cxx/NativeModules.h +2 -2
- package/Shared/CreateModules.h +4 -0
- package/Shared/IHttpResource.h +32 -13
- package/Shared/IWebSocketResource.h +1 -1
- package/Shared/Modules/HttpModule.cpp +198 -0
- package/Shared/Modules/HttpModule.h +53 -0
- package/Shared/Modules/WebSocketModule.cpp +4 -0
- package/Shared/OInstance.cpp +21 -1
- package/Shared/Shared.vcxitems +6 -0
- package/Shared/Shared.vcxitems.filters +21 -0
- package/Shared/Utils/WinRTConversions.cpp +22 -0
- package/Shared/Utils/WinRTConversions.h +15 -0
- package/Shared/WinRTHttpResource.cpp +317 -0
- package/Shared/WinRTHttpResource.h +71 -0
- package/Shared/WinRTWebSocketResource.cpp +29 -20
- package/package.json +1 -1
package/CHANGELOG.json
CHANGED
|
@@ -2,7 +2,28 @@
|
|
|
2
2
|
"name": "react-native-windows",
|
|
3
3
|
"entries": [
|
|
4
4
|
{
|
|
5
|
-
"date": "Mon,
|
|
5
|
+
"date": "Mon, 02 May 2022 15:10:38 GMT",
|
|
6
|
+
"tag": "react-native-windows_v0.66.18",
|
|
7
|
+
"version": "0.66.18",
|
|
8
|
+
"comments": {
|
|
9
|
+
"patch": [
|
|
10
|
+
{
|
|
11
|
+
"comment": "Fix clang-check errors in NativeModules.h",
|
|
12
|
+
"author": "53799235+ZihanChen-MSFT@users.noreply.github.com",
|
|
13
|
+
"commit": "94d54d33e317332fa9145129d228a8e323f9b948",
|
|
14
|
+
"package": "react-native-windows"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"comment": "Handle abrupt WebSocket connection interruption (#9829)",
|
|
18
|
+
"author": "julio.rocha@microsoft.com",
|
|
19
|
+
"commit": "e2571e1d39d3fef4d4ad328a1662ab7f30fec633",
|
|
20
|
+
"package": "react-native-windows"
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"date": "Mon, 25 Apr 2022 15:09:39 GMT",
|
|
6
27
|
"tag": "react-native-windows_v0.66.17",
|
|
7
28
|
"version": "0.66.17",
|
|
8
29
|
"comments": {
|
package/CHANGELOG.md
CHANGED
|
@@ -1,18 +1,27 @@
|
|
|
1
1
|
# Change Log - react-native-windows
|
|
2
2
|
|
|
3
|
-
This log was last generated on Mon,
|
|
3
|
+
This log was last generated on Mon, 02 May 2022 15:10:38 GMT and should not be manually modified.
|
|
4
4
|
|
|
5
5
|
<!-- Start content -->
|
|
6
6
|
|
|
7
|
-
## 0.66.
|
|
7
|
+
## 0.66.18
|
|
8
8
|
|
|
9
|
-
Mon,
|
|
9
|
+
Mon, 02 May 2022 15:10:38 GMT
|
|
10
10
|
|
|
11
11
|
### Patches
|
|
12
12
|
|
|
13
|
-
- Fix
|
|
14
|
-
-
|
|
13
|
+
- Fix clang-check errors in NativeModules.h (53799235+ZihanChen-MSFT@users.noreply.github.com)
|
|
14
|
+
- Handle abrupt WebSocket connection interruption (#9829) (julio.rocha@microsoft.com)
|
|
15
15
|
|
|
16
|
+
## 0.66.17
|
|
17
|
+
|
|
18
|
+
Mon, 25 Apr 2022 15:09:39 GMT
|
|
19
|
+
|
|
20
|
+
### Patches
|
|
21
|
+
|
|
22
|
+
- Fix JSI for Node-API and update V8 to 0.65.11 (vmorozov@microsoft.com)
|
|
23
|
+
- Fix Sequential DispatchQueue deadlock on shutdown (vmorozov@microsoft.com)
|
|
24
|
+
|
|
16
25
|
## 0.66.16
|
|
17
26
|
|
|
18
27
|
Mon, 04 Apr 2022 15:13:07 GMT
|
|
@@ -14,7 +14,6 @@
|
|
|
14
14
|
#include <Modules/ImageViewManagerModule.h>
|
|
15
15
|
#include <Modules/LinkingManagerModule.h>
|
|
16
16
|
#include <Modules/NativeUIManager.h>
|
|
17
|
-
#include <Modules/NetworkingModule.h>
|
|
18
17
|
#include <Modules/PaperUIManagerModule.h>
|
|
19
18
|
#include <Threading/MessageQueueThreadFactory.h>
|
|
20
19
|
|
|
@@ -51,9 +50,7 @@ std::vector<facebook::react::NativeModuleDescription> GetCoreModules(
|
|
|
51
50
|
std::vector<facebook::react::NativeModuleDescription> modules;
|
|
52
51
|
|
|
53
52
|
modules.emplace_back(
|
|
54
|
-
Microsoft::React::
|
|
55
|
-
[]() { return std::make_unique<Microsoft::React::NetworkingModule>(); },
|
|
56
|
-
jsMessageQueue);
|
|
53
|
+
"Networking", []() { return Microsoft::React::CreateHttpModule(); }, jsMessageQueue);
|
|
57
54
|
|
|
58
55
|
modules.emplace_back(
|
|
59
56
|
"Timing",
|
|
@@ -1011,7 +1011,7 @@ template <class TModule, int I, class TMethodSpec>
|
|
|
1011
1011
|
struct ReactMethodVerifier {
|
|
1012
1012
|
static constexpr bool Verify() noexcept {
|
|
1013
1013
|
ReactMethodVerifier verifier{};
|
|
1014
|
-
ReactMemberInfoIterator<TModule>{}.GetMemberInfo<I>(verifier);
|
|
1014
|
+
ReactMemberInfoIterator<TModule>{}.template GetMemberInfo<I>(verifier);
|
|
1015
1015
|
return verifier.m_result;
|
|
1016
1016
|
}
|
|
1017
1017
|
|
|
@@ -1029,7 +1029,7 @@ template <class TModule, int I, class TMethodSpec>
|
|
|
1029
1029
|
struct ReactSyncMethodVerifier {
|
|
1030
1030
|
static constexpr bool Verify() noexcept {
|
|
1031
1031
|
ReactSyncMethodVerifier verifier{};
|
|
1032
|
-
ReactMemberInfoIterator<TModule>{}.GetMemberInfo<I>(verifier);
|
|
1032
|
+
ReactMemberInfoIterator<TModule>{}.template GetMemberInfo<I>(verifier);
|
|
1033
1033
|
return verifier.m_result;
|
|
1034
1034
|
}
|
|
1035
1035
|
|
package/Shared/CreateModules.h
CHANGED
|
@@ -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
|
package/Shared/IHttpResource.h
CHANGED
|
@@ -3,32 +3,51 @@
|
|
|
3
3
|
|
|
4
4
|
#pragma once
|
|
5
5
|
|
|
6
|
-
|
|
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::
|
|
15
|
+
typedef std::unordered_map<std::string, std::string> Headers;
|
|
12
16
|
|
|
13
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
33
|
+
std::string &&method,
|
|
34
|
+
std::string &&url,
|
|
35
|
+
Headers &&headers,
|
|
36
|
+
BodyData &&bodyData,
|
|
37
|
+
std::string &&responseType,
|
|
23
38
|
bool useIncrementalUpdates,
|
|
24
|
-
|
|
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(
|
|
31
|
-
virtual void
|
|
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
|
|
@@ -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
|
}
|
package/Shared/OInstance.cpp
CHANGED
|
@@ -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>
|
|
@@ -66,6 +68,18 @@ using namespace Microsoft::JSI;
|
|
|
66
68
|
|
|
67
69
|
using std::make_shared;
|
|
68
70
|
|
|
71
|
+
namespace Microsoft::React {
|
|
72
|
+
|
|
73
|
+
/*extern*/ std::unique_ptr<facebook::xplat::module::CxxModule> CreateHttpModule() noexcept {
|
|
74
|
+
if (GetRuntimeOptionBool("Http.UseMonolithicModule")) {
|
|
75
|
+
return std::make_unique<NetworkingModule>();
|
|
76
|
+
} else {
|
|
77
|
+
return std::make_unique<HttpModule>();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
} // namespace Microsoft::React
|
|
82
|
+
|
|
69
83
|
namespace facebook {
|
|
70
84
|
namespace react {
|
|
71
85
|
|
|
@@ -525,7 +539,13 @@ std::vector<std::unique_ptr<NativeModule>> InstanceImpl::GetDefaultNativeModules
|
|
|
525
539
|
|
|
526
540
|
modules.push_back(std::make_unique<CxxNativeModule>(
|
|
527
541
|
m_innerInstance,
|
|
528
|
-
|
|
542
|
+
Microsoft::React::GetHttpModuleName(),
|
|
543
|
+
[nativeQueue]() -> std::unique_ptr<xplat::module::CxxModule> { return Microsoft::React::CreateHttpModule(); },
|
|
544
|
+
nativeQueue));
|
|
545
|
+
|
|
546
|
+
modules.push_back(std::make_unique<CxxNativeModule>(
|
|
547
|
+
m_innerInstance,
|
|
548
|
+
Microsoft::React::GetWebSocketModuleName(),
|
|
529
549
|
[nativeQueue]() -> std::unique_ptr<xplat::module::CxxModule> {
|
|
530
550
|
return Microsoft::React::CreateWebSocketModule();
|
|
531
551
|
},
|
package/Shared/Shared.vcxitems
CHANGED
|
@@ -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
|
-
|
|
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.
|