react-native-windows 0.69.7 → 0.69.8

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.
@@ -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,12 +10,15 @@
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>
15
19
 
16
20
  // Windows API
21
+ #include <winrt/Windows.Foundation.Collections.h>
17
22
  #include <winrt/Windows.Security.Cryptography.h>
18
23
  #include <winrt/Windows.Storage.Streams.h>
19
24
  #include <winrt/Windows.Web.Http.Headers.h>
@@ -30,7 +35,7 @@ using std::weak_ptr;
30
35
  using winrt::fire_and_forget;
31
36
  using winrt::hresult_error;
32
37
  using winrt::to_hstring;
33
- using winrt::to_string;
38
+ using winrt::Windows::Foundation::IAsyncOperation;
34
39
  using winrt::Windows::Foundation::IInspectable;
35
40
  using winrt::Windows::Foundation::Uri;
36
41
  using winrt::Windows::Security::Cryptography::CryptographicBuffer;
@@ -54,6 +59,138 @@ WinRTHttpResource::WinRTHttpResource(IHttpClient &&client) noexcept : m_client{s
54
59
 
55
60
  WinRTHttpResource::WinRTHttpResource() noexcept : WinRTHttpResource(winrt::Windows::Web::Http::HttpClient{}) {}
56
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
+
57
194
  #pragma region IHttpResource
58
195
 
59
196
  void WinRTHttpResource::SendRequest(
@@ -77,29 +214,28 @@ void WinRTHttpResource::SendRequest(
77
214
  try {
78
215
  HttpMethod httpMethod{to_hstring(std::move(method))};
79
216
  Uri uri{to_hstring(std::move(url))};
80
- HttpRequestMessage request{httpMethod, uri};
81
-
82
- auto args = winrt::make<RequestArgs>();
83
- auto concreteArgs = args.as<RequestArgs>();
84
- concreteArgs->RequestId = requestId;
85
- concreteArgs->Headers = std::move(headers);
86
- concreteArgs->Data = std::move(data);
87
- concreteArgs->IncrementalUpdates = useIncrementalUpdates;
88
- concreteArgs->WithCredentials = withCredentials;
89
- concreteArgs->ResponseType = std::move(responseType);
90
- concreteArgs->Timeout = timeout;
91
-
92
- 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);
93
229
  } catch (std::exception const &e) {
94
230
  if (m_onError) {
95
- m_onError(requestId, e.what());
231
+ m_onError(requestId, e.what(), false);
96
232
  }
97
233
  } catch (hresult_error const &e) {
98
234
  if (m_onError) {
99
- m_onError(requestId, Utilities::HResultToString(e));
235
+ m_onError(requestId, Utilities::HResultToString(e), false);
100
236
  }
101
237
  } catch (...) {
102
- m_onError(requestId, "Unidentified error sending HTTP request");
238
+ m_onError(requestId, "Unidentified error sending HTTP request", false);
103
239
  }
104
240
  }
105
241
 
@@ -118,7 +254,7 @@ void WinRTHttpResource::AbortRequest(int64_t requestId) noexcept /*override*/ {
118
254
  try {
119
255
  request.Cancel();
120
256
  } catch (hresult_error const &e) {
121
- m_onError(requestId, Utilities::HResultToString(e));
257
+ m_onError(requestId, Utilities::HResultToString(e), false);
122
258
  }
123
259
  }
124
260
 
@@ -147,7 +283,8 @@ void WinRTHttpResource::SetOnData(function<void(int64_t requestId, dynamic &&res
147
283
  m_onDataDynamic = std::move(handler);
148
284
  }
149
285
 
150
- 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
151
288
  /*override*/ {
152
289
  m_onError = std::move(handler);
153
290
  }
@@ -164,146 +301,88 @@ void WinRTHttpResource::UntrackResponse(int64_t requestId) noexcept {
164
301
  m_responses.erase(requestId);
165
302
  }
166
303
 
167
- 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 {
168
306
  // Keep references after coroutine suspension.
169
307
  auto self = shared_from_this();
170
- auto coRequest = std::move(request);
171
308
  auto coArgs = args;
172
- auto coReqArgs = coArgs.as<RequestArgs>();
309
+ auto reqArgs = coArgs.as<RequestArgs>();
310
+ auto coMethod = std::move(method);
311
+ auto coUri = std::move(rtUri);
173
312
 
174
313
  // Ensure background thread
175
314
  co_await winrt::resume_background();
176
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
+
177
324
  // If URI handler is available, it takes over request processing.
178
325
  if (auto uriHandler = self->m_uriHandler.lock()) {
179
326
  auto uri = winrt::to_string(coRequest.RequestUri().ToString());
180
327
  try {
181
- if (uriHandler->Supports(uri, coReqArgs->ResponseType)) {
328
+ if (uriHandler->Supports(uri, reqArgs->ResponseType)) {
182
329
  auto blob = uriHandler->Fetch(uri);
183
330
  if (self->m_onDataDynamic && self->m_onRequestSuccess) {
184
- self->m_onDataDynamic(coReqArgs->RequestId, std::move(blob));
185
- self->m_onRequestSuccess(coReqArgs->RequestId);
331
+ self->m_onDataDynamic(reqArgs->RequestId, std::move(blob));
332
+ self->m_onRequestSuccess(reqArgs->RequestId);
186
333
  }
187
334
 
188
335
  co_return;
189
336
  }
190
337
  } catch (const hresult_error &e) {
191
338
  if (self->m_onError)
192
- co_return self->m_onError(coReqArgs->RequestId, Utilities::HResultToString(e));
339
+ co_return self->m_onError(reqArgs->RequestId, Utilities::HResultToString(e), false);
193
340
  } catch (const std::exception &e) {
194
341
  if (self->m_onError)
195
- co_return self->m_onError(coReqArgs->RequestId, e.what());
342
+ co_return self->m_onError(reqArgs->RequestId, e.what(), false);
196
343
  }
197
344
  }
198
345
 
199
- HttpMediaTypeHeaderValue contentType{nullptr};
200
- string contentEncoding;
201
- string contentLength;
346
+ try {
347
+ auto sendRequestOp = self->m_client.SendRequestAsync(coRequest);
348
+ self->TrackResponse(reqArgs->RequestId, sendRequestOp);
202
349
 
203
- // Headers are generally case-insensitive
204
- // https://www.ietf.org/rfc/rfc2616.txt section 4.2
205
- for (auto &header : coReqArgs->Headers) {
206
- if (boost::iequals(header.first.c_str(), "Content-Type")) {
207
- bool success = HttpMediaTypeHeaderValue::TryParse(to_hstring(header.second), contentType);
208
- if (!success && m_onError) {
209
- co_return m_onError(coReqArgs->RequestId, "Failed to parse Content-Type");
210
- }
211
- } else if (boost::iequals(header.first.c_str(), "Content-Encoding")) {
212
- contentEncoding = header.second;
213
- } else if (boost::iequals(header.first.c_str(), "Content-Length")) {
214
- contentLength = header.second;
215
- } else if (boost::iequals(header.first.c_str(), "Authorization")) {
216
- bool success =
217
- coRequest.Headers().TryAppendWithoutValidation(to_hstring(header.first), to_hstring(header.second));
218
- if (!success && m_onError) {
219
- co_return m_onError(coReqArgs->RequestId, "Failed to append Authorization");
220
- }
221
- } else {
222
- try {
223
- coRequest.Headers().Append(to_hstring(header.first), to_hstring(header.second));
224
- } catch (hresult_error const &e) {
225
- if (self->m_onError) {
226
- co_return self->m_onError(coReqArgs->RequestId, Utilities::HResultToString(e));
227
- }
228
- }
229
- }
230
- }
350
+ if (reqArgs->Timeout > 0) {
351
+ // See https://devblogs.microsoft.com/oldnewthing/20220415-00/?p=106486
352
+ auto timedOut = std::make_shared<bool>(false);
353
+ auto sendRequestTimeout = [](auto timedOut, auto milliseconds) -> ResponseOperation {
354
+ // Convert milliseconds to "ticks" (10^-7 seconds)
355
+ co_await winrt::resume_after(winrt::Windows::Foundation::TimeSpan{milliseconds * 10000});
356
+ *timedOut = true;
357
+ co_return nullptr;
358
+ }(timedOut, reqArgs->Timeout);
231
359
 
232
- IHttpContent content{nullptr};
233
- auto &data = coReqArgs->Data;
234
- if (!data.isNull()) {
235
- auto bodyHandler = self->m_requestBodyHandler.lock();
236
- if (bodyHandler && bodyHandler->Supports(data)) {
237
- auto contentTypeString = contentType ? winrt::to_string(contentType.ToString()) : "";
238
- dynamic blob;
239
- try {
240
- blob = bodyHandler->ToRequestBody(data, contentTypeString);
241
- } catch (const std::invalid_argument &e) {
360
+ co_await lessthrow_await_adapter<ResponseOperation>{winrt::when_any(sendRequestOp, sendRequestTimeout)};
361
+
362
+ // Cancel either still unfinished coroutine.
363
+ sendRequestTimeout.Cancel();
364
+ sendRequestOp.Cancel();
365
+
366
+ if (*timedOut) {
242
367
  if (self->m_onError) {
243
- self->m_onError(coReqArgs->RequestId, e.what());
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);
244
373
  }
245
- co_return;
374
+ co_return self->UntrackResponse(reqArgs->RequestId);
246
375
  }
247
- auto bytes = blob["bytes"];
248
- auto byteVector = vector<uint8_t>(bytes.size());
249
- for (auto &byte : bytes) {
250
- byteVector.push_back(static_cast<uint8_t>(byte.asInt()));
251
- }
252
- auto view = winrt::array_view<uint8_t>{byteVector};
253
- auto buffer = CryptographicBuffer::CreateFromByteArray(view);
254
- content = HttpBufferContent{std::move(buffer)};
255
- } else if (!data["string"].empty()) {
256
- content = HttpStringContent{to_hstring(data["string"].asString())};
257
- } else if (!data["base64"].empty()) {
258
- auto buffer = CryptographicBuffer::DecodeFromBase64String(to_hstring(data["base64"].asString()));
259
- content = HttpBufferContent{std::move(buffer)};
260
- } else if (!data["uri"].empty()) {
261
- auto file = co_await StorageFile::GetFileFromApplicationUriAsync(Uri{to_hstring(data["uri"].asString())});
262
- auto stream = co_await file.OpenReadAsync();
263
- content = HttpStreamContent{std::move(stream)};
264
- } else if (!data["form"].empty()) {
265
- // #9535 - HTTP form data support
266
- // winrt::Windows::Web::Http::HttpMultipartFormDataContent()
267
376
  } else {
268
- // Assume empty request body.
269
- // content = HttpStringContent{L""};
377
+ co_await lessthrow_await_adapter<ResponseOperation>{sendRequestOp};
270
378
  }
271
- }
272
379
 
273
- if (content != nullptr) {
274
- // Attach content headers
275
- if (contentType) {
276
- content.Headers().ContentType(contentType);
277
- }
278
- if (!contentEncoding.empty()) {
279
- if (!content.Headers().ContentEncoding().TryParseAdd(to_hstring(contentEncoding))) {
280
- if (self->m_onError)
281
- self->m_onError(coReqArgs->RequestId, "Failed to parse Content-Encoding");
282
-
283
- co_return;
284
- }
285
- }
286
-
287
- if (!contentLength.empty()) {
288
- const auto contentLengthHeader = _atoi64(contentLength.c_str());
289
- content.Headers().ContentLength(contentLengthHeader);
290
- }
291
-
292
- coRequest.Content(content);
293
- }
294
-
295
- try {
296
- coRequest.Properties().Insert(L"RequestArgs", coArgs);
297
- auto sendRequestOp = self->m_client.SendRequestAsync(coRequest);
298
- self->TrackResponse(coReqArgs->RequestId, sendRequestOp);
299
-
300
- co_await lessthrow_await_adapter<ResponseOperation>{sendRequestOp};
301
380
  auto result = sendRequestOp.ErrorCode();
302
381
  if (result < 0) {
303
382
  if (self->m_onError) {
304
- self->m_onError(coReqArgs->RequestId, Utilities::HResultToString(std::move(result)));
383
+ self->m_onError(reqArgs->RequestId, Utilities::HResultToString(std::move(result)), false);
305
384
  }
306
- co_return self->UntrackResponse(coReqArgs->RequestId);
385
+ co_return self->UntrackResponse(reqArgs->RequestId);
307
386
  }
308
387
 
309
388
  auto response = sendRequestOp.GetResults();
@@ -322,7 +401,7 @@ fire_and_forget WinRTHttpResource::PerformSendRequest(HttpRequestMessage &&reque
322
401
  }
323
402
 
324
403
  self->m_onResponse(
325
- coReqArgs->RequestId,
404
+ reqArgs->RequestId,
326
405
  {static_cast<int32_t>(response.StatusCode()), std::move(url), std::move(responseHeaders)});
327
406
  }
328
407
  }
@@ -337,21 +416,21 @@ fire_and_forget WinRTHttpResource::PerformSendRequest(HttpRequestMessage &&reque
337
416
 
338
417
  // Let response handler take over, if set
339
418
  if (auto responseHandler = self->m_responseHandler.lock()) {
340
- if (responseHandler->Supports(coReqArgs->ResponseType)) {
419
+ if (responseHandler->Supports(reqArgs->ResponseType)) {
341
420
  auto bytes = vector<uint8_t>(reader.UnconsumedBufferLength());
342
421
  reader.ReadBytes(bytes);
343
422
  auto blob = responseHandler->ToResponseData(std::move(bytes));
344
423
 
345
424
  if (self->m_onDataDynamic && self->m_onRequestSuccess) {
346
- self->m_onDataDynamic(coReqArgs->RequestId, std::move(blob));
347
- self->m_onRequestSuccess(coReqArgs->RequestId);
425
+ self->m_onDataDynamic(reqArgs->RequestId, std::move(blob));
426
+ self->m_onRequestSuccess(reqArgs->RequestId);
348
427
  }
349
428
 
350
429
  co_return;
351
430
  }
352
431
  }
353
432
 
354
- auto isText = coReqArgs->ResponseType == "text";
433
+ auto isText = reqArgs->ResponseType == "text";
355
434
  if (isText) {
356
435
  reader.UnicodeEncoding(UnicodeEncoding::Utf8);
357
436
  }
@@ -374,33 +453,33 @@ fire_and_forget WinRTHttpResource::PerformSendRequest(HttpRequestMessage &&reque
374
453
  buffer = reader.ReadBuffer(length);
375
454
  auto data = CryptographicBuffer::EncodeToBase64String(buffer);
376
455
 
377
- responseData += to_string(std::wstring_view(data));
456
+ responseData += winrt::to_string(std::wstring_view(data));
378
457
  }
379
458
  } while (length > 0);
380
459
 
381
460
  if (self->m_onData) {
382
- self->m_onData(coReqArgs->RequestId, std::move(responseData));
461
+ self->m_onData(reqArgs->RequestId, std::move(responseData));
383
462
  }
384
463
  } else {
385
464
  if (self->m_onError) {
386
- 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);
387
466
  }
388
467
  }
389
468
  } catch (std::exception const &e) {
390
469
  if (self->m_onError) {
391
- self->m_onError(coReqArgs->RequestId, e.what());
470
+ self->m_onError(reqArgs->RequestId, e.what(), false);
392
471
  }
393
472
  } catch (hresult_error const &e) {
394
473
  if (self->m_onError) {
395
- self->m_onError(coReqArgs->RequestId, Utilities::HResultToString(e));
474
+ self->m_onError(reqArgs->RequestId, Utilities::HResultToString(e), false);
396
475
  }
397
476
  } catch (...) {
398
477
  if (self->m_onError) {
399
- self->m_onError(coReqArgs->RequestId, "Unhandled exception during request");
478
+ self->m_onError(reqArgs->RequestId, "Unhandled exception during request", false);
400
479
  }
401
480
  }
402
481
 
403
- self->UntrackResponse(coReqArgs->RequestId);
482
+ self->UntrackResponse(reqArgs->RequestId);
404
483
  } // PerformSendRequest
405
484
 
406
485
  #pragma region IHttpModuleProxy
@@ -431,19 +510,25 @@ void WinRTHttpResource::AddResponseHandler(shared_ptr<IResponseHandler> response
431
510
  using namespace winrt::Microsoft::ReactNative;
432
511
  using winrt::Windows::Web::Http::HttpClient;
433
512
 
434
- shared_ptr<WinRTHttpResource> result;
513
+ auto redirFilter = winrt::make<RedirectHttpFilter>();
514
+ HttpClient client;
435
515
 
436
516
  if (static_cast<OriginPolicy>(GetRuntimeOptionInt("Http.OriginPolicy")) == OriginPolicy::None) {
437
- result = std::make_shared<WinRTHttpResource>();
517
+ client = HttpClient{redirFilter};
438
518
  } else {
439
519
  auto globalOrigin = GetRuntimeOptionString("Http.GlobalOrigin");
440
520
  OriginPolicyHttpFilter::SetStaticOrigin(std::move(globalOrigin));
441
- auto opFilter = winrt::make<OriginPolicyHttpFilter>();
442
- auto client = HttpClient{opFilter};
521
+ auto opFilter = winrt::make<OriginPolicyHttpFilter>(redirFilter);
522
+ redirFilter.as<RedirectHttpFilter>()->SetRedirectSource(opFilter.as<IRedirectEventSource>());
443
523
 
444
- result = std::make_shared<WinRTHttpResource>(std::move(client));
524
+ client = HttpClient{opFilter};
445
525
  }
446
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
+
447
532
  // Register resource as HTTP module proxy.
448
533
  if (inspectableProperties) {
449
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
 
@@ -557,6 +557,7 @@ std::vector<std::unique_ptr<NativeModule>> InstanceImpl::GetDefaultNativeModules
557
557
  std::vector<std::unique_ptr<NativeModule>> modules;
558
558
  auto transitionalProps{ReactPropertyBagHelper::CreatePropertyBag()};
559
559
 
560
+ #if (defined(_MSC_VER) && !defined(WINRT))
560
561
  modules.push_back(std::make_unique<CxxNativeModule>(
561
562
  m_innerInstance,
562
563
  Microsoft::React::GetHttpModuleName(),
@@ -564,6 +565,7 @@ std::vector<std::unique_ptr<NativeModule>> InstanceImpl::GetDefaultNativeModules
564
565
  return Microsoft::React::CreateHttpModule(transitionalProps);
565
566
  },
566
567
  nativeQueue));
568
+ #endif
567
569
 
568
570
  modules.push_back(std::make_unique<CxxNativeModule>(
569
571
  m_innerInstance,
@@ -631,8 +633,13 @@ std::vector<std::unique_ptr<NativeModule>> InstanceImpl::GetDefaultNativeModules
631
633
  []() { return std::make_unique<StatusBarManagerModule>(); },
632
634
  nativeQueue));
633
635
 
634
- // #10036 - Blob module not supported in UWP. Need to define property bag lifetime and onwership.
635
- if (Microsoft::React::GetRuntimeOptionBool("Blob.EnableModule")) {
636
+ // These modules are instantiated separately in MSRN (Universal Windows).
637
+ // When there are module name colisions, the last one registered is used.
638
+ // If this code is enabled, we will have unused module instances.
639
+ // Also, MSRN has a different property bag mechanism incompatible with this method's transitionalProps variable.
640
+ #if (defined(_MSC_VER) && !defined(WINRT))
641
+ if (Microsoft::React::GetRuntimeOptionBool("Blob.EnableModule") &&
642
+ !Microsoft::React::GetRuntimeOptionBool("Http.UseMonolithicModule")) {
636
643
  modules.push_back(std::make_unique<CxxNativeModule>(
637
644
  m_innerInstance,
638
645
  Microsoft::React::GetBlobModuleName(),
@@ -645,6 +652,7 @@ std::vector<std::unique_ptr<NativeModule>> InstanceImpl::GetDefaultNativeModules
645
652
  [transitionalProps]() { return Microsoft::React::CreateFileReaderModule(transitionalProps); },
646
653
  nativeQueue));
647
654
  }
655
+ #endif
648
656
 
649
657
  return modules;
650
658
  }
@@ -56,6 +56,7 @@
56
56
  <ClCompile Include="$(MSBuildThisFileDirectory)Modules\StatusBarManagerModule.cpp" />
57
57
  <ClCompile Include="$(MSBuildThisFileDirectory)Modules\WebSocketModule.cpp" />
58
58
  <ClCompile Include="$(MSBuildThisFileDirectory)Networking\OriginPolicyHttpFilter.cpp" />
59
+ <ClCompile Include="$(MSBuildThisFileDirectory)Networking\RedirectHttpFilter.cpp" />
59
60
  <ClCompile Include="$(MSBuildThisFileDirectory)Networking\WinRTHttpResource.cpp" />
60
61
  <ClCompile Include="$(MSBuildThisFileDirectory)Networking\WinRTWebSocketResource.cpp" />
61
62
  <ClCompile Include="$(MSBuildThisFileDirectory)OInstance.cpp" />
@@ -103,9 +104,12 @@
103
104
  <ClInclude Include="$(MSBuildThisFileDirectory)Modules\HttpModule.h" />
104
105
  <ClInclude Include="$(MSBuildThisFileDirectory)Modules\NetworkingModule.h" />
105
106
  <ClInclude Include="$(MSBuildThisFileDirectory)Networking\IHttpResource.h" />
107
+ <ClInclude Include="$(MSBuildThisFileDirectory)Networking\IRedirectEventSource.h" />
106
108
  <ClInclude Include="$(MSBuildThisFileDirectory)Networking\IWebSocketResource.h" />
109
+ <ClInclude Include="$(MSBuildThisFileDirectory)Networking\IWinRTHttpRequestFactory.h" />
107
110
  <ClInclude Include="$(MSBuildThisFileDirectory)Networking\OriginPolicy.h" />
108
111
  <ClInclude Include="$(MSBuildThisFileDirectory)Networking\OriginPolicyHttpFilter.h" />
112
+ <ClInclude Include="$(MSBuildThisFileDirectory)Networking\RedirectHttpFilter.h" />
109
113
  <ClInclude Include="$(MSBuildThisFileDirectory)Networking\WinRTHttpResource.h" />
110
114
  <ClInclude Include="$(MSBuildThisFileDirectory)Networking\WinRTTypes.h" />
111
115
  <ClInclude Include="$(MSBuildThisFileDirectory)Networking\WinRTWebSocketResource.h" />