Skip to content

Commit

Permalink
WKWebView Proxy API sometimes needs to recreate the NSURLSession
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=259300
rdar://112164614

Reviewed by Chris Dumez.

Certain types of nw_proxy_config_t objects need to be set at NSURLSession creation time.
When one of them is in the proxy config array, recreate the NSURLSession if it exists.

* Source/WebKit/NetworkProcess/cocoa/NetworkSessionCocoa.h:
* Source/WebKit/NetworkProcess/cocoa/NetworkSessionCocoa.mm:
(WebKit::SessionWrapper::recreateSessionWithUpdatedProxyConfigurations):
(WebKit::SessionWrapper::initialize):
(WebKit::NetworkSessionCocoa::setProxyConfigData):
(WebKit::NetworkSessionCocoa::applyProxyConfigurationToSessionConfiguration):
* Source/WebKit/UIProcess/API/Cocoa/WKWebsiteDataStore.h:

Canonical link: https://commits.webkit.org/266129@main
  • Loading branch information
beidson committed Jul 18, 2023
1 parent 3f6cf3b commit f47ea0d
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 29 deletions.
4 changes: 4 additions & 0 deletions Source/WebKit/NetworkProcess/cocoa/NetworkSessionCocoa.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ class NetworkSessionCocoa;
struct SessionWrapper : public CanMakeWeakPtr<SessionWrapper> {
void initialize(NSURLSessionConfiguration *, NetworkSessionCocoa&, WebCore::StoredCredentialsPolicy, NavigatingToAppBoundDomain);

void recreateSessionWithUpdatedProxyConfigurations(NetworkSessionCocoa&);

RetainPtr<NSURLSession> session;
RetainPtr<WKNetworkSessionDelegate> delegate;
HashMap<NetworkDataTaskCocoa::TaskIdentifier, NetworkDataTaskCocoa*> dataTaskMap;
Expand Down Expand Up @@ -153,6 +155,8 @@ class NetworkSessionCocoa final : public NetworkSession {

void clearProxyConfigData() final;
void setProxyConfigData(Vector<std::pair<Vector<uint8_t>, WTF::UUID>>&&) final;

void applyProxyConfigurationToSessionConfiguration(NSURLSessionConfiguration *);
#endif

private:
Expand Down
95 changes: 67 additions & 28 deletions Source/WebKit/NetworkProcess/cocoa/NetworkSessionCocoa.mm
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,14 @@
#endif

#if HAVE(NW_PROXY_CONFIG)
#if __has_include(<Network/NSURLSession+Network.h>)
#include <Network/NSURLSession+Network.h>
#endif
SOFT_LINK_LIBRARY_OPTIONAL(libnetwork)
SOFT_LINK_OPTIONAL(libnetwork, nw_context_add_proxy, void, __cdecl, (nw_context_t, nw_proxy_config_t))
SOFT_LINK_OPTIONAL(libnetwork, nw_context_clear_proxies, void, __cdecl, (nw_context_t))
SOFT_LINK_OPTIONAL(libnetwork, nw_proxy_config_create_with_agent_data, nw_proxy_config_t, __cdecl, (const uint8_t*, size_t, const uuid_t))
SOFT_LINK_OPTIONAL(libnetwork, nw_proxy_config_stack_requires_http_protocols, bool, __cdecl, (nw_proxy_config_t))
#endif

#import "DeviceManagementSoftLink.h"
Expand Down Expand Up @@ -481,6 +485,7 @@ @interface WKNetworkSessionDelegate : NSObject <NSURLSessionDataDelegate
> {
WeakPtr<WebKit::NetworkSessionCocoa> _session;
WeakPtr<WebKit::SessionWrapper> _sessionWrapper;
@public
bool _withCredentials;
}

Expand Down Expand Up @@ -1328,6 +1333,23 @@ - (void)URLSession:(NSURLSession *)session webSocketTask:(NSURLSessionWebSocketT
ALLOW_DEPRECATED_DECLARATIONS_END
}

void SessionWrapper::recreateSessionWithUpdatedProxyConfigurations(NetworkSessionCocoa& networkSession)
{
RELEASE_ASSERT(session);
RELEASE_ASSERT(delegate);

auto withCredentials = delegate->_withCredentials;
auto *configuration = session.get().configuration;

#if HAVE(NW_PROXY_CONFIG)
networkSession.applyProxyConfigurationToSessionConfiguration(configuration);
#endif

[delegate sessionInvalidated];
delegate = adoptNS([[WKNetworkSessionDelegate alloc] initWithNetworkSession:networkSession wrapper:*this withCredentials:withCredentials]);
session = [NSURLSession sessionWithConfiguration:configuration delegate:delegate.get() delegateQueue:[NSOperationQueue mainQueue]];

This comment has been minimized.

Copy link
@achristensen07

achristensen07 Jul 21, 2023

Contributor

You should probably clear the dataTaskMap, downloadMap, and webSocketDataTaskMap when this happens.

}

void SessionWrapper::initialize(NSURLSessionConfiguration *configuration, NetworkSessionCocoa& networkSession, WebCore::StoredCredentialsPolicy storedCredentialsPolicy, NavigatingToAppBoundDomain isNavigatingToAppBoundDomain)
{
UNUSED_PARAM(isNavigatingToAppBoundDomain);
Expand All @@ -1340,24 +1362,12 @@ - (void)URLSession:(NSURLSession *)session webSocketTask:(NSURLSessionWebSocketT
if (!configuration._sourceApplicationSecondaryIdentifier && isFullBrowser)
configuration._sourceApplicationSecondaryIdentifier = @"com.apple.WebKit.InAppBrowser";

delegate = adoptNS([[WKNetworkSessionDelegate alloc] initWithNetworkSession:networkSession wrapper:*this withCredentials:storedCredentialsPolicy == WebCore::StoredCredentialsPolicy::Use]);
session = [NSURLSession sessionWithConfiguration:configuration delegate:delegate.get() delegateQueue:[NSOperationQueue mainQueue]];

#if HAVE(NW_PROXY_CONFIG)
auto* clearProxies = nw_context_clear_proxiesPtr();
auto* addProxy = nw_context_add_proxyPtr();
if (!clearProxies || !addProxy)
return;

if (auto networkContext = session.get()._networkContext) {
auto proxyConfigs = networkSession.proxyConfigs();
if (!proxyConfigs.isEmpty())
clearProxies(networkContext);

for (auto& proxyConfig : proxyConfigs)
addProxy(networkContext, proxyConfig.get());
}
networkSession.applyProxyConfigurationToSessionConfiguration(configuration);
#endif

delegate = adoptNS([[WKNetworkSessionDelegate alloc] initWithNetworkSession:networkSession wrapper:*this withCredentials:storedCredentialsPolicy == WebCore::StoredCredentialsPolicy::Use]);
session = [NSURLSession sessionWithConfiguration:configuration delegate:delegate.get() delegateQueue:[NSOperationQueue mainQueue]];
}

#if HAVE(SESSION_CLEANUP)
Expand Down Expand Up @@ -2209,34 +2219,63 @@ void didCompleteWithError(const WebCore::ResourceError& error, const WebCore::Ne
auto* clearProxies = nw_context_clear_proxiesPtr();
auto* addProxy = nw_context_add_proxyPtr();
auto* createProxyConfig = nw_proxy_config_create_with_agent_dataPtr();
if (!clearProxies || !addProxy || !createProxyConfig)
auto* requiresHTTPProtocols = nw_proxy_config_stack_requires_http_protocolsPtr();
if (!clearProxies || !addProxy || !createProxyConfig || !requiresHTTPProtocols)
return;

RetainPtr<NSMutableSet> contexts = adoptNS([[NSMutableSet alloc] init]);
forEachSessionWrapper([&contexts] (SessionWrapper& sessionWrapper) {
if (!sessionWrapper.session)
return;
[contexts.get() addObject:sessionWrapper.session.get()._networkContext];
});

for (nw_context_t context in contexts.get())
clearProxies(context);
m_nwProxyConfigs.clear();

// If any of the proxies pass the `nw_proxy_config_stack_requires_http_protocols` check,
// then we cannot set the proxy on the live nw_context_t and instead must destroy and recreate the NSURLSession
bool recreateSessions = false;
for (auto& config : proxyConfigurations) {
uuid_t identifier;
memcpy(identifier, config.second.toSpan().data(), sizeof(uuid_t));

#if __has_include(<Network/proxy_config_private.h>)
m_nwProxyConfigs.append(adoptNS(createProxyConfig(config.first.data(), config.first.size(), identifier)));
auto nwProxyConfig = adoptNS(createProxyConfig(config.first.data(), config.first.size(), identifier));

if (requiresHTTPProtocols(nwProxyConfig.get()))
recreateSessions = true;

m_nwProxyConfigs.append(WTFMove(nwProxyConfig));
#endif
}


if (recreateSessions) {
forEachSessionWrapper([this](SessionWrapper& sessionWrapper) {
if (sessionWrapper.session)
sessionWrapper.recreateSessionWithUpdatedProxyConfigurations(*this);
});
return;
}

RetainPtr<NSMutableSet> contexts = adoptNS([[NSMutableSet alloc] init]);
forEachSessionWrapper([&contexts] (SessionWrapper& sessionWrapper) {
if (!sessionWrapper.session)
return;
[contexts.get() addObject:sessionWrapper.session.get()._networkContext];
});

for (nw_context_t context in contexts.get()) {
clearProxies(context);

for (auto& proxyConfig : m_nwProxyConfigs)
addProxy(context, proxyConfig.get());
}
}

void NetworkSessionCocoa::applyProxyConfigurationToSessionConfiguration(NSURLSessionConfiguration *configuration)
{
if (!m_nwProxyConfigs.isEmpty()) {
RetainPtr nwProxyConfigurations = adoptNS([[NSMutableArray alloc] initWithCapacity:m_nwProxyConfigs.size()]);
for (auto& proxyConfig : m_nwProxyConfigs)
[nwProxyConfigurations addObject:proxyConfig.get()];

configuration.proxyConfigurations = nwProxyConfigurations.get();
} else
configuration.proxyConfigurations = @[ ];
}
#endif // HAVE(NW_PROXY_CONFIG)

#if USE(APPLE_INTERNAL_SDK)
Expand Down
5 changes: 4 additions & 1 deletion Source/WebKit/UIProcess/API/Cocoa/WKWebsiteDataStore.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,10 @@ WK_CLASS_AVAILABLE(macos(10.11), ios(9.0))
|| ((TARGET_OS_IOS || TARGET_OS_MACCATALYST) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 170000) \
|| (TARGET_OS_WATCH && __WATCH_OS_VERSION_MAX_ALLOWED >= 100000) \
|| (TARGET_OS_TV && __TV_OS_VERSION_MAX_ALLOWED >= 170000))
/*! @abstract Gets or sets the proxy configurations to be used to override networking in all WKWebViews that use this WKWebsiteDataStore. */
/*! @abstract Gets or sets the proxy configurations to be used to override networking in all WKWebViews that use this WKWebsiteDataStore.
@discussion Changing the proxy configurations might interupt current networking operations in any WKWebView that use this WKWebsiteDataStore,
so it is encouraged to finish setting the proxy configurations before starting any page loads.
*/
@property (nullable, nonatomic, copy) NSArray<nw_proxy_config_t> *proxyConfigurations NS_REFINED_FOR_SWIFT API_AVAILABLE(macos(14.0), ios(17.0));
#endif

Expand Down

0 comments on commit f47ea0d

Please sign in to comment.