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.
Files changed (30) hide show
  1. package/Libraries/Core/ReactNativeVersion.js +1 -1
  2. package/Microsoft.ReactNative/JSDispatcherWriter.cpp +60 -22
  3. package/Microsoft.ReactNative/JSDispatcherWriter.h +5 -3
  4. package/Microsoft.ReactNative/Pch/pch.h +0 -1
  5. package/Microsoft.ReactNative/ReactHost/ReactInstanceWin.cpp +1 -0
  6. package/Microsoft.ReactNative/TurboModulesProvider.cpp +146 -84
  7. package/Microsoft.ReactNative/TurboModulesProvider.h +5 -0
  8. package/Microsoft.ReactNative/Views/ViewManagerBase.cpp +4 -2
  9. package/Microsoft.ReactNative.Cxx/JSI/LongLivedJsiValue.h +84 -0
  10. package/Microsoft.ReactNative.Cxx/Microsoft.ReactNative.Cxx.vcxitems +1 -0
  11. package/Microsoft.ReactNative.Cxx/Microsoft.ReactNative.Cxx.vcxitems.filters +3 -0
  12. package/Mso/src/dispatchQueue/uiScheduler_winrt.cpp +6 -1
  13. package/PropertySheets/Generated/PackageVersion.g.props +2 -2
  14. package/Shared/InstanceManager.cpp +29 -0
  15. package/Shared/InstanceManager.h +14 -0
  16. package/Shared/Modules/HttpModule.cpp +4 -2
  17. package/Shared/Networking/IHttpResource.h +1 -1
  18. package/Shared/Networking/IRedirectEventSource.h +18 -0
  19. package/Shared/Networking/IWinRTHttpRequestFactory.h +22 -0
  20. package/Shared/Networking/OriginPolicyHttpFilter.cpp +47 -16
  21. package/Shared/Networking/OriginPolicyHttpFilter.h +16 -3
  22. package/Shared/Networking/RedirectHttpFilter.cpp +283 -0
  23. package/Shared/Networking/RedirectHttpFilter.h +97 -0
  24. package/Shared/Networking/WinRTHttpResource.cpp +207 -154
  25. package/Shared/Networking/WinRTHttpResource.h +17 -4
  26. package/Shared/OInstance.cpp +16 -1
  27. package/Shared/OInstance.h +4 -13
  28. package/Shared/Shared.vcxitems +4 -0
  29. package/Shared/Shared.vcxitems.filters +12 -0
  30. 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::to_string;
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
- HttpRequestMessage request{httpMethod, uri};
82
-
83
- auto args = winrt::make<RequestArgs>();
84
- auto concreteArgs = args.as<RequestArgs>();
85
- concreteArgs->RequestId = requestId;
86
- concreteArgs->Headers = std::move(headers);
87
- concreteArgs->Data = std::move(data);
88
- concreteArgs->IncrementalUpdates = useIncrementalUpdates;
89
- concreteArgs->WithCredentials = withCredentials;
90
- concreteArgs->ResponseType = std::move(responseType);
91
- concreteArgs->Timeout = timeout;
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(function<void(int64_t requestId, string &&errorMessage)> &&handler) noexcept
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 WinRTHttpResource::PerformSendRequest(HttpRequestMessage &&request, IInspectable const &args) noexcept {
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 coReqArgs = coArgs.as<RequestArgs>();
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, coReqArgs->ResponseType)) {
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(coReqArgs->RequestId, std::move(blob));
186
- self->m_onRequestSuccess(coReqArgs->RequestId);
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(coReqArgs->RequestId, Utilities::HResultToString(e));
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(coReqArgs->RequestId, e.what());
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(coReqArgs->RequestId, sendRequestOp);
348
+ self->TrackResponse(reqArgs->RequestId, sendRequestOp);
306
349
 
307
- if (coReqArgs->Timeout > 0) {
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, coReqArgs->Timeout);
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
- self->m_onError(coReqArgs->RequestId, Utilities::HResultToString(HRESULT_FROM_WIN32(ERROR_TIMEOUT)));
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(coReqArgs->RequestId);
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(coReqArgs->RequestId, Utilities::HResultToString(std::move(result)));
383
+ self->m_onError(reqArgs->RequestId, Utilities::HResultToString(std::move(result)), false);
337
384
  }
338
- co_return self->UntrackResponse(coReqArgs->RequestId);
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
- coReqArgs->RequestId,
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(coReqArgs->ResponseType)) {
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(coReqArgs->RequestId, std::move(blob));
379
- self->m_onRequestSuccess(coReqArgs->RequestId);
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 = coReqArgs->ResponseType == "text";
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(coReqArgs->RequestId, std::move(responseData));
461
+ self->m_onData(reqArgs->RequestId, std::move(responseData));
415
462
  }
416
463
  } else {
417
464
  if (self->m_onError) {
418
- self->m_onError(coReqArgs->RequestId, response == nullptr ? "request failed" : "No response content");
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(coReqArgs->RequestId, e.what());
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(coReqArgs->RequestId, Utilities::HResultToString(e));
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(coReqArgs->RequestId, "Unhandled exception during request");
478
+ self->m_onError(reqArgs->RequestId, "Unhandled exception during request", false);
432
479
  }
433
480
  }
434
481
 
435
- self->UntrackResponse(coReqArgs->RequestId);
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
- shared_ptr<WinRTHttpResource> result;
513
+ auto redirFilter = winrt::make<RedirectHttpFilter>();
514
+ HttpClient client;
467
515
 
468
516
  if (static_cast<OriginPolicy>(GetRuntimeOptionInt("Http.OriginPolicy")) == OriginPolicy::None) {
469
- result = std::make_shared<WinRTHttpResource>();
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
- auto client = HttpClient{opFilter};
521
+ auto opFilter = winrt::make<OriginPolicyHttpFilter>(redirFilter);
522
+ redirFilter.as<RedirectHttpFilter>()->SetRedirectSource(opFilter.as<IRedirectEventSource>());
475
523
 
476
- result = std::make_shared<WinRTHttpResource>(std::move(client));
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 /*, bool isTimeout*/)> m_onError;
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::HttpRequestMessage &&request,
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(std::function<void(int64_t requestId, std::string &&errorMessage /*, bool isTimeout*/)>
71
- &&handler) noexcept override;
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