react-native-windows 0.69.13 → 0.69.15

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.
@@ -19,12 +19,8 @@
19
19
 
20
20
  namespace Microsoft::ReactNative {
21
21
 
22
- using winrt::Microsoft::ReactNative::ReactPropertyBag;
23
-
24
22
  namespace {
25
23
 
26
- using winrt::Microsoft::ReactNative::ReactPropertyId;
27
-
28
24
  bool HasPackageIdentity() noexcept {
29
25
  static const bool hasPackageIdentity = []() noexcept {
30
26
  auto packageStatics = winrt::get_activation_factory<winrt::Windows::ApplicationModel::IPackageStatics>(
@@ -39,13 +35,6 @@ bool HasPackageIdentity() noexcept {
39
35
  return hasPackageIdentity;
40
36
  }
41
37
 
42
- ReactPropertyId<bool> HttpUseMonolithicModuleProperty() noexcept {
43
- static ReactPropertyId<bool> propId{
44
- L"ReactNative.Http"
45
- L"UseMonolithicModule"};
46
- return propId;
47
- }
48
-
49
38
  } // namespace
50
39
 
51
40
  std::vector<facebook::react::NativeModuleDescription> GetCoreModules(
@@ -61,17 +50,15 @@ std::vector<facebook::react::NativeModuleDescription> GetCoreModules(
61
50
  [props = context->Properties()]() { return Microsoft::React::CreateHttpModule(props); },
62
51
  jsMessageQueue);
63
52
 
64
- if (!ReactPropertyBag(context->Properties()).Get(HttpUseMonolithicModuleProperty())) {
65
- modules.emplace_back(
66
- Microsoft::React::GetBlobModuleName(),
67
- [props = context->Properties()]() { return Microsoft::React::CreateBlobModule(props); },
68
- batchingUIMessageQueue);
69
-
70
- modules.emplace_back(
71
- Microsoft::React::GetFileReaderModuleName(),
72
- [props = context->Properties()]() { return Microsoft::React::CreateFileReaderModule(props); },
73
- batchingUIMessageQueue);
74
- }
53
+ modules.emplace_back(
54
+ Microsoft::React::GetBlobModuleName(),
55
+ [props = context->Properties()]() { return Microsoft::React::CreateBlobModule(props); },
56
+ batchingUIMessageQueue);
57
+
58
+ modules.emplace_back(
59
+ Microsoft::React::GetFileReaderModuleName(),
60
+ [props = context->Properties()]() { return Microsoft::React::CreateFileReaderModule(props); },
61
+ batchingUIMessageQueue);
75
62
 
76
63
  modules.emplace_back(
77
64
  "Timing",
@@ -10,10 +10,10 @@
10
10
  -->
11
11
  <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
12
12
  <PropertyGroup>
13
- <ReactNativeWindowsVersion>0.69.13</ReactNativeWindowsVersion>
13
+ <ReactNativeWindowsVersion>0.69.15</ReactNativeWindowsVersion>
14
14
  <ReactNativeWindowsMajor>0</ReactNativeWindowsMajor>
15
15
  <ReactNativeWindowsMinor>69</ReactNativeWindowsMinor>
16
- <ReactNativeWindowsPatch>13</ReactNativeWindowsPatch>
16
+ <ReactNativeWindowsPatch>15</ReactNativeWindowsPatch>
17
17
  <ReactNativeWindowsCanary>false</ReactNativeWindowsCanary>
18
18
  </PropertyGroup>
19
19
  </Project>
@@ -4,6 +4,7 @@
4
4
  #include "FileReaderModule.h"
5
5
 
6
6
  #include <ReactPropertyBag.h>
7
+ #include <sstream>
7
8
 
8
9
  // Boost Library
9
10
  #include <boost/archive/iterators/base64_from_binary.hpp>
@@ -33,8 +33,10 @@ constexpr char moduleName[] = "Networking";
33
33
  // React event names
34
34
  constexpr char completedResponse[] = "didCompleteNetworkResponse";
35
35
  constexpr char receivedResponse[] = "didReceiveNetworkResponse";
36
- constexpr char receivedData[] = "didReceiveNetworkData";
36
+ constexpr char sentData[] = "didSendNetworkData";
37
+ constexpr char receivedIncrementalData[] = "didReceiveNetworkIncrementalData";
37
38
  constexpr char receivedDataProgress[] = "didReceiveNetworkDataProgress";
39
+ constexpr char receivedData[] = "didReceiveNetworkData";
38
40
 
39
41
  static void SetUpHttpResource(
40
42
  shared_ptr<IHttpResource> resource,
@@ -60,9 +62,6 @@ static void SetUpHttpResource(
60
62
 
61
63
  resource->SetOnData([weakReactInstance](int64_t requestId, string &&responseData) {
62
64
  SendEvent(weakReactInstance, receivedData, dynamic::array(requestId, std::move(responseData)));
63
-
64
- // TODO: Move into separate method IF not executed right after onData()
65
- SendEvent(weakReactInstance, completedResponse, dynamic::array(requestId));
66
65
  });
67
66
 
68
67
  // Explicitly declaring function type to avoid type inference ambiguity.
@@ -72,6 +71,22 @@ static void SetUpHttpResource(
72
71
  };
73
72
  resource->SetOnData(std::move(onDataDynamic));
74
73
 
74
+ resource->SetOnIncrementalData(
75
+ [weakReactInstance](int64_t requestId, string &&responseData, int64_t progress, int64_t total) {
76
+ SendEvent(
77
+ weakReactInstance,
78
+ receivedIncrementalData,
79
+ dynamic::array(requestId, std::move(responseData), progress, total));
80
+ });
81
+
82
+ resource->SetOnDataProgress([weakReactInstance](int64_t requestId, int64_t progress, int64_t total) {
83
+ SendEvent(weakReactInstance, receivedDataProgress, dynamic::array(requestId, progress, total));
84
+ });
85
+
86
+ resource->SetOnResponseComplete([weakReactInstance](int64_t requestId) {
87
+ SendEvent(weakReactInstance, completedResponse, dynamic::array(requestId));
88
+ });
89
+
75
90
  resource->SetOnError([weakReactInstance](int64_t requestId, string &&message, bool isTimeout) {
76
91
  dynamic args = dynamic::array(requestId, std::move(message));
77
92
  if (isTimeout) {
@@ -108,90 +123,90 @@ std::map<string, dynamic> HttpModule::getConstants() {
108
123
  }
109
124
 
110
125
  // clang-format off
111
- std::vector<facebook::xplat::module::CxxModule::Method> HttpModule::getMethods() {
126
+ std::vector<facebook::xplat::module::CxxModule::Method> HttpModule::getMethods() {
112
127
 
113
- return
114
- {
128
+ return
115
129
  {
116
- "sendRequest",
117
- [weakHolder = weak_ptr<ModuleHolder>(m_holder)](dynamic args, Callback cxxCallback)
118
130
  {
119
- auto holder = weakHolder.lock();
120
- if (!holder) {
121
- return;
122
- }
123
-
124
- auto resource = holder->Module->m_resource;
125
- if (!holder->Module->m_isResourceSetup)
131
+ "sendRequest",
132
+ [weakHolder = weak_ptr<ModuleHolder>(m_holder)](dynamic args, Callback cxxCallback)
126
133
  {
127
- SetUpHttpResource(resource, holder->Module->getInstance(), holder->Module->m_inspectableProperties);
128
- holder->Module->m_isResourceSetup = true;
129
- }
134
+ auto holder = weakHolder.lock();
135
+ if (!holder) {
136
+ return;
137
+ }
130
138
 
131
- auto params = facebook::xplat::jsArgAsObject(args, 0);
132
- IHttpResource::Headers headers;
133
- for (auto& header : params["headers"].items()) {
134
- headers.emplace(header.first.getString(), header.second.getString());
135
- }
139
+ auto resource = holder->Module->m_resource;
140
+ if (!holder->Module->m_isResourceSetup)
141
+ {
142
+ SetUpHttpResource(resource, holder->Module->getInstance(), holder->Module->m_inspectableProperties);
143
+ holder->Module->m_isResourceSetup = true;
144
+ }
136
145
 
137
- resource->SendRequest(
138
- params["method"].asString(),
139
- params["url"].asString(),
140
- params["requestId"].asInt(),
141
- std::move(headers),
142
- std::move(params["data"]),
143
- params["responseType"].asString(),
144
- params["incrementalUpdates"].asBool(),
145
- static_cast<int64_t>(params["timeout"].asDouble()),
146
- params["withCredentials"].asBool(),
147
- [cxxCallback = std::move(cxxCallback)](int64_t requestId) {
148
- cxxCallback({requestId});
146
+ auto params = facebook::xplat::jsArgAsObject(args, 0);
147
+ IHttpResource::Headers headers;
148
+ for (auto& header : params["headers"].items()) {
149
+ headers.emplace(header.first.getString(), header.second.getString());
149
150
  }
150
- );
151
- }
152
- },
153
- {
154
- "abortRequest",
155
- [weakHolder = weak_ptr<ModuleHolder>(m_holder)](dynamic args)
151
+
152
+ resource->SendRequest(
153
+ params["method"].asString(),
154
+ params["url"].asString(),
155
+ params["requestId"].asInt(),
156
+ std::move(headers),
157
+ std::move(params["data"]),
158
+ params["responseType"].asString(),
159
+ params["incrementalUpdates"].asBool(),
160
+ static_cast<int64_t>(params["timeout"].asDouble()),
161
+ params["withCredentials"].asBool(),
162
+ [cxxCallback = std::move(cxxCallback)](int64_t requestId) {
163
+ cxxCallback({requestId});
164
+ }
165
+ );
166
+ }
167
+ },
156
168
  {
157
- auto holder = weakHolder.lock();
158
- if (!holder)
169
+ "abortRequest",
170
+ [weakHolder = weak_ptr<ModuleHolder>(m_holder)](dynamic args)
159
171
  {
160
- return;
161
- }
172
+ auto holder = weakHolder.lock();
173
+ if (!holder)
174
+ {
175
+ return;
176
+ }
162
177
 
163
- auto resource = holder->Module->m_resource;
164
- if (!holder->Module->m_isResourceSetup)
165
- {
166
- SetUpHttpResource(resource, holder->Module->getInstance(), holder->Module->m_inspectableProperties);
167
- holder->Module->m_isResourceSetup = true;
168
- }
178
+ auto resource = holder->Module->m_resource;
179
+ if (!holder->Module->m_isResourceSetup)
180
+ {
181
+ SetUpHttpResource(resource, holder->Module->getInstance(), holder->Module->m_inspectableProperties);
182
+ holder->Module->m_isResourceSetup = true;
183
+ }
169
184
 
170
- resource->AbortRequest(facebook::xplat::jsArgAsInt(args, 0));
171
- }
172
- },
173
- {
174
- "clearCookies",
175
- [weakHolder = weak_ptr<ModuleHolder>(m_holder)](dynamic args)
185
+ resource->AbortRequest(facebook::xplat::jsArgAsInt(args, 0));
186
+ }
187
+ },
176
188
  {
177
- auto holder = weakHolder.lock();
178
- if (!holder)
189
+ "clearCookies",
190
+ [weakHolder = weak_ptr<ModuleHolder>(m_holder)](dynamic args)
179
191
  {
180
- return;
181
- }
192
+ auto holder = weakHolder.lock();
193
+ if (!holder)
194
+ {
195
+ return;
196
+ }
182
197
 
183
- auto resource = holder->Module->m_resource;
184
- if (!holder->Module->m_isResourceSetup)
185
- {
186
- SetUpHttpResource(resource, holder->Module->getInstance(), holder->Module->m_inspectableProperties);
187
- holder->Module->m_isResourceSetup = true;
188
- }
198
+ auto resource = holder->Module->m_resource;
199
+ if (!holder->Module->m_isResourceSetup)
200
+ {
201
+ SetUpHttpResource(resource, holder->Module->getInstance(), holder->Module->m_inspectableProperties);
202
+ holder->Module->m_isResourceSetup = true;
203
+ }
189
204
 
190
- resource->ClearCookies();
205
+ resource->ClearCookies();
206
+ }
191
207
  }
192
- }
193
- };
194
- }
208
+ };
209
+ }
195
210
  // clang-format on
196
211
 
197
212
  #pragma endregion CxxModule
@@ -91,10 +91,136 @@ struct IHttpResource {
91
91
 
92
92
  virtual void ClearCookies() noexcept = 0;
93
93
 
94
+ /// <summary>
95
+ /// Sets a function to be invoked when a request has been successfully responded.
96
+ /// </summary>
97
+ /// <param name="handler">
98
+ ///
99
+ /// Parameters:
100
+ /// <param name="requestId">
101
+ /// Unique number identifying the HTTP request
102
+ /// </param>
103
+ /// </param>
94
104
  virtual void SetOnRequestSuccess(std::function<void(int64_t requestId)> &&handler) noexcept = 0;
105
+
106
+ /// <summary>
107
+ /// Sets a function to be invoked when a response arrives and its headers are received.
108
+ /// </summary>
109
+ /// <param name="handler">
110
+ ///
111
+ /// Parameters:
112
+ /// <param name="requestId">
113
+ /// Unique number identifying the HTTP request
114
+ /// </param>
115
+ /// <param name="response">
116
+ /// Object containing basic response data
117
+ /// </param>
118
+ /// </param>
95
119
  virtual void SetOnResponse(std::function<void(int64_t requestId, Response &&response)> &&handler) noexcept = 0;
120
+
121
+ /// <summary>
122
+ /// Sets a function to be invoked when response content data has been received.
123
+ /// </summary>
124
+ /// <param name="handler">
125
+ ///
126
+ /// Parameters:
127
+ /// <param name="requestId">
128
+ /// Unique number identifying the HTTP request
129
+ /// </param>
130
+ /// <param name="responseData">
131
+ /// Response content payload (plain text or Base64-encoded)
132
+ /// </param>
133
+ /// </param>
96
134
  virtual void SetOnData(std::function<void(int64_t requestId, std::string &&responseData)> &&handler) noexcept = 0;
135
+
136
+ /// <summary>
137
+ /// Sets a function to be invoked when response content data has been received.
138
+ /// </summary>
139
+ /// <param name="handler">
140
+ ///
141
+ /// Parameters:
142
+ /// <param name="requestId">
143
+ /// Unique number identifying the HTTP request
144
+ /// </param>
145
+ /// <param name="responseData">
146
+ /// Structured response content payload (i.e. Blob data)
147
+ /// </param>
148
+ /// </param>
97
149
  virtual void SetOnData(std::function<void(int64_t requestId, folly::dynamic &&responseData)> &&handler) noexcept = 0;
150
+
151
+ /// <summary>
152
+ /// Sets a function to be invoked when a response content increment has been received.
153
+ /// </summary>
154
+ /// <remarks>
155
+ /// The handler set by this method will only be called if the request sets the incremental updates flag.
156
+ /// The handler is also mutually exclusive with those set by `SetOnData`, which are used for one pass, non-incremental
157
+ /// updates.
158
+ /// </remarks>
159
+ /// <param name="handler">
160
+ ///
161
+ /// Parameters:
162
+ /// <param name="requestId">
163
+ /// Unique number identifying the HTTP request
164
+ /// </param>
165
+ /// <param name="responseData">
166
+ /// Partial response content data increment (non-accumulative)
167
+ /// </param>
168
+ /// <param name="progress">
169
+ /// Number of bytes received so far
170
+ /// </param>
171
+ /// <param name="total">
172
+ /// Number of total bytes to receive
173
+ /// </param>
174
+ /// </param>
175
+ virtual void SetOnIncrementalData(
176
+ std::function<void(int64_t requestId, std::string &&responseData, int64_t progress, int64_t total)>
177
+ &&handler) noexcept = 0;
178
+
179
+ /// <summary>
180
+ /// Sets a function to be invoked when response content download progress is reported.
181
+ /// </summary>
182
+ /// <param name="handler">
183
+ ///
184
+ /// Parameters:
185
+ /// <param name="requestId">
186
+ /// Unique number identifying the HTTP request
187
+ /// </param>
188
+ /// <param name="progress">
189
+ /// Number of bytes received so far
190
+ /// </param>
191
+ /// <param name="total">
192
+ /// Number of total bytes to receive
193
+ /// </param>
194
+ /// </param>
195
+ virtual void SetOnDataProgress(
196
+ std::function<void(int64_t requestId, int64_t progress, int64_t total)> &&handler) noexcept = 0;
197
+
198
+ /// <summary>
199
+ /// Sets a function to be invoked when a response has been fully handled (either succeeded or failed).
200
+ /// </summary>
201
+ /// <param name="handler">
202
+ ///
203
+ /// Parameters:
204
+ /// <param name="requestId">
205
+ /// Unique number identifying the HTTP request
206
+ /// </param>
207
+ /// </param>
208
+ virtual void SetOnResponseComplete(std::function<void(int64_t requestId)> &&handler) noexcept = 0;
209
+
210
+ /// <summary>
211
+ /// Sets a function to be invoked when an error condition is found.
212
+ /// </summary>
213
+ /// <remarks>
214
+ /// The handler's purpose is not to report any given HTTP error status (i.e. 403, 501).
215
+ /// It is meant to report application errors when executing HTTP requests.
216
+ /// </remarks>
217
+ /// <param name="handler">
218
+ ///
219
+ /// Parameters:
220
+ /// <param name="requestId">
221
+ /// Unique number identifying the HTTP request
222
+ /// </param>
223
+ /// </param>
98
224
  virtual void SetOnError(
99
225
  std::function<void(int64_t requestId, std::string &&errorMessage, bool isTimeout)> &&handler) noexcept = 0;
100
226
  };
@@ -120,12 +120,12 @@ bool OriginPolicyHttpFilter::ConstWcharComparer::operator()(const wchar_t *a, co
120
120
  }
121
121
 
122
122
  /*static*/ bool OriginPolicyHttpFilter::IsSimpleCorsRequest(HttpRequestMessage const &request) noexcept {
123
- // Ensure header is in Simple CORS white list
123
+ // Ensure header is in Simple CORS allowlist
124
124
  for (const auto &header : request.Headers()) {
125
125
  if (s_simpleCorsRequestHeaderNames.find(header.Key().c_str()) == s_simpleCorsRequestHeaderNames.cend())
126
126
  return false;
127
127
 
128
- // Ensure Content-Type value is in Simple CORS white list, if present
128
+ // Ensure Content-Type value is in Simple CORS allowlist, if present
129
129
  if (boost::iequals(header.Key(), L"Content-Type")) {
130
130
  if (s_simpleCorsContentTypeValues.find(header.Value().c_str()) != s_simpleCorsContentTypeValues.cend())
131
131
  return false;
@@ -135,12 +135,12 @@ bool OriginPolicyHttpFilter::ConstWcharComparer::operator()(const wchar_t *a, co
135
135
  // WinRT separates request headers from request content headers
136
136
  if (auto content = request.Content()) {
137
137
  for (const auto &header : content.Headers()) {
138
- // WinRT automatically appends non-whitelisted header Content-Length when Content-Type is set. Skip it.
138
+ // WinRT automatically appends non-allowlisted header Content-Length when Content-Type is set. Skip it.
139
139
  if (s_simpleCorsRequestHeaderNames.find(header.Key().c_str()) == s_simpleCorsRequestHeaderNames.cend() &&
140
140
  !boost::iequals(header.Key(), "Content-Length"))
141
141
  return false;
142
142
 
143
- // Ensure Content-Type value is in Simple CORS white list, if present
143
+ // Ensure Content-Type value is in Simple CORS allowlist, if present
144
144
  if (boost::iequals(header.Key(), L"Content-Type")) {
145
145
  if (s_simpleCorsContentTypeValues.find(header.Value().c_str()) == s_simpleCorsContentTypeValues.cend())
146
146
  return false;
@@ -148,7 +148,7 @@ bool OriginPolicyHttpFilter::ConstWcharComparer::operator()(const wchar_t *a, co
148
148
  }
149
149
  }
150
150
 
151
- // Ensure method is in Simple CORS white list
151
+ // Ensure method is in Simple CORS allowlist
152
152
  return s_simpleCorsMethods.find(request.Method().ToString().c_str()) != s_simpleCorsMethods.cend();
153
153
  }
154
154
 
@@ -599,7 +599,7 @@ void OriginPolicyHttpFilter::ValidateResponse(HttpResponseMessage const &respons
599
599
  }
600
600
 
601
601
  if (originPolicy == OriginPolicy::SimpleCrossOriginResourceSharing) {
602
- // Filter out response headers that are not in the Simple CORS whitelist
602
+ // Filter out response headers that are not in the Simple CORS allowlist
603
603
  std::queue<hstring> nonSimpleNames;
604
604
  for (const auto &header : response.Headers().GetView()) {
605
605
  if (s_simpleCorsResponseHeaderNames.find(header.Key().c_str()) == s_simpleCorsResponseHeaderNames.cend())
@@ -651,21 +651,26 @@ ResponseOperation OriginPolicyHttpFilter::SendPreflightAsync(HttpRequestMessage
651
651
  preflightRequest.Headers().Insert(L"Access-Control-Request-Method", coRequest.Method().ToString());
652
652
 
653
653
  auto headerNames = wstring{};
654
- auto headerItr = coRequest.Headers().begin();
655
- if (headerItr != coRequest.Headers().end()) {
656
- headerNames += (*headerItr).Key();
654
+ auto writeSeparator = false;
655
+ for (const auto &header : coRequest.Headers()) {
656
+ if (writeSeparator) {
657
+ headerNames += L", ";
658
+ } else {
659
+ writeSeparator = true;
660
+ }
657
661
 
658
- while (++headerItr != coRequest.Headers().end())
659
- headerNames += L", " + (*headerItr).Key();
662
+ headerNames += header.Key();
660
663
  }
661
664
 
662
665
  if (coRequest.Content()) {
663
- headerItr = coRequest.Content().Headers().begin();
664
- if (headerItr != coRequest.Content().Headers().end()) {
665
- headerNames += (*headerItr).Key();
666
+ for (const auto &header : coRequest.Content().Headers()) {
667
+ if (writeSeparator) {
668
+ headerNames += L", ";
669
+ } else {
670
+ writeSeparator = true;
671
+ }
666
672
 
667
- while (++headerItr != coRequest.Content().Headers().end())
668
- headerNames += L", " + (*headerItr).Key();
673
+ headerNames += header.Key();
669
674
  }
670
675
  }
671
676
 
@@ -51,8 +51,51 @@ using winrt::Windows::Web::Http::IHttpClient;
51
51
  using winrt::Windows::Web::Http::IHttpContent;
52
52
  using winrt::Windows::Web::Http::Headers::HttpMediaTypeHeaderValue;
53
53
 
54
+ namespace {
55
+
56
+ constexpr uint32_t operator""_KiB(unsigned long long int x) {
57
+ return static_cast<uint32_t>(1024 * x);
58
+ }
59
+
60
+ constexpr uint32_t operator""_MiB(unsigned long long int x) {
61
+ return static_cast<uint32_t>(1024_KiB * x);
62
+ }
63
+
64
+ constexpr char responseTypeText[] = "text";
65
+ constexpr char responseTypeBase64[] = "base64";
66
+ constexpr char responseTypeBlob[] = "blob";
67
+
68
+ } // namespace
54
69
  namespace Microsoft::React::Networking {
55
70
 
71
+ // May throw winrt::hresult_error
72
+ void AttachMultipartHeaders(IHttpContent content, const dynamic &headers) {
73
+ HttpMediaTypeHeaderValue contentType{nullptr};
74
+
75
+ // Headers are generally case-insensitive
76
+ // https://www.ietf.org/rfc/rfc2616.txt section 4.2
77
+ // TODO: Consolidate with PerformRequest's header parsing.
78
+ for (auto &header : headers.items()) {
79
+ auto &name = header.first.getString();
80
+ auto &value = header.second.getString();
81
+
82
+ if (boost::iequals(name.c_str(), "Content-Type")) {
83
+ contentType = HttpMediaTypeHeaderValue::Parse(to_hstring(value));
84
+ } else if (boost::iequals(name.c_str(), "Authorization")) {
85
+ bool success = content.Headers().TryAppendWithoutValidation(to_hstring(name), to_hstring(value));
86
+ if (!success) {
87
+ throw hresult_error{E_INVALIDARG, L"Failed to append Authorization"};
88
+ }
89
+ } else {
90
+ content.Headers().Append(to_hstring(name), to_hstring(value));
91
+ }
92
+ }
93
+
94
+ if (contentType) {
95
+ content.Headers().ContentType(contentType);
96
+ }
97
+ }
98
+
56
99
  #pragma region WinRTHttpResource
57
100
 
58
101
  WinRTHttpResource::WinRTHttpResource(IHttpClient &&client) noexcept : m_client{std::move(client)} {}
@@ -81,20 +124,23 @@ IAsyncOperation<HttpRequestMessage> WinRTHttpResource::CreateRequest(
81
124
  // Headers are generally case-insensitive
82
125
  // https://www.ietf.org/rfc/rfc2616.txt section 4.2
83
126
  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);
127
+ auto &name = header.first;
128
+ auto &value = header.second;
129
+
130
+ if (boost::iequals(name.c_str(), "Content-Type")) {
131
+ bool success = HttpMediaTypeHeaderValue::TryParse(to_hstring(value), contentType);
86
132
  if (!success) {
87
133
  if (self->m_onError) {
88
134
  self->m_onError(reqArgs->RequestId, "Failed to parse Content-Type", false);
89
135
  }
90
136
  co_return nullptr;
91
137
  }
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));
138
+ } else if (boost::iequals(name.c_str(), "Content-Encoding")) {
139
+ contentEncoding = value;
140
+ } else if (boost::iequals(name.c_str(), "Content-Length")) {
141
+ contentLength = value;
142
+ } else if (boost::iequals(name.c_str(), "Authorization")) {
143
+ bool success = request.Headers().TryAppendWithoutValidation(to_hstring(name), to_hstring(value));
98
144
  if (!success) {
99
145
  if (self->m_onError) {
100
146
  self->m_onError(reqArgs->RequestId, "Failed to append Authorization", false);
@@ -103,7 +149,7 @@ IAsyncOperation<HttpRequestMessage> WinRTHttpResource::CreateRequest(
103
149
  }
104
150
  } else {
105
151
  try {
106
- request.Headers().Append(to_hstring(header.first), to_hstring(header.second));
152
+ request.Headers().Append(to_hstring(name), to_hstring(value));
107
153
  } catch (hresult_error const &e) {
108
154
  if (self->m_onError) {
109
155
  self->m_onError(reqArgs->RequestId, Utilities::HResultToString(e), false);
@@ -146,9 +192,31 @@ IAsyncOperation<HttpRequestMessage> WinRTHttpResource::CreateRequest(
146
192
  auto file = co_await StorageFile::GetFileFromApplicationUriAsync(Uri{to_hstring(data["uri"].asString())});
147
193
  auto stream = co_await file.OpenReadAsync();
148
194
  content = HttpStreamContent{std::move(stream)};
149
- } else if (!data["form"].empty()) {
150
- // #9535 - HTTP form data support
151
- // winrt::Windows::Web::Http::HttpMultipartFormDataContent()
195
+ } else if (!data["formData"].empty()) {
196
+ winrt::Windows::Web::Http::HttpMultipartFormDataContent multiPartContent;
197
+ auto formData = data["formData"];
198
+
199
+ // #6046 - Overwriting WinRT's HttpMultipartFormDataContent implicit Content-Type clears the generated boundary
200
+ contentType = nullptr;
201
+
202
+ for (auto &formDataPart : formData) {
203
+ IHttpContent formContent{nullptr};
204
+ if (!formDataPart["string"].isNull()) {
205
+ formContent = HttpStringContent{to_hstring(formDataPart["string"].asString())};
206
+ } else if (!formDataPart["uri"].empty()) {
207
+ auto filePath = to_hstring(formDataPart["uri"].asString());
208
+ auto file = co_await StorageFile::GetFileFromPathAsync(filePath);
209
+ auto stream = co_await file.OpenReadAsync();
210
+ formContent = HttpStreamContent{stream};
211
+ }
212
+
213
+ if (formContent) {
214
+ AttachMultipartHeaders(formContent, formDataPart["headers"]);
215
+ multiPartContent.Add(formContent, to_hstring(formDataPart["fieldName"].asString()));
216
+ }
217
+ } // foreach form data part
218
+
219
+ content = multiPartContent;
152
220
  }
153
221
  }
154
222
 
@@ -205,7 +273,7 @@ void WinRTHttpResource::SendRequest(
205
273
  bool withCredentials,
206
274
  std::function<void(int64_t)> &&callback) noexcept /*override*/ {
207
275
  // Enforce supported args
208
- assert(responseType == "text" || responseType == "base64" || responseType == "blob");
276
+ assert(responseType == responseTypeText || responseType == responseTypeBase64 || responseType == responseTypeBlob);
209
277
 
210
278
  if (callback) {
211
279
  callback(requestId);
@@ -283,6 +351,22 @@ void WinRTHttpResource::SetOnData(function<void(int64_t requestId, dynamic &&res
283
351
  m_onDataDynamic = std::move(handler);
284
352
  }
285
353
 
354
+ void WinRTHttpResource::SetOnIncrementalData(
355
+ function<void(int64_t requestId, string &&responseData, int64_t progress, int64_t total)> &&handler) noexcept
356
+ /*override*/ {
357
+ m_onIncrementalData = std::move(handler);
358
+ }
359
+
360
+ void WinRTHttpResource::SetOnDataProgress(
361
+ function<void(int64_t requestId, int64_t progress, int64_t total)> &&handler) noexcept
362
+ /*override*/ {
363
+ m_onDataProgress = std::move(handler);
364
+ }
365
+
366
+ void WinRTHttpResource::SetOnResponseComplete(function<void(int64_t requestId)> &&handler) noexcept /*override*/ {
367
+ m_onComplete = std::move(handler);
368
+ }
369
+
286
370
  void WinRTHttpResource::SetOnError(
287
371
  function<void(int64_t requestId, string &&errorMessage, bool isTimeout)> &&handler) noexcept
288
372
  /*override*/ {
@@ -316,11 +400,18 @@ WinRTHttpResource::PerformSendRequest(HttpMethod &&method, Uri &&rtUri, IInspect
316
400
  auto props = winrt::multi_threaded_map<winrt::hstring, IInspectable>();
317
401
  props.Insert(L"RequestArgs", coArgs);
318
402
 
319
- auto coRequest = co_await CreateRequest(std::move(coMethod), std::move(coUri), props);
320
- if (!coRequest) {
321
- co_return;
403
+ auto coRequestOp = CreateRequest(std::move(coMethod), std::move(coUri), props);
404
+ co_await lessthrow_await_adapter<IAsyncOperation<HttpRequestMessage>>{coRequestOp};
405
+ auto coRequestOpHR = coRequestOp.ErrorCode();
406
+ if (coRequestOpHR < 0) {
407
+ if (self->m_onError) {
408
+ self->m_onError(reqArgs->RequestId, Utilities::HResultToString(std::move(coRequestOpHR)), false);
409
+ }
410
+ co_return self->UntrackResponse(reqArgs->RequestId);
322
411
  }
323
412
 
413
+ auto coRequest = coRequestOp.GetResults();
414
+
324
415
  // If URI handler is available, it takes over request processing.
325
416
  if (auto uriHandler = self->m_uriHandler.lock()) {
326
417
  auto uri = winrt::to_string(coRequest.RequestUri().ToString());
@@ -332,6 +423,10 @@ WinRTHttpResource::PerformSendRequest(HttpMethod &&method, Uri &&rtUri, IInspect
332
423
  self->m_onRequestSuccess(reqArgs->RequestId);
333
424
  }
334
425
 
426
+ if (self->m_onComplete) {
427
+ self->m_onComplete(reqArgs->RequestId);
428
+ }
429
+
335
430
  co_return;
336
431
  }
337
432
  } catch (const hresult_error &e) {
@@ -345,6 +440,9 @@ WinRTHttpResource::PerformSendRequest(HttpMethod &&method, Uri &&rtUri, IInspect
345
440
 
346
441
  try {
347
442
  auto sendRequestOp = self->m_client.SendRequestAsync(coRequest);
443
+
444
+ auto isText = reqArgs->ResponseType == responseTypeText;
445
+
348
446
  self->TrackResponse(reqArgs->RequestId, sendRequestOp);
349
447
 
350
448
  if (reqArgs->Timeout > 0) {
@@ -411,55 +509,86 @@ WinRTHttpResource::PerformSendRequest(HttpMethod &&method, Uri &&rtUri, IInspect
411
509
  auto inputStream = co_await response.Content().ReadAsInputStreamAsync();
412
510
  auto reader = DataReader{inputStream};
413
511
 
414
- // #9510 - 10mb limit on fetch
415
- co_await reader.LoadAsync(10 * 1024 * 1024);
512
+ // Accumulate all incoming request data in 8MB chunks
513
+ // Note, the minimum apparent valid chunk size is 128 KB
514
+ // Apple's implementation appears to grab 5-8 KB chunks
515
+ const uint32_t segmentSize = reqArgs->IncrementalUpdates ? 128_KiB : 8_MiB;
416
516
 
417
517
  // Let response handler take over, if set
418
518
  if (auto responseHandler = self->m_responseHandler.lock()) {
419
519
  if (responseHandler->Supports(reqArgs->ResponseType)) {
420
- auto bytes = vector<uint8_t>(reader.UnconsumedBufferLength());
421
- reader.ReadBytes(bytes);
422
- auto blob = responseHandler->ToResponseData(std::move(bytes));
520
+ vector<uint8_t> responseData{};
521
+ while (auto loaded = co_await reader.LoadAsync(segmentSize)) {
522
+ auto length = reader.UnconsumedBufferLength();
523
+ auto data = vector<uint8_t>(length);
524
+ reader.ReadBytes(data);
525
+
526
+ responseData.insert(responseData.cend(), data.cbegin(), data.cend());
527
+ }
528
+
529
+ auto blob = responseHandler->ToResponseData(std::move(responseData));
423
530
 
424
531
  if (self->m_onDataDynamic && self->m_onRequestSuccess) {
425
532
  self->m_onDataDynamic(reqArgs->RequestId, std::move(blob));
426
533
  self->m_onRequestSuccess(reqArgs->RequestId);
427
534
  }
428
535
 
536
+ if (self->m_onComplete) {
537
+ self->m_onComplete(reqArgs->RequestId);
538
+ }
429
539
  co_return;
430
540
  }
431
541
  }
432
542
 
433
- auto isText = reqArgs->ResponseType == "text";
434
543
  if (isText) {
435
544
  reader.UnicodeEncoding(UnicodeEncoding::Utf8);
436
545
  }
437
546
 
438
- // #9510 - We currently accumulate all incoming request data in 10MB chunks.
439
- uint32_t segmentSize = 10 * 1024 * 1024;
547
+ int64_t receivedBytes = 0;
440
548
  string responseData;
441
549
  winrt::Windows::Storage::Streams::IBuffer buffer;
442
- uint32_t length;
443
- do {
444
- co_await reader.LoadAsync(segmentSize);
445
- length = reader.UnconsumedBufferLength();
550
+ while (auto loaded = co_await reader.LoadAsync(segmentSize)) {
551
+ auto length = reader.UnconsumedBufferLength();
552
+ receivedBytes += length;
446
553
 
447
554
  if (isText) {
448
- auto data = std::vector<uint8_t>(length);
555
+ auto data = vector<uint8_t>(length);
449
556
  reader.ReadBytes(data);
450
557
 
451
- responseData += string(Common::Utilities::CheckedReinterpretCast<char *>(data.data()), data.size());
558
+ auto incrementData = string(Common::Utilities::CheckedReinterpretCast<char *>(data.data()), data.size());
559
+ // #9534 - Send incremental updates.
560
+ // See https://github.com/facebook/react-native/blob/v0.70.6/Libraries/Network/RCTNetworking.mm#L561
561
+ if (reqArgs->IncrementalUpdates) {
562
+ responseData = std::move(incrementData);
563
+
564
+ if (self->m_onIncrementalData) {
565
+ // For total, see #10849
566
+ self->m_onIncrementalData(reqArgs->RequestId, std::move(responseData), receivedBytes, 0 /*total*/);
567
+ }
568
+ } else {
569
+ responseData += std::move(incrementData);
570
+ }
452
571
  } else {
453
572
  buffer = reader.ReadBuffer(length);
454
573
  auto data = CryptographicBuffer::EncodeToBase64String(buffer);
455
574
 
456
575
  responseData += winrt::to_string(std::wstring_view(data));
576
+
577
+ if (self->m_onDataProgress) {
578
+ // For total, see #10849
579
+ self->m_onDataProgress(reqArgs->RequestId, receivedBytes, 0 /*total*/);
580
+ }
457
581
  }
458
- } while (length > 0);
582
+ }
459
583
 
460
- if (self->m_onData) {
584
+ // If dealing with text-incremental response data, use m_onIncrementalData instead
585
+ if (self->m_onData && !(reqArgs->IncrementalUpdates && isText)) {
461
586
  self->m_onData(reqArgs->RequestId, std::move(responseData));
462
587
  }
588
+
589
+ if (self->m_onComplete) {
590
+ self->m_onComplete(reqArgs->RequestId);
591
+ }
463
592
  } else {
464
593
  if (self->m_onError) {
465
594
  self->m_onError(reqArgs->RequestId, response == nullptr ? "request failed" : "No response content", false);
@@ -30,6 +30,10 @@ class WinRTHttpResource : public IHttpResource,
30
30
  std::function<void(int64_t requestId, std::string &&responseData)> m_onData;
31
31
  std::function<void(int64_t requestId, folly::dynamic &&responseData)> m_onDataDynamic;
32
32
  std::function<void(int64_t requestId, std::string &&errorMessage, bool isTimeout)> m_onError;
33
+ std::function<void(int64_t requestId, std::string &&responseData, int64_t progress, int64_t total)>
34
+ m_onIncrementalData;
35
+ std::function<void(int64_t requestId, int64_t progress, int64_t total)> m_onDataProgress;
36
+ std::function<void(int64_t requestId)> m_onComplete;
33
37
 
34
38
  // Used for IHttpModuleProxy
35
39
  std::weak_ptr<IUriHandler> m_uriHandler;
@@ -80,6 +84,12 @@ class WinRTHttpResource : public IHttpResource,
80
84
  void SetOnResponse(std::function<void(int64_t requestId, Response &&response)> &&handler) noexcept override;
81
85
  void SetOnData(std::function<void(int64_t requestId, std::string &&responseData)> &&handler) noexcept override;
82
86
  void SetOnData(std::function<void(int64_t requestId, folly::dynamic &&responseData)> &&handler) noexcept override;
87
+ void SetOnIncrementalData(
88
+ std::function<void(int64_t requestId, std::string &&responseData, int64_t progress, int64_t total)>
89
+ &&handler) noexcept override;
90
+ void SetOnDataProgress(
91
+ std::function<void(int64_t requestId, int64_t progress, int64_t total)> &&handler) noexcept override;
92
+ void SetOnResponseComplete(std::function<void(int64_t requestId)> &&handler) noexcept override;
83
93
  void SetOnError(
84
94
  std::function<void(int64_t requestId, std::string &&errorMessage, bool isTimeout)> &&handler) noexcept override;
85
95
 
@@ -27,7 +27,6 @@
27
27
 
28
28
  #include <Modules/ExceptionsManagerModule.h>
29
29
  #include <Modules/HttpModule.h>
30
- #include <Modules/NetworkingModule.h>
31
30
  #include <Modules/PlatformConstantsModule.h>
32
31
  #include <Modules/SourceCodeModule.h>
33
32
  #include <Modules/StatusBarManagerModule.h>
@@ -73,11 +72,7 @@ namespace Microsoft::React {
73
72
 
74
73
  /*extern*/ std::unique_ptr<facebook::xplat::module::CxxModule> CreateHttpModule(
75
74
  winrt::Windows::Foundation::IInspectable const &inspectableProperties) noexcept {
76
- if (GetRuntimeOptionBool("Http.UseMonolithicModule")) {
77
- return std::make_unique<NetworkingModule>();
78
- } else {
79
- return std::make_unique<HttpModule>(inspectableProperties);
80
- }
75
+ return std::make_unique<HttpModule>(inspectableProperties);
81
76
  }
82
77
 
83
78
  } // namespace Microsoft::React
@@ -638,8 +633,7 @@ std::vector<std::unique_ptr<NativeModule>> InstanceImpl::GetDefaultNativeModules
638
633
  // If this code is enabled, we will have unused module instances.
639
634
  // Also, MSRN has a different property bag mechanism incompatible with this method's transitionalProps variable.
640
635
  #if (defined(_MSC_VER) && !defined(WINRT))
641
- if (Microsoft::React::GetRuntimeOptionBool("Blob.EnableModule") &&
642
- !Microsoft::React::GetRuntimeOptionBool("Http.UseMonolithicModule")) {
636
+ if (Microsoft::React::GetRuntimeOptionBool("Blob.EnableModule")) {
643
637
  modules.push_back(std::make_unique<CxxNativeModule>(
644
638
  m_innerInstance,
645
639
  Microsoft::React::GetBlobModuleName(),
@@ -50,7 +50,9 @@
50
50
  <ClCompile Include="$(MSBuildThisFileDirectory)Modules\FileReaderModule.cpp" />
51
51
  <ClCompile Include="$(MSBuildThisFileDirectory)Modules\HttpModule.cpp" />
52
52
  <ClCompile Include="$(MSBuildThisFileDirectory)Modules\I18nModule.cpp" />
53
- <ClCompile Include="$(MSBuildThisFileDirectory)Modules\NetworkingModule.cpp" />
53
+ <ClCompile Include="$(MSBuildThisFileDirectory)Modules\NetworkingModule.cpp">
54
+ <ExcludedFromBuild>true</ExcludedFromBuild>
55
+ </ClCompile>
54
56
  <ClCompile Include="$(MSBuildThisFileDirectory)Modules\PlatformConstantsModule.cpp" />
55
57
  <ClCompile Include="$(MSBuildThisFileDirectory)Modules\SourceCodeModule.cpp" />
56
58
  <ClCompile Include="$(MSBuildThisFileDirectory)Modules\StatusBarManagerModule.cpp" />
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-windows",
3
- "version": "0.69.13",
3
+ "version": "0.69.15",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -26,7 +26,7 @@
26
26
  "@react-native-community/cli": "^8.0.0",
27
27
  "@react-native-community/cli-platform-android": "^8.0.0",
28
28
  "@react-native-community/cli-platform-ios": "^8.0.0",
29
- "@react-native-windows/cli": "0.69.4",
29
+ "@react-native-windows/cli": "0.69.5",
30
30
  "@react-native-windows/virtualized-list": "0.69.1",
31
31
  "@react-native/assets": "1.0.0",
32
32
  "@react-native/normalize-color": "2.0.0",