2 * Copyright (C) 2015-2017 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
27 #import "NetworkSessionCocoa.h"
29 #if USE(NETWORK_SESSION)
31 #import "AuthenticationManager.h"
32 #import "DataReference.h"
34 #import "LegacyCustomProtocolManager.h"
36 #import "NetworkLoad.h"
37 #import "NetworkProcess.h"
38 #import "NetworkSessionCreationParameters.h"
39 #import "SessionTracker.h"
40 #import <Foundation/NSURLSession.h>
41 #import <WebCore/Credential.h>
42 #import <WebCore/FormDataStreamMac.h>
43 #import <WebCore/FrameLoaderTypes.h>
44 #import <WebCore/NetworkStorageSession.h>
45 #import <WebCore/NotImplemented.h>
46 #import <WebCore/ResourceError.h>
47 #import <WebCore/ResourceRequest.h>
48 #import <WebCore/ResourceResponse.h>
49 #import <WebCore/SharedBuffer.h>
50 #import <WebCore/URL.h>
51 #import <WebCore/WebCoreURLResponse.h>
52 #import <pal/spi/cf/CFNetworkSPI.h>
53 #import <wtf/MainThread.h>
54 #import <wtf/NeverDestroyed.h>
56 using namespace WebKit;
58 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300) || (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000)
59 @interface NSURLSessionConfiguration (WKStaging)
60 @property (nullable, copy) NSSet *_suppressedAutoAddedHTTPHeaders;
64 static NSURLSessionResponseDisposition toNSURLSessionResponseDisposition(WebCore::PolicyAction disposition)
66 switch (disposition) {
67 case WebCore::PolicyAction::Ignore:
68 return NSURLSessionResponseCancel;
69 case WebCore::PolicyAction::Use:
70 return NSURLSessionResponseAllow;
71 case WebCore::PolicyAction::Download:
72 return NSURLSessionResponseBecomeDownload;
76 static NSURLSessionAuthChallengeDisposition toNSURLSessionAuthChallengeDisposition(WebKit::AuthenticationChallengeDisposition disposition)
78 switch (disposition) {
79 case WebKit::AuthenticationChallengeDisposition::UseCredential:
80 return NSURLSessionAuthChallengeUseCredential;
81 case WebKit::AuthenticationChallengeDisposition::PerformDefaultHandling:
82 return NSURLSessionAuthChallengePerformDefaultHandling;
83 case WebKit::AuthenticationChallengeDisposition::Cancel:
84 return NSURLSessionAuthChallengeCancelAuthenticationChallenge;
85 case WebKit::AuthenticationChallengeDisposition::RejectProtectionSpace:
86 return NSURLSessionAuthChallengeRejectProtectionSpace;
90 static WebCore::NetworkLoadPriority toNetworkLoadPriority(float priority)
92 if (priority <= NSURLSessionTaskPriorityLow)
93 return WebCore::NetworkLoadPriority::Low;
94 if (priority >= NSURLSessionTaskPriorityHigh)
95 return WebCore::NetworkLoadPriority::High;
96 return WebCore::NetworkLoadPriority::Medium;
99 @interface WKNetworkSessionDelegate : NSObject <NSURLSessionDataDelegate> {
100 RefPtr<WebKit::NetworkSessionCocoa> _session;
101 bool _withCredentials;
104 - (id)initWithNetworkSession:(WebKit::NetworkSessionCocoa&)session withCredentials:(bool)withCredentials;
105 - (void)sessionInvalidated;
109 @implementation WKNetworkSessionDelegate
111 - (id)initWithNetworkSession:(WebKit::NetworkSessionCocoa&)session withCredentials:(bool)withCredentials
118 _withCredentials = withCredentials;
123 - (void)sessionInvalidated
128 - (NetworkDataTaskCocoa*)existingTask:(NSURLSessionTask *)task
136 auto storedCredentialsPolicy = _withCredentials ? WebCore::StoredCredentialsPolicy::Use : WebCore::StoredCredentialsPolicy::DoNotUse;
137 return _session->dataTaskForIdentifier(task.taskIdentifier, storedCredentialsPolicy);
140 - (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(nullable NSError *)error
145 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
147 if (auto* networkDataTask = [self existingTask:task])
148 networkDataTask->didSendData(totalBytesSent, totalBytesExpectedToSend);
151 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task needNewBodyStream:(void (^)(NSInputStream *bodyStream))completionHandler
153 auto* networkDataTask = [self existingTask:task];
154 if (!networkDataTask) {
155 completionHandler(nil);
159 auto* body = networkDataTask->firstRequest().httpBody();
161 completionHandler(nil);
165 completionHandler(WebCore::createHTTPBodyNSInputStream(*body).get());
168 #if USE(CFNETWORK_IGNORE_HSTS)
169 static NSURLRequest* downgradeRequest(NSURLRequest *request)
171 NSMutableURLRequest *nsMutableRequest = [[request mutableCopy] autorelease];
172 if ([nsMutableRequest.URL.scheme isEqualToString:@"https"]) {
173 NSURLComponents *components = [NSURLComponents componentsWithURL:nsMutableRequest.URL resolvingAgainstBaseURL:NO];
174 components.scheme = @"http";
175 [nsMutableRequest setURL:components.URL];
176 ASSERT([nsMutableRequest.URL.scheme isEqualToString:@"http"]);
177 return nsMutableRequest;
180 ASSERT_NOT_REACHED();
185 static NSURLRequest* updateIgnoreStrictTransportSecuritySettingIfNecessary(NSURLRequest *request, bool shouldIgnoreHSTS)
187 #if USE(CFNETWORK_IGNORE_HSTS)
188 if ([request.URL.scheme isEqualToString:@"https"] && shouldIgnoreHSTS && ignoreHSTS(request)) {
189 // The request was upgraded for some other reason than HSTS.
190 // Don't ignore HSTS to avoid the risk of another downgrade.
191 NSMutableURLRequest *nsMutableRequest = [[request mutableCopy] autorelease];
192 setIgnoreHSTS(nsMutableRequest, false);
193 return nsMutableRequest;
196 if ([request.URL.scheme isEqualToString:@"http"] && ignoreHSTS(request) != shouldIgnoreHSTS) {
197 NSMutableURLRequest *nsMutableRequest = [[request mutableCopy] autorelease];
198 setIgnoreHSTS(nsMutableRequest, shouldIgnoreHSTS);
199 return nsMutableRequest;
202 UNUSED_PARAM(shouldIgnoreHSTS);
208 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest *))completionHandler
210 auto taskIdentifier = task.taskIdentifier;
211 LOG(NetworkSession, "%llu willPerformHTTPRedirection from %s to %s", taskIdentifier, response.URL.absoluteString.UTF8String, request.URL.absoluteString.UTF8String);
213 if (auto* networkDataTask = [self existingTask:task]) {
214 auto completionHandlerCopy = Block_copy(completionHandler);
216 bool shouldIgnoreHSTS = false;
217 #if USE(CFNETWORK_IGNORE_HSTS)
218 shouldIgnoreHSTS = schemeWasUpgradedDueToDynamicHSTS(request) && WebCore::NetworkStorageSession::storageSession(_session->sessionID())->shouldBlockCookies(request);
219 if (shouldIgnoreHSTS) {
220 request = downgradeRequest(request);
221 ASSERT([request.URL.scheme isEqualToString:@"http"]);
222 LOG(NetworkSession, "%llu Downgraded %s from https to http", taskIdentifier, request.URL.absoluteString.UTF8String);
226 networkDataTask->willPerformHTTPRedirection(response, request, [completionHandlerCopy, taskIdentifier, shouldIgnoreHSTS](auto&& request) {
228 LOG(NetworkSession, "%llu willPerformHTTPRedirection completionHandler (%s)", taskIdentifier, request.url().string().utf8().data());
230 UNUSED_PARAM(taskIdentifier);
232 auto nsRequest = request.nsURLRequest(WebCore::UpdateHTTPBody);
233 nsRequest = updateIgnoreStrictTransportSecuritySettingIfNecessary(nsRequest, shouldIgnoreHSTS);
234 completionHandlerCopy(nsRequest);
235 Block_release(completionHandlerCopy);
238 LOG(NetworkSession, "%llu willPerformHTTPRedirection completionHandler (nil)", taskIdentifier);
239 completionHandler(nil);
243 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask*)task _schemeUpgraded:(NSURLRequest*)request completionHandler:(void (^)(NSURLRequest*))completionHandler
245 auto taskIdentifier = task.taskIdentifier;
246 LOG(NetworkSession, "%llu _schemeUpgraded %s", taskIdentifier, request.URL.absoluteString.UTF8String);
248 if (auto* networkDataTask = [self existingTask:task]) {
249 bool shouldIgnoreHSTS = false;
250 #if USE(CFNETWORK_IGNORE_HSTS)
251 shouldIgnoreHSTS = schemeWasUpgradedDueToDynamicHSTS(request) && WebCore::NetworkStorageSession::storageSession(_session->sessionID())->shouldBlockCookies(request);
252 if (shouldIgnoreHSTS) {
253 request = downgradeRequest(request);
254 ASSERT([request.URL.scheme isEqualToString:@"http"]);
255 LOG(NetworkSession, "%llu Downgraded %s from https to http", taskIdentifier, request.URL.absoluteString.UTF8String);
259 auto completionHandlerCopy = Block_copy(completionHandler);
260 networkDataTask->willPerformHTTPRedirection(WebCore::synthesizeRedirectResponseIfNecessary([task currentRequest], request, nil), request, [completionHandlerCopy, taskIdentifier, shouldIgnoreHSTS](auto&& request) {
262 LOG(NetworkSession, "%llu _schemeUpgraded completionHandler (%s)", taskIdentifier, request.url().string().utf8().data());
264 UNUSED_PARAM(taskIdentifier);
266 auto nsRequest = request.nsURLRequest(WebCore::UpdateHTTPBody);
267 nsRequest = updateIgnoreStrictTransportSecuritySettingIfNecessary(nsRequest, shouldIgnoreHSTS);
268 completionHandlerCopy(nsRequest);
269 Block_release(completionHandlerCopy);
272 LOG(NetworkSession, "%llu _schemeUpgraded completionHandler (nil)", taskIdentifier);
273 completionHandler(nil);
277 - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask willCacheResponse:(NSCachedURLResponse *)proposedResponse completionHandler:(void (^)(NSCachedURLResponse * _Nullable cachedResponse))completionHandler
280 completionHandler(nil);
284 // FIXME: remove if <rdar://problem/20001985> is ever resolved.
285 if ([proposedResponse.response respondsToSelector:@selector(allHeaderFields)]
286 && [[(id)proposedResponse.response allHeaderFields] objectForKey:@"Content-Range"])
287 completionHandler(nil);
289 completionHandler(proposedResponse);
292 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
294 auto taskIdentifier = task.taskIdentifier;
295 LOG(NetworkSession, "%llu didReceiveChallenge", taskIdentifier);
297 // Proxy authentication is handled by CFNetwork internally. We can get here if the user cancels
298 // CFNetwork authentication dialog, and we shouldn't ask the client to display another one in that case.
299 if (challenge.protectionSpace.isProxy) {
300 completionHandler(NSURLSessionAuthChallengeUseCredential, nil);
304 // Handle server trust evaluation at platform-level if requested, for performance reasons.
305 if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust] && !NetworkProcess::singleton().canHandleHTTPSServerTrustEvaluation()) {
306 if (NetworkSessionCocoa::allowsSpecificHTTPSCertificateForHost(challenge))
307 completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
309 completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, nil);
313 if (auto* networkDataTask = [self existingTask:task]) {
314 WebCore::AuthenticationChallenge authenticationChallenge(challenge);
315 auto completionHandlerCopy = Block_copy(completionHandler);
316 auto sessionID = _session->sessionID();
317 auto challengeCompletionHandler = [completionHandlerCopy, sessionID, authenticationChallenge, taskIdentifier, partition = networkDataTask->partition()](WebKit::AuthenticationChallengeDisposition disposition, const WebCore::Credential& credential)
320 LOG(NetworkSession, "%llu didReceiveChallenge completionHandler %d", taskIdentifier, disposition);
322 UNUSED_PARAM(taskIdentifier);
324 #if !USE(CREDENTIAL_STORAGE_WITH_NETWORK_SESSION)
325 UNUSED_PARAM(sessionID);
326 UNUSED_PARAM(authenticationChallenge);
328 if (credential.persistence() == WebCore::CredentialPersistenceForSession && authenticationChallenge.protectionSpace().isPasswordBased()) {
330 WebCore::Credential nonPersistentCredential(credential.user(), credential.password(), WebCore::CredentialPersistenceNone);
331 WebCore::URL urlToStore;
332 if (authenticationChallenge.failureResponse().httpStatusCode() == 401)
333 urlToStore = authenticationChallenge.failureResponse().url();
334 if (auto storageSession = WebCore::NetworkStorageSession::storageSession(sessionID))
335 storageSession->credentialStorage().set(partition, nonPersistentCredential, authenticationChallenge.protectionSpace(), urlToStore);
337 ASSERT_NOT_REACHED();
339 completionHandlerCopy(toNSURLSessionAuthChallengeDisposition(disposition), nonPersistentCredential.nsCredential());
342 completionHandlerCopy(toNSURLSessionAuthChallengeDisposition(disposition), credential.nsCredential());
343 Block_release(completionHandlerCopy);
345 networkDataTask->didReceiveChallenge(challenge, WTFMove(challengeCompletionHandler));
347 auto downloadID = _session->downloadID(task.taskIdentifier);
348 if (downloadID.downloadID()) {
349 if (auto* download = WebKit::NetworkProcess::singleton().downloadManager().download(downloadID)) {
350 // Received an authentication challenge for a download being resumed.
351 WebCore::AuthenticationChallenge authenticationChallenge { challenge };
352 auto completionHandlerCopy = Block_copy(completionHandler);
353 auto challengeCompletionHandler = [completionHandlerCopy, authenticationChallenge](WebKit::AuthenticationChallengeDisposition disposition, const WebCore::Credential& credential) {
354 completionHandlerCopy(toNSURLSessionAuthChallengeDisposition(disposition), credential.nsCredential());
355 Block_release(completionHandlerCopy);
357 download->didReceiveChallenge(challenge, WTFMove(challengeCompletionHandler));
361 LOG(NetworkSession, "%llu didReceiveChallenge completionHandler (cancel)", taskIdentifier);
362 completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
366 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
371 LOG(NetworkSession, "%llu didCompleteWithError %@", task.taskIdentifier, error);
372 if (auto* networkDataTask = [self existingTask:task])
373 networkDataTask->didCompleteWithError(error, networkDataTask->networkLoadMetrics());
375 auto downloadID = _session->takeDownloadID(task.taskIdentifier);
376 if (downloadID.downloadID()) {
377 if (auto* download = WebKit::NetworkProcess::singleton().downloadManager().download(downloadID)) {
378 NSData *resumeData = nil;
379 if (id userInfo = error.userInfo) {
380 if ([userInfo isKindOfClass:[NSDictionary class]])
381 resumeData = userInfo[@"NSURLSessionDownloadTaskResumeData"];
384 if (resumeData && [resumeData isKindOfClass:[NSData class]])
385 download->didFail(error, { static_cast<const uint8_t*>(resumeData.bytes), resumeData.length });
387 download->didFail(error, { });
393 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics
395 LOG(NetworkSession, "%llu didFinishCollectingMetrics", task.taskIdentifier);
396 if (auto* networkDataTask = [self existingTask:task]) {
397 NSURLSessionTaskTransactionMetrics *m = metrics.transactionMetrics.lastObject;
398 NSDate *fetchStartDate = m.fetchStartDate;
399 NSTimeInterval domainLookupStartInterval = m.domainLookupStartDate ? [m.domainLookupStartDate timeIntervalSinceDate:fetchStartDate] : -1;
400 NSTimeInterval domainLookupEndInterval = m.domainLookupEndDate ? [m.domainLookupEndDate timeIntervalSinceDate:fetchStartDate] : -1;
401 NSTimeInterval connectStartInterval = m.connectStartDate ? [m.connectStartDate timeIntervalSinceDate:fetchStartDate] : -1;
402 NSTimeInterval secureConnectionStartInterval = m.secureConnectionStartDate ? [m.secureConnectionStartDate timeIntervalSinceDate:fetchStartDate] : -1;
403 NSTimeInterval connectEndInterval = m.connectEndDate ? [m.connectEndDate timeIntervalSinceDate:fetchStartDate] : -1;
404 NSTimeInterval requestStartInterval = [m.requestStartDate timeIntervalSinceDate:fetchStartDate];
405 NSTimeInterval responseStartInterval = [m.responseStartDate timeIntervalSinceDate:fetchStartDate];
406 NSTimeInterval responseEndInterval = [m.responseEndDate timeIntervalSinceDate:fetchStartDate];
408 auto& networkLoadMetrics = networkDataTask->networkLoadMetrics();
409 networkLoadMetrics.domainLookupStart = Seconds(domainLookupStartInterval);
410 networkLoadMetrics.domainLookupEnd = Seconds(domainLookupEndInterval);
411 networkLoadMetrics.connectStart = Seconds(connectStartInterval);
412 networkLoadMetrics.secureConnectionStart = Seconds(secureConnectionStartInterval);
413 networkLoadMetrics.connectEnd = Seconds(connectEndInterval);
414 networkLoadMetrics.requestStart = Seconds(requestStartInterval);
415 networkLoadMetrics.responseStart = Seconds(responseStartInterval);
416 networkLoadMetrics.responseEnd = Seconds(responseEndInterval);
417 networkLoadMetrics.markComplete();
418 networkLoadMetrics.protocol = String(m.networkProtocolName);
420 if (networkDataTask->shouldCaptureExtraNetworkLoadMetrics()) {
421 networkLoadMetrics.priority = toNetworkLoadPriority(task.priority);
423 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300) || (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000)
424 networkLoadMetrics.remoteAddress = String(m._remoteAddressAndPort);
425 networkLoadMetrics.connectionIdentifier = String([m._connectionIdentifier UUIDString]);
428 __block WebCore::HTTPHeaderMap requestHeaders;
429 [m.request.allHTTPHeaderFields enumerateKeysAndObjectsUsingBlock:^(NSString *name, NSString *value, BOOL *) {
430 requestHeaders.set(String(name), String(value));
432 networkLoadMetrics.requestHeaders = WTFMove(requestHeaders);
434 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300) || (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000)
435 uint64_t requestHeaderBytesSent = 0;
436 uint64_t responseHeaderBytesReceived = 0;
437 uint64_t responseBodyBytesReceived = 0;
438 uint64_t responseBodyDecodedSize = 0;
440 for (NSURLSessionTaskTransactionMetrics *transactionMetrics in metrics.transactionMetrics) {
441 requestHeaderBytesSent += transactionMetrics._requestHeaderBytesSent;
442 responseHeaderBytesReceived += transactionMetrics._responseHeaderBytesReceived;
443 responseBodyBytesReceived += transactionMetrics._responseBodyBytesReceived;
444 responseBodyDecodedSize += transactionMetrics._responseBodyBytesDecoded ? transactionMetrics._responseBodyBytesDecoded : transactionMetrics._responseBodyBytesReceived;
447 networkLoadMetrics.requestHeaderBytesSent = requestHeaderBytesSent;
448 networkLoadMetrics.requestBodyBytesSent = task.countOfBytesSent;
449 networkLoadMetrics.responseHeaderBytesReceived = responseHeaderBytesReceived;
450 networkLoadMetrics.responseBodyBytesReceived = responseBodyBytesReceived;
451 networkLoadMetrics.responseBodyDecodedSize = responseBodyDecodedSize;
457 - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler
459 auto taskIdentifier = dataTask.taskIdentifier;
460 LOG(NetworkSession, "%llu didReceiveResponse", taskIdentifier);
461 if (auto* networkDataTask = [self existingTask:dataTask]) {
462 ASSERT(RunLoop::isMain());
464 // Avoid MIME type sniffing if the response comes back as 304 Not Modified.
465 int statusCode = [response respondsToSelector:@selector(statusCode)] ? [(id)response statusCode] : 0;
466 if (statusCode != 304) {
467 bool isMainResourceLoad = networkDataTask->firstRequest().requester() == WebCore::ResourceRequest::Requester::Main;
468 WebCore::adjustMIMETypeIfNecessary(response._CFURLResponse, isMainResourceLoad);
471 WebCore::ResourceResponse resourceResponse(response);
472 // Lazy initialization is not helpful in the WebKit2 case because we always end up initializing
473 // all the fields when sending the response to the WebContent process over IPC.
474 resourceResponse.disableLazyInitialization();
476 // FIXME: This cannot be eliminated until other code no longer relies on ResourceResponse's
477 // NetworkLoadMetrics. For example, PerformanceTiming.
478 copyTimingData([dataTask _timingData], resourceResponse.deprecatedNetworkLoadMetrics());
480 auto completionHandlerCopy = Block_copy(completionHandler);
481 networkDataTask->didReceiveResponse(WTFMove(resourceResponse), [completionHandlerCopy, taskIdentifier](WebCore::PolicyAction policyAction) {
483 LOG(NetworkSession, "%llu didReceiveResponse completionHandler (%d)", taskIdentifier, policyAction);
485 UNUSED_PARAM(taskIdentifier);
487 completionHandlerCopy(toNSURLSessionResponseDisposition(policyAction));
488 Block_release(completionHandlerCopy);
491 LOG(NetworkSession, "%llu didReceiveResponse completionHandler (cancel)", taskIdentifier);
492 completionHandler(NSURLSessionResponseCancel);
496 - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
498 if (auto* networkDataTask = [self existingTask:dataTask])
499 networkDataTask->didReceiveData(WebCore::SharedBuffer::create(data));
502 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
507 auto downloadID = _session->takeDownloadID([downloadTask taskIdentifier]);
508 if (auto* download = WebKit::NetworkProcess::singleton().downloadManager().download(downloadID))
509 download->didFinish();
512 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
517 ASSERT_WITH_MESSAGE(![self existingTask:downloadTask], "The NetworkDataTask should be destroyed immediately after didBecomeDownloadTask returns");
519 auto downloadID = _session->downloadID([downloadTask taskIdentifier]);
520 if (auto* download = WebKit::NetworkProcess::singleton().downloadManager().download(downloadID))
521 download->didReceiveData(bytesWritten);
524 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes
532 - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask
534 if (auto* networkDataTask = [self existingTask:dataTask]) {
535 Ref<NetworkDataTaskCocoa> protectedNetworkDataTask(*networkDataTask);
536 auto downloadID = networkDataTask->pendingDownloadID();
537 auto& downloadManager = WebKit::NetworkProcess::singleton().downloadManager();
538 auto download = std::make_unique<WebKit::Download>(downloadManager, downloadID, downloadTask, _session->sessionID(), networkDataTask->suggestedFilename());
539 networkDataTask->transferSandboxExtensionToDownload(*download);
540 ASSERT(WebCore::FileSystem::fileExists(networkDataTask->pendingDownloadLocation()));
541 download->didCreateDestination(networkDataTask->pendingDownloadLocation());
542 downloadManager.dataTaskBecameDownloadTask(downloadID, WTFMove(download));
544 _session->addDownloadID([downloadTask taskIdentifier], downloadID);
552 static bool usesNetworkCache { false };
555 static bool sessionsCreated = false;
558 static NSURLSessionConfiguration *configurationForSessionID(const PAL::SessionID& session)
560 if (session.isEphemeral()) {
561 NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
562 configuration._shouldSkipPreferredClientCertificateLookup = YES;
563 return configuration;
565 return [NSURLSessionConfiguration defaultSessionConfiguration];
568 static RetainPtr<CFDataRef>& globalSourceApplicationAuditTokenData()
570 static NeverDestroyed<RetainPtr<CFDataRef>> sourceApplicationAuditTokenData;
571 return sourceApplicationAuditTokenData.get();
574 static String& globalSourceApplicationBundleIdentifier()
576 static NeverDestroyed<String> sourceApplicationBundleIdentifier;
577 return sourceApplicationBundleIdentifier.get();
580 static String& globalSourceApplicationSecondaryIdentifier()
582 static NeverDestroyed<String> sourceApplicationSecondaryIdentifier;
583 return sourceApplicationSecondaryIdentifier.get();
587 static String& globalCTDataConnectionServiceType()
589 static NeverDestroyed<String> ctDataConnectionServiceType;
590 return ctDataConnectionServiceType.get();
594 void NetworkSessionCocoa::setSourceApplicationAuditTokenData(RetainPtr<CFDataRef>&& data)
596 ASSERT(!sessionsCreated);
597 globalSourceApplicationAuditTokenData() = data;
600 void NetworkSessionCocoa::setSourceApplicationBundleIdentifier(const String& identifier)
602 ASSERT(!sessionsCreated);
603 globalSourceApplicationBundleIdentifier() = identifier;
606 void NetworkSessionCocoa::setSourceApplicationSecondaryIdentifier(const String& identifier)
608 ASSERT(!sessionsCreated);
609 globalSourceApplicationSecondaryIdentifier() = identifier;
612 void NetworkSessionCocoa::setUsesNetworkCache(bool value)
614 usesNetworkCache = value;
618 void NetworkSessionCocoa::setCTDataConnectionServiceType(const String& type)
620 ASSERT(!sessionsCreated);
621 globalCTDataConnectionServiceType() = type;
625 Ref<NetworkSession> NetworkSessionCocoa::create(NetworkSessionCreationParameters&& parameters)
627 return adoptRef(*new NetworkSessionCocoa(WTFMove(parameters)));
630 NetworkSessionCocoa::NetworkSessionCocoa(NetworkSessionCreationParameters&& parameters)
631 : NetworkSession(parameters.sessionID)
632 , m_boundInterfaceIdentifier(parameters.boundInterfaceIdentifier)
634 relaxAdoptionRequirement();
637 sessionsCreated = true;
640 NSURLSessionConfiguration *configuration = configurationForSessionID(m_sessionID);
642 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300) || (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000)
643 // Without this, CFNetwork would sometimes add a Content-Type header to our requests (rdar://problem/34748470).
644 if ([configuration respondsToSelector:@selector(_suppressedAutoAddedHTTPHeaders)])
645 configuration._suppressedAutoAddedHTTPHeaders = [NSSet setWithObject:@"Content-Type"];
648 if (parameters.allowsCellularAccess == AllowsCellularAccess::No)
649 configuration.allowsCellularAccess = NO;
651 if (usesNetworkCache)
652 configuration.URLCache = nil;
654 if (auto& data = globalSourceApplicationAuditTokenData())
655 configuration._sourceApplicationAuditTokenData = (NSData *)data.get();
657 auto& sourceApplicationBundleIdentifier = globalSourceApplicationBundleIdentifier();
658 if (!sourceApplicationBundleIdentifier.isEmpty()) {
659 configuration._sourceApplicationBundleIdentifier = sourceApplicationBundleIdentifier;
660 configuration._sourceApplicationAuditTokenData = nil;
663 auto& sourceApplicationSecondaryIdentifier = globalSourceApplicationSecondaryIdentifier();
664 if (!sourceApplicationSecondaryIdentifier.isEmpty())
665 configuration._sourceApplicationSecondaryIdentifier = sourceApplicationSecondaryIdentifier;
668 auto& ctDataConnectionServiceType = globalCTDataConnectionServiceType();
669 if (!ctDataConnectionServiceType.isEmpty())
670 configuration._CTDataConnectionServiceType = ctDataConnectionServiceType;
673 if (parameters.legacyCustomProtocolManager)
674 parameters.legacyCustomProtocolManager->registerProtocolClass(configuration);
676 #if HAVE(TIMINGDATAOPTIONS)
677 configuration._timingDataOptions = _TimingDataOptionsEnableW3CNavigationTiming;
679 setCollectsTimingData();
682 auto* storageSession = WebCore::NetworkStorageSession::storageSession(parameters.sessionID);
683 RELEASE_ASSERT(storageSession);
684 if (CFHTTPCookieStorageRef storage = storageSession->cookieStorage().get())
685 configuration.HTTPCookieStorage = [[[NSHTTPCookieStorage alloc] _initWithCFHTTPCookieStorage:storage] autorelease];
687 m_sessionWithCredentialStorageDelegate = adoptNS([[WKNetworkSessionDelegate alloc] initWithNetworkSession:*this withCredentials:true]);
688 m_sessionWithCredentialStorage = [NSURLSession sessionWithConfiguration:configuration delegate:static_cast<id>(m_sessionWithCredentialStorageDelegate.get()) delegateQueue:[NSOperationQueue mainQueue]];
689 LOG(NetworkSession, "Created NetworkSession with cookieAcceptPolicy %lu", configuration.HTTPCookieStorage.cookieAcceptPolicy);
691 configuration.URLCredentialStorage = nil;
692 configuration._shouldSkipPreferredClientCertificateLookup = YES;
693 // FIXME: https://bugs.webkit.org/show_bug.cgi?id=177394
694 // configuration.HTTPCookieStorage = nil;
695 // configuration.HTTPCookieAcceptPolicy = NSHTTPCookieAcceptPolicyNever;
697 m_statelessSessionDelegate = adoptNS([[WKNetworkSessionDelegate alloc] initWithNetworkSession:*this withCredentials:false]);
698 m_statelessSession = [NSURLSession sessionWithConfiguration:configuration delegate:static_cast<id>(m_statelessSessionDelegate.get()) delegateQueue:[NSOperationQueue mainQueue]];
701 NetworkSessionCocoa::~NetworkSessionCocoa()
705 void NetworkSessionCocoa::invalidateAndCancel()
707 NetworkSession::invalidateAndCancel();
709 [m_sessionWithCredentialStorage invalidateAndCancel];
710 [m_statelessSession invalidateAndCancel];
711 [m_sessionWithCredentialStorageDelegate sessionInvalidated];
712 [m_statelessSessionDelegate sessionInvalidated];
715 void NetworkSessionCocoa::clearCredentials()
717 #if !USE(CREDENTIAL_STORAGE_WITH_NETWORK_SESSION)
718 ASSERT(m_dataTaskMapWithCredentials.isEmpty());
719 ASSERT(m_dataTaskMapWithoutState.isEmpty());
720 ASSERT(m_downloadMap.isEmpty());
721 // FIXME: Use resetWithCompletionHandler instead.
722 m_sessionWithCredentialStorage = [NSURLSession sessionWithConfiguration:m_sessionWithCredentialStorage.get().configuration delegate:static_cast<id>(m_sessionWithCredentialStorageDelegate.get()) delegateQueue:[NSOperationQueue mainQueue]];
723 m_statelessSession = [NSURLSession sessionWithConfiguration:m_statelessSession.get().configuration delegate:static_cast<id>(m_statelessSessionDelegate.get()) delegateQueue:[NSOperationQueue mainQueue]];
727 NetworkDataTaskCocoa* NetworkSessionCocoa::dataTaskForIdentifier(NetworkDataTaskCocoa::TaskIdentifier taskIdentifier, WebCore::StoredCredentialsPolicy storedCredentialsPolicy)
729 ASSERT(RunLoop::isMain());
730 if (storedCredentialsPolicy == WebCore::StoredCredentialsPolicy::Use)
731 return m_dataTaskMapWithCredentials.get(taskIdentifier);
732 return m_dataTaskMapWithoutState.get(taskIdentifier);
735 NSURLSessionDownloadTask* NetworkSessionCocoa::downloadTaskWithResumeData(NSData* resumeData)
737 return [m_sessionWithCredentialStorage downloadTaskWithResumeData:resumeData];
740 void NetworkSessionCocoa::addDownloadID(NetworkDataTaskCocoa::TaskIdentifier taskIdentifier, DownloadID downloadID)
743 ASSERT(!m_downloadMap.contains(taskIdentifier));
744 for (auto idInMap : m_downloadMap.values())
745 ASSERT(idInMap != downloadID);
747 m_downloadMap.add(taskIdentifier, downloadID);
750 DownloadID NetworkSessionCocoa::downloadID(NetworkDataTaskCocoa::TaskIdentifier taskIdentifier)
752 ASSERT(m_downloadMap.get(taskIdentifier).downloadID());
753 return m_downloadMap.get(taskIdentifier);
756 DownloadID NetworkSessionCocoa::takeDownloadID(NetworkDataTaskCocoa::TaskIdentifier taskIdentifier)
758 auto downloadID = m_downloadMap.take(taskIdentifier);
762 static bool certificatesMatch(SecTrustRef trust1, SecTrustRef trust2)
764 if (!trust1 || !trust2)
767 CFIndex count1 = SecTrustGetCertificateCount(trust1);
768 CFIndex count2 = SecTrustGetCertificateCount(trust2);
769 if (count1 != count2)
772 for (CFIndex i = 0; i < count1; i++) {
773 auto cert1 = SecTrustGetCertificateAtIndex(trust1, i);
774 auto cert2 = SecTrustGetCertificateAtIndex(trust2, i);
775 RELEASE_ASSERT(cert1);
776 RELEASE_ASSERT(cert2);
777 if (!CFEqual(cert1, cert2))
784 bool NetworkSessionCocoa::allowsSpecificHTTPSCertificateForHost(const WebCore::AuthenticationChallenge& challenge)
786 const String& host = challenge.protectionSpace().host();
787 NSArray *certificates = [NSURLRequest allowsSpecificHTTPSCertificateForHost:host];
791 bool requireServerCertificates = challenge.protectionSpace().authenticationScheme() == WebCore::ProtectionSpaceAuthenticationScheme::ProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested;
792 RetainPtr<SecPolicyRef> policy = adoptCF(SecPolicyCreateSSL(requireServerCertificates, host.createCFString().get()));
794 SecTrustRef trustRef = nullptr;
795 if (SecTrustCreateWithCertificates((CFArrayRef)certificates, policy.get(), &trustRef) != noErr)
797 RetainPtr<SecTrustRef> trust = adoptCF(trustRef);
799 return certificatesMatch(trust.get(), challenge.nsURLAuthenticationChallenge().protectionSpace.serverTrust);