From 80e950aa9a1ca0e73edec8d8a3ea97392e217fef Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Mon, 14 Nov 2022 19:38:24 -0800 Subject: [PATCH 01/14] Declare remaining HTTP module event names --- vnext/Shared/Modules/HttpModule.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/vnext/Shared/Modules/HttpModule.cpp b/vnext/Shared/Modules/HttpModule.cpp index 20f4ea0fd04..795b864a015 100644 --- a/vnext/Shared/Modules/HttpModule.cpp +++ b/vnext/Shared/Modules/HttpModule.cpp @@ -33,8 +33,10 @@ constexpr char moduleName[] = "Networking"; // React event names constexpr char completedResponse[] = "didCompleteNetworkResponse"; constexpr char receivedResponse[] = "didReceiveNetworkResponse"; -constexpr char receivedData[] = "didReceiveNetworkData"; +constexpr char sentData[] = "didSendNetworkData"; +constexpr char receivedIncrementalData[] = "didReceiveNetworkIncrementalData"; constexpr char receivedDataProgress[] = "didReceiveNetworkDataProgress"; +constexpr char receivedData[] = "didReceiveNetworkData"; static void SetUpHttpResource( shared_ptr resource, From 380c76690f8a9f5b38752e108a890dd896428612 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Mon, 14 Nov 2022 20:02:25 -0800 Subject: [PATCH 02/14] Set Progress handler --- vnext/Shared/Networking/WinRTHttpResource.cpp | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/vnext/Shared/Networking/WinRTHttpResource.cpp b/vnext/Shared/Networking/WinRTHttpResource.cpp index 374d89824f2..3a46223ab6f 100644 --- a/vnext/Shared/Networking/WinRTHttpResource.cpp +++ b/vnext/Shared/Networking/WinRTHttpResource.cpp @@ -405,6 +405,35 @@ WinRTHttpResource::PerformSendRequest(HttpMethod &&method, Uri &&rtUri, IInspect try { auto sendRequestOp = self->m_client.SendRequestAsync(coRequest); + + sendRequestOp.Progress([](auto &&operation, auto &&progress) { + using winrt::Windows::Web::Http::HttpProgressStage; + if (progress.Stage == HttpProgressStage::None) { + int x = 99; + } else if (progress.Stage == HttpProgressStage::DetectingProxy) { + int x = 99; + } else if (progress.Stage == HttpProgressStage::ResolvingName) { + int x = 99; + } else if (progress.Stage == HttpProgressStage::ConnectingToServer) { + int x = 99; + } else if (progress.Stage == HttpProgressStage::NegotiatingSsl) { + int x = 99; + } else if (progress.Stage == HttpProgressStage::SendingHeaders) { + int x = 99; + } else if (progress.Stage == HttpProgressStage::SendingContent) { + int x = 99; + } else if (progress.Stage == HttpProgressStage::WaitingForResponse) { + int x = 99; + } else if (progress.Stage == HttpProgressStage::ReceivingHeaders) { + int x = 99; + } else if (progress.Stage == HttpProgressStage::ReceivingContent) { + int x = 99; + } + + int y = 99; + }); + //sendRequestOp.Completed([](auto &&operation, auto &&progress) {}); // Crashes int. tests + self->TrackResponse(reqArgs->RequestId, sendRequestOp); if (reqArgs->Timeout > 0) { From 9cffa5b4cb37b11bb6a1a37164ed07a87add2bef Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Tue, 15 Nov 2022 20:50:37 -0800 Subject: [PATCH 03/14] More debug data --- vnext/Shared/Networking/WinRTHttpResource.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/vnext/Shared/Networking/WinRTHttpResource.cpp b/vnext/Shared/Networking/WinRTHttpResource.cpp index 3a46223ab6f..e9d581d67de 100644 --- a/vnext/Shared/Networking/WinRTHttpResource.cpp +++ b/vnext/Shared/Networking/WinRTHttpResource.cpp @@ -427,6 +427,9 @@ WinRTHttpResource::PerformSendRequest(HttpMethod &&method, Uri &&rtUri, IInspect } else if (progress.Stage == HttpProgressStage::ReceivingHeaders) { int x = 99; } else if (progress.Stage == HttpProgressStage::ReceivingContent) { + winrt::Windows::Web::Http::HttpProgress p = progress; + auto a = p.BytesReceived; + auto b = p.TotalBytesToReceive; int x = 99; } From b3724e8944e4bfcbbbb5c02fcfe18c3a6acb6fae Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Wed, 16 Nov 2022 15:07:19 -0800 Subject: [PATCH 04/14] Update AutolinkedNativeModules.g --- .../windows/playground-win32/AutolinkedNativeModules.g.cpp | 5 +---- .../playground-win32/AutolinkedNativeModules.g.targets | 3 --- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/packages/playground/windows/playground-win32/AutolinkedNativeModules.g.cpp b/packages/playground/windows/playground-win32/AutolinkedNativeModules.g.cpp index 549054e1af6..1213d4c2fc6 100644 --- a/packages/playground/windows/playground-win32/AutolinkedNativeModules.g.cpp +++ b/packages/playground/windows/playground-win32/AutolinkedNativeModules.g.cpp @@ -3,8 +3,6 @@ #include "pch.h" #include "AutolinkedNativeModules.g.h" -#include - // Includes from @react-native-picker/picker #include @@ -12,10 +10,9 @@ namespace winrt::Microsoft::ReactNative { void RegisterAutolinkedNativeModulePackages(winrt::Windows::Foundation::Collections::IVector const& packageProviders) -{ +{ // IReactPackageProviders from @react-native-picker/picker packageProviders.Append(winrt::ReactNativePicker::ReactPackageProvider()); - packageProviders.Append(winrt::PlaygroundNativeModules::ReactPackageProvider()); } } diff --git a/packages/playground/windows/playground-win32/AutolinkedNativeModules.g.targets b/packages/playground/windows/playground-win32/AutolinkedNativeModules.g.targets index ce02b2c34ae..37111ca737e 100644 --- a/packages/playground/windows/playground-win32/AutolinkedNativeModules.g.targets +++ b/packages/playground/windows/playground-win32/AutolinkedNativeModules.g.targets @@ -6,8 +6,5 @@ {bedcc600-4541-41f2-aa46-9e058202b6ad} - - {fbc281f9-e7fa-4d3f-9a15-f7507a803007} - From 4993c4de6f50849c42ca242b040c80edaf65d438 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Wed, 16 Nov 2022 15:15:23 -0800 Subject: [PATCH 05/14] Define IHttpResource::SetOnIncrementalData --- vnext/Shared/Networking/IHttpResource.h | 1 + vnext/Shared/Networking/WinRTHttpResource.cpp | 14 ++++++++++++-- vnext/Shared/Networking/WinRTHttpResource.h | 4 ++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/vnext/Shared/Networking/IHttpResource.h b/vnext/Shared/Networking/IHttpResource.h index 17d791d1868..bb4865cd31a 100644 --- a/vnext/Shared/Networking/IHttpResource.h +++ b/vnext/Shared/Networking/IHttpResource.h @@ -95,6 +95,7 @@ struct IHttpResource { virtual void SetOnResponse(std::function &&handler) noexcept = 0; virtual void SetOnData(std::function &&handler) noexcept = 0; virtual void SetOnData(std::function &&handler) noexcept = 0; + virtual void SetOnIncrementalData(std::function &&handler) noexcept = 0; virtual void SetOnError( std::function &&handler) noexcept = 0; }; diff --git a/vnext/Shared/Networking/WinRTHttpResource.cpp b/vnext/Shared/Networking/WinRTHttpResource.cpp index 128bb29dbfa..2759504d327 100644 --- a/vnext/Shared/Networking/WinRTHttpResource.cpp +++ b/vnext/Shared/Networking/WinRTHttpResource.cpp @@ -336,6 +336,12 @@ void WinRTHttpResource::SetOnData(function + && handler) noexcept /*override*/ { + m_onIncData = std::move(handler); +} + void WinRTHttpResource::SetOnError( function &&handler) noexcept /*override*/ { @@ -406,7 +412,8 @@ WinRTHttpResource::PerformSendRequest(HttpMethod &&method, Uri &&rtUri, IInspect try { auto sendRequestOp = self->m_client.SendRequestAsync(coRequest); - sendRequestOp.Progress([](auto &&operation, auto &&progress) { + using winrt::Windows::Web::Http::HttpProgress; + sendRequestOp.Progress([](auto &&operation, const HttpProgress &progress) { using winrt::Windows::Web::Http::HttpProgressStage; if (progress.Stage == HttpProgressStage::None) { int x = 99; @@ -427,10 +434,13 @@ WinRTHttpResource::PerformSendRequest(HttpMethod &&method, Uri &&rtUri, IInspect } else if (progress.Stage == HttpProgressStage::ReceivingHeaders) { int x = 99; } else if (progress.Stage == HttpProgressStage::ReceivingContent) { - winrt::Windows::Web::Http::HttpProgress p = progress; + HttpProgress p = progress; auto a = p.BytesReceived; auto b = p.TotalBytesToReceive; int x = 99; + + auto total = p.TotalBytesToReceive ? p.TotalBytesToReceive.Value() : 0; + } int y = 99; diff --git a/vnext/Shared/Networking/WinRTHttpResource.h b/vnext/Shared/Networking/WinRTHttpResource.h index 23cc68dd299..fed8ba2c12a 100644 --- a/vnext/Shared/Networking/WinRTHttpResource.h +++ b/vnext/Shared/Networking/WinRTHttpResource.h @@ -30,6 +30,7 @@ class WinRTHttpResource : public IHttpResource, std::function m_onData; std::function m_onDataDynamic; std::function m_onError; + std::function m_onIncData; // Used for IHttpModuleProxy std::weak_ptr m_uriHandler; @@ -80,6 +81,9 @@ class WinRTHttpResource : public IHttpResource, void SetOnResponse(std::function &&handler) noexcept override; void SetOnData(std::function &&handler) noexcept override; void SetOnData(std::function &&handler) noexcept override; + void SetOnIncrementalData( + std::function &&handler) noexcept + override; void SetOnError( std::function &&handler) noexcept override; From 2b4e144922fd3ecb31ed41b7d7b2c388693640b9 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Wed, 16 Nov 2022 15:41:21 -0800 Subject: [PATCH 06/14] Set up and invoke OnIncrementalData --- vnext/Shared/Modules/HttpModule.cpp | 8 ++++++++ vnext/Shared/Networking/WinRTHttpResource.cpp | 17 ++++++----------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/vnext/Shared/Modules/HttpModule.cpp b/vnext/Shared/Modules/HttpModule.cpp index 795b864a015..b1cde55de35 100644 --- a/vnext/Shared/Modules/HttpModule.cpp +++ b/vnext/Shared/Modules/HttpModule.cpp @@ -74,6 +74,14 @@ static void SetUpHttpResource( }; resource->SetOnData(std::move(onDataDynamic)); + resource->SetOnIncrementalData( + [weakReactInstance](int64_t requestId, string &&responseData, int64_t progress, int64_t total) { + SendEvent( + weakReactInstance, + receivedIncrementalData, + dynamic::array(requestId, std::move(responseData), progress, total)); + }); + resource->SetOnError([weakReactInstance](int64_t requestId, string &&message, bool isTimeout) { dynamic args = dynamic::array(requestId, std::move(message)); if (isTimeout) { diff --git a/vnext/Shared/Networking/WinRTHttpResource.cpp b/vnext/Shared/Networking/WinRTHttpResource.cpp index 2759504d327..ec57fffbbfb 100644 --- a/vnext/Shared/Networking/WinRTHttpResource.cpp +++ b/vnext/Shared/Networking/WinRTHttpResource.cpp @@ -337,7 +337,7 @@ void WinRTHttpResource::SetOnData(function + function && handler) noexcept /*override*/ { m_onIncData = std::move(handler); } @@ -413,7 +413,7 @@ WinRTHttpResource::PerformSendRequest(HttpMethod &&method, Uri &&rtUri, IInspect auto sendRequestOp = self->m_client.SendRequestAsync(coRequest); using winrt::Windows::Web::Http::HttpProgress; - sendRequestOp.Progress([](auto &&operation, const HttpProgress &progress) { + sendRequestOp.Progress([self = self->shared_from_this(), requestId = reqArgs->RequestId](auto &&operation, const HttpProgress &progress) { using winrt::Windows::Web::Http::HttpProgressStage; if (progress.Stage == HttpProgressStage::None) { int x = 99; @@ -434,16 +434,11 @@ WinRTHttpResource::PerformSendRequest(HttpMethod &&method, Uri &&rtUri, IInspect } else if (progress.Stage == HttpProgressStage::ReceivingHeaders) { int x = 99; } else if (progress.Stage == HttpProgressStage::ReceivingContent) { - HttpProgress p = progress; - auto a = p.BytesReceived; - auto b = p.TotalBytesToReceive; - int x = 99; - - auto total = p.TotalBytesToReceive ? p.TotalBytesToReceive.Value() : 0; - + int64_t total = progress.TotalBytesToReceive ? progress.TotalBytesToReceive.Value() : 0; + if (self->m_onIncData) { + self->m_onIncData(requestId, "TODO", static_cast(progress.BytesReceived), total); + } } - - int y = 99; }); //sendRequestOp.Completed([](auto &&operation, auto &&progress) {}); // Crashes int. tests From b417599e20b46aeedd3e90591fce959ccb70e44f Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Wed, 16 Nov 2022 16:29:17 -0800 Subject: [PATCH 07/14] Clean up Progress functor --- vnext/Shared/Networking/WinRTHttpResource.cpp | 26 +++---------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/vnext/Shared/Networking/WinRTHttpResource.cpp b/vnext/Shared/Networking/WinRTHttpResource.cpp index ec57fffbbfb..c7f2d2dbb4d 100644 --- a/vnext/Shared/Networking/WinRTHttpResource.cpp +++ b/vnext/Shared/Networking/WinRTHttpResource.cpp @@ -413,34 +413,16 @@ WinRTHttpResource::PerformSendRequest(HttpMethod &&method, Uri &&rtUri, IInspect auto sendRequestOp = self->m_client.SendRequestAsync(coRequest); using winrt::Windows::Web::Http::HttpProgress; - sendRequestOp.Progress([self = self->shared_from_this(), requestId = reqArgs->RequestId](auto &&operation, const HttpProgress &progress) { - using winrt::Windows::Web::Http::HttpProgressStage; - if (progress.Stage == HttpProgressStage::None) { - int x = 99; - } else if (progress.Stage == HttpProgressStage::DetectingProxy) { - int x = 99; - } else if (progress.Stage == HttpProgressStage::ResolvingName) { - int x = 99; - } else if (progress.Stage == HttpProgressStage::ConnectingToServer) { - int x = 99; - } else if (progress.Stage == HttpProgressStage::NegotiatingSsl) { - int x = 99; - } else if (progress.Stage == HttpProgressStage::SendingHeaders) { - int x = 99; - } else if (progress.Stage == HttpProgressStage::SendingContent) { - int x = 99; - } else if (progress.Stage == HttpProgressStage::WaitingForResponse) { - int x = 99; - } else if (progress.Stage == HttpProgressStage::ReceivingHeaders) { - int x = 99; - } else if (progress.Stage == HttpProgressStage::ReceivingContent) { + using winrt::Windows::Web::Http::HttpProgressStage; + sendRequestOp.Progress([self = self->shared_from_this(), requestId = reqArgs->RequestId]( + ResponseOperation const &operation, HttpProgress const &progress) { + if (progress.Stage == HttpProgressStage::ReceivingContent) { int64_t total = progress.TotalBytesToReceive ? progress.TotalBytesToReceive.Value() : 0; if (self->m_onIncData) { self->m_onIncData(requestId, "TODO", static_cast(progress.BytesReceived), total); } } }); - //sendRequestOp.Completed([](auto &&operation, auto &&progress) {}); // Crashes int. tests self->TrackResponse(reqArgs->RequestId, sendRequestOp); From 33a94f0c5ce5eb59f23cf979bf2e9b36f8035576 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Wed, 16 Nov 2022 19:02:31 -0800 Subject: [PATCH 08/14] Handle didReceiveNetworkDataProgress event - Define IHttpResource::SetOnDataProgress --- vnext/Shared/Modules/HttpModule.cpp | 4 ++ vnext/Shared/Networking/IHttpResource.h | 2 + vnext/Shared/Networking/WinRTHttpResource.cpp | 37 +++++++++++++------ vnext/Shared/Networking/WinRTHttpResource.h | 3 ++ 4 files changed, 35 insertions(+), 11 deletions(-) diff --git a/vnext/Shared/Modules/HttpModule.cpp b/vnext/Shared/Modules/HttpModule.cpp index b1cde55de35..1ca86ef5878 100644 --- a/vnext/Shared/Modules/HttpModule.cpp +++ b/vnext/Shared/Modules/HttpModule.cpp @@ -82,6 +82,10 @@ static void SetUpHttpResource( dynamic::array(requestId, std::move(responseData), progress, total)); }); + resource->SetOnDataProgress([weakReactInstance](int64_t requestId, int64_t progress, int64_t total) { + SendEvent(weakReactInstance, receivedDataProgress, dynamic::array(requestId, progress, total)); + }); + resource->SetOnError([weakReactInstance](int64_t requestId, string &&message, bool isTimeout) { dynamic args = dynamic::array(requestId, std::move(message)); if (isTimeout) { diff --git a/vnext/Shared/Networking/IHttpResource.h b/vnext/Shared/Networking/IHttpResource.h index bb4865cd31a..7233beb919e 100644 --- a/vnext/Shared/Networking/IHttpResource.h +++ b/vnext/Shared/Networking/IHttpResource.h @@ -96,6 +96,8 @@ struct IHttpResource { virtual void SetOnData(std::function &&handler) noexcept = 0; virtual void SetOnData(std::function &&handler) noexcept = 0; virtual void SetOnIncrementalData(std::function &&handler) noexcept = 0; + virtual void SetOnDataProgress( + std::function &&handler) noexcept = 0; virtual void SetOnError( std::function &&handler) noexcept = 0; }; diff --git a/vnext/Shared/Networking/WinRTHttpResource.cpp b/vnext/Shared/Networking/WinRTHttpResource.cpp index c7f2d2dbb4d..9bc6226df5a 100644 --- a/vnext/Shared/Networking/WinRTHttpResource.cpp +++ b/vnext/Shared/Networking/WinRTHttpResource.cpp @@ -342,6 +342,12 @@ void WinRTHttpResource::SetOnIncrementalData( m_onIncData = std::move(handler); } +void WinRTHttpResource::SetOnDataProgress( + function &&handler) noexcept +/*override*/ { + m_onDataProgress = std::move(handler); +} + void WinRTHttpResource::SetOnError( function &&handler) noexcept /*override*/ { @@ -412,17 +418,27 @@ WinRTHttpResource::PerformSendRequest(HttpMethod &&method, Uri &&rtUri, IInspect try { auto sendRequestOp = self->m_client.SendRequestAsync(coRequest); - using winrt::Windows::Web::Http::HttpProgress; - using winrt::Windows::Web::Http::HttpProgressStage; - sendRequestOp.Progress([self = self->shared_from_this(), requestId = reqArgs->RequestId]( - ResponseOperation const &operation, HttpProgress const &progress) { - if (progress.Stage == HttpProgressStage::ReceivingContent) { - int64_t total = progress.TotalBytesToReceive ? progress.TotalBytesToReceive.Value() : 0; - if (self->m_onIncData) { - self->m_onIncData(requestId, "TODO", static_cast(progress.BytesReceived), total); + auto isText = reqArgs->ResponseType == "text"; + + if (reqArgs->IncrementalUpdates) { + using winrt::Windows::Web::Http::HttpProgress; + using winrt::Windows::Web::Http::HttpProgressStage; + sendRequestOp.Progress([self = self->shared_from_this(), requestId = reqArgs->RequestId, isText]( + ResponseOperation const &operation, HttpProgress const &progress) { + if (progress.Stage == HttpProgressStage::ReceivingContent) { + int64_t total = progress.TotalBytesToReceive ? progress.TotalBytesToReceive.Value() : 0; + if (isText) { + if (self->m_onIncData) { + self->m_onIncData(requestId, "TODO", static_cast(progress.BytesReceived), total); + } + } else { + if (self->m_onDataProgress) { + self->m_onDataProgress(requestId, static_cast(progress.BytesReceived), total); + } + } } - } - }); + }); + } self->TrackResponse(reqArgs->RequestId, sendRequestOp); @@ -517,7 +533,6 @@ WinRTHttpResource::PerformSendRequest(HttpMethod &&method, Uri &&rtUri, IInspect } } - auto isText = reqArgs->ResponseType == "text"; if (isText) { reader.UnicodeEncoding(UnicodeEncoding::Utf8); } diff --git a/vnext/Shared/Networking/WinRTHttpResource.h b/vnext/Shared/Networking/WinRTHttpResource.h index fed8ba2c12a..2586782d357 100644 --- a/vnext/Shared/Networking/WinRTHttpResource.h +++ b/vnext/Shared/Networking/WinRTHttpResource.h @@ -31,6 +31,7 @@ class WinRTHttpResource : public IHttpResource, std::function m_onDataDynamic; std::function m_onError; std::function m_onIncData; + std::function m_onDataProgress; // Used for IHttpModuleProxy std::weak_ptr m_uriHandler; @@ -84,6 +85,8 @@ class WinRTHttpResource : public IHttpResource, void SetOnIncrementalData( std::function &&handler) noexcept override; + void SetOnDataProgress( + std::function &&handler) noexcept override; void SetOnError( std::function &&handler) noexcept override; From 108329b6d8565c7e8dce18714ef7d02c0aeb4ef7 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Wed, 23 Nov 2022 12:47:47 -0800 Subject: [PATCH 09/14] Default segment size to 8MB --- vnext/Shared/Networking/WinRTHttpResource.cpp | 25 +++---------------- 1 file changed, 3 insertions(+), 22 deletions(-) diff --git a/vnext/Shared/Networking/WinRTHttpResource.cpp b/vnext/Shared/Networking/WinRTHttpResource.cpp index 9bc6226df5a..dbe8cfd6168 100644 --- a/vnext/Shared/Networking/WinRTHttpResource.cpp +++ b/vnext/Shared/Networking/WinRTHttpResource.cpp @@ -420,26 +420,6 @@ WinRTHttpResource::PerformSendRequest(HttpMethod &&method, Uri &&rtUri, IInspect auto isText = reqArgs->ResponseType == "text"; - if (reqArgs->IncrementalUpdates) { - using winrt::Windows::Web::Http::HttpProgress; - using winrt::Windows::Web::Http::HttpProgressStage; - sendRequestOp.Progress([self = self->shared_from_this(), requestId = reqArgs->RequestId, isText]( - ResponseOperation const &operation, HttpProgress const &progress) { - if (progress.Stage == HttpProgressStage::ReceivingContent) { - int64_t total = progress.TotalBytesToReceive ? progress.TotalBytesToReceive.Value() : 0; - if (isText) { - if (self->m_onIncData) { - self->m_onIncData(requestId, "TODO", static_cast(progress.BytesReceived), total); - } - } else { - if (self->m_onDataProgress) { - self->m_onDataProgress(requestId, static_cast(progress.BytesReceived), total); - } - } - } - }); - } - self->TrackResponse(reqArgs->RequestId, sendRequestOp); if (reqArgs->Timeout > 0) { @@ -506,8 +486,9 @@ WinRTHttpResource::PerformSendRequest(HttpMethod &&method, Uri &&rtUri, IInspect auto inputStream = co_await response.Content().ReadAsInputStreamAsync(); auto reader = DataReader{inputStream}; - // #9510 - We currently accumulate all incoming request data in 10MB chunks - const uint32_t segmentSize = 10 * 1024 * 1024; + // Accumulate all incoming request data in 8MB chunks + // Note, the minimum apparent valid chunk size is 128 KB + const uint32_t segmentSize = 8 * 1024 * 1024; // Let response handler take over, if set if (auto responseHandler = self->m_responseHandler.lock()) { From 80fad79740613fddef445074e58029df8eb7d7ed Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Wed, 23 Nov 2022 14:50:57 -0800 Subject: [PATCH 10/14] Define IHttpResource::SetOnResponseComplete - Separates request completion event from non-incremental data reception event. --- vnext/Shared/Modules/HttpModule.cpp | 7 +-- vnext/Shared/Networking/IHttpResource.h | 5 +- vnext/Shared/Networking/WinRTHttpResource.cpp | 48 ++++++++++++++++--- vnext/Shared/Networking/WinRTHttpResource.h | 6 ++- 4 files changed, 53 insertions(+), 13 deletions(-) diff --git a/vnext/Shared/Modules/HttpModule.cpp b/vnext/Shared/Modules/HttpModule.cpp index 1ca86ef5878..7995320d961 100644 --- a/vnext/Shared/Modules/HttpModule.cpp +++ b/vnext/Shared/Modules/HttpModule.cpp @@ -62,9 +62,6 @@ static void SetUpHttpResource( resource->SetOnData([weakReactInstance](int64_t requestId, string &&responseData) { SendEvent(weakReactInstance, receivedData, dynamic::array(requestId, std::move(responseData))); - - // TODO: Move into separate method IF not executed right after onData() - SendEvent(weakReactInstance, completedResponse, dynamic::array(requestId)); }); // Explicitly declaring function type to avoid type inference ambiguity. @@ -86,6 +83,10 @@ static void SetUpHttpResource( SendEvent(weakReactInstance, receivedDataProgress, dynamic::array(requestId, progress, total)); }); + resource->SetOnResponseComplete([weakReactInstance](int64_t requestId) { + SendEvent(weakReactInstance, completedResponse, dynamic::array(requestId)); + }); + resource->SetOnError([weakReactInstance](int64_t requestId, string &&message, bool isTimeout) { dynamic args = dynamic::array(requestId, std::move(message)); if (isTimeout) { diff --git a/vnext/Shared/Networking/IHttpResource.h b/vnext/Shared/Networking/IHttpResource.h index 7233beb919e..0ff1b1fbe22 100644 --- a/vnext/Shared/Networking/IHttpResource.h +++ b/vnext/Shared/Networking/IHttpResource.h @@ -95,9 +95,12 @@ struct IHttpResource { virtual void SetOnResponse(std::function &&handler) noexcept = 0; virtual void SetOnData(std::function &&handler) noexcept = 0; virtual void SetOnData(std::function &&handler) noexcept = 0; - virtual void SetOnIncrementalData(std::function &&handler) noexcept = 0; + virtual void SetOnIncrementalData( + std::function + &&handler) noexcept = 0; virtual void SetOnDataProgress( std::function &&handler) noexcept = 0; + virtual void SetOnResponseComplete(std::function &&handler) noexcept = 0; virtual void SetOnError( std::function &&handler) noexcept = 0; }; diff --git a/vnext/Shared/Networking/WinRTHttpResource.cpp b/vnext/Shared/Networking/WinRTHttpResource.cpp index dbe8cfd6168..973a29b5f99 100644 --- a/vnext/Shared/Networking/WinRTHttpResource.cpp +++ b/vnext/Shared/Networking/WinRTHttpResource.cpp @@ -337,8 +337,8 @@ void WinRTHttpResource::SetOnData(function - && handler) noexcept /*override*/ { + function &&handler) noexcept +/*override*/ { m_onIncData = std::move(handler); } @@ -348,6 +348,10 @@ void WinRTHttpResource::SetOnDataProgress( m_onDataProgress = std::move(handler); } +void WinRTHttpResource::SetOnResponseComplete(function &&handler) noexcept /*override*/ { + m_onComplete = std::move(handler); +} + void WinRTHttpResource::SetOnError( function &&handler) noexcept /*override*/ { @@ -404,6 +408,10 @@ WinRTHttpResource::PerformSendRequest(HttpMethod &&method, Uri &&rtUri, IInspect self->m_onRequestSuccess(reqArgs->RequestId); } + if (self->m_onComplete) { + self->m_onComplete(reqArgs->RequestId); + } + co_return; } } catch (const hresult_error &e) { @@ -488,12 +496,12 @@ WinRTHttpResource::PerformSendRequest(HttpMethod &&method, Uri &&rtUri, IInspect // Accumulate all incoming request data in 8MB chunks // Note, the minimum apparent valid chunk size is 128 KB - const uint32_t segmentSize = 8 * 1024 * 1024; + // Apple's implementation appears to grab 5-8 KB chunks + const uint32_t segmentSize = (reqArgs->IncrementalUpdates ? 128 : 8 * 1024) * 1024; // Let response handler take over, if set if (auto responseHandler = self->m_responseHandler.lock()) { if (responseHandler->Supports(reqArgs->ResponseType)) { - // #9510 vector responseData{}; while (auto loaded = co_await reader.LoadAsync(segmentSize)) { auto length = reader.UnconsumedBufferLength(); @@ -510,6 +518,9 @@ WinRTHttpResource::PerformSendRequest(HttpMethod &&method, Uri &&rtUri, IInspect self->m_onRequestSuccess(reqArgs->RequestId); } + if (self->m_onComplete) { + self->m_onComplete(reqArgs->RequestId); + } co_return; } } @@ -518,28 +529,51 @@ WinRTHttpResource::PerformSendRequest(HttpMethod &&method, Uri &&rtUri, IInspect reader.UnicodeEncoding(UnicodeEncoding::Utf8); } - // #9510 + int64_t received = 0; string responseData; winrt::Windows::Storage::Streams::IBuffer buffer; while (auto loaded = co_await reader.LoadAsync(segmentSize)) { auto length = reader.UnconsumedBufferLength(); + received += length; if (isText) { auto data = vector(length); reader.ReadBytes(data); - responseData += string(Common::Utilities::CheckedReinterpretCast(data.data()), data.size()); + auto increment = string(Common::Utilities::CheckedReinterpretCast(data.data()), data.size()); + // #9534 - Send incremental updates. + // See https://github.com/facebook/react-native/blob/v0.70.6/Libraries/Network/RCTNetworking.mm#L561 + if (reqArgs->IncrementalUpdates) { + responseData = std::move(increment); + + if (self->m_onIncData) { + // For total, see #10849 + self->m_onIncData(reqArgs->RequestId, std::move(responseData), received, 0 /*total*/); + } + } else { + responseData += std::move(increment); + } } else { buffer = reader.ReadBuffer(length); auto data = CryptographicBuffer::EncodeToBase64String(buffer); responseData += winrt::to_string(std::wstring_view(data)); + + if (self->m_onDataProgress) { + // For total, see #10849 + self->m_onDataProgress(reqArgs->RequestId, received, 0 /*total*/); + } } } - if (self->m_onData) { + // If dealing with text-incremental response data, use m_onIncData instead + if (self->m_onData && !(reqArgs->IncrementalUpdates && isText)) { self->m_onData(reqArgs->RequestId, std::move(responseData)); } + + if (self->m_onComplete) { + self->m_onComplete(reqArgs->RequestId); + } } else { if (self->m_onError) { self->m_onError(reqArgs->RequestId, response == nullptr ? "request failed" : "No response content", false); diff --git a/vnext/Shared/Networking/WinRTHttpResource.h b/vnext/Shared/Networking/WinRTHttpResource.h index 2586782d357..274b230e464 100644 --- a/vnext/Shared/Networking/WinRTHttpResource.h +++ b/vnext/Shared/Networking/WinRTHttpResource.h @@ -32,6 +32,7 @@ class WinRTHttpResource : public IHttpResource, std::function m_onError; std::function m_onIncData; std::function m_onDataProgress; + std::function m_onComplete; // Used for IHttpModuleProxy std::weak_ptr m_uriHandler; @@ -83,10 +84,11 @@ class WinRTHttpResource : public IHttpResource, void SetOnData(std::function &&handler) noexcept override; void SetOnData(std::function &&handler) noexcept override; void SetOnIncrementalData( - std::function &&handler) noexcept - override; + std::function + &&handler) noexcept override; void SetOnDataProgress( std::function &&handler) noexcept override; + void SetOnResponseComplete(std::function &&handler) noexcept override; void SetOnError( std::function &&handler) noexcept override; From cc2dad2f79b1efe19298e311d03c2151427a5fc9 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Wed, 23 Nov 2022 15:08:40 -0800 Subject: [PATCH 11/14] Revert playground-win32 generated files --- .../windows/playground-win32/AutolinkedNativeModules.g.cpp | 5 ++++- .../playground-win32/AutolinkedNativeModules.g.targets | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/playground/windows/playground-win32/AutolinkedNativeModules.g.cpp b/packages/playground/windows/playground-win32/AutolinkedNativeModules.g.cpp index 1213d4c2fc6..549054e1af6 100644 --- a/packages/playground/windows/playground-win32/AutolinkedNativeModules.g.cpp +++ b/packages/playground/windows/playground-win32/AutolinkedNativeModules.g.cpp @@ -3,6 +3,8 @@ #include "pch.h" #include "AutolinkedNativeModules.g.h" +#include + // Includes from @react-native-picker/picker #include @@ -10,9 +12,10 @@ namespace winrt::Microsoft::ReactNative { void RegisterAutolinkedNativeModulePackages(winrt::Windows::Foundation::Collections::IVector const& packageProviders) -{ +{ // IReactPackageProviders from @react-native-picker/picker packageProviders.Append(winrt::ReactNativePicker::ReactPackageProvider()); + packageProviders.Append(winrt::PlaygroundNativeModules::ReactPackageProvider()); } } diff --git a/packages/playground/windows/playground-win32/AutolinkedNativeModules.g.targets b/packages/playground/windows/playground-win32/AutolinkedNativeModules.g.targets index 37111ca737e..ce02b2c34ae 100644 --- a/packages/playground/windows/playground-win32/AutolinkedNativeModules.g.targets +++ b/packages/playground/windows/playground-win32/AutolinkedNativeModules.g.targets @@ -6,5 +6,8 @@ {bedcc600-4541-41f2-aa46-9e058202b6ad} + + {fbc281f9-e7fa-4d3f-9a15-f7507a803007} + From 95321e662a0a9069081dfc4836abb6bde89211a3 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Wed, 23 Nov 2022 15:10:26 -0800 Subject: [PATCH 12/14] Change files --- ...ative-windows-00b587ec-6a97-4334-b5eb-18d571ce94d8.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 change/react-native-windows-00b587ec-6a97-4334-b5eb-18d571ce94d8.json diff --git a/change/react-native-windows-00b587ec-6a97-4334-b5eb-18d571ce94d8.json b/change/react-native-windows-00b587ec-6a97-4334-b5eb-18d571ce94d8.json new file mode 100644 index 00000000000..b1668063e0e --- /dev/null +++ b/change/react-native-windows-00b587ec-6a97-4334-b5eb-18d571ce94d8.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "Implement HTTP incremental updates", + "packageName": "react-native-windows", + "email": "julio.rocha@microsoft.com", + "dependentChangeType": "patch" +} From 9c5624e62e7dace16477b8837c66e04d1c548591 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Tue, 6 Dec 2022 18:22:00 -0800 Subject: [PATCH 13/14] Address feedback --- vnext/Shared/Networking/WinRTHttpResource.cpp | 41 +++++++++++++------ vnext/Shared/Networking/WinRTHttpResource.h | 3 +- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/vnext/Shared/Networking/WinRTHttpResource.cpp b/vnext/Shared/Networking/WinRTHttpResource.cpp index 973a29b5f99..6022450205b 100644 --- a/vnext/Shared/Networking/WinRTHttpResource.cpp +++ b/vnext/Shared/Networking/WinRTHttpResource.cpp @@ -51,6 +51,21 @@ using winrt::Windows::Web::Http::IHttpClient; using winrt::Windows::Web::Http::IHttpContent; using winrt::Windows::Web::Http::Headers::HttpMediaTypeHeaderValue; +namespace { + +constexpr uint32_t operator""_KiB(unsigned long long int x) { + return static_cast(1024 * x); +} + +constexpr uint32_t operator""_MiB(unsigned long long int x) { + return static_cast(1024_KiB * x); +} + +constexpr char responseTypeText[] = "text"; +constexpr char responseTypeBase64[] = "base64"; +constexpr char responseTypeBlob[] = "blob"; + +} // namespace namespace Microsoft::React::Networking { // May throw winrt::hresult_error @@ -258,7 +273,7 @@ void WinRTHttpResource::SendRequest( bool withCredentials, std::function &&callback) noexcept /*override*/ { // Enforce supported args - assert(responseType == "text" || responseType == "base64" || responseType == "blob"); + assert(responseType == responseTypeText || responseType == responseTypeBase64 || responseType == responseTypeBlob); if (callback) { callback(requestId); @@ -339,7 +354,7 @@ void WinRTHttpResource::SetOnData(function &&handler) noexcept /*override*/ { - m_onIncData = std::move(handler); + m_onIncrementalData = std::move(handler); } void WinRTHttpResource::SetOnDataProgress( @@ -426,7 +441,7 @@ WinRTHttpResource::PerformSendRequest(HttpMethod &&method, Uri &&rtUri, IInspect try { auto sendRequestOp = self->m_client.SendRequestAsync(coRequest); - auto isText = reqArgs->ResponseType == "text"; + auto isText = reqArgs->ResponseType == responseTypeText; self->TrackResponse(reqArgs->RequestId, sendRequestOp); @@ -497,7 +512,7 @@ WinRTHttpResource::PerformSendRequest(HttpMethod &&method, Uri &&rtUri, IInspect // Accumulate all incoming request data in 8MB chunks // Note, the minimum apparent valid chunk size is 128 KB // Apple's implementation appears to grab 5-8 KB chunks - const uint32_t segmentSize = (reqArgs->IncrementalUpdates ? 128 : 8 * 1024) * 1024; + const uint32_t segmentSize = reqArgs->IncrementalUpdates ? 128_KiB : 8_MiB; // Let response handler take over, if set if (auto responseHandler = self->m_responseHandler.lock()) { @@ -529,29 +544,29 @@ WinRTHttpResource::PerformSendRequest(HttpMethod &&method, Uri &&rtUri, IInspect reader.UnicodeEncoding(UnicodeEncoding::Utf8); } - int64_t received = 0; + int64_t receivedBytes = 0; string responseData; winrt::Windows::Storage::Streams::IBuffer buffer; while (auto loaded = co_await reader.LoadAsync(segmentSize)) { auto length = reader.UnconsumedBufferLength(); - received += length; + receivedBytes += length; if (isText) { auto data = vector(length); reader.ReadBytes(data); - auto increment = string(Common::Utilities::CheckedReinterpretCast(data.data()), data.size()); + auto incrementData = string(Common::Utilities::CheckedReinterpretCast(data.data()), data.size()); // #9534 - Send incremental updates. // See https://github.com/facebook/react-native/blob/v0.70.6/Libraries/Network/RCTNetworking.mm#L561 if (reqArgs->IncrementalUpdates) { - responseData = std::move(increment); + responseData = std::move(incrementData); - if (self->m_onIncData) { + if (self->m_onIncrementalData) { // For total, see #10849 - self->m_onIncData(reqArgs->RequestId, std::move(responseData), received, 0 /*total*/); + self->m_onIncrementalData(reqArgs->RequestId, std::move(responseData), receivedBytes, 0 /*total*/); } } else { - responseData += std::move(increment); + responseData += std::move(incrementData); } } else { buffer = reader.ReadBuffer(length); @@ -561,12 +576,12 @@ WinRTHttpResource::PerformSendRequest(HttpMethod &&method, Uri &&rtUri, IInspect if (self->m_onDataProgress) { // For total, see #10849 - self->m_onDataProgress(reqArgs->RequestId, received, 0 /*total*/); + self->m_onDataProgress(reqArgs->RequestId, receivedBytes, 0 /*total*/); } } } - // If dealing with text-incremental response data, use m_onIncData instead + // If dealing with text-incremental response data, use m_onIncrementalData instead if (self->m_onData && !(reqArgs->IncrementalUpdates && isText)) { self->m_onData(reqArgs->RequestId, std::move(responseData)); } diff --git a/vnext/Shared/Networking/WinRTHttpResource.h b/vnext/Shared/Networking/WinRTHttpResource.h index 274b230e464..7b18c69c6aa 100644 --- a/vnext/Shared/Networking/WinRTHttpResource.h +++ b/vnext/Shared/Networking/WinRTHttpResource.h @@ -30,7 +30,8 @@ class WinRTHttpResource : public IHttpResource, std::function m_onData; std::function m_onDataDynamic; std::function m_onError; - std::function m_onIncData; + std::function + m_onIncrementalData; std::function m_onDataProgress; std::function m_onComplete; From 4f734df1a4eff84be25ee1348788400566ecc5fc Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Tue, 6 Dec 2022 19:25:07 -0800 Subject: [PATCH 14/14] Added docs for IHttpResource methods --- vnext/Shared/Networking/IHttpResource.h | 120 ++++++++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/vnext/Shared/Networking/IHttpResource.h b/vnext/Shared/Networking/IHttpResource.h index 0ff1b1fbe22..c590e91aac7 100644 --- a/vnext/Shared/Networking/IHttpResource.h +++ b/vnext/Shared/Networking/IHttpResource.h @@ -91,16 +91,136 @@ struct IHttpResource { virtual void ClearCookies() noexcept = 0; + /// + /// Sets a function to be invoked when a request has been successfully responded. + /// + /// + /// + /// Parameters: + /// + /// Unique number identifying the HTTP request + /// + /// virtual void SetOnRequestSuccess(std::function &&handler) noexcept = 0; + + /// + /// Sets a function to be invoked when a response arrives and its headers are received. + /// + /// + /// + /// Parameters: + /// + /// Unique number identifying the HTTP request + /// + /// + /// Object containing basic response data + /// + /// virtual void SetOnResponse(std::function &&handler) noexcept = 0; + + /// + /// Sets a function to be invoked when response content data has been received. + /// + /// + /// + /// Parameters: + /// + /// Unique number identifying the HTTP request + /// + /// + /// Response content payload (plain text or Base64-encoded) + /// + /// virtual void SetOnData(std::function &&handler) noexcept = 0; + + /// + /// Sets a function to be invoked when response content data has been received. + /// + /// + /// + /// Parameters: + /// + /// Unique number identifying the HTTP request + /// + /// + /// Structured response content payload (i.e. Blob data) + /// + /// virtual void SetOnData(std::function &&handler) noexcept = 0; + + /// + /// Sets a function to be invoked when a response content increment has been received. + /// + /// + /// The handler set by this method will only be called if the request sets the incremental updates flag. + /// The handler is also mutually exclusive with those set by `SetOnData`, which are used for one pass, non-incremental + /// updates. + /// + /// + /// + /// Parameters: + /// + /// Unique number identifying the HTTP request + /// + /// + /// Partial response content data increment (non-accumulative) + /// + /// + /// Number of bytes received so far + /// + /// + /// Number of total bytes to receive + /// + /// virtual void SetOnIncrementalData( std::function &&handler) noexcept = 0; + + /// + /// Sets a function to be invoked when response content download progress is reported. + /// + /// + /// + /// Parameters: + /// + /// Unique number identifying the HTTP request + /// + /// + /// Number of bytes received so far + /// + /// + /// Number of total bytes to receive + /// + /// virtual void SetOnDataProgress( std::function &&handler) noexcept = 0; + + /// + /// Sets a function to be invoked when a response has been fully handled (either succeeded or failed). + /// + /// + /// + /// Parameters: + /// + /// Unique number identifying the HTTP request + /// + /// virtual void SetOnResponseComplete(std::function &&handler) noexcept = 0; + + /// + /// Sets a function to be invoked when an error condition is found. + /// + /// + /// The handler's purpose is not to report any given HTTP error status (i.e. 403, 501). + /// It is meant to report application errors when executing HTTP requests. + /// + /// + /// + /// Parameters: + /// + /// Unique number identifying the HTTP request + /// + /// virtual void SetOnError( std::function &&handler) noexcept = 0; };