diff --git a/change/@office-iss-react-native-win32-91558e86-80c0-4e3b-8fec-3c36d7c4917a.json b/change/@office-iss-react-native-win32-91558e86-80c0-4e3b-8fec-3c36d7c4917a.json new file mode 100644 index 00000000000..4bc63ec886c --- /dev/null +++ b/change/@office-iss-react-native-win32-91558e86-80c0-4e3b-8fec-3c36d7c4917a.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Refresh HTTP proxy state after network changes", + "packageName": "@office-iss/react-native-win32", + "email": "khosany@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/vnext/Shared/Networking/WinRTHttpResource.cpp b/vnext/Shared/Networking/WinRTHttpResource.cpp index 069692f3077..41d3cad7586 100644 --- a/vnext/Shared/Networking/WinRTHttpResource.cpp +++ b/vnext/Shared/Networking/WinRTHttpResource.cpp @@ -40,6 +40,7 @@ using winrt::Microsoft::ReactNative::JSValueObject; using winrt::Windows::Foundation::IAsyncOperation; using winrt::Windows::Foundation::IInspectable; using winrt::Windows::Foundation::Uri; +using winrt::Windows::Networking::Connectivity::NetworkInformation; using winrt::Windows::Security::Cryptography::CryptographicBuffer; using winrt::Windows::Storage::StorageFile; using winrt::Windows::Storage::Streams::DataReader; @@ -104,6 +105,15 @@ WinRTHttpResource::WinRTHttpResource(IHttpClient &&client) noexcept : m_client{s WinRTHttpResource::WinRTHttpResource() noexcept : WinRTHttpResource(winrt::Windows::Web::Http::HttpClient{}) {} +WinRTHttpResource::WinRTHttpResource( + winrt::hstring &&defaultUserAgent, + OriginPolicy originPolicy, + std::string &&globalOrigin) noexcept + : m_refreshClientOnNetworkChange{true}, + m_defaultUserAgent{std::move(defaultUserAgent)}, + m_originPolicy{originPolicy}, + m_globalOrigin{std::move(globalOrigin)} {} + #pragma region IWinRTHttpRequestFactory IAsyncOperation WinRTHttpResource::CreateRequest( @@ -398,6 +408,49 @@ void WinRTHttpResource::UntrackResponse(int64_t requestId) noexcept { m_responses.erase(requestId); } +IHttpClient WinRTHttpResource::CreateClient() { + auto redirFilter = winrt::make(m_defaultUserAgent); + redirFilter.as()->SetRequestFactory(weak_from_this()); + + if (m_originPolicy == OriginPolicy::None) { + return winrt::Windows::Web::Http::HttpClient{redirFilter}; + } + + auto opFilter = winrt::make(std::string{m_globalOrigin}, redirFilter); + redirFilter.as()->SetRedirectSource(opFilter.as()); + return winrt::Windows::Web::Http::HttpClient{opFilter}; +} + +IHttpClient WinRTHttpResource::GetClient() { + scoped_lock lock{m_clientMutex}; + if (m_refreshClientOnNetworkChange && m_clientNeedsRefresh.exchange(false, std::memory_order_acq_rel)) { + try { + m_client = CreateClient(); + } catch (...) { + m_clientNeedsRefresh.store(true, std::memory_order_release); + throw; + } + } + + return m_client; +} + +void WinRTHttpResource::InitializeNetworkStatusMonitoring() { + { + scoped_lock lock{m_clientMutex}; + m_client = CreateClient(); + } + + m_networkStatusChangedRevoker = + NetworkInformation::NetworkStatusChanged(winrt::auto_revoke, [weakThis = weak_from_this()](auto &&) noexcept { + if (auto strongThis = weakThis.lock()) { + // HttpBaseProtocolFilter caches system proxy state. Recreate it before the next request after a network + // change. + strongThis->m_clientNeedsRefresh.store(true, std::memory_order_release); + } + }); +} + fire_and_forget WinRTHttpResource::PerformSendRequest(HttpMethod &&method, Uri &&rtUri, IInspectable const &args) noexcept { // Keep references after coroutine suspension. @@ -452,7 +505,8 @@ WinRTHttpResource::PerformSendRequest(HttpMethod &&method, Uri &&rtUri, IInspect } try { - auto sendRequestOp = self->m_client.SendRequestAsync(coRequest); + auto client = self->GetClient(); + auto sendRequestOp = client.SendRequestAsync(coRequest); auto isText = reqArgs->ResponseType == responseTypeText; @@ -649,7 +703,6 @@ void WinRTHttpResource::AddResponseHandler(shared_ptr response /*static*/ shared_ptr IHttpResource::Make(IInspectable const &inspectableProperties) noexcept { using namespace winrt::Microsoft::ReactNative; - using winrt::Windows::Web::Http::HttpClient; winrt::hstring defaultUserAgent; if (inspectableProperties) { @@ -664,23 +717,10 @@ void WinRTHttpResource::AddResponseHandler(shared_ptr response defaultUserAgent = winrt::to_hstring(userAgent); } - auto redirFilter = winrt::make(defaultUserAgent); - HttpClient client; - - if (static_cast(GetRuntimeOptionInt("Http.OriginPolicy")) == OriginPolicy::None) { - client = HttpClient{redirFilter}; - } else { - auto globalOrigin = GetRuntimeOptionString("Http.GlobalOrigin"); - auto opFilter = winrt::make(std::move(globalOrigin), redirFilter); - redirFilter.as()->SetRedirectSource(opFilter.as()); - - client = HttpClient{opFilter}; - } - - auto result = std::make_shared(std::move(client)); - - // Allow redirect filter to create requests based on the resource's state - redirFilter.as()->SetRequestFactory(weak_ptr{result}); + auto originPolicy = static_cast(GetRuntimeOptionInt("Http.OriginPolicy")); + auto globalOrigin = originPolicy == OriginPolicy::None ? std::string{} : GetRuntimeOptionString("Http.GlobalOrigin"); + auto result = std::make_shared(std::move(defaultUserAgent), originPolicy, std::move(globalOrigin)); + result->InitializeNetworkStatusMonitoring(); // Register resource as HTTP module proxy. if (inspectableProperties) { diff --git a/vnext/Shared/Networking/WinRTHttpResource.h b/vnext/Shared/Networking/WinRTHttpResource.h index 31da2e57a45..b9c8b5c315b 100644 --- a/vnext/Shared/Networking/WinRTHttpResource.h +++ b/vnext/Shared/Networking/WinRTHttpResource.h @@ -4,6 +4,7 @@ #pragma once #include "IHttpResource.h" +#include "OriginPolicy.h" #include "HttpSettings.g.h" #include @@ -11,9 +12,11 @@ #include "WinRTTypes.h" // Windows API +#include #include // Standard Library +#include #include namespace Microsoft::React::Networking { @@ -22,7 +25,18 @@ class WinRTHttpResource : public IHttpResource, public IHttpModuleProxy, public IWinRTHttpRequestFactory, public std::enable_shared_from_this { + friend struct IHttpResource; + winrt::Windows::Web::Http::IHttpClient m_client; + std::mutex m_clientMutex; + std::atomic_bool m_clientNeedsRefresh{false}; + bool m_refreshClientOnNetworkChange{false}; + winrt::hstring m_defaultUserAgent; + OriginPolicy m_originPolicy{OriginPolicy::None}; + std::string m_globalOrigin; + winrt::Windows::Networking::Connectivity::NetworkInformation::NetworkStatusChanged_revoker + m_networkStatusChangedRevoker; + std::mutex m_mutex; std::unordered_map m_responses; @@ -45,6 +59,12 @@ class WinRTHttpResource : public IHttpResource, void UntrackResponse(int64_t requestId) noexcept; + winrt::Windows::Web::Http::IHttpClient CreateClient(); + + winrt::Windows::Web::Http::IHttpClient GetClient(); + + void InitializeNetworkStatusMonitoring(); + winrt::fire_and_forget PerformSendRequest( winrt::Windows::Web::Http::HttpMethod &&method, winrt::Windows::Foundation::Uri &&uri, @@ -55,6 +75,8 @@ class WinRTHttpResource : public IHttpResource, WinRTHttpResource(winrt::Windows::Web::Http::IHttpClient &&client) noexcept; + WinRTHttpResource(winrt::hstring &&defaultUserAgent, OriginPolicy originPolicy, std::string &&globalOrigin) noexcept; + #pragma region IWinRTHttpRequestFactory winrt::Windows::Foundation::IAsyncOperation CreateRequest( @@ -84,8 +106,9 @@ class WinRTHttpResource : public IHttpResource, void SetOnRequestSuccess(std::function &&handler) noexcept override; void SetOnResponse(std::function &&handler) noexcept override; void SetOnData(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;