Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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"
}
78 changes: 59 additions & 19 deletions vnext/Shared/Networking/WinRTHttpResource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<HttpRequestMessage> WinRTHttpResource::CreateRequest(
Expand Down Expand Up @@ -398,6 +408,49 @@ void WinRTHttpResource::UntrackResponse(int64_t requestId) noexcept {
m_responses.erase(requestId);
}

IHttpClient WinRTHttpResource::CreateClient() {
auto redirFilter = winrt::make<RedirectHttpFilter>(m_defaultUserAgent);
redirFilter.as<RedirectHttpFilter>()->SetRequestFactory(weak_from_this());

if (m_originPolicy == OriginPolicy::None) {
return winrt::Windows::Web::Http::HttpClient{redirFilter};
}

auto opFilter = winrt::make<OriginPolicyHttpFilter>(std::string{m_globalOrigin}, redirFilter);
redirFilter.as<RedirectHttpFilter>()->SetRedirectSource(opFilter.as<IRedirectEventSource>());
Comment on lines +419 to +420
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);
Comment on lines +424 to +430
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);
}
});
}
Comment on lines +438 to +452

fire_and_forget
WinRTHttpResource::PerformSendRequest(HttpMethod &&method, Uri &&rtUri, IInspectable const &args) noexcept {
// Keep references after coroutine suspension.
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -649,7 +703,6 @@ void WinRTHttpResource::AddResponseHandler(shared_ptr<IResponseHandler> response

/*static*/ shared_ptr<IHttpResource> IHttpResource::Make(IInspectable const &inspectableProperties) noexcept {
using namespace winrt::Microsoft::ReactNative;
using winrt::Windows::Web::Http::HttpClient;

winrt::hstring defaultUserAgent;
if (inspectableProperties) {
Expand All @@ -664,23 +717,10 @@ void WinRTHttpResource::AddResponseHandler(shared_ptr<IResponseHandler> response
defaultUserAgent = winrt::to_hstring(userAgent);
}

auto redirFilter = winrt::make<RedirectHttpFilter>(defaultUserAgent);
HttpClient client;

if (static_cast<OriginPolicy>(GetRuntimeOptionInt("Http.OriginPolicy")) == OriginPolicy::None) {
client = HttpClient{redirFilter};
} else {
auto globalOrigin = GetRuntimeOptionString("Http.GlobalOrigin");
auto opFilter = winrt::make<OriginPolicyHttpFilter>(std::move(globalOrigin), redirFilter);
redirFilter.as<RedirectHttpFilter>()->SetRedirectSource(opFilter.as<IRedirectEventSource>());

client = HttpClient{opFilter};
}

auto result = std::make_shared<WinRTHttpResource>(std::move(client));

// Allow redirect filter to create requests based on the resource's state
redirFilter.as<RedirectHttpFilter>()->SetRequestFactory(weak_ptr<IWinRTHttpRequestFactory>{result});
auto originPolicy = static_cast<OriginPolicy>(GetRuntimeOptionInt("Http.OriginPolicy"));
auto globalOrigin = originPolicy == OriginPolicy::None ? std::string{} : GetRuntimeOptionString("Http.GlobalOrigin");
auto result = std::make_shared<WinRTHttpResource>(std::move(defaultUserAgent), originPolicy, std::move(globalOrigin));
result->InitializeNetworkStatusMonitoring();

// Register resource as HTTP module proxy.
if (inspectableProperties) {
Expand Down
27 changes: 25 additions & 2 deletions vnext/Shared/Networking/WinRTHttpResource.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,19 @@
#pragma once

#include "IHttpResource.h"
#include "OriginPolicy.h"

#include "HttpSettings.g.h"
#include <Modules/IHttpModuleProxy.h>
#include "IWinRTHttpRequestFactory.h"
#include "WinRTTypes.h"

// Windows API
#include <winrt/Windows.Networking.Connectivity.h>
#include <winrt/Windows.Web.Http.h>

// Standard Library
#include <atomic>
#include <mutex>

namespace Microsoft::React::Networking {
Expand All @@ -22,7 +25,18 @@ class WinRTHttpResource : public IHttpResource,
public IHttpModuleProxy,
public IWinRTHttpRequestFactory,
public std::enable_shared_from_this<WinRTHttpResource> {
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<int64_t, ResponseOperation> m_responses;

Expand All @@ -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,
Expand All @@ -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<winrt::Windows::Web::Http::HttpRequestMessage> CreateRequest(
Expand Down Expand Up @@ -84,8 +106,9 @@ class WinRTHttpResource : public IHttpResource,
void SetOnRequestSuccess(std::function<void(int64_t requestId)> &&handler) noexcept override;
void SetOnResponse(std::function<void(int64_t requestId, Response &&response)> &&handler) noexcept override;
void SetOnData(std::function<void(int64_t requestId, std::string &&responseData)> &&handler) noexcept override;
void SetOnData(std::function<void(int64_t requestId, winrt::Microsoft::ReactNative::JSValueObject &&responseData)>
&&handler) noexcept override;
void SetOnData(
std::function<void(int64_t requestId, winrt::Microsoft::ReactNative::JSValueObject &&responseData)>
&&handler) noexcept override;
void SetOnIncrementalData(
std::function<void(int64_t requestId, std::string &&responseData, int64_t progress, int64_t total)>
&&handler) noexcept override;
Expand Down
Loading