react-native-windows 0.70.0-preview.2 → 0.70.1
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/Libraries/Core/ReactNativeVersion.js +1 -1
- package/Microsoft.ReactNative/JSDispatcherWriter.cpp +60 -22
- package/Microsoft.ReactNative/JSDispatcherWriter.h +5 -3
- package/Microsoft.ReactNative/Pch/pch.h +0 -1
- package/Microsoft.ReactNative/ReactHost/ReactInstanceWin.cpp +1 -0
- package/Microsoft.ReactNative/TurboModulesProvider.cpp +146 -84
- package/Microsoft.ReactNative/TurboModulesProvider.h +5 -0
- package/Microsoft.ReactNative/Views/ViewManagerBase.cpp +4 -2
- package/Microsoft.ReactNative.Cxx/JSI/LongLivedJsiValue.h +84 -0
- package/Microsoft.ReactNative.Cxx/Microsoft.ReactNative.Cxx.vcxitems +1 -0
- package/Microsoft.ReactNative.Cxx/Microsoft.ReactNative.Cxx.vcxitems.filters +3 -0
- package/Mso/src/dispatchQueue/uiScheduler_winrt.cpp +6 -1
- package/PropertySheets/Generated/PackageVersion.g.props +2 -2
- package/Shared/InstanceManager.cpp +29 -0
- package/Shared/InstanceManager.h +14 -0
- package/Shared/Modules/HttpModule.cpp +4 -2
- package/Shared/Networking/IHttpResource.h +1 -1
- package/Shared/Networking/IRedirectEventSource.h +18 -0
- package/Shared/Networking/IWinRTHttpRequestFactory.h +22 -0
- package/Shared/Networking/OriginPolicyHttpFilter.cpp +47 -16
- package/Shared/Networking/OriginPolicyHttpFilter.h +16 -3
- package/Shared/Networking/RedirectHttpFilter.cpp +283 -0
- package/Shared/Networking/RedirectHttpFilter.h +97 -0
- package/Shared/Networking/WinRTHttpResource.cpp +207 -154
- package/Shared/Networking/WinRTHttpResource.h +17 -4
- package/Shared/OInstance.cpp +16 -1
- package/Shared/OInstance.h +4 -13
- package/Shared/Shared.vcxitems +4 -0
- package/Shared/Shared.vcxitems.filters +12 -0
- package/package.json +8 -8
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// Copyright (c) Microsoft Corporation.
|
|
2
2
|
// Licensed under the MIT License.
|
|
3
3
|
|
|
4
|
+
#undef WINRT_LEAN_AND_MEAN
|
|
5
|
+
|
|
4
6
|
#include "WinRTHttpResource.h"
|
|
5
7
|
|
|
6
8
|
#include <CppRuntimeOptions.h>
|
|
@@ -8,7 +10,9 @@
|
|
|
8
10
|
#include <Utils/CppWinrtLessExceptions.h>
|
|
9
11
|
#include <Utils/WinRTConversions.h>
|
|
10
12
|
#include <utilities.h>
|
|
13
|
+
#include "IRedirectEventSource.h"
|
|
11
14
|
#include "OriginPolicyHttpFilter.h"
|
|
15
|
+
#include "RedirectHttpFilter.h"
|
|
12
16
|
|
|
13
17
|
// Boost Libraries
|
|
14
18
|
#include <boost/algorithm/string.hpp>
|
|
@@ -31,7 +35,7 @@ using std::weak_ptr;
|
|
|
31
35
|
using winrt::fire_and_forget;
|
|
32
36
|
using winrt::hresult_error;
|
|
33
37
|
using winrt::to_hstring;
|
|
34
|
-
using winrt::
|
|
38
|
+
using winrt::Windows::Foundation::IAsyncOperation;
|
|
35
39
|
using winrt::Windows::Foundation::IInspectable;
|
|
36
40
|
using winrt::Windows::Foundation::Uri;
|
|
37
41
|
using winrt::Windows::Security::Cryptography::CryptographicBuffer;
|
|
@@ -55,6 +59,138 @@ WinRTHttpResource::WinRTHttpResource(IHttpClient &&client) noexcept : m_client{s
|
|
|
55
59
|
|
|
56
60
|
WinRTHttpResource::WinRTHttpResource() noexcept : WinRTHttpResource(winrt::Windows::Web::Http::HttpClient{}) {}
|
|
57
61
|
|
|
62
|
+
#pragma region IWinRTHttpRequestFactory
|
|
63
|
+
|
|
64
|
+
IAsyncOperation<HttpRequestMessage> WinRTHttpResource::CreateRequest(
|
|
65
|
+
HttpMethod &&method,
|
|
66
|
+
Uri &&uri,
|
|
67
|
+
winrt::Windows::Foundation::Collections::IMap<winrt::hstring, IInspectable> props) noexcept /*override*/ {
|
|
68
|
+
auto request = HttpRequestMessage{std::move(method), std::move(uri)};
|
|
69
|
+
for (auto prop : props) {
|
|
70
|
+
request.Properties().Insert(prop.Key(), prop.Value());
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
auto iReqArgs = request.Properties().Lookup(L"RequestArgs");
|
|
74
|
+
auto reqArgs = iReqArgs.as<RequestArgs>();
|
|
75
|
+
auto self = shared_from_this();
|
|
76
|
+
|
|
77
|
+
HttpMediaTypeHeaderValue contentType{nullptr};
|
|
78
|
+
string contentEncoding;
|
|
79
|
+
string contentLength;
|
|
80
|
+
|
|
81
|
+
// Headers are generally case-insensitive
|
|
82
|
+
// https://www.ietf.org/rfc/rfc2616.txt section 4.2
|
|
83
|
+
for (auto &header : reqArgs->Headers) {
|
|
84
|
+
if (boost::iequals(header.first.c_str(), "Content-Type")) {
|
|
85
|
+
bool success = HttpMediaTypeHeaderValue::TryParse(to_hstring(header.second), contentType);
|
|
86
|
+
if (!success) {
|
|
87
|
+
if (self->m_onError) {
|
|
88
|
+
self->m_onError(reqArgs->RequestId, "Failed to parse Content-Type", false);
|
|
89
|
+
}
|
|
90
|
+
co_return nullptr;
|
|
91
|
+
}
|
|
92
|
+
} else if (boost::iequals(header.first.c_str(), "Content-Encoding")) {
|
|
93
|
+
contentEncoding = header.second;
|
|
94
|
+
} else if (boost::iequals(header.first.c_str(), "Content-Length")) {
|
|
95
|
+
contentLength = header.second;
|
|
96
|
+
} else if (boost::iequals(header.first.c_str(), "Authorization")) {
|
|
97
|
+
bool success = request.Headers().TryAppendWithoutValidation(to_hstring(header.first), to_hstring(header.second));
|
|
98
|
+
if (!success) {
|
|
99
|
+
if (self->m_onError) {
|
|
100
|
+
self->m_onError(reqArgs->RequestId, "Failed to append Authorization", false);
|
|
101
|
+
}
|
|
102
|
+
co_return nullptr;
|
|
103
|
+
}
|
|
104
|
+
} else {
|
|
105
|
+
try {
|
|
106
|
+
request.Headers().Append(to_hstring(header.first), to_hstring(header.second));
|
|
107
|
+
} catch (hresult_error const &e) {
|
|
108
|
+
if (self->m_onError) {
|
|
109
|
+
self->m_onError(reqArgs->RequestId, Utilities::HResultToString(e), false);
|
|
110
|
+
}
|
|
111
|
+
co_return nullptr;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Initialize content
|
|
117
|
+
IHttpContent content{nullptr};
|
|
118
|
+
auto &data = reqArgs->Data;
|
|
119
|
+
if (!data.isNull()) {
|
|
120
|
+
auto bodyHandler = self->m_requestBodyHandler.lock();
|
|
121
|
+
if (bodyHandler && bodyHandler->Supports(data)) {
|
|
122
|
+
auto contentTypeString = contentType ? winrt::to_string(contentType.ToString()) : "";
|
|
123
|
+
dynamic blob;
|
|
124
|
+
try {
|
|
125
|
+
blob = bodyHandler->ToRequestBody(data, contentTypeString);
|
|
126
|
+
} catch (const std::invalid_argument &e) {
|
|
127
|
+
if (self->m_onError) {
|
|
128
|
+
self->m_onError(reqArgs->RequestId, e.what(), false);
|
|
129
|
+
}
|
|
130
|
+
co_return nullptr;
|
|
131
|
+
}
|
|
132
|
+
auto bytes = blob["bytes"];
|
|
133
|
+
auto byteVector = vector<uint8_t>(bytes.size());
|
|
134
|
+
for (auto &byte : bytes) {
|
|
135
|
+
byteVector.push_back(static_cast<uint8_t>(byte.asInt()));
|
|
136
|
+
}
|
|
137
|
+
auto view = winrt::array_view<uint8_t const>{byteVector};
|
|
138
|
+
auto buffer = CryptographicBuffer::CreateFromByteArray(view);
|
|
139
|
+
content = HttpBufferContent{std::move(buffer)};
|
|
140
|
+
} else if (!data["string"].isNull()) {
|
|
141
|
+
content = HttpStringContent{to_hstring(data["string"].asString())};
|
|
142
|
+
} else if (!data["base64"].empty()) {
|
|
143
|
+
auto buffer = CryptographicBuffer::DecodeFromBase64String(to_hstring(data["base64"].asString()));
|
|
144
|
+
content = HttpBufferContent{std::move(buffer)};
|
|
145
|
+
} else if (!data["uri"].empty()) {
|
|
146
|
+
auto file = co_await StorageFile::GetFileFromApplicationUriAsync(Uri{to_hstring(data["uri"].asString())});
|
|
147
|
+
auto stream = co_await file.OpenReadAsync();
|
|
148
|
+
content = HttpStreamContent{std::move(stream)};
|
|
149
|
+
} else if (!data["form"].empty()) {
|
|
150
|
+
// #9535 - HTTP form data support
|
|
151
|
+
// winrt::Windows::Web::Http::HttpMultipartFormDataContent()
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Attach content headers
|
|
156
|
+
if (content != nullptr) {
|
|
157
|
+
if (contentType) {
|
|
158
|
+
content.Headers().ContentType(contentType);
|
|
159
|
+
}
|
|
160
|
+
if (!contentEncoding.empty()) {
|
|
161
|
+
if (!content.Headers().ContentEncoding().TryParseAdd(to_hstring(contentEncoding))) {
|
|
162
|
+
if (self->m_onError)
|
|
163
|
+
self->m_onError(reqArgs->RequestId, "Failed to parse Content-Encoding", false);
|
|
164
|
+
|
|
165
|
+
co_return nullptr;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (!contentLength.empty()) {
|
|
170
|
+
try {
|
|
171
|
+
const auto contentLengthHeader = std::stol(contentLength);
|
|
172
|
+
content.Headers().ContentLength(contentLengthHeader);
|
|
173
|
+
} catch (const std::invalid_argument &e) {
|
|
174
|
+
if (self->m_onError)
|
|
175
|
+
self->m_onError(reqArgs->RequestId, e.what() + string{" ["} + contentLength + "]", false);
|
|
176
|
+
|
|
177
|
+
co_return nullptr;
|
|
178
|
+
} catch (const std::out_of_range &e) {
|
|
179
|
+
if (self->m_onError)
|
|
180
|
+
self->m_onError(reqArgs->RequestId, e.what() + string{" ["} + contentLength + "]", false);
|
|
181
|
+
|
|
182
|
+
co_return nullptr;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
request.Content(content);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
co_return request;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
#pragma endregion IWinRTHttpRequestFactory
|
|
193
|
+
|
|
58
194
|
#pragma region IHttpResource
|
|
59
195
|
|
|
60
196
|
void WinRTHttpResource::SendRequest(
|
|
@@ -78,29 +214,28 @@ void WinRTHttpResource::SendRequest(
|
|
|
78
214
|
try {
|
|
79
215
|
HttpMethod httpMethod{to_hstring(std::move(method))};
|
|
80
216
|
Uri uri{to_hstring(std::move(url))};
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
auto
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
PerformSendRequest(std::move(request), args);
|
|
217
|
+
|
|
218
|
+
auto iReqArgs = winrt::make<RequestArgs>();
|
|
219
|
+
auto reqArgs = iReqArgs.as<RequestArgs>();
|
|
220
|
+
reqArgs->RequestId = requestId;
|
|
221
|
+
reqArgs->Headers = std::move(headers);
|
|
222
|
+
reqArgs->Data = std::move(data);
|
|
223
|
+
reqArgs->IncrementalUpdates = useIncrementalUpdates;
|
|
224
|
+
reqArgs->WithCredentials = withCredentials;
|
|
225
|
+
reqArgs->ResponseType = std::move(responseType);
|
|
226
|
+
reqArgs->Timeout = timeout;
|
|
227
|
+
|
|
228
|
+
PerformSendRequest(std::move(httpMethod), std::move(uri), iReqArgs);
|
|
94
229
|
} catch (std::exception const &e) {
|
|
95
230
|
if (m_onError) {
|
|
96
|
-
m_onError(requestId, e.what());
|
|
231
|
+
m_onError(requestId, e.what(), false);
|
|
97
232
|
}
|
|
98
233
|
} catch (hresult_error const &e) {
|
|
99
234
|
if (m_onError) {
|
|
100
|
-
m_onError(requestId, Utilities::HResultToString(e));
|
|
235
|
+
m_onError(requestId, Utilities::HResultToString(e), false);
|
|
101
236
|
}
|
|
102
237
|
} catch (...) {
|
|
103
|
-
m_onError(requestId, "Unidentified error sending HTTP request");
|
|
238
|
+
m_onError(requestId, "Unidentified error sending HTTP request", false);
|
|
104
239
|
}
|
|
105
240
|
}
|
|
106
241
|
|
|
@@ -119,7 +254,7 @@ void WinRTHttpResource::AbortRequest(int64_t requestId) noexcept /*override*/ {
|
|
|
119
254
|
try {
|
|
120
255
|
request.Cancel();
|
|
121
256
|
} catch (hresult_error const &e) {
|
|
122
|
-
m_onError(requestId, Utilities::HResultToString(e));
|
|
257
|
+
m_onError(requestId, Utilities::HResultToString(e), false);
|
|
123
258
|
}
|
|
124
259
|
}
|
|
125
260
|
|
|
@@ -148,7 +283,8 @@ void WinRTHttpResource::SetOnData(function<void(int64_t requestId, dynamic &&res
|
|
|
148
283
|
m_onDataDynamic = std::move(handler);
|
|
149
284
|
}
|
|
150
285
|
|
|
151
|
-
void WinRTHttpResource::SetOnError(
|
|
286
|
+
void WinRTHttpResource::SetOnError(
|
|
287
|
+
function<void(int64_t requestId, string &&errorMessage, bool isTimeout)> &&handler) noexcept
|
|
152
288
|
/*override*/ {
|
|
153
289
|
m_onError = std::move(handler);
|
|
154
290
|
}
|
|
@@ -165,146 +301,53 @@ void WinRTHttpResource::UntrackResponse(int64_t requestId) noexcept {
|
|
|
165
301
|
m_responses.erase(requestId);
|
|
166
302
|
}
|
|
167
303
|
|
|
168
|
-
fire_and_forget
|
|
304
|
+
fire_and_forget
|
|
305
|
+
WinRTHttpResource::PerformSendRequest(HttpMethod &&method, Uri &&rtUri, IInspectable const &args) noexcept {
|
|
169
306
|
// Keep references after coroutine suspension.
|
|
170
307
|
auto self = shared_from_this();
|
|
171
|
-
auto coRequest = std::move(request);
|
|
172
308
|
auto coArgs = args;
|
|
173
|
-
auto
|
|
309
|
+
auto reqArgs = coArgs.as<RequestArgs>();
|
|
310
|
+
auto coMethod = std::move(method);
|
|
311
|
+
auto coUri = std::move(rtUri);
|
|
174
312
|
|
|
175
313
|
// Ensure background thread
|
|
176
314
|
co_await winrt::resume_background();
|
|
177
315
|
|
|
316
|
+
auto props = winrt::multi_threaded_map<winrt::hstring, IInspectable>();
|
|
317
|
+
props.Insert(L"RequestArgs", coArgs);
|
|
318
|
+
|
|
319
|
+
auto coRequest = co_await CreateRequest(std::move(coMethod), std::move(coUri), props);
|
|
320
|
+
if (!coRequest) {
|
|
321
|
+
co_return;
|
|
322
|
+
}
|
|
323
|
+
|
|
178
324
|
// If URI handler is available, it takes over request processing.
|
|
179
325
|
if (auto uriHandler = self->m_uriHandler.lock()) {
|
|
180
326
|
auto uri = winrt::to_string(coRequest.RequestUri().ToString());
|
|
181
327
|
try {
|
|
182
|
-
if (uriHandler->Supports(uri,
|
|
328
|
+
if (uriHandler->Supports(uri, reqArgs->ResponseType)) {
|
|
183
329
|
auto blob = uriHandler->Fetch(uri);
|
|
184
330
|
if (self->m_onDataDynamic && self->m_onRequestSuccess) {
|
|
185
|
-
self->m_onDataDynamic(
|
|
186
|
-
self->m_onRequestSuccess(
|
|
331
|
+
self->m_onDataDynamic(reqArgs->RequestId, std::move(blob));
|
|
332
|
+
self->m_onRequestSuccess(reqArgs->RequestId);
|
|
187
333
|
}
|
|
188
334
|
|
|
189
335
|
co_return;
|
|
190
336
|
}
|
|
191
337
|
} catch (const hresult_error &e) {
|
|
192
338
|
if (self->m_onError)
|
|
193
|
-
co_return self->m_onError(
|
|
339
|
+
co_return self->m_onError(reqArgs->RequestId, Utilities::HResultToString(e), false);
|
|
194
340
|
} catch (const std::exception &e) {
|
|
195
341
|
if (self->m_onError)
|
|
196
|
-
co_return self->m_onError(
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
HttpMediaTypeHeaderValue contentType{nullptr};
|
|
201
|
-
string contentEncoding;
|
|
202
|
-
string contentLength;
|
|
203
|
-
|
|
204
|
-
// Headers are generally case-insensitive
|
|
205
|
-
// https://www.ietf.org/rfc/rfc2616.txt section 4.2
|
|
206
|
-
for (auto &header : coReqArgs->Headers) {
|
|
207
|
-
if (boost::iequals(header.first.c_str(), "Content-Type")) {
|
|
208
|
-
bool success = HttpMediaTypeHeaderValue::TryParse(to_hstring(header.second), contentType);
|
|
209
|
-
if (!success && m_onError) {
|
|
210
|
-
co_return m_onError(coReqArgs->RequestId, "Failed to parse Content-Type");
|
|
211
|
-
}
|
|
212
|
-
} else if (boost::iequals(header.first.c_str(), "Content-Encoding")) {
|
|
213
|
-
contentEncoding = header.second;
|
|
214
|
-
} else if (boost::iequals(header.first.c_str(), "Content-Length")) {
|
|
215
|
-
contentLength = header.second;
|
|
216
|
-
} else if (boost::iequals(header.first.c_str(), "Authorization")) {
|
|
217
|
-
bool success =
|
|
218
|
-
coRequest.Headers().TryAppendWithoutValidation(to_hstring(header.first), to_hstring(header.second));
|
|
219
|
-
if (!success && m_onError) {
|
|
220
|
-
co_return m_onError(coReqArgs->RequestId, "Failed to append Authorization");
|
|
221
|
-
}
|
|
222
|
-
} else if (boost::iequals(header.first.c_str(), "User-Agent")) {
|
|
223
|
-
bool success =
|
|
224
|
-
coRequest.Headers().TryAppendWithoutValidation(to_hstring(header.first), to_hstring(header.second));
|
|
225
|
-
if (!success && m_onError) {
|
|
226
|
-
co_return m_onError(coReqArgs->RequestId, "Failed to append User-Agent");
|
|
227
|
-
}
|
|
228
|
-
} else {
|
|
229
|
-
try {
|
|
230
|
-
coRequest.Headers().Append(to_hstring(header.first), to_hstring(header.second));
|
|
231
|
-
} catch (hresult_error const &e) {
|
|
232
|
-
if (self->m_onError) {
|
|
233
|
-
co_return self->m_onError(coReqArgs->RequestId, Utilities::HResultToString(e));
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
IHttpContent content{nullptr};
|
|
240
|
-
auto &data = coReqArgs->Data;
|
|
241
|
-
if (!data.isNull()) {
|
|
242
|
-
auto bodyHandler = self->m_requestBodyHandler.lock();
|
|
243
|
-
if (bodyHandler && bodyHandler->Supports(data)) {
|
|
244
|
-
auto contentTypeString = contentType ? winrt::to_string(contentType.ToString()) : "";
|
|
245
|
-
dynamic blob;
|
|
246
|
-
try {
|
|
247
|
-
blob = bodyHandler->ToRequestBody(data, contentTypeString);
|
|
248
|
-
} catch (const std::invalid_argument &e) {
|
|
249
|
-
if (self->m_onError) {
|
|
250
|
-
self->m_onError(coReqArgs->RequestId, e.what());
|
|
251
|
-
}
|
|
252
|
-
co_return;
|
|
253
|
-
}
|
|
254
|
-
auto bytes = blob["bytes"];
|
|
255
|
-
auto byteVector = vector<uint8_t>(bytes.size());
|
|
256
|
-
for (auto &byte : bytes) {
|
|
257
|
-
byteVector.push_back(static_cast<uint8_t>(byte.asInt()));
|
|
258
|
-
}
|
|
259
|
-
auto view = winrt::array_view<uint8_t const>{byteVector};
|
|
260
|
-
auto buffer = CryptographicBuffer::CreateFromByteArray(view);
|
|
261
|
-
content = HttpBufferContent{std::move(buffer)};
|
|
262
|
-
} else if (!data["string"].empty()) {
|
|
263
|
-
content = HttpStringContent{to_hstring(data["string"].asString())};
|
|
264
|
-
} else if (!data["base64"].empty()) {
|
|
265
|
-
auto buffer = CryptographicBuffer::DecodeFromBase64String(to_hstring(data["base64"].asString()));
|
|
266
|
-
content = HttpBufferContent{std::move(buffer)};
|
|
267
|
-
} else if (!data["uri"].empty()) {
|
|
268
|
-
auto file = co_await StorageFile::GetFileFromApplicationUriAsync(Uri{to_hstring(data["uri"].asString())});
|
|
269
|
-
auto stream = co_await file.OpenReadAsync();
|
|
270
|
-
content = HttpStreamContent{std::move(stream)};
|
|
271
|
-
} else if (!data["form"].empty()) {
|
|
272
|
-
// #9535 - HTTP form data support
|
|
273
|
-
// winrt::Windows::Web::Http::HttpMultipartFormDataContent()
|
|
274
|
-
} else {
|
|
275
|
-
// Assume empty request body.
|
|
276
|
-
// content = HttpStringContent{L""};
|
|
342
|
+
co_return self->m_onError(reqArgs->RequestId, e.what(), false);
|
|
277
343
|
}
|
|
278
344
|
}
|
|
279
345
|
|
|
280
|
-
if (content != nullptr) {
|
|
281
|
-
// Attach content headers
|
|
282
|
-
if (contentType) {
|
|
283
|
-
content.Headers().ContentType(contentType);
|
|
284
|
-
}
|
|
285
|
-
if (!contentEncoding.empty()) {
|
|
286
|
-
if (!content.Headers().ContentEncoding().TryParseAdd(to_hstring(contentEncoding))) {
|
|
287
|
-
if (self->m_onError)
|
|
288
|
-
self->m_onError(coReqArgs->RequestId, "Failed to parse Content-Encoding");
|
|
289
|
-
|
|
290
|
-
co_return;
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
if (!contentLength.empty()) {
|
|
295
|
-
const auto contentLengthHeader = _atoi64(contentLength.c_str());
|
|
296
|
-
content.Headers().ContentLength(contentLengthHeader);
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
coRequest.Content(content);
|
|
300
|
-
}
|
|
301
|
-
|
|
302
346
|
try {
|
|
303
|
-
coRequest.Properties().Insert(L"RequestArgs", coArgs);
|
|
304
347
|
auto sendRequestOp = self->m_client.SendRequestAsync(coRequest);
|
|
305
|
-
self->TrackResponse(
|
|
348
|
+
self->TrackResponse(reqArgs->RequestId, sendRequestOp);
|
|
306
349
|
|
|
307
|
-
if (
|
|
350
|
+
if (reqArgs->Timeout > 0) {
|
|
308
351
|
// See https://devblogs.microsoft.com/oldnewthing/20220415-00/?p=106486
|
|
309
352
|
auto timedOut = std::make_shared<bool>(false);
|
|
310
353
|
auto sendRequestTimeout = [](auto timedOut, auto milliseconds) -> ResponseOperation {
|
|
@@ -312,7 +355,7 @@ fire_and_forget WinRTHttpResource::PerformSendRequest(HttpRequestMessage &&reque
|
|
|
312
355
|
co_await winrt::resume_after(winrt::Windows::Foundation::TimeSpan{milliseconds * 10000});
|
|
313
356
|
*timedOut = true;
|
|
314
357
|
co_return nullptr;
|
|
315
|
-
}(timedOut,
|
|
358
|
+
}(timedOut, reqArgs->Timeout);
|
|
316
359
|
|
|
317
360
|
co_await lessthrow_await_adapter<ResponseOperation>{winrt::when_any(sendRequestOp, sendRequestTimeout)};
|
|
318
361
|
|
|
@@ -322,9 +365,13 @@ fire_and_forget WinRTHttpResource::PerformSendRequest(HttpRequestMessage &&reque
|
|
|
322
365
|
|
|
323
366
|
if (*timedOut) {
|
|
324
367
|
if (self->m_onError) {
|
|
325
|
-
|
|
368
|
+
// TODO: Try to replace with either:
|
|
369
|
+
// WININET_E_TIMEOUT
|
|
370
|
+
// ERROR_INTERNET_TIMEOUT
|
|
371
|
+
// INET_E_CONNECTION_TIMEOUT
|
|
372
|
+
self->m_onError(reqArgs->RequestId, Utilities::HResultToString(HRESULT_FROM_WIN32(ERROR_TIMEOUT)), true);
|
|
326
373
|
}
|
|
327
|
-
co_return self->UntrackResponse(
|
|
374
|
+
co_return self->UntrackResponse(reqArgs->RequestId);
|
|
328
375
|
}
|
|
329
376
|
} else {
|
|
330
377
|
co_await lessthrow_await_adapter<ResponseOperation>{sendRequestOp};
|
|
@@ -333,9 +380,9 @@ fire_and_forget WinRTHttpResource::PerformSendRequest(HttpRequestMessage &&reque
|
|
|
333
380
|
auto result = sendRequestOp.ErrorCode();
|
|
334
381
|
if (result < 0) {
|
|
335
382
|
if (self->m_onError) {
|
|
336
|
-
self->m_onError(
|
|
383
|
+
self->m_onError(reqArgs->RequestId, Utilities::HResultToString(std::move(result)), false);
|
|
337
384
|
}
|
|
338
|
-
co_return self->UntrackResponse(
|
|
385
|
+
co_return self->UntrackResponse(reqArgs->RequestId);
|
|
339
386
|
}
|
|
340
387
|
|
|
341
388
|
auto response = sendRequestOp.GetResults();
|
|
@@ -354,7 +401,7 @@ fire_and_forget WinRTHttpResource::PerformSendRequest(HttpRequestMessage &&reque
|
|
|
354
401
|
}
|
|
355
402
|
|
|
356
403
|
self->m_onResponse(
|
|
357
|
-
|
|
404
|
+
reqArgs->RequestId,
|
|
358
405
|
{static_cast<int32_t>(response.StatusCode()), std::move(url), std::move(responseHeaders)});
|
|
359
406
|
}
|
|
360
407
|
}
|
|
@@ -369,21 +416,21 @@ fire_and_forget WinRTHttpResource::PerformSendRequest(HttpRequestMessage &&reque
|
|
|
369
416
|
|
|
370
417
|
// Let response handler take over, if set
|
|
371
418
|
if (auto responseHandler = self->m_responseHandler.lock()) {
|
|
372
|
-
if (responseHandler->Supports(
|
|
419
|
+
if (responseHandler->Supports(reqArgs->ResponseType)) {
|
|
373
420
|
auto bytes = vector<uint8_t>(reader.UnconsumedBufferLength());
|
|
374
421
|
reader.ReadBytes(bytes);
|
|
375
422
|
auto blob = responseHandler->ToResponseData(std::move(bytes));
|
|
376
423
|
|
|
377
424
|
if (self->m_onDataDynamic && self->m_onRequestSuccess) {
|
|
378
|
-
self->m_onDataDynamic(
|
|
379
|
-
self->m_onRequestSuccess(
|
|
425
|
+
self->m_onDataDynamic(reqArgs->RequestId, std::move(blob));
|
|
426
|
+
self->m_onRequestSuccess(reqArgs->RequestId);
|
|
380
427
|
}
|
|
381
428
|
|
|
382
429
|
co_return;
|
|
383
430
|
}
|
|
384
431
|
}
|
|
385
432
|
|
|
386
|
-
auto isText =
|
|
433
|
+
auto isText = reqArgs->ResponseType == "text";
|
|
387
434
|
if (isText) {
|
|
388
435
|
reader.UnicodeEncoding(UnicodeEncoding::Utf8);
|
|
389
436
|
}
|
|
@@ -406,33 +453,33 @@ fire_and_forget WinRTHttpResource::PerformSendRequest(HttpRequestMessage &&reque
|
|
|
406
453
|
buffer = reader.ReadBuffer(length);
|
|
407
454
|
auto data = CryptographicBuffer::EncodeToBase64String(buffer);
|
|
408
455
|
|
|
409
|
-
responseData += to_string(std::wstring_view(data));
|
|
456
|
+
responseData += winrt::to_string(std::wstring_view(data));
|
|
410
457
|
}
|
|
411
458
|
} while (length > 0);
|
|
412
459
|
|
|
413
460
|
if (self->m_onData) {
|
|
414
|
-
self->m_onData(
|
|
461
|
+
self->m_onData(reqArgs->RequestId, std::move(responseData));
|
|
415
462
|
}
|
|
416
463
|
} else {
|
|
417
464
|
if (self->m_onError) {
|
|
418
|
-
self->m_onError(
|
|
465
|
+
self->m_onError(reqArgs->RequestId, response == nullptr ? "request failed" : "No response content", false);
|
|
419
466
|
}
|
|
420
467
|
}
|
|
421
468
|
} catch (std::exception const &e) {
|
|
422
469
|
if (self->m_onError) {
|
|
423
|
-
self->m_onError(
|
|
470
|
+
self->m_onError(reqArgs->RequestId, e.what(), false);
|
|
424
471
|
}
|
|
425
472
|
} catch (hresult_error const &e) {
|
|
426
473
|
if (self->m_onError) {
|
|
427
|
-
self->m_onError(
|
|
474
|
+
self->m_onError(reqArgs->RequestId, Utilities::HResultToString(e), false);
|
|
428
475
|
}
|
|
429
476
|
} catch (...) {
|
|
430
477
|
if (self->m_onError) {
|
|
431
|
-
self->m_onError(
|
|
478
|
+
self->m_onError(reqArgs->RequestId, "Unhandled exception during request", false);
|
|
432
479
|
}
|
|
433
480
|
}
|
|
434
481
|
|
|
435
|
-
self->UntrackResponse(
|
|
482
|
+
self->UntrackResponse(reqArgs->RequestId);
|
|
436
483
|
} // PerformSendRequest
|
|
437
484
|
|
|
438
485
|
#pragma region IHttpModuleProxy
|
|
@@ -463,19 +510,25 @@ void WinRTHttpResource::AddResponseHandler(shared_ptr<IResponseHandler> response
|
|
|
463
510
|
using namespace winrt::Microsoft::ReactNative;
|
|
464
511
|
using winrt::Windows::Web::Http::HttpClient;
|
|
465
512
|
|
|
466
|
-
|
|
513
|
+
auto redirFilter = winrt::make<RedirectHttpFilter>();
|
|
514
|
+
HttpClient client;
|
|
467
515
|
|
|
468
516
|
if (static_cast<OriginPolicy>(GetRuntimeOptionInt("Http.OriginPolicy")) == OriginPolicy::None) {
|
|
469
|
-
|
|
517
|
+
client = HttpClient{redirFilter};
|
|
470
518
|
} else {
|
|
471
519
|
auto globalOrigin = GetRuntimeOptionString("Http.GlobalOrigin");
|
|
472
520
|
OriginPolicyHttpFilter::SetStaticOrigin(std::move(globalOrigin));
|
|
473
|
-
auto opFilter = winrt::make<OriginPolicyHttpFilter>();
|
|
474
|
-
|
|
521
|
+
auto opFilter = winrt::make<OriginPolicyHttpFilter>(redirFilter);
|
|
522
|
+
redirFilter.as<RedirectHttpFilter>()->SetRedirectSource(opFilter.as<IRedirectEventSource>());
|
|
475
523
|
|
|
476
|
-
|
|
524
|
+
client = HttpClient{opFilter};
|
|
477
525
|
}
|
|
478
526
|
|
|
527
|
+
auto result = std::make_shared<WinRTHttpResource>(std::move(client));
|
|
528
|
+
|
|
529
|
+
// Allow redirect filter to create requests based on the resource's state
|
|
530
|
+
redirFilter.as<RedirectHttpFilter>()->SetRequestFactory(weak_ptr<IWinRTHttpRequestFactory>{result});
|
|
531
|
+
|
|
479
532
|
// Register resource as HTTP module proxy.
|
|
480
533
|
if (inspectableProperties) {
|
|
481
534
|
auto propId = ReactPropertyId<ReactNonAbiValue<weak_ptr<IHttpModuleProxy>>>{L"HttpModule.Proxy"};
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
#include "IHttpResource.h"
|
|
7
7
|
|
|
8
8
|
#include <Modules/IHttpModuleProxy.h>
|
|
9
|
+
#include "IWinRTHttpRequestFactory.h"
|
|
9
10
|
#include "WinRTTypes.h"
|
|
10
11
|
|
|
11
12
|
// Windows API
|
|
@@ -18,6 +19,7 @@ namespace Microsoft::React::Networking {
|
|
|
18
19
|
|
|
19
20
|
class WinRTHttpResource : public IHttpResource,
|
|
20
21
|
public IHttpModuleProxy,
|
|
22
|
+
public IWinRTHttpRequestFactory,
|
|
21
23
|
public std::enable_shared_from_this<WinRTHttpResource> {
|
|
22
24
|
winrt::Windows::Web::Http::IHttpClient m_client;
|
|
23
25
|
std::mutex m_mutex;
|
|
@@ -27,7 +29,7 @@ class WinRTHttpResource : public IHttpResource,
|
|
|
27
29
|
std::function<void(int64_t requestId, Response &&response)> m_onResponse;
|
|
28
30
|
std::function<void(int64_t requestId, std::string &&responseData)> m_onData;
|
|
29
31
|
std::function<void(int64_t requestId, folly::dynamic &&responseData)> m_onDataDynamic;
|
|
30
|
-
std::function<void(int64_t requestId, std::string &&errorMessage
|
|
32
|
+
std::function<void(int64_t requestId, std::string &&errorMessage, bool isTimeout)> m_onError;
|
|
31
33
|
|
|
32
34
|
// Used for IHttpModuleProxy
|
|
33
35
|
std::weak_ptr<IUriHandler> m_uriHandler;
|
|
@@ -39,7 +41,8 @@ class WinRTHttpResource : public IHttpResource,
|
|
|
39
41
|
void UntrackResponse(int64_t requestId) noexcept;
|
|
40
42
|
|
|
41
43
|
winrt::fire_and_forget PerformSendRequest(
|
|
42
|
-
winrt::Windows::Web::Http::
|
|
44
|
+
winrt::Windows::Web::Http::HttpMethod &&method,
|
|
45
|
+
winrt::Windows::Foundation::Uri &&uri,
|
|
43
46
|
winrt::Windows::Foundation::IInspectable const &args) noexcept;
|
|
44
47
|
|
|
45
48
|
public:
|
|
@@ -47,6 +50,16 @@ class WinRTHttpResource : public IHttpResource,
|
|
|
47
50
|
|
|
48
51
|
WinRTHttpResource(winrt::Windows::Web::Http::IHttpClient &&client) noexcept;
|
|
49
52
|
|
|
53
|
+
#pragma region IWinRTHttpRequestFactory
|
|
54
|
+
|
|
55
|
+
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::Web::Http::HttpRequestMessage> CreateRequest(
|
|
56
|
+
winrt::Windows::Web::Http::HttpMethod &&method,
|
|
57
|
+
winrt::Windows::Foundation::Uri &&uri,
|
|
58
|
+
winrt::Windows::Foundation::Collections::IMap<winrt::hstring, winrt::Windows::Foundation::IInspectable>
|
|
59
|
+
props) noexcept override;
|
|
60
|
+
|
|
61
|
+
#pragma endregion IWinRTHttpRequestFactory
|
|
62
|
+
|
|
50
63
|
#pragma region IHttpResource
|
|
51
64
|
|
|
52
65
|
void SendRequest(
|
|
@@ -67,8 +80,8 @@ class WinRTHttpResource : public IHttpResource,
|
|
|
67
80
|
void SetOnResponse(std::function<void(int64_t requestId, Response &&response)> &&handler) noexcept override;
|
|
68
81
|
void SetOnData(std::function<void(int64_t requestId, std::string &&responseData)> &&handler) noexcept override;
|
|
69
82
|
void SetOnData(std::function<void(int64_t requestId, folly::dynamic &&responseData)> &&handler) noexcept override;
|
|
70
|
-
void SetOnError(
|
|
71
|
-
|
|
83
|
+
void SetOnError(
|
|
84
|
+
std::function<void(int64_t requestId, std::string &&errorMessage, bool isTimeout)> &&handler) noexcept override;
|
|
72
85
|
|
|
73
86
|
#pragma endregion IHttpResource
|
|
74
87
|
|