blob: 0a3c1b81902ce2a82a96a08aea42aa911789c1ef [file] [log] [blame]
/*
* Copyright (C) 2015-2021 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "config.h"
#import "NetworkSessionCocoa.h"
#import "AuthenticationChallengeDisposition.h"
#import "AuthenticationManager.h"
#import "DataReference.h"
#import "DefaultWebBrowserChecks.h"
#import "Download.h"
#import "LegacyCustomProtocolManager.h"
#import "Logging.h"
#import "NetworkLoad.h"
#import "NetworkProcess.h"
#import "NetworkSessionCreationParameters.h"
#import "WebPageNetworkParameters.h"
#import "WebSocketTask.h"
#import <Foundation/NSURLSession.h>
#import <WebCore/Credential.h>
#import <WebCore/FormDataStreamMac.h>
#import <WebCore/FrameLoaderTypes.h>
#import <WebCore/NetworkStorageSession.h>
#import <WebCore/NotImplemented.h>
#import <WebCore/ResourceError.h>
#import <WebCore/ResourceRequest.h>
#import <WebCore/ResourceResponse.h>
#import <WebCore/SharedBuffer.h>
#import <WebCore/WebCoreURLResponse.h>
#import <pal/spi/cf/CFNetworkSPI.h>
#import <wtf/BlockPtr.h>
#import <wtf/MainThread.h>
#import <wtf/NakedRef.h>
#import <wtf/NeverDestroyed.h>
#import <wtf/ObjCRuntimeExtras.h>
#import <wtf/ProcessPrivilege.h>
#import <wtf/SoftLinking.h>
#import <wtf/URL.h>
#import <wtf/WeakObjCPtr.h>
#import <wtf/cocoa/RuntimeApplicationChecksCocoa.h>
#import <wtf/cocoa/TypeCastsCocoa.h>
#import <wtf/cocoa/VectorCocoa.h>
#import <wtf/darwin/WeakLinking.h>
#import <wtf/text/WTFString.h>
#if USE(APPLE_INTERNAL_SDK)
#if ENABLE(APP_PRIVACY_REPORT) && HAVE(SYMPTOMS_FRAMEWORK)
#import <Symptoms/SymptomAnalytics.h>
#import <Symptoms/SymptomPresentationFeed.h>
SOFT_LINK_PRIVATE_FRAMEWORK_IN_UMBRELLA_OPTIONAL(Symptoms, SymptomAnalytics);
SOFT_LINK_PRIVATE_FRAMEWORK_IN_UMBRELLA_OPTIONAL(Symptoms, SymptomPresentationFeed);
SOFT_LINK_PRIVATE_FRAMEWORK_IN_UMBRELLA_OPTIONAL(Symptoms, SymptomPresentationLite);
SOFT_LINK_CLASS_OPTIONAL(SymptomAnalytics, AnalyticsWorkspace);
SOFT_LINK_CLASS_OPTIONAL(SymptomPresentationFeed, UsageFeed);
SOFT_LINK_CONSTANT_MAY_FAIL(SymptomPresentationFeed, kSymptomAnalyticsServiceDomainTrackingClearHistoryBundleIDs, const NSString *);
SOFT_LINK_CONSTANT_MAY_FAIL(SymptomPresentationFeed, kSymptomAnalyticsServiceDomainTrackingClearHistoryStartDate, const NSString *);
SOFT_LINK_CONSTANT_MAY_FAIL(SymptomPresentationFeed, kSymptomAnalyticsServiceDomainTrackingClearHistoryEndDate, const NSString *);
SOFT_LINK_CONSTANT_MAY_FAIL(SymptomPresentationFeed, kSymptomAnalyticsServiceDomainTrackingClearHistoryKey, const NSString *);
SOFT_LINK_CONSTANT_MAY_FAIL(SymptomPresentationLite, kSymptomAnalyticsServiceEndpoint, NSString *)
#endif
#else
void WebKit::NetworkSessionCocoa::removeNetworkWebsiteData(std::optional<WallTime>, std::optional<HashSet<WebCore::RegistrableDomain>>&&, CompletionHandler<void()>&& completionHandler) { completionHandler(); }
#endif
#import "DeviceManagementSoftLink.h"
// FIXME: Remove this soft link once rdar://problem/50109631 is in a build and bots are updated.
SOFT_LINK_FRAMEWORK(CFNetwork)
SOFT_LINK_CLASS_OPTIONAL(CFNetwork, _NSHSTSStorage)
using namespace WebKit;
CFStringRef const WebKit2HTTPProxyDefaultsKey = static_cast<CFStringRef>(@"WebKit2HTTPProxy");
CFStringRef const WebKit2HTTPSProxyDefaultsKey = static_cast<CFStringRef>(@"WebKit2HTTPSProxy");
constexpr unsigned maxNumberOfIsolatedSessions { 10 };
static NSURLSessionResponseDisposition toNSURLSessionResponseDisposition(WebCore::PolicyAction disposition)
{
switch (disposition) {
case WebCore::PolicyAction::StopAllLoads:
ASSERT_NOT_REACHED();
#if !ASSERT_ENABLED
FALLTHROUGH;
#endif
case WebCore::PolicyAction::Ignore:
return NSURLSessionResponseCancel;
case WebCore::PolicyAction::Use:
return NSURLSessionResponseAllow;
case WebCore::PolicyAction::Download:
return NSURLSessionResponseBecomeDownload;
}
}
static NSURLSessionAuthChallengeDisposition toNSURLSessionAuthChallengeDisposition(WebKit::AuthenticationChallengeDisposition disposition)
{
switch (disposition) {
case WebKit::AuthenticationChallengeDisposition::UseCredential:
return NSURLSessionAuthChallengeUseCredential;
case WebKit::AuthenticationChallengeDisposition::PerformDefaultHandling:
return NSURLSessionAuthChallengePerformDefaultHandling;
case WebKit::AuthenticationChallengeDisposition::Cancel:
return NSURLSessionAuthChallengeCancelAuthenticationChallenge;
case WebKit::AuthenticationChallengeDisposition::RejectProtectionSpaceAndContinue:
return NSURLSessionAuthChallengeRejectProtectionSpace;
}
}
static WebCore::NetworkLoadPriority toNetworkLoadPriority(float priority)
{
if (priority <= NSURLSessionTaskPriorityLow)
return WebCore::NetworkLoadPriority::Low;
if (priority >= NSURLSessionTaskPriorityHigh)
return WebCore::NetworkLoadPriority::High;
return WebCore::NetworkLoadPriority::Medium;
}
#if HAVE(NETWORK_CONNECTION_PRIVACY_STANCE)
static WebCore::PrivacyStance toPrivacyStance(nw_connection_privacy_stance_t stance)
{
switch (stance) {
case nw_connection_privacy_stance_unknown:
return WebCore::PrivacyStance::Unknown;
case nw_connection_privacy_stance_not_eligible:
return WebCore::PrivacyStance::NotEligible;
case nw_connection_privacy_stance_proxied:
return WebCore::PrivacyStance::Proxied;
case nw_connection_privacy_stance_failed:
return WebCore::PrivacyStance::Failed;
case nw_connection_privacy_stance_direct:
return WebCore::PrivacyStance::Direct;
}
ASSERT_NOT_REACHED();
return WebCore::PrivacyStance::Unknown;
}
#endif
#if HAVE(CFNETWORK_METRICS_APIS_V4)
static String stringForTLSProtocolVersion(tls_protocol_version_t protocol)
{
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
switch (protocol) {
case tls_protocol_version_TLSv10:
return "TLS 1.0"_s;
case tls_protocol_version_TLSv11:
return "TLS 1.1"_s;
case tls_protocol_version_TLSv12:
return "TLS 1.2"_s;
case tls_protocol_version_TLSv13:
return "TLS 1.3"_s;
case tls_protocol_version_DTLSv10:
return "DTLS 1.0"_s;
case tls_protocol_version_DTLSv12:
return "DTLS 1.2"_s;
}
ALLOW_DEPRECATED_DECLARATIONS_END
return { };
}
static String stringForTLSCipherSuite(tls_ciphersuite_t suite)
{
#define STRINGIFY_CIPHER(cipher) \
case tls_ciphersuite_##cipher: \
return "" #cipher ""_s
switch (suite) {
STRINGIFY_CIPHER(RSA_WITH_3DES_EDE_CBC_SHA);
STRINGIFY_CIPHER(RSA_WITH_AES_128_CBC_SHA);
STRINGIFY_CIPHER(RSA_WITH_AES_256_CBC_SHA);
STRINGIFY_CIPHER(RSA_WITH_AES_128_GCM_SHA256);
STRINGIFY_CIPHER(RSA_WITH_AES_256_GCM_SHA384);
STRINGIFY_CIPHER(RSA_WITH_AES_128_CBC_SHA256);
STRINGIFY_CIPHER(RSA_WITH_AES_256_CBC_SHA256);
STRINGIFY_CIPHER(ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA);
STRINGIFY_CIPHER(ECDHE_ECDSA_WITH_AES_128_CBC_SHA);
STRINGIFY_CIPHER(ECDHE_ECDSA_WITH_AES_256_CBC_SHA);
STRINGIFY_CIPHER(ECDHE_RSA_WITH_3DES_EDE_CBC_SHA);
STRINGIFY_CIPHER(ECDHE_RSA_WITH_AES_128_CBC_SHA);
STRINGIFY_CIPHER(ECDHE_RSA_WITH_AES_256_CBC_SHA);
STRINGIFY_CIPHER(ECDHE_ECDSA_WITH_AES_128_CBC_SHA256);
STRINGIFY_CIPHER(ECDHE_ECDSA_WITH_AES_256_CBC_SHA384);
STRINGIFY_CIPHER(ECDHE_RSA_WITH_AES_128_CBC_SHA256);
STRINGIFY_CIPHER(ECDHE_RSA_WITH_AES_256_CBC_SHA384);
STRINGIFY_CIPHER(ECDHE_ECDSA_WITH_AES_128_GCM_SHA256);
STRINGIFY_CIPHER(ECDHE_ECDSA_WITH_AES_256_GCM_SHA384);
STRINGIFY_CIPHER(ECDHE_RSA_WITH_AES_128_GCM_SHA256);
STRINGIFY_CIPHER(ECDHE_RSA_WITH_AES_256_GCM_SHA384);
STRINGIFY_CIPHER(ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256);
STRINGIFY_CIPHER(ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256);
STRINGIFY_CIPHER(AES_128_GCM_SHA256);
STRINGIFY_CIPHER(AES_256_GCM_SHA384);
STRINGIFY_CIPHER(CHACHA20_POLY1305_SHA256);
}
return { };
#undef STRINGIFY_CIPHER
}
#else // HAVE(CFNETWORK_METRICS_APIS_V4)
static String stringForSSLProtocol(SSLProtocol protocol)
{
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
switch (protocol) {
case kDTLSProtocol1:
return "DTLS 1.0"_s;
case kSSLProtocol2:
return "SSL 2.0"_s;
case kSSLProtocol3:
return "SSL 3.0"_s;
case kSSLProtocol3Only:
return "SSL 3.0 (Only)"_s;
case kTLSProtocol1:
return "TLS 1.0"_s;
case kTLSProtocol1Only:
return "TLS 1.0 (Only)"_s;
case kTLSProtocol11:
return "TLS 1.1"_s;
case kTLSProtocol12:
return "TLS 1.2"_s;
case kTLSProtocol13:
return "TLS 1.3"_s;
case kSSLProtocolAll:
return "All";
case kSSLProtocolUnknown:
return "Unknown";
case kTLSProtocolMaxSupported:
default:
ASSERT_NOT_REACHED();
return emptyString();
}
ALLOW_DEPRECATED_DECLARATIONS_END
}
static String stringForSSLCipher(SSLCipherSuite cipher)
{
#define STRINGIFY_CIPHER(cipher) \
case cipher: \
return "" #cipher ""_s
switch (cipher) {
STRINGIFY_CIPHER(SSL_RSA_EXPORT_WITH_RC4_40_MD5);
STRINGIFY_CIPHER(SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5);
STRINGIFY_CIPHER(SSL_RSA_WITH_IDEA_CBC_SHA);
STRINGIFY_CIPHER(SSL_RSA_EXPORT_WITH_DES40_CBC_SHA);
STRINGIFY_CIPHER(SSL_RSA_WITH_DES_CBC_SHA);
STRINGIFY_CIPHER(SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA);
STRINGIFY_CIPHER(SSL_DH_DSS_WITH_DES_CBC_SHA);
STRINGIFY_CIPHER(SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA);
STRINGIFY_CIPHER(SSL_DH_RSA_WITH_DES_CBC_SHA);
STRINGIFY_CIPHER(SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA);
STRINGIFY_CIPHER(SSL_DHE_DSS_WITH_DES_CBC_SHA);
STRINGIFY_CIPHER(SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA);
STRINGIFY_CIPHER(SSL_DHE_RSA_WITH_DES_CBC_SHA);
STRINGIFY_CIPHER(SSL_DH_anon_EXPORT_WITH_RC4_40_MD5);
STRINGIFY_CIPHER(SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA);
STRINGIFY_CIPHER(SSL_DH_anon_WITH_DES_CBC_SHA);
STRINGIFY_CIPHER(SSL_FORTEZZA_DMS_WITH_NULL_SHA);
STRINGIFY_CIPHER(SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA);
STRINGIFY_CIPHER(TLS_RSA_WITH_AES_128_CBC_SHA);
STRINGIFY_CIPHER(TLS_DH_DSS_WITH_AES_128_CBC_SHA);
STRINGIFY_CIPHER(TLS_DH_RSA_WITH_AES_128_CBC_SHA);
STRINGIFY_CIPHER(TLS_DHE_DSS_WITH_AES_128_CBC_SHA);
STRINGIFY_CIPHER(TLS_DHE_RSA_WITH_AES_128_CBC_SHA);
STRINGIFY_CIPHER(TLS_DH_anon_WITH_AES_128_CBC_SHA);
STRINGIFY_CIPHER(TLS_RSA_WITH_AES_256_CBC_SHA);
STRINGIFY_CIPHER(TLS_DH_DSS_WITH_AES_256_CBC_SHA);
STRINGIFY_CIPHER(TLS_DH_RSA_WITH_AES_256_CBC_SHA);
STRINGIFY_CIPHER(TLS_DHE_DSS_WITH_AES_256_CBC_SHA);
STRINGIFY_CIPHER(TLS_DHE_RSA_WITH_AES_256_CBC_SHA);
STRINGIFY_CIPHER(TLS_DH_anon_WITH_AES_256_CBC_SHA);
STRINGIFY_CIPHER(TLS_ECDH_ECDSA_WITH_NULL_SHA);
STRINGIFY_CIPHER(TLS_ECDH_ECDSA_WITH_RC4_128_SHA);
STRINGIFY_CIPHER(TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA);
STRINGIFY_CIPHER(TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA);
STRINGIFY_CIPHER(TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA);
STRINGIFY_CIPHER(TLS_ECDHE_ECDSA_WITH_NULL_SHA);
STRINGIFY_CIPHER(TLS_ECDHE_ECDSA_WITH_RC4_128_SHA);
STRINGIFY_CIPHER(TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA);
STRINGIFY_CIPHER(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA);
STRINGIFY_CIPHER(TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA);
STRINGIFY_CIPHER(TLS_ECDH_RSA_WITH_NULL_SHA);
STRINGIFY_CIPHER(TLS_ECDH_RSA_WITH_RC4_128_SHA);
STRINGIFY_CIPHER(TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA);
STRINGIFY_CIPHER(TLS_ECDH_RSA_WITH_AES_128_CBC_SHA);
STRINGIFY_CIPHER(TLS_ECDH_RSA_WITH_AES_256_CBC_SHA);
STRINGIFY_CIPHER(TLS_ECDHE_RSA_WITH_NULL_SHA);
STRINGIFY_CIPHER(TLS_ECDHE_RSA_WITH_RC4_128_SHA);
STRINGIFY_CIPHER(TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA);
STRINGIFY_CIPHER(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA);
STRINGIFY_CIPHER(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA);
STRINGIFY_CIPHER(TLS_ECDH_anon_WITH_NULL_SHA);
STRINGIFY_CIPHER(TLS_ECDH_anon_WITH_RC4_128_SHA);
STRINGIFY_CIPHER(TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA);
STRINGIFY_CIPHER(TLS_ECDH_anon_WITH_AES_128_CBC_SHA);
STRINGIFY_CIPHER(TLS_ECDH_anon_WITH_AES_256_CBC_SHA);
// STRINGIFY_CIPHER(SSL_NULL_WITH_NULL_NULL);
STRINGIFY_CIPHER(TLS_NULL_WITH_NULL_NULL);
// STRINGIFY_CIPHER(SSL_RSA_WITH_NULL_MD5);
STRINGIFY_CIPHER(TLS_RSA_WITH_NULL_MD5);
// STRINGIFY_CIPHER(SSL_RSA_WITH_NULL_SHA);
STRINGIFY_CIPHER(TLS_RSA_WITH_NULL_SHA);
// STRINGIFY_CIPHER(SSL_RSA_WITH_RC4_128_MD5);
STRINGIFY_CIPHER(TLS_RSA_WITH_RC4_128_MD5);
// STRINGIFY_CIPHER(SSL_RSA_WITH_RC4_128_SHA);
STRINGIFY_CIPHER(TLS_RSA_WITH_RC4_128_SHA);
// STRINGIFY_CIPHER(SSL_RSA_WITH_3DES_EDE_CBC_SHA);
STRINGIFY_CIPHER(TLS_RSA_WITH_3DES_EDE_CBC_SHA);
STRINGIFY_CIPHER(TLS_RSA_WITH_NULL_SHA256);
STRINGIFY_CIPHER(TLS_RSA_WITH_AES_128_CBC_SHA256);
STRINGIFY_CIPHER(TLS_RSA_WITH_AES_256_CBC_SHA256);
// STRINGIFY_CIPHER(SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA);
STRINGIFY_CIPHER(TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA);
// STRINGIFY_CIPHER(SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA);
STRINGIFY_CIPHER(TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA);
// STRINGIFY_CIPHER(SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA);
STRINGIFY_CIPHER(TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA);
// STRINGIFY_CIPHER(SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA);
STRINGIFY_CIPHER(TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA);
STRINGIFY_CIPHER(TLS_DH_DSS_WITH_AES_128_CBC_SHA256);
STRINGIFY_CIPHER(TLS_DH_RSA_WITH_AES_128_CBC_SHA256);
STRINGIFY_CIPHER(TLS_DHE_DSS_WITH_AES_128_CBC_SHA256);
STRINGIFY_CIPHER(TLS_DHE_RSA_WITH_AES_128_CBC_SHA256);
STRINGIFY_CIPHER(TLS_DH_DSS_WITH_AES_256_CBC_SHA256);
STRINGIFY_CIPHER(TLS_DH_RSA_WITH_AES_256_CBC_SHA256);
STRINGIFY_CIPHER(TLS_DHE_DSS_WITH_AES_256_CBC_SHA256);
STRINGIFY_CIPHER(TLS_DHE_RSA_WITH_AES_256_CBC_SHA256);
// STRINGIFY_CIPHER(SSL_DH_anon_WITH_RC4_128_MD5);
STRINGIFY_CIPHER(TLS_DH_anon_WITH_RC4_128_MD5);
// STRINGIFY_CIPHER(SSL_DH_anon_WITH_3DES_EDE_CBC_SHA);
STRINGIFY_CIPHER(TLS_DH_anon_WITH_3DES_EDE_CBC_SHA);
STRINGIFY_CIPHER(TLS_DH_anon_WITH_AES_128_CBC_SHA256);
STRINGIFY_CIPHER(TLS_DH_anon_WITH_AES_256_CBC_SHA256);
STRINGIFY_CIPHER(TLS_PSK_WITH_RC4_128_SHA);
STRINGIFY_CIPHER(TLS_PSK_WITH_3DES_EDE_CBC_SHA);
STRINGIFY_CIPHER(TLS_PSK_WITH_AES_128_CBC_SHA);
STRINGIFY_CIPHER(TLS_PSK_WITH_AES_256_CBC_SHA);
STRINGIFY_CIPHER(TLS_DHE_PSK_WITH_RC4_128_SHA);
STRINGIFY_CIPHER(TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA);
STRINGIFY_CIPHER(TLS_DHE_PSK_WITH_AES_128_CBC_SHA);
STRINGIFY_CIPHER(TLS_DHE_PSK_WITH_AES_256_CBC_SHA);
STRINGIFY_CIPHER(TLS_RSA_PSK_WITH_RC4_128_SHA);
STRINGIFY_CIPHER(TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA);
STRINGIFY_CIPHER(TLS_RSA_PSK_WITH_AES_128_CBC_SHA);
STRINGIFY_CIPHER(TLS_RSA_PSK_WITH_AES_256_CBC_SHA);
STRINGIFY_CIPHER(TLS_PSK_WITH_NULL_SHA);
STRINGIFY_CIPHER(TLS_DHE_PSK_WITH_NULL_SHA);
STRINGIFY_CIPHER(TLS_RSA_PSK_WITH_NULL_SHA);
STRINGIFY_CIPHER(TLS_RSA_WITH_AES_128_GCM_SHA256);
STRINGIFY_CIPHER(TLS_RSA_WITH_AES_256_GCM_SHA384);
STRINGIFY_CIPHER(TLS_DHE_RSA_WITH_AES_128_GCM_SHA256);
STRINGIFY_CIPHER(TLS_DHE_RSA_WITH_AES_256_GCM_SHA384);
STRINGIFY_CIPHER(TLS_DH_RSA_WITH_AES_128_GCM_SHA256);
STRINGIFY_CIPHER(TLS_DH_RSA_WITH_AES_256_GCM_SHA384);
STRINGIFY_CIPHER(TLS_DHE_DSS_WITH_AES_128_GCM_SHA256);
STRINGIFY_CIPHER(TLS_DHE_DSS_WITH_AES_256_GCM_SHA384);
STRINGIFY_CIPHER(TLS_DH_DSS_WITH_AES_128_GCM_SHA256);
STRINGIFY_CIPHER(TLS_DH_DSS_WITH_AES_256_GCM_SHA384);
STRINGIFY_CIPHER(TLS_DH_anon_WITH_AES_128_GCM_SHA256);
STRINGIFY_CIPHER(TLS_DH_anon_WITH_AES_256_GCM_SHA384);
STRINGIFY_CIPHER(TLS_PSK_WITH_AES_128_GCM_SHA256);
STRINGIFY_CIPHER(TLS_PSK_WITH_AES_256_GCM_SHA384);
STRINGIFY_CIPHER(TLS_DHE_PSK_WITH_AES_128_GCM_SHA256);
STRINGIFY_CIPHER(TLS_DHE_PSK_WITH_AES_256_GCM_SHA384);
STRINGIFY_CIPHER(TLS_RSA_PSK_WITH_AES_128_GCM_SHA256);
STRINGIFY_CIPHER(TLS_RSA_PSK_WITH_AES_256_GCM_SHA384);
STRINGIFY_CIPHER(TLS_PSK_WITH_AES_128_CBC_SHA256);
STRINGIFY_CIPHER(TLS_PSK_WITH_AES_256_CBC_SHA384);
STRINGIFY_CIPHER(TLS_PSK_WITH_NULL_SHA256);
STRINGIFY_CIPHER(TLS_PSK_WITH_NULL_SHA384);
STRINGIFY_CIPHER(TLS_DHE_PSK_WITH_AES_128_CBC_SHA256);
STRINGIFY_CIPHER(TLS_DHE_PSK_WITH_AES_256_CBC_SHA384);
STRINGIFY_CIPHER(TLS_DHE_PSK_WITH_NULL_SHA256);
STRINGIFY_CIPHER(TLS_DHE_PSK_WITH_NULL_SHA384);
STRINGIFY_CIPHER(TLS_RSA_PSK_WITH_AES_128_CBC_SHA256);
STRINGIFY_CIPHER(TLS_RSA_PSK_WITH_AES_256_CBC_SHA384);
STRINGIFY_CIPHER(TLS_RSA_PSK_WITH_NULL_SHA256);
STRINGIFY_CIPHER(TLS_RSA_PSK_WITH_NULL_SHA384);
STRINGIFY_CIPHER(TLS_AES_128_GCM_SHA256);
STRINGIFY_CIPHER(TLS_AES_256_GCM_SHA384);
STRINGIFY_CIPHER(TLS_CHACHA20_POLY1305_SHA256);
STRINGIFY_CIPHER(TLS_AES_128_CCM_SHA256);
STRINGIFY_CIPHER(TLS_AES_128_CCM_8_SHA256);
STRINGIFY_CIPHER(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256);
STRINGIFY_CIPHER(TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384);
STRINGIFY_CIPHER(TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256);
STRINGIFY_CIPHER(TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384);
STRINGIFY_CIPHER(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256);
STRINGIFY_CIPHER(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384);
STRINGIFY_CIPHER(TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256);
STRINGIFY_CIPHER(TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384);
STRINGIFY_CIPHER(TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256);
STRINGIFY_CIPHER(TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384);
STRINGIFY_CIPHER(TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256);
STRINGIFY_CIPHER(TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384);
STRINGIFY_CIPHER(TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256);
STRINGIFY_CIPHER(TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384);
STRINGIFY_CIPHER(TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256);
STRINGIFY_CIPHER(TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384);
STRINGIFY_CIPHER(TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256);
STRINGIFY_CIPHER(TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256);
STRINGIFY_CIPHER(TLS_EMPTY_RENEGOTIATION_INFO_SCSV);
STRINGIFY_CIPHER(SSL_RSA_WITH_RC2_CBC_MD5);
STRINGIFY_CIPHER(SSL_RSA_WITH_IDEA_CBC_MD5);
STRINGIFY_CIPHER(SSL_RSA_WITH_DES_CBC_MD5);
STRINGIFY_CIPHER(SSL_RSA_WITH_3DES_EDE_CBC_MD5);
STRINGIFY_CIPHER(SSL_NO_SUCH_CIPHERSUITE);
default:
ASSERT_NOT_REACHED();
return emptyString();
}
#undef STRINGIFY_CIPHER
}
#endif // HAVE(CFNETWORK_METRICS_APIS_V4)
@interface WKNetworkSessionDelegate : NSObject <NSURLSessionDataDelegate
#if HAVE(NSURLSESSION_WEBSOCKET)
, NSURLSessionWebSocketDelegate
#endif
> {
WeakPtr<WebKit::NetworkSessionCocoa> _session;
WeakPtr<WebKit::SessionWrapper> _sessionWrapper;
bool _withCredentials;
}
- (id)initWithNetworkSession:(NakedRef<WebKit::NetworkSessionCocoa>)session wrapper:(WebKit::SessionWrapper&)sessionWrapper withCredentials:(bool)withCredentials;
- (void)sessionInvalidated;
@end
@implementation WKNetworkSessionDelegate
- (id)initWithNetworkSession:(NakedRef<WebKit::NetworkSessionCocoa>)session wrapper:(WebKit::SessionWrapper&)sessionWrapper withCredentials:(bool)withCredentials
{
self = [super init];
if (!self)
return nil;
_session = session.get();
_sessionWrapper = sessionWrapper;
_withCredentials = withCredentials;
return self;
}
- (void)sessionInvalidated
{
_sessionWrapper = nullptr;
}
- (NetworkDataTaskCocoa*)existingTask:(NSURLSessionTask *)task
{
if (!_sessionWrapper)
return nullptr;
if (!task)
return nullptr;
return _sessionWrapper->dataTaskMap.get(task.taskIdentifier);
}
- (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(NSError *)error
{
ASSERT(!_sessionWrapper);
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
{
if (auto* networkDataTask = [self existingTask:task])
networkDataTask->didSendData(totalBytesSent, totalBytesExpectedToSend);
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task needNewBodyStream:(void (^)(NSInputStream *bodyStream))completionHandler
{
auto* networkDataTask = [self existingTask:task];
if (!networkDataTask) {
completionHandler(nil);
return;
}
auto* body = networkDataTask->firstRequest().httpBody();
if (!body) {
completionHandler(nil);
return;
}
completionHandler(WebCore::createHTTPBodyNSInputStream(*body).get());
}
#if ENABLE(INTELLIGENT_TRACKING_PREVENTION)
static NSURLRequest* downgradeRequest(NSURLRequest *request)
{
auto nsMutableRequest = adoptNS([request mutableCopy]);
if ([[nsMutableRequest URL].scheme isEqualToString:@"https"]) {
NSURLComponents *components = [NSURLComponents componentsWithURL:[nsMutableRequest URL] resolvingAgainstBaseURL:NO];
components.scheme = @"http";
[nsMutableRequest setURL:components.URL];
ASSERT([[nsMutableRequest URL].scheme isEqualToString:@"http"]);
return nsMutableRequest.autorelease();
}
ASSERT_NOT_REACHED();
return request;
}
static bool schemeWasUpgradedDueToDynamicHSTS(NSURLRequest *request)
{
return [request respondsToSelector:@selector(_schemeWasUpgradedDueToDynamicHSTS)]
&& [request _schemeWasUpgradedDueToDynamicHSTS];
}
#endif
static void setIgnoreHSTS(NSMutableURLRequest *request, bool ignoreHSTS)
{
if ([request respondsToSelector:@selector(_setIgnoreHSTS:)])
[request _setIgnoreHSTS:ignoreHSTS];
}
static void setIgnoreHSTS(RetainPtr<NSURLRequest>& request, bool shouldIgnoreHSTS)
{
auto mutableRequest = adoptNS([request mutableCopy]);
setIgnoreHSTS(mutableRequest.get(), shouldIgnoreHSTS);
request = mutableRequest;
}
static bool ignoreHSTS(NSURLRequest *request)
{
return [request respondsToSelector:@selector(_ignoreHSTS)]
&& [request _ignoreHSTS];
}
static void updateIgnoreStrictTransportSecuritySetting(RetainPtr<NSURLRequest>& request, bool shouldIgnoreHSTS)
{
auto scheme = request.get().URL.scheme;
if ([scheme isEqualToString:@"https"]) {
if (shouldIgnoreHSTS && ignoreHSTS(request.get())) {
// The request was upgraded for some other reason than HSTS.
// Don't ignore HSTS to avoid the risk of another downgrade.
setIgnoreHSTS(request, false);
}
} else if ([scheme isEqualToString:@"http"]) {
if (ignoreHSTS(request.get()) != shouldIgnoreHSTS)
setIgnoreHSTS(request, shouldIgnoreHSTS);
}
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest *))completionHandler
{
auto taskIdentifier = task.taskIdentifier;
LOG(NetworkSession, "%llu willPerformHTTPRedirection from %s to %s", taskIdentifier, response.URL.absoluteString.UTF8String, request.URL.absoluteString.UTF8String);
if (auto* networkDataTask = [self existingTask:task]) {
bool shouldIgnoreHSTS = false;
#if ENABLE(INTELLIGENT_TRACKING_PREVENTION)
if (auto* sessionCocoa = networkDataTask->networkSession()) {
auto* storageSession = sessionCocoa->networkProcess().storageSession(sessionCocoa->sessionID());
NSURL *firstPartyForCookies = networkDataTask->isTopLevelNavigation() ? request.URL : request.mainDocumentURL;
shouldIgnoreHSTS = schemeWasUpgradedDueToDynamicHSTS(request)
&& storageSession->shouldBlockCookies(firstPartyForCookies, request.URL, networkDataTask->frameID(), networkDataTask->pageID(), networkDataTask->shouldRelaxThirdPartyCookieBlocking());
if (shouldIgnoreHSTS) {
request = downgradeRequest(request);
ASSERT([request.URL.scheme isEqualToString:@"http"]);
LOG(NetworkSession, "%llu Downgraded %s from https to http", taskIdentifier, request.URL.absoluteString.UTF8String);
}
} else
ASSERT_NOT_REACHED();
#endif
WebCore::ResourceResponse resourceResponse(response);
networkDataTask->checkTAO(resourceResponse);
bool isAppInitiated = true;
#if ENABLE(APP_PRIVACY_REPORT)
isAppInitiated = request.attribution == NSURLRequestAttributionDeveloper;
#endif
networkDataTask->willPerformHTTPRedirection(WTFMove(resourceResponse), request, [session = networkDataTask->networkSession(), completionHandler = makeBlockPtr(completionHandler), taskIdentifier, shouldIgnoreHSTS, isAppInitiated](auto&& request) {
#if !LOG_DISABLED
LOG(NetworkSession, "%llu willPerformHTTPRedirection completionHandler (%s)", taskIdentifier, request.url().string().utf8().data());
#else
UNUSED_PARAM(taskIdentifier);
#endif
auto nsRequest = retainPtr(request.nsURLRequest(WebCore::HTTPBodyUpdatePolicy::UpdateHTTPBody));
#if ENABLE(APP_PRIVACY_REPORT)
if (session) {
RetainPtr<NSMutableURLRequest> mutableRequest = adoptNS([nsRequest mutableCopy]);
mutableRequest.get().attribution = isAppInitiated ? NSURLRequestAttributionDeveloper : NSURLRequestAttributionUser;
nsRequest = mutableRequest.get();
session->appPrivacyReportTestingData().didLoadAppInitiatedRequest(nsRequest.get().attribution == NSURLRequestAttributionDeveloper);
}
#else
UNUSED_PARAM(isAppInitiated);
UNUSED_PARAM(session);
#endif
updateIgnoreStrictTransportSecuritySetting(nsRequest, shouldIgnoreHSTS);
completionHandler(nsRequest.get());
});
} else {
LOG(NetworkSession, "%llu willPerformHTTPRedirection completionHandler (nil)", taskIdentifier);
completionHandler(nil);
}
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask*)task _schemeUpgraded:(NSURLRequest*)request completionHandler:(void (^)(NSURLRequest*))completionHandler
{
auto taskIdentifier = task.taskIdentifier;
LOG(NetworkSession, "%llu _schemeUpgraded %s", taskIdentifier, request.URL.absoluteString.UTF8String);
if (auto* networkDataTask = [self existingTask:task]) {
bool shouldIgnoreHSTS = false;
#if ENABLE(INTELLIGENT_TRACKING_PREVENTION)
if (auto* sessionCocoa = networkDataTask->networkSession()) {
auto* storageSession = sessionCocoa->networkProcess().storageSession(sessionCocoa->sessionID());
shouldIgnoreHSTS = schemeWasUpgradedDueToDynamicHSTS(request)
&& storageSession->shouldBlockCookies(request, networkDataTask->frameID(), networkDataTask->pageID(), networkDataTask->shouldRelaxThirdPartyCookieBlocking());
if (shouldIgnoreHSTS) {
request = downgradeRequest(request);
ASSERT([request.URL.scheme isEqualToString:@"http"]);
LOG(NetworkSession, "%llu Downgraded %s from https to http", taskIdentifier, request.URL.absoluteString.UTF8String);
}
} else
ASSERT_NOT_REACHED();
#endif
networkDataTask->willPerformHTTPRedirection(WebCore::synthesizeRedirectResponseIfNecessary([task currentRequest], request, nil), request, [completionHandler = makeBlockPtr(completionHandler), taskIdentifier, shouldIgnoreHSTS](auto&& request) {
#if !LOG_DISABLED
LOG(NetworkSession, "%llu _schemeUpgraded completionHandler (%s)", taskIdentifier, request.url().string().utf8().data());
#else
UNUSED_PARAM(taskIdentifier);
#endif
auto nsRequest = retainPtr(request.nsURLRequest(WebCore::HTTPBodyUpdatePolicy::UpdateHTTPBody));
updateIgnoreStrictTransportSecuritySetting(nsRequest, shouldIgnoreHSTS);
completionHandler(nsRequest.get());
});
} else {
LOG(NetworkSession, "%llu _schemeUpgraded completionHandler (nil)", taskIdentifier);
completionHandler(nil);
}
}
static inline void processServerTrustEvaluation(NetworkSessionCocoa& session, SessionWrapper& sessionWrapper, NSURLAuthenticationChallenge *challenge, NegotiatedLegacyTLS negotiatedLegacyTLS, NetworkDataTaskCocoa::TaskIdentifier taskIdentifier, NetworkDataTaskCocoa* networkDataTask, CompletionHandler<void(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential)>&& completionHandler)
{
session.continueDidReceiveChallenge(sessionWrapper, challenge, negotiatedLegacyTLS, taskIdentifier, networkDataTask, [completionHandler = WTFMove(completionHandler), secTrust = retainPtr(challenge.protectionSpace.serverTrust)] (WebKit::AuthenticationChallengeDisposition disposition, const WebCore::Credential& credential) mutable {
// FIXME: UIProcess should send us back non nil credentials but the credential IPC encoder currently only serializes ns credentials for username/password.
if (disposition == WebKit::AuthenticationChallengeDisposition::UseCredential && !credential.nsCredential()) {
completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust: secTrust.get()]);
return;
}
completionHandler(toNSURLSessionAuthChallengeDisposition(disposition), credential.nsCredential());
});
}
- (NetworkSessionCocoa*)sessionFromTask:(NSURLSessionTask *)task {
if (auto* networkDataTask = [self existingTask:task])
return static_cast<NetworkSessionCocoa*>(networkDataTask->networkSession());
if (!_sessionWrapper)
return nullptr;
if (auto downloadID = _sessionWrapper->downloadMap.get(task.taskIdentifier)) {
if (auto download = _session->networkProcess().downloadManager().download(downloadID))
return static_cast<NetworkSessionCocoa*>(_session->networkProcess().networkSession(download->sessionID()));
return nullptr;
}
#if HAVE(NSURLSESSION_WEBSOCKET)
if (auto* webSocketTask = _sessionWrapper->webSocketDataTaskMap.get(task.taskIdentifier))
return webSocketTask->networkSession();
#endif
return nullptr;
}
void NetworkSessionCocoa::setClientAuditToken(const WebCore::AuthenticationChallenge& challenge)
{
#if ENABLE(APP_PRIVACY_REPORT)
if (auto auditToken = networkProcess().sourceApplicationAuditToken()) {
auto& tokenValue = *auditToken;
RetainPtr<NSData> token = adoptNS([[NSData alloc] initWithBytes:(uint8_t *)&tokenValue length:sizeof(tokenValue)]);
SecTrustSetClientAuditToken(challenge.nsURLAuthenticationChallenge().protectionSpace.serverTrust, bridge_cast(token.get()));
}
#else
UNUSED_PARAM(challenge);
#endif
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
auto* sessionCocoa = [self sessionFromTask: task];
if (!sessionCocoa || [task state] == NSURLSessionTaskStateCanceling) {
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
return;
}
auto taskIdentifier = task.taskIdentifier;
LOG(NetworkSession, "%llu didReceiveChallenge", taskIdentifier);
// Proxy authentication is handled by CFNetwork internally. We can get here if the user cancels
// CFNetwork authentication dialog, and we shouldn't ask the client to display another one in that case.
if (challenge.protectionSpace.isProxy && !sessionCocoa->preventsSystemHTTPProxyAuthentication()) {
completionHandler(NSURLSessionAuthChallengeUseCredential, nil);
return;
}
NegotiatedLegacyTLS negotiatedLegacyTLS = NegotiatedLegacyTLS::No;
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
sessionCocoa->setClientAuditToken(challenge);
if (NetworkSessionCocoa::allowsSpecificHTTPSCertificateForHost(challenge))
return completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
NSURLSessionTaskTransactionMetrics *metrics = task._incompleteTaskMetrics.transactionMetrics.lastObject;
auto tlsVersion = (tls_protocol_version_t)metrics.negotiatedTLSProtocolVersion.unsignedShortValue;
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
if (tlsVersion == tls_protocol_version_TLSv10 || tlsVersion == tls_protocol_version_TLSv11)
negotiatedLegacyTLS = NegotiatedLegacyTLS::Yes;
ALLOW_DEPRECATED_DECLARATIONS_END
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
if (negotiatedLegacyTLS == NegotiatedLegacyTLS::No && [task respondsToSelector:@selector(_TLSNegotiatedProtocolVersion)]) {
SSLProtocol tlsVersion = [task _TLSNegotiatedProtocolVersion];
if (tlsVersion == kTLSProtocol11 || tlsVersion == kTLSProtocol1)
negotiatedLegacyTLS = NegotiatedLegacyTLS::Yes;
}
ALLOW_DEPRECATED_DECLARATIONS_END
if (negotiatedLegacyTLS == NegotiatedLegacyTLS::Yes && task._preconnect)
return completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
// Handle server trust evaluation at platform-level if requested, for performance reasons and to use ATS defaults.
if (sessionCocoa->fastServerTrustEvaluationEnabled() && negotiatedLegacyTLS == NegotiatedLegacyTLS::No) {
auto* networkDataTask = [self existingTask:task];
if (networkDataTask) {
NSURLProtectionSpace *protectionSpace = challenge.protectionSpace;
networkDataTask->didNegotiateModernTLS(URL(URL(), makeString(String(protectionSpace.protocol), "://", String(protectionSpace.host), ':', protectionSpace.port)));
}
auto decisionHandler = makeBlockPtr([weakSelf = WeakObjCPtr<WKNetworkSessionDelegate>(self), sessionCocoa = WeakPtr { sessionCocoa }, completionHandler = makeBlockPtr(completionHandler), taskIdentifier, networkDataTask = RefPtr { networkDataTask }, negotiatedLegacyTLS](NSURLAuthenticationChallenge *challenge, OSStatus trustResult) mutable {
auto strongSelf = weakSelf.get();
if (!strongSelf)
return completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
auto task = WTFMove(networkDataTask);
auto* session = sessionCocoa.get();
if (trustResult == noErr || !session) {
completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
return;
}
processServerTrustEvaluation(*session, *strongSelf->_sessionWrapper, challenge, negotiatedLegacyTLS, taskIdentifier, task.get(), WTFMove(completionHandler));
});
[NSURLSession _strictTrustEvaluate:challenge queue:[NSOperationQueue mainQueue].underlyingQueue completionHandler:decisionHandler.get()];
return;
}
}
sessionCocoa->continueDidReceiveChallenge(*_sessionWrapper, challenge, negotiatedLegacyTLS, taskIdentifier, [self existingTask:task], [completionHandler = makeBlockPtr(completionHandler)] (WebKit::AuthenticationChallengeDisposition disposition, const WebCore::Credential& credential) mutable {
completionHandler(toNSURLSessionAuthChallengeDisposition(disposition), credential.nsCredential());
});
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
LOG(NetworkSession, "%llu didCompleteWithError %@", task.taskIdentifier, error);
if (error) {
NSDictionary *oldUserInfo = [error userInfo];
NSMutableDictionary *newUserInfo = oldUserInfo ? [NSMutableDictionary dictionaryWithDictionary:oldUserInfo] : [NSMutableDictionary dictionary];
newUserInfo[@"networkTaskDescription"] = [task description];
error = [NSError errorWithDomain:[error domain] code:[error code] userInfo:newUserInfo];
}
if (auto* networkDataTask = [self existingTask:task])
networkDataTask->didCompleteWithError(error, networkDataTask->networkLoadMetrics());
else if (error) {
if (!_sessionWrapper)
return;
auto downloadID = _sessionWrapper->downloadMap.take(task.taskIdentifier);
if (!downloadID)
return;
if (!_session)
return;
auto* download = _session->networkProcess().downloadManager().download(downloadID);
if (!download)
return;
NSData *resumeData = nil;
if (id userInfo = error.userInfo) {
if ([userInfo isKindOfClass:[NSDictionary class]]) {
resumeData = userInfo[@"NSURLSessionDownloadTaskResumeData"];
if (resumeData && ![resumeData isKindOfClass:[NSData class]]) {
RELEASE_LOG(NetworkSession, "Download task %llu finished with resume data of wrong class: %s", (unsigned long long)task.taskIdentifier, NSStringFromClass([resumeData class]).UTF8String);
ASSERT_NOT_REACHED();
resumeData = nil;
}
}
}
auto resumeDataReference = resumeData ? IPC::DataReference { static_cast<const uint8_t*>(resumeData.bytes), resumeData.length } : IPC::DataReference { };
download->didFail(error, resumeDataReference);
}
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics
{
LOG(NetworkSession, "%llu didFinishCollectingMetrics", task.taskIdentifier);
if (auto* networkDataTask = [self existingTask:task]) {
NSArray<NSURLSessionTaskTransactionMetrics *> *transactionMetrics = metrics.transactionMetrics;
NSURLSessionTaskTransactionMetrics *m = transactionMetrics.lastObject;
auto dateToMonotonicTime = [] (NSDate *date) {
if (auto interval = date.timeIntervalSince1970)
return WallTime::fromRawSeconds(interval).approximateMonotonicTime();
return MonotonicTime { };
};
auto& networkLoadMetrics = networkDataTask->networkLoadMetrics();
networkLoadMetrics.redirectStart = dateToMonotonicTime(transactionMetrics.firstObject.fetchStartDate);
networkLoadMetrics.fetchStart = dateToMonotonicTime(m.fetchStartDate);
networkLoadMetrics.domainLookupStart = dateToMonotonicTime(m.domainLookupStartDate);
networkLoadMetrics.domainLookupEnd = dateToMonotonicTime(m.domainLookupEndDate);
networkLoadMetrics.connectStart = dateToMonotonicTime(m.connectStartDate);
if (m.reusedConnection && [m.response.URL.scheme isEqualToString:@"https"])
networkLoadMetrics.secureConnectionStart = WebCore::reusedTLSConnectionSentinel;
else
networkLoadMetrics.secureConnectionStart = dateToMonotonicTime(m.secureConnectionStartDate);
networkLoadMetrics.connectEnd = dateToMonotonicTime(m.connectEndDate);
networkLoadMetrics.requestStart = dateToMonotonicTime(m.requestStartDate);
networkLoadMetrics.responseStart = dateToMonotonicTime(m.responseStartDate);
networkLoadMetrics.responseEnd = dateToMonotonicTime(m.responseEndDate);
networkLoadMetrics.markComplete();
networkLoadMetrics.redirectCount = metrics.redirectCount;
networkLoadMetrics.protocol = String(m.networkProtocolName);
networkLoadMetrics.cellular = m.cellular;
networkLoadMetrics.expensive = m.expensive;
networkLoadMetrics.constrained = m.constrained;
networkLoadMetrics.multipath = m.multipath;
networkLoadMetrics.isReusedConnection = m.isReusedConnection;
#if HAVE(NETWORK_CONNECTION_PRIVACY_STANCE)
networkLoadMetrics.privacyStance = toPrivacyStance(m._privacyStance);
#endif
if (networkDataTask->shouldCaptureExtraNetworkLoadMetrics()) {
auto additionalMetrics = WebCore::AdditionalNetworkLoadMetricsForWebInspector::create();
additionalMetrics->priority = toNetworkLoadPriority(task.priority);
#if HAVE(CFNETWORK_METRICS_APIS_V4)
if (auto port = [m.remotePort unsignedIntValue])
additionalMetrics->remoteAddress = makeString(String(m.remoteAddress), ':', port);
else
additionalMetrics->remoteAddress = m.remoteAddress;
#else
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
additionalMetrics->remoteAddress = String(m._remoteAddressAndPort);
ALLOW_DEPRECATED_DECLARATIONS_END
#endif
additionalMetrics->connectionIdentifier = String([m._connectionIdentifier UUIDString]);
#if HAVE(CFNETWORK_METRICS_APIS_V4)
additionalMetrics->tlsProtocol = stringForTLSProtocolVersion((tls_protocol_version_t)[m.negotiatedTLSProtocolVersion unsignedShortValue]);
additionalMetrics->tlsCipher = stringForTLSCipherSuite((tls_ciphersuite_t)[m.negotiatedTLSCipherSuite unsignedShortValue]);
#else
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
additionalMetrics->tlsProtocol = stringForSSLProtocol(m._negotiatedTLSProtocol);
additionalMetrics->tlsCipher = stringForSSLCipher(m._negotiatedTLSCipher);
ALLOW_DEPRECATED_DECLARATIONS_END
#endif
__block WebCore::HTTPHeaderMap requestHeaders;
[m.request.allHTTPHeaderFields enumerateKeysAndObjectsUsingBlock:^(NSString *name, NSString *value, BOOL *) {
requestHeaders.set(String(name), String(value));
}];
additionalMetrics->requestHeaders = WTFMove(requestHeaders);
uint64_t requestHeaderBytesSent = 0;
uint64_t responseHeaderBytesReceived = 0;
for (NSURLSessionTaskTransactionMetrics *transactionMetrics in metrics.transactionMetrics) {
#if HAVE(CFNETWORK_METRICS_APIS_V4)
requestHeaderBytesSent += transactionMetrics.countOfRequestHeaderBytesSent;
responseHeaderBytesReceived += transactionMetrics.countOfResponseHeaderBytesReceived;
#else
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
requestHeaderBytesSent += transactionMetrics._requestHeaderBytesSent;
responseHeaderBytesReceived += transactionMetrics._responseHeaderBytesReceived;
ALLOW_DEPRECATED_DECLARATIONS_END
#endif
}
additionalMetrics->requestHeaderBytesSent = requestHeaderBytesSent;
additionalMetrics->requestBodyBytesSent = task.countOfBytesSent;
additionalMetrics->responseHeaderBytesReceived = responseHeaderBytesReceived;
networkLoadMetrics.additionalNetworkLoadMetricsForWebInspector = WTFMove(additionalMetrics);
}
#if HAVE(CFNETWORK_METRICS_APIS_V4)
networkLoadMetrics.responseBodyBytesReceived = m.countOfResponseBodyBytesReceived;
networkLoadMetrics.responseBodyDecodedSize = m.countOfResponseBodyBytesAfterDecoding;
#else
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
networkLoadMetrics.responseBodyBytesReceived = m._responseBodyBytesReceived;
networkLoadMetrics.responseBodyDecodedSize = m._responseBodyBytesDecoded;
ALLOW_DEPRECATED_DECLARATIONS_END
#endif
// Sometimes the encoded body bytes received contains a few (3 or so) bytes from the header when there is no body.
// When this happens, trim our metrics to make more sense.
if (!networkLoadMetrics.responseBodyDecodedSize)
networkLoadMetrics.responseBodyBytesReceived = 0;
}
}
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler
{
auto taskIdentifier = dataTask.taskIdentifier;
LOG(NetworkSession, "%llu didReceiveResponse", taskIdentifier);
if (auto* networkDataTask = [self existingTask:dataTask]) {
ASSERT(RunLoop::isMain());
NegotiatedLegacyTLS negotiatedLegacyTLS = NegotiatedLegacyTLS::No;
NSURLSessionTaskMetrics *taskMetrics = dataTask._incompleteTaskMetrics;
NSURLSessionTaskTransactionMetrics *metrics = taskMetrics.transactionMetrics.lastObject;
auto tlsVersion = (tls_protocol_version_t)metrics.negotiatedTLSProtocolVersion.unsignedShortValue;
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
if (tlsVersion == tls_protocol_version_TLSv10 || tlsVersion == tls_protocol_version_TLSv11)
negotiatedLegacyTLS = NegotiatedLegacyTLS::Yes;
ALLOW_DEPRECATED_DECLARATIONS_END
// Avoid MIME type sniffing if the response comes back as 304 Not Modified.
int statusCode = [response isKindOfClass:NSHTTPURLResponse.class] ? [(NSHTTPURLResponse *)response statusCode] : 0;
if (statusCode != 304) {
bool isMainResourceLoad = networkDataTask->firstRequest().requester() == WebCore::ResourceRequest::Requester::Main;
WebCore::adjustMIMETypeIfNecessary(response._CFURLResponse, isMainResourceLoad);
}
WebCore::ResourceResponse resourceResponse(response);
// Lazy initialization is not helpful in the WebKit2 case because we always end up initializing
// all the fields when sending the response to the WebContent process over IPC.
resourceResponse.disableLazyInitialization();
networkDataTask->checkTAO(resourceResponse);
resourceResponse.setDeprecatedNetworkLoadMetrics(WebCore::copyTimingData(taskMetrics, networkDataTask->networkLoadMetrics()));
networkDataTask->didReceiveResponse(WTFMove(resourceResponse), negotiatedLegacyTLS, [completionHandler = makeBlockPtr(completionHandler), taskIdentifier](WebCore::PolicyAction policyAction) {
#if !LOG_DISABLED
LOG(NetworkSession, "%llu didReceiveResponse completionHandler (%d)", taskIdentifier, policyAction);
#else
UNUSED_PARAM(taskIdentifier);
#endif
completionHandler(toNSURLSessionResponseDisposition(policyAction));
});
} else {
LOG(NetworkSession, "%llu didReceiveResponse completionHandler (cancel)", taskIdentifier);
completionHandler(NSURLSessionResponseCancel);
}
}
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
if (auto* networkDataTask = [self existingTask:dataTask])
networkDataTask->didReceiveData(WebCore::SharedBuffer::create(data));
}
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
if (!_sessionWrapper)
return;
auto downloadID = _sessionWrapper->downloadMap.take([downloadTask taskIdentifier]);
if (!downloadID)
return;
if (!_session)
return;
auto* download = _session->networkProcess().downloadManager().download(downloadID);
if (!download)
return;
download->didFinish();
}
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
ASSERT_WITH_MESSAGE(![self existingTask:downloadTask], "The NetworkDataTask should be destroyed immediately after didBecomeDownloadTask returns");
if (!_sessionWrapper)
return;
auto downloadID = _sessionWrapper->downloadMap.get([downloadTask taskIdentifier]);
if (!downloadID)
return;
if (!_session)
return;
auto* download = _session->networkProcess().downloadManager().download(downloadID);
if (!download)
return;
download->didReceiveData(bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
}
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes
{
#if HAVE(BROKEN_DOWNLOAD_RESUME_UNLINK)
// This is to work around rdar://problem/63249830
if ([downloadTask respondsToSelector:@selector(downloadFile)] && [downloadTask.downloadFile respondsToSelector:@selector(setSkipUnlink:)])
downloadTask.downloadFile.skipUnlink = YES;
#endif
}
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask
{
auto* networkDataTask = [self existingTask:dataTask];
if (!networkDataTask)
return;
auto* sessionCocoa = networkDataTask->networkSession();
if (!sessionCocoa)
return;
Ref<NetworkDataTaskCocoa> protectedNetworkDataTask(*networkDataTask);
auto downloadID = networkDataTask->pendingDownloadID();
auto& downloadManager = sessionCocoa->networkProcess().downloadManager();
auto download = makeUnique<WebKit::Download>(downloadManager, downloadID, downloadTask, *sessionCocoa, networkDataTask->suggestedFilename());
networkDataTask->transferSandboxExtensionToDownload(*download);
ASSERT(FileSystem::fileExists(networkDataTask->pendingDownloadLocation()));
download->didCreateDestination(networkDataTask->pendingDownloadLocation());
downloadManager.dataTaskBecameDownloadTask(downloadID, WTFMove(download));
RELEASE_ASSERT(!_sessionWrapper->downloadMap.contains(downloadTask.taskIdentifier));
_sessionWrapper->downloadMap.add(downloadTask.taskIdentifier, downloadID);
}
#if HAVE(NSURLSESSION_WEBSOCKET)
- (WebSocketTask*)existingWebSocketTask:(NSURLSessionWebSocketTask *)task
{
if (!_sessionWrapper)
return nullptr;
if (!task)
return nullptr;
return _sessionWrapper->webSocketDataTaskMap.get(task.taskIdentifier);
}
- (void)URLSession:(NSURLSession *)session webSocketTask:(NSURLSessionWebSocketTask *)task didOpenWithProtocol:(NSString *) protocol
{
if (auto* webSocketTask = [self existingWebSocketTask:task])
webSocketTask->didConnect(protocol);
}
- (void)URLSession:(NSURLSession *)session webSocketTask:(NSURLSessionWebSocketTask *)task didCloseWithCode:(NSURLSessionWebSocketCloseCode)closeCode reason:(NSData *)reason
{
if (auto* webSocketTask = [self existingWebSocketTask:task]) {
// FIXME: We can re-enable ASSERT below once NSURLSession bug rdar://problem/72383646 is fixed.
// ASSERT([reason isEqualToData:task.closeReason]);
ASSERT(closeCode == [task closeCode]);
auto closeReason = adoptNS([[NSString alloc] initWithData:reason encoding:NSUTF8StringEncoding]);
webSocketTask->didClose(closeCode, closeReason.get());
}
}
#endif
@end
namespace WebKit {
#if ASSERT_ENABLED
static bool sessionsCreated = false;
#endif
static NSURLSessionConfiguration *configurationForSessionID(PAL::SessionID session, bool isFullWebBrowser)
{
#if HAVE(LOGGING_PRIVACY_LEVEL)
auto loggingPrivacyLevel = nw_context_privacy_level_sensitive;
#endif
NSURLSessionConfiguration *configuration;
if (session.isEphemeral()) {
configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
#if HAVE(LOGGING_PRIVACY_LEVEL) && defined(NW_CONTEXT_HAS_PRIVACY_LEVEL_SILENT)
if (isFullWebBrowser)
loggingPrivacyLevel = nw_context_privacy_level_silent;
#endif
} else
configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
#if PLATFORM(MAC)
bool preventCFNetworkClientCertificateLookup = linkedOnOrAfter(SDKVersion::FirstWithoutClientCertificateLookup) || session.isEphemeral();
#else
bool preventCFNetworkClientCertificateLookup = true;
#endif
configuration._shouldSkipPreferredClientCertificateLookup = preventCFNetworkClientCertificateLookup;
#if HAVE(LOGGING_PRIVACY_LEVEL)
auto setLoggingPrivacyLevel = NSSelectorFromString(@"set_loggingPrivacyLevel:");
if ([configuration respondsToSelector:setLoggingPrivacyLevel]) {
wtfObjCMsgSend<void>(configuration, setLoggingPrivacyLevel, loggingPrivacyLevel);
RELEASE_LOG(NetworkSession, "Setting logging level for %{public}s session %" PRIu64 " to %{public}s", session.isEphemeral() ? "Ephemeral" : "Regular", session.toUInt64(), loggingPrivacyLevel == nw_context_privacy_level_silent ? "silent" : "sensitive");
}
#elif HAVE(ALLOWS_SENSITIVE_LOGGING)
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
configuration._allowsSensitiveLogging = NO;
ALLOW_DEPRECATED_DECLARATIONS_END
#endif
#if HAVE(CFNETWORK_NSURLSESSION_CONNECTION_CACHE_LIMITS)
if (WebCore::ResourceRequest::resourcePrioritiesEnabled()) {
configuration._connectionCacheNumPriorityLevels = WebCore::resourceLoadPriorityCount;
configuration._connectionCacheMinimumFastLanePriority = toPlatformRequestPriority(WebCore::ResourceLoadPriority::Medium);
configuration._connectionCacheNumFastLanes = 1;
}
#endif
return configuration;
}
_NSHSTSStorage *NetworkSessionCocoa::hstsStorage() const
{
#if HAVE(HSTS_STORAGE)
NSURLSessionConfiguration *configuration = m_defaultSessionSet->sessionWithCredentialStorage.session.get().configuration;
// FIXME: Remove this respondsToSelector check once rdar://problem/50109631 is in a build and bots are updated.
if ([configuration respondsToSelector:@selector(_hstsStorage)])
return configuration._hstsStorage;
#endif
return nil;
}
const String& NetworkSessionCocoa::boundInterfaceIdentifier() const
{
return m_boundInterfaceIdentifier;
}
const String& NetworkSessionCocoa::sourceApplicationBundleIdentifier() const
{
return m_sourceApplicationBundleIdentifier;
}
const String& NetworkSessionCocoa::sourceApplicationSecondaryIdentifier() const
{
return m_sourceApplicationSecondaryIdentifier;
}
#if PLATFORM(IOS_FAMILY)
const String& NetworkSessionCocoa::dataConnectionServiceType() const
{
return m_dataConnectionServiceType;
}
#endif
std::unique_ptr<NetworkSession> NetworkSessionCocoa::create(NetworkProcess& networkProcess, const NetworkSessionCreationParameters& parameters)
{
return makeUnique<NetworkSessionCocoa>(networkProcess, parameters);
}
static RetainPtr<NSDictionary> proxyDictionary(const URL& httpProxy, const URL& httpsProxy)
{
if (!httpProxy.isValid() && !httpsProxy.isValid())
return nil;
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
auto dictionary = adoptNS([[NSMutableDictionary alloc] init]);
if (httpProxy.isValid()) {
[dictionary setObject:httpProxy.host().toString() forKey:(NSString *)kCFStreamPropertyHTTPProxyHost];
if (auto port = httpProxy.port())
[dictionary setObject:@(*port) forKey:(NSString *)kCFStreamPropertyHTTPProxyPort];
}
if (httpsProxy.isValid()) {
[dictionary setObject:httpsProxy.host().toString() forKey:(NSString *)kCFStreamPropertyHTTPSProxyHost];
if (auto port = httpsProxy.port())
[dictionary setObject:@(*port) forKey:(NSString *)kCFStreamPropertyHTTPSProxyPort];
}
return dictionary;
ALLOW_DEPRECATED_DECLARATIONS_END
}
void SessionWrapper::initialize(NSURLSessionConfiguration *configuration, NetworkSessionCocoa& networkSession, WebCore::StoredCredentialsPolicy storedCredentialsPolicy, NavigatingToAppBoundDomain isNavigatingToAppBoundDomain)
{
UNUSED_PARAM(isNavigatingToAppBoundDomain);
auto isFullBrowser = isParentProcessAFullWebBrowser(networkSession.networkProcess());
#if PLATFORM(MAC)
isFullBrowser = WebCore::MacApplication::isSafari();
#endif
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(SESSION_CLEANUP)
static void activateSessionCleanup(NetworkSessionCocoa& session, const NetworkSessionCreationParameters& parameters)
{
#if (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 140000) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 110000)
// Don't override an explicitly set value.
if (parameters.resourceLoadStatisticsParameters.isItpStateExplicitlySet)
return;
#if !PLATFORM(IOS_FAMILY_SIMULATOR)
bool itpEnabled = doesParentProcessHaveITPEnabled(session.networkProcess(), parameters.appHasRequestedCrossWebsiteTrackingPermission);
bool passedEnabledState = session.isResourceLoadStatisticsEnabled();
// We do not need to log a discrepancy between states for WebKitTestRunner or TestWebKitAPI.
if (itpEnabled != passedEnabledState && !isRunningTest(WebCore::applicationBundleIdentifier()))
WTFLogAlways("Passed ITP enabled state (%d) does not match TCC setting (%d)\n", passedEnabledState, itpEnabled);
session.setResourceLoadStatisticsEnabled(passedEnabledState);
#endif
#endif
}
#endif
NetworkSessionCocoa::NetworkSessionCocoa(NetworkProcess& networkProcess, const NetworkSessionCreationParameters& parameters)
: NetworkSession(networkProcess, parameters)
, m_defaultSessionSet(SessionSet::create())
, m_boundInterfaceIdentifier(parameters.boundInterfaceIdentifier)
, m_sourceApplicationBundleIdentifier(parameters.sourceApplicationBundleIdentifier)
, m_sourceApplicationSecondaryIdentifier(parameters.sourceApplicationSecondaryIdentifier)
, m_proxyConfiguration(parameters.proxyConfiguration)
, m_shouldLogCookieInformation(parameters.shouldLogCookieInformation)
, m_fastServerTrustEvaluationEnabled(parameters.fastServerTrustEvaluationEnabled)
, m_dataConnectionServiceType(parameters.dataConnectionServiceType)
, m_preventsSystemHTTPProxyAuthentication(parameters.preventsSystemHTTPProxyAuthentication)
{
ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
#if ASSERT_ENABLED
sessionsCreated = true;
#endif
NSURLSessionConfiguration *configuration = configurationForSessionID(m_sessionID, isParentProcessAFullWebBrowser(networkProcess));
#if HAVE(HSTS_STORAGE)
if (!!parameters.hstsStorageDirectory && !m_sessionID.isEphemeral()) {
SandboxExtension::consumePermanently(parameters.hstsStorageDirectoryExtensionHandle);
// FIXME: Remove this respondsToSelector check once rdar://problem/50109631 is in a build and bots are updated.
if ([configuration respondsToSelector:@selector(_hstsStorage)])
configuration._hstsStorage = adoptNS([alloc_NSHSTSStorageInstance() initPersistentStoreWithURL:[NSURL fileURLWithPath:parameters.hstsStorageDirectory isDirectory:YES]]).get();
}
#endif
#if HAVE(NETWORK_LOADER)
RELEASE_LOG_IF(parameters.useNetworkLoader, NetworkSession, "Using experimental network loader.");
configuration._usesNWLoader = parameters.useNetworkLoader;
#endif
if (parameters.allowsHSTSWithUntrustedRootCertificate && [configuration respondsToSelector:@selector(_allowsHSTSWithUntrustedRootCertificate)])
configuration._allowsHSTSWithUntrustedRootCertificate = YES;
#if HAVE(APP_SSO) || PLATFORM(MACCATALYST)
configuration._preventsAppSSO = true;
#endif
// Without this, CFNetwork would sometimes add a Content-Type header to our requests (rdar://problem/34748470).
configuration._suppressedAutoAddedHTTPHeaders = [NSSet setWithObject:@"Content-Type"];
if (parameters.allowsCellularAccess == AllowsCellularAccess::No)
configuration.allowsCellularAccess = NO;
// The WebKit network cache was already queried.
configuration.URLCache = nil;
if (auto data = networkProcess.sourceApplicationAuditData())
configuration._sourceApplicationAuditTokenData = (__bridge NSData *)data.get();
if (!m_sourceApplicationBundleIdentifier.isEmpty()) {
configuration._sourceApplicationBundleIdentifier = m_sourceApplicationBundleIdentifier;
configuration._sourceApplicationAuditTokenData = nil;
}
if (!m_sourceApplicationSecondaryIdentifier.isEmpty())
configuration._sourceApplicationSecondaryIdentifier = m_sourceApplicationSecondaryIdentifier;
#if HAVE(CFNETWORK_ALTERNATIVE_SERVICE)
if (!parameters.alternativeServiceDirectory.isEmpty()) {
SandboxExtension::consumePermanently(parameters.alternativeServiceDirectoryExtensionHandle);
configuration._alternativeServicesStorage = adoptNS([[_NSHTTPAlternativeServicesStorage alloc] initPersistentStoreWithURL:[[NSURL fileURLWithPath:parameters.alternativeServiceDirectory isDirectory:YES] URLByAppendingPathComponent:@"AlternativeService.sqlite" isDirectory:NO]]).get();
}
if (parameters.http3Enabled)
configuration._allowsHTTP3 = YES;
#endif
configuration._preventsSystemHTTPProxyAuthentication = parameters.preventsSystemHTTPProxyAuthentication;
configuration._requiresSecureHTTPSProxyConnection = parameters.requiresSecureHTTPSProxyConnection;
configuration.connectionProxyDictionary = (NSDictionary *)parameters.proxyConfiguration.get() ?: proxyDictionary(parameters.httpProxy, parameters.httpsProxy).get();
#if PLATFORM(IOS_FAMILY)
if (!m_dataConnectionServiceType.isEmpty())
configuration._CTDataConnectionServiceType = m_dataConnectionServiceType;
#endif
#if ENABLE(LEGACY_CUSTOM_PROTOCOL_MANAGER)
networkProcess.supplement<LegacyCustomProtocolManager>()->registerProtocolClass(configuration);
#endif
configuration._timingDataOptions = _TimingDataOptionsEnableW3CNavigationTiming;
// FIXME: Replace @"kCFStreamPropertyAutoErrorOnSystemChange" with a constant from the SDK once rdar://problem/40650244 is in a build.
if (parameters.suppressesConnectionTerminationOnSystemChange)
configuration._socketStreamProperties = @{ @"kCFStreamPropertyAutoErrorOnSystemChange" : @NO };
#if PLATFORM(WATCHOS)
configuration._companionProxyPreference = NSURLSessionCompanionProxyPreferencePreferDirectToCloud;
#endif
auto* storageSession = networkProcess.storageSession(parameters.sessionID);
RELEASE_ASSERT(storageSession);
RetainPtr<NSHTTPCookieStorage> cookieStorage;
if (CFHTTPCookieStorageRef storage = storageSession->cookieStorage().get()) {
cookieStorage = adoptNS([[NSHTTPCookieStorage alloc] _initWithCFHTTPCookieStorage:storage]);
configuration.HTTPCookieStorage = cookieStorage.get();
} else
cookieStorage = storageSession->nsCookieStorage();
#if HAVE(CFNETWORK_OVERRIDE_SESSION_COOKIE_ACCEPT_POLICY)
// We still need to check the selector since CFNetwork updates and WebKit updates are separate
// on older macOS.
if ([cookieStorage respondsToSelector:@selector(_overrideSessionCookieAcceptPolicy)])
cookieStorage.get()._overrideSessionCookieAcceptPolicy = YES;
#endif
initializeNSURLSessionsInSet(m_defaultSessionSet.get(), configuration);
m_deviceManagementRestrictionsEnabled = parameters.deviceManagementRestrictionsEnabled;
m_allLoadsBlockedByDeviceManagementRestrictionsForTesting = parameters.allLoadsBlockedByDeviceManagementRestrictionsForTesting;
m_webPushDaemonUsesMockBundlesForTesting = parameters.webPushDaemonConnectionConfiguration.useMockBundlesForTesting;
#if ENABLE(APP_BOUND_DOMAINS)
if (m_resourceLoadStatistics && !parameters.resourceLoadStatisticsParameters.appBoundDomains.isEmpty())
m_resourceLoadStatistics->setAppBoundDomains(HashSet<WebCore::RegistrableDomain> { parameters.resourceLoadStatisticsParameters.appBoundDomains }, [] { });
#endif
#if HAVE(SESSION_CLEANUP)
activateSessionCleanup(*this, parameters);
#endif
}
NetworkSessionCocoa::~NetworkSessionCocoa() = default;
void NetworkSessionCocoa::initializeNSURLSessionsInSet(SessionSet& sessionSet, NSURLSessionConfiguration *configuration)
{
sessionSet.sessionWithCredentialStorage.initialize(configuration, *this, WebCore::StoredCredentialsPolicy::Use, NavigatingToAppBoundDomain::No);
auto cookieAcceptPolicy = configuration.HTTPCookieStorage.cookieAcceptPolicy;
LOG(NetworkSession, "Created NetworkSession with cookieAcceptPolicy %lu", cookieAcceptPolicy);
RELEASE_LOG_IF(cookieAcceptPolicy == NSHTTPCookieAcceptPolicyNever, NetworkSession, "Creating network session with ID %" PRIu64 " that will not accept cookies.", m_sessionID.toUInt64());
configuration.URLCredentialStorage = nil;
sessionSet.sessionWithoutCredentialStorage.initialize(configuration, *this, WebCore::StoredCredentialsPolicy::DoNotUse, NavigatingToAppBoundDomain::No);
}
SessionSet& NetworkSessionCocoa::sessionSetForPage(WebPageProxyIdentifier webPageProxyID)
{
SessionSet* sessionSet = webPageProxyID ? m_perPageSessionSets.get(webPageProxyID) : nullptr;
return sessionSet ? *sessionSet : m_defaultSessionSet.get();
}
const SessionSet& NetworkSessionCocoa::sessionSetForPage(WebPageProxyIdentifier webPageProxyID) const
{
SessionSet* sessionSet = webPageProxyID ? m_perPageSessionSets.get(webPageProxyID) : nullptr;
return sessionSet ? *sessionSet : m_defaultSessionSet.get();
}
SessionWrapper& NetworkSessionCocoa::initializeEphemeralStatelessSessionIfNeeded(WebPageProxyIdentifier webPageProxyID, NavigatingToAppBoundDomain isNavigatingToAppBoundDomain)
{
return sessionSetForPage(webPageProxyID).initializeEphemeralStatelessSessionIfNeeded(isNavigatingToAppBoundDomain, *this);
}
SessionWrapper& SessionSet::initializeEphemeralStatelessSessionIfNeeded(NavigatingToAppBoundDomain isNavigatingToAppBoundDomain, NetworkSessionCocoa& session)
{
if (ephemeralStatelessSession.session)
return ephemeralStatelessSession;
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
NSURLSessionConfiguration *existingConfiguration = sessionWithoutCredentialStorage.session.get().configuration;
configuration.HTTPCookieAcceptPolicy = NSHTTPCookieAcceptPolicyNever;
configuration.URLCredentialStorage = nil;
configuration.URLCache = nil;
configuration.allowsCellularAccess = existingConfiguration.allowsCellularAccess;
configuration.connectionProxyDictionary = existingConfiguration.connectionProxyDictionary;
configuration._shouldSkipPreferredClientCertificateLookup = YES;
configuration._sourceApplicationAuditTokenData = existingConfiguration._sourceApplicationAuditTokenData;
configuration._sourceApplicationSecondaryIdentifier = existingConfiguration._sourceApplicationSecondaryIdentifier;
#if PLATFORM(IOS_FAMILY)
configuration._CTDataConnectionServiceType = existingConfiguration._CTDataConnectionServiceType;
#endif
ephemeralStatelessSession.initialize(configuration, session, WebCore::StoredCredentialsPolicy::EphemeralStateless, isNavigatingToAppBoundDomain);
return ephemeralStatelessSession;
}
SessionWrapper& NetworkSessionCocoa::sessionWrapperForTask(WebPageProxyIdentifier webPageProxyID, const WebCore::ResourceRequest& request, WebCore::StoredCredentialsPolicy storedCredentialsPolicy, std::optional<NavigatingToAppBoundDomain> isNavigatingToAppBoundDomain)
{
auto shouldBeConsideredAppBound = isNavigatingToAppBoundDomain ? *isNavigatingToAppBoundDomain : NavigatingToAppBoundDomain::Yes;
if (isParentProcessAFullWebBrowser(networkProcess()))
shouldBeConsideredAppBound = NavigatingToAppBoundDomain::No;
#if ENABLE(INTELLIGENT_TRACKING_PREVENTION)
if (auto* storageSession = networkStorageSession()) {
auto firstParty = WebCore::RegistrableDomain(request.firstPartyForCookies());
if (storageSession->shouldBlockThirdPartyCookiesButKeepFirstPartyCookiesFor(firstParty))
return sessionSetForPage(webPageProxyID).isolatedSession(storedCredentialsPolicy, firstParty, shouldBeConsideredAppBound, *this);
} else
ASSERT_NOT_REACHED();
#else
UNUSED_PARAM(request);
#endif
#if ENABLE(APP_BOUND_DOMAINS)
if (shouldBeConsideredAppBound == NavigatingToAppBoundDomain::Yes)
return appBoundSession(webPageProxyID, storedCredentialsPolicy);
#endif
switch (storedCredentialsPolicy) {
case WebCore::StoredCredentialsPolicy::Use:
return sessionSetForPage(webPageProxyID).sessionWithCredentialStorage;
case WebCore::StoredCredentialsPolicy::DoNotUse:
return sessionSetForPage(webPageProxyID).sessionWithoutCredentialStorage;
case WebCore::StoredCredentialsPolicy::EphemeralStateless:
return initializeEphemeralStatelessSessionIfNeeded(webPageProxyID, NavigatingToAppBoundDomain::No);
}
}
#if ENABLE(APP_BOUND_DOMAINS)
SessionWrapper& NetworkSessionCocoa::appBoundSession(WebPageProxyIdentifier webPageProxyID, WebCore::StoredCredentialsPolicy storedCredentialsPolicy)
{
auto& sessionSet = sessionSetForPage(webPageProxyID);
if (!sessionSet.appBoundSession) {
sessionSet.appBoundSession = makeUnique<IsolatedSession>();
sessionSet.appBoundSession->sessionWithCredentialStorage.initialize(sessionSet.sessionWithCredentialStorage.session.get().configuration, *this, WebCore::StoredCredentialsPolicy::Use, NavigatingToAppBoundDomain::Yes);
sessionSet.appBoundSession->sessionWithoutCredentialStorage.initialize(sessionSet.sessionWithoutCredentialStorage.session.get().configuration, *this, WebCore::StoredCredentialsPolicy::DoNotUse, NavigatingToAppBoundDomain::Yes);
}
auto& sessionWrapper = [&] (auto storedCredentialsPolicy) -> SessionWrapper& {
switch (storedCredentialsPolicy) {
case WebCore::StoredCredentialsPolicy::Use:
LOG(NetworkSession, "Using app-bound NSURLSession with credential storage.");
return sessionSet.appBoundSession->sessionWithCredentialStorage;
case WebCore::StoredCredentialsPolicy::DoNotUse:
LOG(NetworkSession, "Using app-bound NSURLSession without credential storage.");
return sessionSet.appBoundSession->sessionWithoutCredentialStorage;
case WebCore::StoredCredentialsPolicy::EphemeralStateless:
return initializeEphemeralStatelessSessionIfNeeded(webPageProxyID, NavigatingToAppBoundDomain::Yes);
}
} (storedCredentialsPolicy);
return sessionWrapper;
}
bool NetworkSessionCocoa::hasAppBoundSession() const
{
if (!!m_defaultSessionSet->appBoundSession)
return true;
for (auto& sessionSet : m_perPageSessionSets.values()) {
if (!!sessionSet->appBoundSession)
return true;
}
return false;
}
void NetworkSessionCocoa::clearAppBoundSession()
{
m_defaultSessionSet->appBoundSession = nullptr;
for (auto& sessionSet : m_perPageSessionSets.values())
sessionSet->appBoundSession = nullptr;
}
#endif
SessionWrapper& NetworkSessionCocoa::isolatedSession(WebPageProxyIdentifier webPageProxyID, WebCore::StoredCredentialsPolicy storedCredentialsPolicy, const WebCore::RegistrableDomain& firstPartyDomain, NavigatingToAppBoundDomain isNavigatingToAppBoundDomain)
{
return sessionSetForPage(webPageProxyID).isolatedSession(storedCredentialsPolicy, firstPartyDomain, isNavigatingToAppBoundDomain, *this);
}
SessionWrapper& SessionSet::isolatedSession(WebCore::StoredCredentialsPolicy storedCredentialsPolicy, const WebCore::RegistrableDomain& firstPartyDomain, NavigatingToAppBoundDomain isNavigatingToAppBoundDomain, NetworkSessionCocoa& session)
{
auto& entry = isolatedSessions.ensure(firstPartyDomain, [this, &session, isNavigatingToAppBoundDomain] {
auto newEntry = makeUnique<IsolatedSession>();
newEntry->sessionWithCredentialStorage.initialize(sessionWithCredentialStorage.session.get().configuration, session, WebCore::StoredCredentialsPolicy::Use, isNavigatingToAppBoundDomain);
newEntry->sessionWithoutCredentialStorage.initialize(sessionWithoutCredentialStorage.session.get().configuration, session, WebCore::StoredCredentialsPolicy::DoNotUse, isNavigatingToAppBoundDomain);
return newEntry;
}).iterator->value;
entry->lastUsed = WallTime::now();
auto& sessionWrapper = [&] (auto storedCredentialsPolicy) -> SessionWrapper& {
switch (storedCredentialsPolicy) {
case WebCore::StoredCredentialsPolicy::Use:
LOG(NetworkSession, "Using isolated NSURLSession with credential storage.");
return entry->sessionWithCredentialStorage;
case WebCore::StoredCredentialsPolicy::DoNotUse:
LOG(NetworkSession, "Using isolated NSURLSession without credential storage.");
return entry->sessionWithoutCredentialStorage;
case WebCore::StoredCredentialsPolicy::EphemeralStateless:
return initializeEphemeralStatelessSessionIfNeeded(isNavigatingToAppBoundDomain, session);
}
} (storedCredentialsPolicy);
if (isolatedSessions.size() > maxNumberOfIsolatedSessions) {
WebCore::RegistrableDomain keyToRemove;
auto oldestTimestamp = WallTime::now();
for (auto& key : isolatedSessions.keys()) {
auto timestamp = isolatedSessions.get(key)->lastUsed;
if (timestamp < oldestTimestamp) {
oldestTimestamp = timestamp;
keyToRemove = key;
}
}
LOG(NetworkSession, "About to remove isolated NSURLSession.");
isolatedSessions.remove(keyToRemove);
}
RELEASE_ASSERT(isolatedSessions.size() <= maxNumberOfIsolatedSessions);
return sessionWrapper;
}
bool NetworkSessionCocoa::hasIsolatedSession(const WebCore::RegistrableDomain& domain) const
{
if (m_defaultSessionSet->isolatedSessions.contains(domain))
return true;
for (auto& sessionSet : m_perPageSessionSets.values()) {
if (sessionSet->isolatedSessions.contains(domain))
return true;
}
return false;
}
void NetworkSessionCocoa::clearIsolatedSessions()
{
m_defaultSessionSet->isolatedSessions.clear();
for (auto& sessionSet : m_perPageSessionSets.values())
sessionSet->isolatedSessions.clear();
}
void NetworkSessionCocoa::invalidateAndCancelSessionSet(SessionSet& sessionSet)
{
[sessionSet.sessionWithCredentialStorage.session invalidateAndCancel];
[sessionSet.sessionWithoutCredentialStorage.session invalidateAndCancel];
[sessionSet.ephemeralStatelessSession.session invalidateAndCancel];
[sessionSet.sessionWithCredentialStorage.delegate sessionInvalidated];
[sessionSet.sessionWithoutCredentialStorage.delegate sessionInvalidated];
[sessionSet.ephemeralStatelessSession.delegate sessionInvalidated];
for (auto& session : sessionSet.isolatedSessions.values()) {
[session->sessionWithCredentialStorage.session invalidateAndCancel];
[session->sessionWithCredentialStorage.delegate sessionInvalidated];
[session->sessionWithoutCredentialStorage.session invalidateAndCancel];
[session->sessionWithoutCredentialStorage.delegate sessionInvalidated];
}
sessionSet.isolatedSessions.clear();
if (sessionSet.appBoundSession) {
[sessionSet.appBoundSession->sessionWithCredentialStorage.session invalidateAndCancel];
[sessionSet.appBoundSession->sessionWithCredentialStorage.delegate sessionInvalidated];
[sessionSet.appBoundSession->sessionWithoutCredentialStorage.session invalidateAndCancel];
[sessionSet.appBoundSession->sessionWithoutCredentialStorage.delegate sessionInvalidated];
}
}
void NetworkSessionCocoa::invalidateAndCancel()
{
NetworkSession::invalidateAndCancel();
invalidateAndCancelSessionSet(m_defaultSessionSet.get());
for (auto& sessionSet : m_perPageSessionSets.values())
invalidateAndCancelSessionSet(sessionSet.get());
}
void NetworkSessionCocoa::clearCredentials()
{
#if !USE(CREDENTIAL_STORAGE_WITH_NETWORK_SESSION)
ASSERT(m_dataTaskMapWithCredentials.isEmpty());
ASSERT(m_dataTaskMapWithoutState.isEmpty());
ASSERT(m_downloadMap.isEmpty());
// FIXME: Use resetWithCompletionHandler instead.
m_sessionWithCredentialStorage = [NSURLSession sessionWithConfiguration:m_sessionWithCredentialStorage.get().configuration delegate:static_cast<id>(m_sessionWithCredentialStorageDelegate.get()) delegateQueue:[NSOperationQueue mainQueue]];
m_statelessSession = [NSURLSession sessionWithConfiguration:m_statelessSession.get().configuration delegate:static_cast<id>(m_statelessSessionDelegate.get()) delegateQueue:[NSOperationQueue mainQueue]];
for (auto& entry : m_isolatedSessions.values())
entry.session = [NSURLSession sessionWithConfiguration:entry.session.get().configuration delegate:static_cast<id>(entry.delegate.get()) delegateQueue:[NSOperationQueue mainQueue]];
m_appBoundSession.session = [NSURLSession sessionWithConfiguration:m_appBoundSession.session.get().configuration delegate:static_cast<id>(m_appBoundSession.delegate.get()) delegateQueue:[NSOperationQueue mainQueue]];
#endif
}
bool NetworkSessionCocoa::allowsSpecificHTTPSCertificateForHost(const WebCore::AuthenticationChallenge& challenge)
{
const String& host = challenge.protectionSpace().host();
NSArray *certificates = [NSURLRequest allowsSpecificHTTPSCertificateForHost:host];
if (!certificates)
return false;
bool requireServerCertificates = challenge.protectionSpace().authenticationScheme() == WebCore::ProtectionSpace::AuthenticationScheme::ServerTrustEvaluationRequested;
RetainPtr<SecPolicyRef> policy = adoptCF(SecPolicyCreateSSL(requireServerCertificates, host.createCFString().get()));
SecTrustRef trustRef = nullptr;
if (SecTrustCreateWithCertificates((CFArrayRef)certificates, policy.get(), &trustRef) != noErr)
return false;
RetainPtr<SecTrustRef> trust = adoptCF(trustRef);
return WebCore::certificatesMatch(trust.get(), challenge.nsURLAuthenticationChallenge().protectionSpace.serverTrust);
}
static CompletionHandler<void(WebKit::AuthenticationChallengeDisposition disposition, const WebCore::Credential& credential)> createChallengeCompletionHandler(Ref<NetworkProcess>&& networkProcess, PAL::SessionID sessionID, const WebCore::AuthenticationChallenge& challenge, const String& partition, uint64_t taskIdentifier, CompletionHandler<void(WebKit::AuthenticationChallengeDisposition, const WebCore::Credential&)>&& completionHandler)
{
WebCore::AuthenticationChallenge authenticationChallenge { challenge };
return [completionHandler = WTFMove(completionHandler), networkProcess = WTFMove(networkProcess), sessionID, authenticationChallenge, taskIdentifier, partition](WebKit::AuthenticationChallengeDisposition disposition, const WebCore::Credential& credential) mutable {
#if !LOG_DISABLED
LOG(NetworkSession, "%llu didReceiveChallenge completionHandler %d", taskIdentifier, disposition);
#else
UNUSED_PARAM(taskIdentifier);
#endif
#if !USE(CREDENTIAL_STORAGE_WITH_NETWORK_SESSION)
UNUSED_PARAM(sessionID);
UNUSED_PARAM(authenticationChallenge);
#else
if (credential.persistence() == WebCore::CredentialPersistenceForSession && authenticationChallenge.protectionSpace().isPasswordBased()) {
WebCore::Credential nonPersistentCredential(credential.user(), credential.password(), WebCore::CredentialPersistenceNone);
URL urlToStore;
if (authenticationChallenge.failureResponse().httpStatusCode() == 401)
urlToStore = authenticationChallenge.failureResponse().url();
if (auto storageSession = networkProcess->storageSession(sessionID))
storageSession->credentialStorage().set(partition, nonPersistentCredential, authenticationChallenge.protectionSpace(), urlToStore);
else
ASSERT_NOT_REACHED();
completionHandler(disposition, nonPersistentCredential);
return;
}
#endif
completionHandler(disposition, credential);
};
}
void NetworkSessionCocoa::continueDidReceiveChallenge(SessionWrapper& sessionWrapper, const WebCore::AuthenticationChallenge& challenge, NegotiatedLegacyTLS negotiatedLegacyTLS, NetworkDataTaskCocoa::TaskIdentifier taskIdentifier, NetworkDataTaskCocoa* networkDataTask, CompletionHandler<void(WebKit::AuthenticationChallengeDisposition, const WebCore::Credential&)>&& completionHandler)
{
if (!networkDataTask) {
#if HAVE(NSURLSESSION_WEBSOCKET)
if (auto* webSocketTask = sessionWrapper.webSocketDataTaskMap.get(taskIdentifier)) {
auto challengeCompletionHandler = createChallengeCompletionHandler(networkProcess(), sessionID(), challenge, webSocketTask->partition(), 0, WTFMove(completionHandler));
networkProcess().authenticationManager().didReceiveAuthenticationChallenge(sessionID(), webSocketTask->pageID(), !webSocketTask->topOrigin().isEmpty() ? &webSocketTask->topOrigin() : nullptr, challenge, negotiatedLegacyTLS, WTFMove(challengeCompletionHandler));
return;
}
#endif
auto downloadID = sessionWrapper.downloadMap.get(taskIdentifier);
if (downloadID) {
if (auto* download = networkProcess().downloadManager().download(downloadID)) {
WebCore::AuthenticationChallenge authenticationChallenge { challenge };
// Received an authentication challenge for a download being resumed.
download->didReceiveChallenge(authenticationChallenge, WTFMove(completionHandler));
return;
}
}
LOG(NetworkSession, "%llu didReceiveChallenge completionHandler (cancel)", taskIdentifier);
completionHandler(AuthenticationChallengeDisposition::Cancel, { });
return;
}
auto challengeCompletionHandler = createChallengeCompletionHandler(networkProcess(), sessionID(), challenge, networkDataTask->partition(), taskIdentifier, WTFMove(completionHandler));
if (negotiatedLegacyTLS == NegotiatedLegacyTLS::Yes
&& fastServerTrustEvaluationEnabled()
&& !networkDataTask->isTopLevelNavigation())
return challengeCompletionHandler(AuthenticationChallengeDisposition::Cancel, { });
networkDataTask->didReceiveChallenge(WebCore::AuthenticationChallenge { challenge }, negotiatedLegacyTLS, WTFMove(challengeCompletionHandler));
}
DMFWebsitePolicyMonitor *NetworkSessionCocoa::deviceManagementPolicyMonitor()
{
#if HAVE(DEVICE_MANAGEMENT)
ASSERT(m_deviceManagementRestrictionsEnabled);
if (!m_deviceManagementPolicyMonitor)
m_deviceManagementPolicyMonitor = adoptNS([allocDMFWebsitePolicyMonitorInstance() initWithPolicyChangeHandler:nil]);
return m_deviceManagementPolicyMonitor.get();
#else
RELEASE_ASSERT_NOT_REACHED();
return nil;
#endif
}
#if HAVE(NSURLSESSION_WEBSOCKET)
std::unique_ptr<WebSocketTask> NetworkSessionCocoa::createWebSocketTask(WebPageProxyIdentifier webPageProxyID, NetworkSocketChannel& channel, const WebCore::ResourceRequest& request, const String& protocol, const WebCore::ClientOrigin& clientOrigin)
{
ASSERT(!request.hasHTTPHeaderField(WebCore::HTTPHeaderName::SecWebSocketProtocol));
auto nsRequest = retainPtr(request.nsURLRequest(WebCore::HTTPBodyUpdatePolicy::DoNotUpdateHTTPBody));
if (!protocol.isNull()) {
auto requestWithProtocols = adoptNS([nsRequest mutableCopy]);
[requestWithProtocols addValue: StringView(protocol).createNSString().get() forHTTPHeaderField:@"Sec-WebSocket-Protocol"];
nsRequest = WTFMove(requestWithProtocols);
}
// rdar://problem/68057031: explicitly disable sniffing for WebSocket handshakes.
[nsRequest _setProperty:@NO forKey:(NSString *)_kCFURLConnectionPropertyShouldSniff];
#if ENABLE(APP_PRIVACY_REPORT)
if (!request.isAppInitiated()) {
RetainPtr<NSMutableURLRequest> mutableRequest = adoptNS([nsRequest.get() mutableCopy]);
mutableRequest.get().attribution = NSURLRequestAttributionUser;
nsRequest = WTFMove(mutableRequest);
}
appPrivacyReportTestingData().didLoadAppInitiatedRequest(nsRequest.get().attribution == NSURLRequestAttributionDeveloper);
#endif
auto& sessionSet = sessionSetForPage(webPageProxyID);
RetainPtr<NSURLSessionWebSocketTask> task = [sessionSet.sessionWithCredentialStorage.session webSocketTaskWithRequest:nsRequest.get()];
// Although the WebSocket protocol allows full 64-bit lengths, Chrome and Firefox limit the length to 2^63 - 1
task.get().maximumMessageSize = 0x7FFFFFFFFFFFFFFFull;
return makeUnique<WebSocketTask>(channel, webPageProxyID, sessionSet, request, clientOrigin, WTFMove(task));
}
void NetworkSessionCocoa::addWebSocketTask(WebPageProxyIdentifier webPageProxyID, WebSocketTask& task)
{
auto addResult = sessionSetForPage(webPageProxyID).sessionWithCredentialStorage.webSocketDataTaskMap.add(task.identifier(), &task);
RELEASE_ASSERT(addResult.isNewEntry);
}
void NetworkSessionCocoa::removeWebSocketTask(SessionSet& sessionSet, WebSocketTask& task)
{
bool contained = sessionSet.sessionWithCredentialStorage.webSocketDataTaskMap.remove(task.identifier());
RELEASE_ASSERT(contained);
}
#endif // HAVE(NSURLSESSION_WEBSOCKET)
void NetworkSessionCocoa::addWebPageNetworkParameters(WebPageProxyIdentifier pageID, WebPageNetworkParameters&& parameters)
{
auto addResult1 = m_perParametersSessionSets.add(parameters, nullptr);
if (auto set = addResult1.iterator->value) {
m_perPageSessionSets.add(pageID, *set);
return;
}
auto addResult2 = m_perPageSessionSets.add(pageID, SessionSet::create());
ASSERT(addResult2.isNewEntry);
RetainPtr<NSURLSessionConfiguration> configuration = adoptNS([m_defaultSessionSet->sessionWithCredentialStorage.session.get().configuration copy]);
#if HAVE(CFNETWORK_NSURLSESSION_ATTRIBUTED_BUNDLE_IDENTIFIER) && USE(APPLE_INTERNAL_SDK)
if ([configuration respondsToSelector:@selector(_attributedBundleIdentifier)])
configuration.get()._attributedBundleIdentifier = parameters.attributedBundleIdentifier();
#endif
initializeNSURLSessionsInSet(addResult2.iterator->value.get(), configuration.get());
addResult1.iterator->value = addResult2.iterator->value.get();
m_attributedBundleIdentifierFromPageIdentifiers.add(pageID, parameters.attributedBundleIdentifier());
}
void NetworkSessionCocoa::requestResource(WebPageProxyIdentifier pageID, WebCore::ResourceRequest&& request, CompletionHandler<void(const IPC::DataReference&, WebCore::ResourceResponse&&, WebCore::ResourceError&&)>&& completionHandler)
{
auto session = sessionWrapperForTask(pageID, request, WebCore::StoredCredentialsPolicy::Use, std::nullopt).session;
auto nsRequest = request.nsURLRequest(WebCore::HTTPBodyUpdatePolicy::UpdateHTTPBody);
auto completionBlock = makeBlockPtr([completionHandler = WTFMove(completionHandler)] (NSData *data, NSURLResponse *response, NSError *error) mutable {
completionHandler(IPC::DataReference { static_cast<const uint8_t*>(data.bytes), data.length }, WebCore::ResourceResponse { response }, WebCore::ResourceError { error });
});
[[session dataTaskWithRequest:nsRequest completionHandler:completionBlock.get()] resume];
}
void NetworkSessionCocoa::removeWebPageNetworkParameters(WebPageProxyIdentifier pageID)
{
m_perPageSessionSets.remove(pageID);
m_attributedBundleIdentifierFromPageIdentifiers.remove(pageID);
}
size_t NetworkSessionCocoa::countNonDefaultSessionSets() const
{
HashSet<Ref<SessionSet>> uniqueSets;
for (auto& set : m_perPageSessionSets.values())
uniqueSets.add(set);
return uniqueSets.size();
}
Vector<WebCore::SecurityOriginData> NetworkSessionCocoa::hostNamesWithAlternativeServices() const
{
#if HAVE(CFNETWORK_ALTERNATIVE_SERVICE)
Vector<WebCore::SecurityOriginData> origins;
_NSHTTPAlternativeServicesStorage* storage = m_defaultSessionSet->sessionWithCredentialStorage.session.get().configuration._alternativeServicesStorage;
NSArray<_NSHTTPAlternativeServiceEntry *> *entries = [storage HTTPServiceEntriesWithFilter:_NSHTTPAlternativeServicesFilter.emptyFilter];
for (_NSHTTPAlternativeServiceEntry* entry in entries) {
WebCore::SecurityOriginData origin = { "https"_s, entry.host, entry.port };
origins.append(origin);
}
return origins;
#else
return { };
#endif
}
void NetworkSessionCocoa::deleteAlternativeServicesForHostNames(const Vector<String>& hosts)
{
#if HAVE(CFNETWORK_ALTERNATIVE_SERVICE)
_NSHTTPAlternativeServicesStorage* storage = m_defaultSessionSet->sessionWithCredentialStorage.session.get().configuration._alternativeServicesStorage;
for (auto& host : hosts)
[storage removeHTTPAlternativeServiceEntriesWithRegistrableDomain:host];
#else
UNUSED_PARAM(hosts);
#endif
}
void NetworkSessionCocoa::clearAlternativeServices(WallTime modifiedSince)
{
#if HAVE(CFNETWORK_ALTERNATIVE_SERVICE)
_NSHTTPAlternativeServicesStorage* storage = m_defaultSessionSet->sessionWithCredentialStorage.session.get().configuration._alternativeServicesStorage;
NSTimeInterval timeInterval = modifiedSince.secondsSinceEpoch().seconds();
NSDate *date = [NSDate dateWithTimeIntervalSince1970:timeInterval];
[storage removeHTTPAlternativeServiceEntriesCreatedAfterDate:date];
#else
UNUSED_PARAM(modifiedSince);
#endif
}
#if USE(APPLE_INTERNAL_SDK)
#if ENABLE(APP_PRIVACY_REPORT) && HAVE(SYMPTOMS_FRAMEWORK)
static bool isActingOnBehalfOfAFullWebBrowser(const String& bundleID)
{
return bundleID == "com.apple.webbookmarksd";
}
#endif
void NetworkSessionCocoa::removeNetworkWebsiteData(std::optional<WallTime> modifiedSince, std::optional<HashSet<WebCore::RegistrableDomain>>&& domains, CompletionHandler<void()>&& completionHandler)
{
#if ENABLE(APP_PRIVACY_REPORT) && HAVE(SYMPTOMS_FRAMEWORK)
// FIXME: Add automated test for this once rdar://81420753 is resolved.
if (sessionID().isEphemeral()) {
completionHandler();
return;
}
auto bundleID = WebCore::applicationBundleIdentifier();
if (!isParentProcessAFullWebBrowser(networkProcess()) && !isActingOnBehalfOfAFullWebBrowser(bundleID))
return completionHandler();
if (!SymptomAnalyticsLibrary()
|| !SymptomPresentationFeedLibrary()
|| !SymptomPresentationLiteLibrary()
|| !getAnalyticsWorkspaceClass()
|| !getUsageFeedClass()
|| !canLoadkSymptomAnalyticsServiceEndpoint()) {
completionHandler();
return;
}
RetainPtr<AnalyticsWorkspace> workspace = adoptNS([allocAnalyticsWorkspaceInstance() initWorkspaceWithService:getkSymptomAnalyticsServiceEndpoint()]);
RetainPtr<UsageFeed> usageFeed = adoptNS([allocUsageFeedInstance() initWithWorkspace:workspace.get()]);
if (![usageFeed.get() respondsToSelector:@selector(performNetworkDomainsActionWithOptions:reply:)]
|| !canLoadkSymptomAnalyticsServiceDomainTrackingClearHistoryKey()
|| !canLoadkSymptomAnalyticsServiceDomainTrackingClearHistoryBundleIDs()
|| !canLoadkSymptomAnalyticsServiceDomainTrackingClearHistoryStartDate()
|| !canLoadkSymptomAnalyticsServiceDomainTrackingClearHistoryEndDate()) {
completionHandler();
return;
}
auto *startDate = [NSDate distantPast];
if (modifiedSince) {
NSTimeInterval timeInterval = modifiedSince.value().secondsSinceEpoch().seconds();
startDate = [NSDate dateWithTimeIntervalSince1970:timeInterval];
}
auto contextArray = adoptNS([[NSMutableArray alloc] init]);
if (domains) {
contextArray = createNSArray(*domains, [&] (auto domain) {
return [NSString stringWithUTF8String:domain.string().utf8().data()];
});
}
if (isActingOnBehalfOfAFullWebBrowser(bundleID))
bundleID = "com.apple.mobilesafari";
NSDictionary *options = @{
(id)getkSymptomAnalyticsServiceDomainTrackingClearHistoryKey(): @{
(id)getkSymptomAnalyticsServiceDomainTrackingClearHistoryBundleIDs(): @{
bundleID : contextArray.get(),
},
(id)getkSymptomAnalyticsServiceDomainTrackingClearHistoryStartDate(): startDate,
(id)getkSymptomAnalyticsServiceDomainTrackingClearHistoryEndDate(): [NSDate distantFuture]
}
};
bool result = [usageFeed performNetworkDomainsActionWithOptions:options reply:makeBlockPtr([completionHandler = WTFMove(completionHandler)](NSDictionary *reply, NSError *error) mutable {
if (error)
LOG(NetworkSession, "Error deleting network domain data %" PUBLIC_LOG_STRING, error);
completionHandler();
}).get()];
if (!result)
LOG(NetworkSession, "Error deleting network domain data: invalid parameter or failure to contact the service");
#else
UNUSED_PARAM(modifiedSince);
completionHandler();
#endif
}
#endif // USE(APPLE_INTERNAL_SDK)
} // namespace WebKit