Remove legacyCustomProtocolManager from NetworkSessionCreationParameters
[WebKit-https.git] / Source / WebKit / NetworkProcess / cocoa / NetworkSessionCocoa.mm
1 /*
2  * Copyright (C) 2015-2018 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
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.
12  *
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.
24  */
25
26 #import "config.h"
27 #import "NetworkSessionCocoa.h"
28
29 #import "AuthenticationManager.h"
30 #import "DataReference.h"
31 #import "Download.h"
32 #import "LegacyCustomProtocolManager.h"
33 #import "Logging.h"
34 #import "NetworkLoad.h"
35 #import "NetworkProcess.h"
36 #import "NetworkSessionCreationParameters.h"
37 #import "SessionTracker.h"
38 #import <Foundation/NSURLSession.h>
39 #import <WebCore/Credential.h>
40 #import <WebCore/FormDataStreamMac.h>
41 #import <WebCore/FrameLoaderTypes.h>
42 #import <WebCore/NetworkStorageSession.h>
43 #import <WebCore/NotImplemented.h>
44 #import <WebCore/ResourceError.h>
45 #import <WebCore/ResourceRequest.h>
46 #import <WebCore/ResourceResponse.h>
47 #import <WebCore/SharedBuffer.h>
48 #import <WebCore/URL.h>
49 #import <WebCore/WebCoreURLResponse.h>
50 #import <pal/spi/cf/CFNetworkSPI.h>
51 #import <wtf/MainThread.h>
52 #import <wtf/NeverDestroyed.h>
53 #import <wtf/ProcessPrivilege.h>
54
55 using namespace WebKit;
56
57 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300) || (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000)
58 @interface NSURLSessionConfiguration (WKStaging)
59 @property (nullable, copy) NSSet *_suppressedAutoAddedHTTPHeaders;
60 @end
61 #endif
62
63 static NSURLSessionResponseDisposition toNSURLSessionResponseDisposition(WebCore::PolicyAction disposition)
64 {
65     switch (disposition) {
66     case WebCore::PolicyAction::Suspend:
67         LOG_ERROR("PolicyAction::Suspend encountered - Treating as PolicyAction::Ignore for now");
68         FALLTHROUGH;
69     case WebCore::PolicyAction::Ignore:
70         return NSURLSessionResponseCancel;
71     case WebCore::PolicyAction::Use:
72         return NSURLSessionResponseAllow;
73     case WebCore::PolicyAction::Download:
74         return NSURLSessionResponseBecomeDownload;
75     }
76 }
77
78 static NSURLSessionAuthChallengeDisposition toNSURLSessionAuthChallengeDisposition(WebKit::AuthenticationChallengeDisposition disposition)
79 {
80     switch (disposition) {
81     case WebKit::AuthenticationChallengeDisposition::UseCredential:
82         return NSURLSessionAuthChallengeUseCredential;
83     case WebKit::AuthenticationChallengeDisposition::PerformDefaultHandling:
84         return NSURLSessionAuthChallengePerformDefaultHandling;
85     case WebKit::AuthenticationChallengeDisposition::Cancel:
86         return NSURLSessionAuthChallengeCancelAuthenticationChallenge;
87     case WebKit::AuthenticationChallengeDisposition::RejectProtectionSpace:
88         return NSURLSessionAuthChallengeRejectProtectionSpace;
89     }
90 }
91
92 static WebCore::NetworkLoadPriority toNetworkLoadPriority(float priority)
93 {
94     if (priority <= NSURLSessionTaskPriorityLow)
95         return WebCore::NetworkLoadPriority::Low;
96     if (priority >= NSURLSessionTaskPriorityHigh)
97         return WebCore::NetworkLoadPriority::High;
98     return WebCore::NetworkLoadPriority::Medium;
99 }
100
101 @interface WKNetworkSessionDelegate : NSObject <NSURLSessionDataDelegate> {
102     RefPtr<WebKit::NetworkSessionCocoa> _session;
103     bool _withCredentials;
104 }
105
106 - (id)initWithNetworkSession:(WebKit::NetworkSessionCocoa&)session withCredentials:(bool)withCredentials;
107 - (void)sessionInvalidated;
108
109 @end
110
111 @implementation WKNetworkSessionDelegate
112
113 - (id)initWithNetworkSession:(WebKit::NetworkSessionCocoa&)session withCredentials:(bool)withCredentials
114 {
115     self = [super init];
116     if (!self)
117         return nil;
118
119     _session = &session;
120     _withCredentials = withCredentials;
121
122     return self;
123 }
124
125 - (void)sessionInvalidated
126 {
127     _session = nullptr;
128 }
129
130 - (NetworkDataTaskCocoa*)existingTask:(NSURLSessionTask *)task
131 {
132     if (!_session)
133         return nullptr;
134
135     if (!task)
136         return nullptr;
137
138     auto storedCredentialsPolicy = _withCredentials ? WebCore::StoredCredentialsPolicy::Use : WebCore::StoredCredentialsPolicy::DoNotUse;
139     return _session->dataTaskForIdentifier(task.taskIdentifier, storedCredentialsPolicy);
140 }
141
142 - (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(nullable NSError *)error
143 {
144     ASSERT(!_session);
145 }
146
147 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
148 {
149     if (auto* networkDataTask = [self existingTask:task])
150         networkDataTask->didSendData(totalBytesSent, totalBytesExpectedToSend);
151 }
152
153 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task needNewBodyStream:(void (^)(NSInputStream *bodyStream))completionHandler
154 {
155     auto* networkDataTask = [self existingTask:task];
156     if (!networkDataTask) {
157         completionHandler(nil);
158         return;
159     }
160
161     auto* body = networkDataTask->firstRequest().httpBody();
162     if (!body) {
163         completionHandler(nil);
164         return;
165     }
166
167     completionHandler(WebCore::createHTTPBodyNSInputStream(*body).get());
168 }
169
170 #if USE(CFNETWORK_IGNORE_HSTS)
171 static NSURLRequest* downgradeRequest(NSURLRequest *request)
172 {
173     NSMutableURLRequest *nsMutableRequest = [[request mutableCopy] autorelease];
174     if ([nsMutableRequest.URL.scheme isEqualToString:@"https"]) {
175         NSURLComponents *components = [NSURLComponents componentsWithURL:nsMutableRequest.URL resolvingAgainstBaseURL:NO];
176         components.scheme = @"http";
177         [nsMutableRequest setURL:components.URL];
178         ASSERT([nsMutableRequest.URL.scheme isEqualToString:@"http"]);
179         return nsMutableRequest;
180     }
181
182     ASSERT_NOT_REACHED();
183     return request;
184 }
185 #endif
186
187 static NSURLRequest* updateIgnoreStrictTransportSecuritySettingIfNecessary(NSURLRequest *request, bool shouldIgnoreHSTS)
188 {
189 #if USE(CFNETWORK_IGNORE_HSTS)
190     if ([request.URL.scheme isEqualToString:@"https"] && shouldIgnoreHSTS && ignoreHSTS(request)) {
191         // The request was upgraded for some other reason than HSTS.
192         // Don't ignore HSTS to avoid the risk of another downgrade.
193         NSMutableURLRequest *nsMutableRequest = [[request mutableCopy] autorelease];
194         setIgnoreHSTS(nsMutableRequest, false);
195         return nsMutableRequest;
196     }
197     
198     if ([request.URL.scheme isEqualToString:@"http"] && ignoreHSTS(request) != shouldIgnoreHSTS) {
199         NSMutableURLRequest *nsMutableRequest = [[request mutableCopy] autorelease];
200         setIgnoreHSTS(nsMutableRequest, shouldIgnoreHSTS);
201         return nsMutableRequest;
202     }
203 #else
204     UNUSED_PARAM(shouldIgnoreHSTS);
205 #endif
206
207     return request;
208 }
209
210 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest *))completionHandler
211 {
212     auto taskIdentifier = task.taskIdentifier;
213     LOG(NetworkSession, "%llu willPerformHTTPRedirection from %s to %s", taskIdentifier, response.URL.absoluteString.UTF8String, request.URL.absoluteString.UTF8String);
214
215     if (auto* networkDataTask = [self existingTask:task]) {
216         auto completionHandlerCopy = Block_copy(completionHandler);
217
218         bool shouldIgnoreHSTS = false;
219 #if USE(CFNETWORK_IGNORE_HSTS)
220         shouldIgnoreHSTS = schemeWasUpgradedDueToDynamicHSTS(request) && WebCore::NetworkStorageSession::storageSession(_session->sessionID())->shouldBlockCookies(request);
221         if (shouldIgnoreHSTS) {
222             request = downgradeRequest(request);
223             ASSERT([request.URL.scheme isEqualToString:@"http"]);
224             LOG(NetworkSession, "%llu Downgraded %s from https to http", taskIdentifier, request.URL.absoluteString.UTF8String);
225         }
226 #endif
227
228         networkDataTask->willPerformHTTPRedirection(response, request, [completionHandlerCopy, taskIdentifier, shouldIgnoreHSTS](auto&& request) {
229 #if !LOG_DISABLED
230             LOG(NetworkSession, "%llu willPerformHTTPRedirection completionHandler (%s)", taskIdentifier, request.url().string().utf8().data());
231 #else
232             UNUSED_PARAM(taskIdentifier);
233 #endif
234             auto nsRequest = request.nsURLRequest(WebCore::UpdateHTTPBody);
235             nsRequest = updateIgnoreStrictTransportSecuritySettingIfNecessary(nsRequest, shouldIgnoreHSTS);
236             completionHandlerCopy(nsRequest);
237             Block_release(completionHandlerCopy);
238         });
239     } else {
240         LOG(NetworkSession, "%llu willPerformHTTPRedirection completionHandler (nil)", taskIdentifier);
241         completionHandler(nil);
242     }
243 }
244
245 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask*)task _schemeUpgraded:(NSURLRequest*)request completionHandler:(void (^)(NSURLRequest*))completionHandler
246 {
247     auto taskIdentifier = task.taskIdentifier;
248     LOG(NetworkSession, "%llu _schemeUpgraded %s", taskIdentifier, request.URL.absoluteString.UTF8String);
249
250     if (auto* networkDataTask = [self existingTask:task]) {
251         bool shouldIgnoreHSTS = false;
252 #if USE(CFNETWORK_IGNORE_HSTS)
253         shouldIgnoreHSTS = schemeWasUpgradedDueToDynamicHSTS(request) && WebCore::NetworkStorageSession::storageSession(_session->sessionID())->shouldBlockCookies(request);
254         if (shouldIgnoreHSTS) {
255             request = downgradeRequest(request);
256             ASSERT([request.URL.scheme isEqualToString:@"http"]);
257             LOG(NetworkSession, "%llu Downgraded %s from https to http", taskIdentifier, request.URL.absoluteString.UTF8String);
258         }
259 #endif
260
261         auto completionHandlerCopy = Block_copy(completionHandler);
262         networkDataTask->willPerformHTTPRedirection(WebCore::synthesizeRedirectResponseIfNecessary([task currentRequest], request, nil), request, [completionHandlerCopy, taskIdentifier, shouldIgnoreHSTS](auto&& request) {
263 #if !LOG_DISABLED
264             LOG(NetworkSession, "%llu _schemeUpgraded completionHandler (%s)", taskIdentifier, request.url().string().utf8().data());
265 #else
266             UNUSED_PARAM(taskIdentifier);
267 #endif
268             auto nsRequest = request.nsURLRequest(WebCore::UpdateHTTPBody);
269             nsRequest = updateIgnoreStrictTransportSecuritySettingIfNecessary(nsRequest, shouldIgnoreHSTS);
270             completionHandlerCopy(nsRequest);
271             Block_release(completionHandlerCopy);
272         });
273     } else {
274         LOG(NetworkSession, "%llu _schemeUpgraded completionHandler (nil)", taskIdentifier);
275         completionHandler(nil);
276     }
277 }
278
279 - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask willCacheResponse:(NSCachedURLResponse *)proposedResponse completionHandler:(void (^)(NSCachedURLResponse * _Nullable cachedResponse))completionHandler
280 {
281     if (!_session) {
282         completionHandler(nil);
283         return;
284     }
285
286     // FIXME: remove if <rdar://problem/20001985> is ever resolved.
287     if ([proposedResponse.response respondsToSelector:@selector(allHeaderFields)]
288         && [[(id)proposedResponse.response allHeaderFields] objectForKey:@"Content-Range"])
289         completionHandler(nil);
290     else
291         completionHandler(proposedResponse);
292 }
293
294 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
295 {
296     if (!_session) {
297         completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
298         return;
299     }
300
301     auto taskIdentifier = task.taskIdentifier;
302     LOG(NetworkSession, "%llu didReceiveChallenge", taskIdentifier);
303     
304     // Proxy authentication is handled by CFNetwork internally. We can get here if the user cancels
305     // CFNetwork authentication dialog, and we shouldn't ask the client to display another one in that case.
306     if (challenge.protectionSpace.isProxy) {
307         completionHandler(NSURLSessionAuthChallengeUseCredential, nil);
308         return;
309     }
310
311     // Handle server trust evaluation at platform-level if requested, for performance reasons.
312     if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust] && !NetworkProcess::singleton().canHandleHTTPSServerTrustEvaluation()) {
313         if (NetworkSessionCocoa::allowsSpecificHTTPSCertificateForHost(challenge))
314             completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
315         else
316             completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, nil);
317         return;
318     }
319
320     if (auto* networkDataTask = [self existingTask:task]) {
321         WebCore::AuthenticationChallenge authenticationChallenge(challenge);
322         auto completionHandlerCopy = Block_copy(completionHandler);
323         auto sessionID = _session->sessionID();
324         auto challengeCompletionHandler = [completionHandlerCopy, sessionID, authenticationChallenge, taskIdentifier, partition = networkDataTask->partition()](WebKit::AuthenticationChallengeDisposition disposition, const WebCore::Credential& credential)
325         {
326 #if !LOG_DISABLED
327             LOG(NetworkSession, "%llu didReceiveChallenge completionHandler %d", taskIdentifier, disposition);
328 #else
329             UNUSED_PARAM(taskIdentifier);
330 #endif
331 #if !USE(CREDENTIAL_STORAGE_WITH_NETWORK_SESSION)
332             UNUSED_PARAM(sessionID);
333             UNUSED_PARAM(authenticationChallenge);
334 #else
335             if (credential.persistence() == WebCore::CredentialPersistenceForSession && authenticationChallenge.protectionSpace().isPasswordBased()) {
336
337                 WebCore::Credential nonPersistentCredential(credential.user(), credential.password(), WebCore::CredentialPersistenceNone);
338                 WebCore::URL urlToStore;
339                 if (authenticationChallenge.failureResponse().httpStatusCode() == 401)
340                     urlToStore = authenticationChallenge.failureResponse().url();
341                 if (auto storageSession = WebCore::NetworkStorageSession::storageSession(sessionID))
342                     storageSession->credentialStorage().set(partition, nonPersistentCredential, authenticationChallenge.protectionSpace(), urlToStore);
343                 else
344                     ASSERT_NOT_REACHED();
345
346                 completionHandlerCopy(toNSURLSessionAuthChallengeDisposition(disposition), nonPersistentCredential.nsCredential());
347             } else
348 #endif
349                 completionHandlerCopy(toNSURLSessionAuthChallengeDisposition(disposition), credential.nsCredential());
350             Block_release(completionHandlerCopy);
351         };
352         networkDataTask->didReceiveChallenge(challenge, WTFMove(challengeCompletionHandler));
353     } else {
354         auto downloadID = _session->downloadID(taskIdentifier);
355         if (downloadID.downloadID()) {
356             if (auto* download = WebKit::NetworkProcess::singleton().downloadManager().download(downloadID)) {
357                 // Received an authentication challenge for a download being resumed.
358                 WebCore::AuthenticationChallenge authenticationChallenge { challenge };
359                 auto completionHandlerCopy = Block_copy(completionHandler);
360                 auto challengeCompletionHandler = [completionHandlerCopy, authenticationChallenge](WebKit::AuthenticationChallengeDisposition disposition, const WebCore::Credential& credential) {
361                     completionHandlerCopy(toNSURLSessionAuthChallengeDisposition(disposition), credential.nsCredential());
362                     Block_release(completionHandlerCopy);
363                 };
364                 download->didReceiveChallenge(challenge, WTFMove(challengeCompletionHandler));
365                 return;
366             }
367         }
368         LOG(NetworkSession, "%llu didReceiveChallenge completionHandler (cancel)", taskIdentifier);
369         completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
370     }
371 }
372
373 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
374 {
375     if (!_session)
376         return;
377
378     LOG(NetworkSession, "%llu didCompleteWithError %@", task.taskIdentifier, error);
379     if (auto* networkDataTask = [self existingTask:task])
380         networkDataTask->didCompleteWithError(error, networkDataTask->networkLoadMetrics());
381     else if (error) {
382         auto downloadID = _session->takeDownloadID(task.taskIdentifier);
383         if (downloadID.downloadID()) {
384             if (auto* download = WebKit::NetworkProcess::singleton().downloadManager().download(downloadID)) {
385                 NSData *resumeData = nil;
386                 if (id userInfo = error.userInfo) {
387                     if ([userInfo isKindOfClass:[NSDictionary class]])
388                         resumeData = userInfo[@"NSURLSessionDownloadTaskResumeData"];
389                 }
390                 
391                 if (resumeData && [resumeData isKindOfClass:[NSData class]])
392                     download->didFail(error, { static_cast<const uint8_t*>(resumeData.bytes), resumeData.length });
393                 else
394                     download->didFail(error, { });
395             }
396         }
397     }
398 }
399
400 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics
401 {
402     LOG(NetworkSession, "%llu didFinishCollectingMetrics", task.taskIdentifier);
403     if (auto* networkDataTask = [self existingTask:task]) {
404         NSURLSessionTaskTransactionMetrics *m = metrics.transactionMetrics.lastObject;
405         NSDate *fetchStartDate = m.fetchStartDate;
406         NSTimeInterval domainLookupStartInterval = m.domainLookupStartDate ? [m.domainLookupStartDate timeIntervalSinceDate:fetchStartDate] : -1;
407         NSTimeInterval domainLookupEndInterval = m.domainLookupEndDate ? [m.domainLookupEndDate timeIntervalSinceDate:fetchStartDate] : -1;
408         NSTimeInterval connectStartInterval = m.connectStartDate ? [m.connectStartDate timeIntervalSinceDate:fetchStartDate] : -1;
409         NSTimeInterval secureConnectionStartInterval = m.secureConnectionStartDate ? [m.secureConnectionStartDate timeIntervalSinceDate:fetchStartDate] : -1;
410         NSTimeInterval connectEndInterval = m.connectEndDate ? [m.connectEndDate timeIntervalSinceDate:fetchStartDate] : -1;
411         NSTimeInterval requestStartInterval = [m.requestStartDate timeIntervalSinceDate:fetchStartDate];
412         NSTimeInterval responseStartInterval = [m.responseStartDate timeIntervalSinceDate:fetchStartDate];
413         NSTimeInterval responseEndInterval = [m.responseEndDate timeIntervalSinceDate:fetchStartDate];
414
415         auto& networkLoadMetrics = networkDataTask->networkLoadMetrics();
416         networkLoadMetrics.domainLookupStart = Seconds(domainLookupStartInterval);
417         networkLoadMetrics.domainLookupEnd = Seconds(domainLookupEndInterval);
418         networkLoadMetrics.connectStart = Seconds(connectStartInterval);
419         networkLoadMetrics.secureConnectionStart = Seconds(secureConnectionStartInterval);
420         networkLoadMetrics.connectEnd = Seconds(connectEndInterval);
421         networkLoadMetrics.requestStart = Seconds(requestStartInterval);
422         networkLoadMetrics.responseStart = Seconds(responseStartInterval);
423         networkLoadMetrics.responseEnd = Seconds(responseEndInterval);
424         networkLoadMetrics.markComplete();
425         networkLoadMetrics.protocol = String(m.networkProtocolName);
426
427         if (networkDataTask->shouldCaptureExtraNetworkLoadMetrics()) {
428             networkLoadMetrics.priority = toNetworkLoadPriority(task.priority);
429
430 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300) || (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000)
431             networkLoadMetrics.remoteAddress = String(m._remoteAddressAndPort);
432             networkLoadMetrics.connectionIdentifier = String([m._connectionIdentifier UUIDString]);
433 #endif
434
435             __block WebCore::HTTPHeaderMap requestHeaders;
436             [m.request.allHTTPHeaderFields enumerateKeysAndObjectsUsingBlock:^(NSString *name, NSString *value, BOOL *) {
437                 requestHeaders.set(String(name), String(value));
438             }];
439             networkLoadMetrics.requestHeaders = WTFMove(requestHeaders);
440
441 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300) || (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000)
442             uint64_t requestHeaderBytesSent = 0;
443             uint64_t responseHeaderBytesReceived = 0;
444             uint64_t responseBodyBytesReceived = 0;
445             uint64_t responseBodyDecodedSize = 0;
446
447             for (NSURLSessionTaskTransactionMetrics *transactionMetrics in metrics.transactionMetrics) {
448                 requestHeaderBytesSent += transactionMetrics._requestHeaderBytesSent;
449                 responseHeaderBytesReceived += transactionMetrics._responseHeaderBytesReceived;
450                 responseBodyBytesReceived += transactionMetrics._responseBodyBytesReceived;
451                 responseBodyDecodedSize += transactionMetrics._responseBodyBytesDecoded ? transactionMetrics._responseBodyBytesDecoded : transactionMetrics._responseBodyBytesReceived;
452             }
453
454             networkLoadMetrics.requestHeaderBytesSent = requestHeaderBytesSent;
455             networkLoadMetrics.requestBodyBytesSent = task.countOfBytesSent;
456             networkLoadMetrics.responseHeaderBytesReceived = responseHeaderBytesReceived;
457             networkLoadMetrics.responseBodyBytesReceived = responseBodyBytesReceived;
458             networkLoadMetrics.responseBodyDecodedSize = responseBodyDecodedSize;
459 #endif
460         }
461     }
462 }
463
464 - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler
465 {
466     auto taskIdentifier = dataTask.taskIdentifier;
467     LOG(NetworkSession, "%llu didReceiveResponse", taskIdentifier);
468     if (auto* networkDataTask = [self existingTask:dataTask]) {
469         ASSERT(RunLoop::isMain());
470         
471         // Avoid MIME type sniffing if the response comes back as 304 Not Modified.
472         int statusCode = [response respondsToSelector:@selector(statusCode)] ? [(id)response statusCode] : 0;
473         if (statusCode != 304) {
474             bool isMainResourceLoad = networkDataTask->firstRequest().requester() == WebCore::ResourceRequest::Requester::Main;
475             WebCore::adjustMIMETypeIfNecessary(response._CFURLResponse, isMainResourceLoad);
476         }
477
478         WebCore::ResourceResponse resourceResponse(response);
479         // Lazy initialization is not helpful in the WebKit2 case because we always end up initializing
480         // all the fields when sending the response to the WebContent process over IPC.
481         resourceResponse.disableLazyInitialization();
482
483         // FIXME: This cannot be eliminated until other code no longer relies on ResourceResponse's
484         // NetworkLoadMetrics. For example, PerformanceTiming.
485         copyTimingData([dataTask _timingData], resourceResponse.deprecatedNetworkLoadMetrics());
486
487         auto completionHandlerCopy = Block_copy(completionHandler);
488         networkDataTask->didReceiveResponse(WTFMove(resourceResponse), [completionHandlerCopy, taskIdentifier](WebCore::PolicyAction policyAction) {
489 #if !LOG_DISABLED
490             LOG(NetworkSession, "%llu didReceiveResponse completionHandler (%d)", taskIdentifier, policyAction);
491 #else
492             UNUSED_PARAM(taskIdentifier);
493 #endif
494             completionHandlerCopy(toNSURLSessionResponseDisposition(policyAction));
495             Block_release(completionHandlerCopy);
496         });
497     } else {
498         LOG(NetworkSession, "%llu didReceiveResponse completionHandler (cancel)", taskIdentifier);
499         completionHandler(NSURLSessionResponseCancel);
500     }
501 }
502
503 - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
504 {
505     if (auto* networkDataTask = [self existingTask:dataTask])
506         networkDataTask->didReceiveData(WebCore::SharedBuffer::create(data));
507 }
508
509 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
510 {
511     if (!_session)
512         return;
513
514     auto downloadID = _session->takeDownloadID([downloadTask taskIdentifier]);
515     if (auto* download = WebKit::NetworkProcess::singleton().downloadManager().download(downloadID))
516         download->didFinish();
517 }
518
519 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
520 {
521     if (!_session)
522         return;
523
524     ASSERT_WITH_MESSAGE(![self existingTask:downloadTask], "The NetworkDataTask should be destroyed immediately after didBecomeDownloadTask returns");
525
526     auto downloadID = _session->downloadID([downloadTask taskIdentifier]);
527     if (auto* download = WebKit::NetworkProcess::singleton().downloadManager().download(downloadID))
528         download->didReceiveData(bytesWritten);
529 }
530
531 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes
532 {
533     if (!_session)
534         return;
535
536     notImplemented();
537 }
538
539 - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask
540 {
541     if (auto* networkDataTask = [self existingTask:dataTask]) {
542         Ref<NetworkDataTaskCocoa> protectedNetworkDataTask(*networkDataTask);
543         auto downloadID = networkDataTask->pendingDownloadID();
544         auto& downloadManager = WebKit::NetworkProcess::singleton().downloadManager();
545         auto download = std::make_unique<WebKit::Download>(downloadManager, downloadID, downloadTask, _session->sessionID(), networkDataTask->suggestedFilename());
546         networkDataTask->transferSandboxExtensionToDownload(*download);
547         ASSERT(WebCore::FileSystem::fileExists(networkDataTask->pendingDownloadLocation()));
548         download->didCreateDestination(networkDataTask->pendingDownloadLocation());
549         downloadManager.dataTaskBecameDownloadTask(downloadID, WTFMove(download));
550
551         _session->addDownloadID([downloadTask taskIdentifier], downloadID);
552     }
553 }
554
555 @end
556
557 namespace WebKit {
558     
559 static bool usesNetworkCache { false };
560
561 #if !ASSERT_DISABLED
562 static bool sessionsCreated = false;
563 #endif
564
565 static NSURLSessionConfiguration *configurationForSessionID(const PAL::SessionID& session)
566 {
567     if (session.isEphemeral()) {
568         NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
569         configuration._shouldSkipPreferredClientCertificateLookup = YES;
570         return configuration;
571     }
572     return [NSURLSessionConfiguration defaultSessionConfiguration];
573 }
574
575 static RetainPtr<CFDataRef>& globalSourceApplicationAuditTokenData()
576 {
577     static NeverDestroyed<RetainPtr<CFDataRef>> sourceApplicationAuditTokenData;
578     return sourceApplicationAuditTokenData.get();
579 }
580
581 static String& globalSourceApplicationBundleIdentifier()
582 {
583     static NeverDestroyed<String> sourceApplicationBundleIdentifier;
584     return sourceApplicationBundleIdentifier.get();
585 }
586
587 static String& globalSourceApplicationSecondaryIdentifier()
588 {
589     static NeverDestroyed<String> sourceApplicationSecondaryIdentifier;
590     return sourceApplicationSecondaryIdentifier.get();
591 }
592
593 #if PLATFORM(IOS)
594 static String& globalCTDataConnectionServiceType()
595 {
596     static NeverDestroyed<String> ctDataConnectionServiceType;
597     return ctDataConnectionServiceType.get();
598 }
599 #endif
600     
601 void NetworkSessionCocoa::setSourceApplicationAuditTokenData(RetainPtr<CFDataRef>&& data)
602 {
603     ASSERT(!sessionsCreated);
604     globalSourceApplicationAuditTokenData() = data;
605 }
606
607 void NetworkSessionCocoa::setSourceApplicationBundleIdentifier(const String& identifier)
608 {
609     ASSERT(!sessionsCreated);
610     globalSourceApplicationBundleIdentifier() = identifier;
611 }
612
613 void NetworkSessionCocoa::setSourceApplicationSecondaryIdentifier(const String& identifier)
614 {
615     ASSERT(!sessionsCreated);
616     globalSourceApplicationSecondaryIdentifier() = identifier;
617 }
618
619 void NetworkSessionCocoa::setUsesNetworkCache(bool value)
620 {
621     usesNetworkCache = value;
622 }
623
624 #if PLATFORM(IOS)
625 void NetworkSessionCocoa::setCTDataConnectionServiceType(const String& type)
626 {
627     ASSERT(!sessionsCreated);
628     globalCTDataConnectionServiceType() = type;
629 }
630 #endif
631
632 Ref<NetworkSession> NetworkSessionCocoa::create(NetworkSessionCreationParameters&& parameters)
633 {
634     return adoptRef(*new NetworkSessionCocoa(WTFMove(parameters)));
635 }
636
637 NetworkSessionCocoa::NetworkSessionCocoa(NetworkSessionCreationParameters&& parameters)
638     : NetworkSession(parameters.sessionID)
639     , m_boundInterfaceIdentifier(parameters.boundInterfaceIdentifier)
640 {
641     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
642
643     relaxAdoptionRequirement();
644
645 #if !ASSERT_DISABLED
646     sessionsCreated = true;
647 #endif
648
649     NSURLSessionConfiguration *configuration = configurationForSessionID(m_sessionID);
650
651 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300) || (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000)
652     // Without this, CFNetwork would sometimes add a Content-Type header to our requests (rdar://problem/34748470).
653     if ([configuration respondsToSelector:@selector(_suppressedAutoAddedHTTPHeaders)])
654         configuration._suppressedAutoAddedHTTPHeaders = [NSSet setWithObject:@"Content-Type"];
655 #endif
656
657     if (parameters.allowsCellularAccess == AllowsCellularAccess::No)
658         configuration.allowsCellularAccess = NO;
659
660     if (usesNetworkCache)
661         configuration.URLCache = nil;
662
663     if (auto& data = globalSourceApplicationAuditTokenData())
664         configuration._sourceApplicationAuditTokenData = (NSData *)data.get();
665
666     auto& sourceApplicationBundleIdentifier = globalSourceApplicationBundleIdentifier();
667     if (!sourceApplicationBundleIdentifier.isEmpty()) {
668         configuration._sourceApplicationBundleIdentifier = sourceApplicationBundleIdentifier;
669         configuration._sourceApplicationAuditTokenData = nil;
670     }
671
672     auto& sourceApplicationSecondaryIdentifier = globalSourceApplicationSecondaryIdentifier();
673     if (!sourceApplicationSecondaryIdentifier.isEmpty())
674         configuration._sourceApplicationSecondaryIdentifier = sourceApplicationSecondaryIdentifier;
675
676 #if PLATFORM(IOS)
677     auto& ctDataConnectionServiceType = globalCTDataConnectionServiceType();
678     if (!ctDataConnectionServiceType.isEmpty())
679         configuration._CTDataConnectionServiceType = ctDataConnectionServiceType;
680 #endif
681
682 #if ENABLE(LEGACY_CUSTOM_PROTOCOL_MANAGER)
683     NetworkProcess::singleton().supplement<LegacyCustomProtocolManager>()->registerProtocolClass(configuration);
684 #endif
685
686 #if HAVE(TIMINGDATAOPTIONS)
687     configuration._timingDataOptions = _TimingDataOptionsEnableW3CNavigationTiming;
688 #else
689     setCollectsTimingData();
690 #endif
691
692     auto* storageSession = WebCore::NetworkStorageSession::storageSession(parameters.sessionID);
693     RELEASE_ASSERT(storageSession);
694     if (CFHTTPCookieStorageRef storage = storageSession->cookieStorage().get())
695         configuration.HTTPCookieStorage = [[[NSHTTPCookieStorage alloc] _initWithCFHTTPCookieStorage:storage] autorelease];
696
697     m_sessionWithCredentialStorageDelegate = adoptNS([[WKNetworkSessionDelegate alloc] initWithNetworkSession:*this withCredentials:true]);
698     m_sessionWithCredentialStorage = [NSURLSession sessionWithConfiguration:configuration delegate:static_cast<id>(m_sessionWithCredentialStorageDelegate.get()) delegateQueue:[NSOperationQueue mainQueue]];
699     LOG(NetworkSession, "Created NetworkSession with cookieAcceptPolicy %lu", configuration.HTTPCookieStorage.cookieAcceptPolicy);
700
701     configuration.URLCredentialStorage = nil;
702     configuration._shouldSkipPreferredClientCertificateLookup = YES;
703     // FIXME: https://bugs.webkit.org/show_bug.cgi?id=177394
704     // configuration.HTTPCookieStorage = nil;
705     // configuration.HTTPCookieAcceptPolicy = NSHTTPCookieAcceptPolicyNever;
706
707     m_statelessSessionDelegate = adoptNS([[WKNetworkSessionDelegate alloc] initWithNetworkSession:*this withCredentials:false]);
708     m_statelessSession = [NSURLSession sessionWithConfiguration:configuration delegate:static_cast<id>(m_statelessSessionDelegate.get()) delegateQueue:[NSOperationQueue mainQueue]];
709 }
710
711 NetworkSessionCocoa::~NetworkSessionCocoa()
712 {
713 }
714
715 void NetworkSessionCocoa::invalidateAndCancel()
716 {
717     NetworkSession::invalidateAndCancel();
718
719     [m_sessionWithCredentialStorage invalidateAndCancel];
720     [m_statelessSession invalidateAndCancel];
721     [m_sessionWithCredentialStorageDelegate sessionInvalidated];
722     [m_statelessSessionDelegate sessionInvalidated];
723 }
724
725 void NetworkSessionCocoa::clearCredentials()
726 {
727 #if !USE(CREDENTIAL_STORAGE_WITH_NETWORK_SESSION)
728     ASSERT(m_dataTaskMapWithCredentials.isEmpty());
729     ASSERT(m_dataTaskMapWithoutState.isEmpty());
730     ASSERT(m_downloadMap.isEmpty());
731     // FIXME: Use resetWithCompletionHandler instead.
732     m_sessionWithCredentialStorage = [NSURLSession sessionWithConfiguration:m_sessionWithCredentialStorage.get().configuration delegate:static_cast<id>(m_sessionWithCredentialStorageDelegate.get()) delegateQueue:[NSOperationQueue mainQueue]];
733     m_statelessSession = [NSURLSession sessionWithConfiguration:m_statelessSession.get().configuration delegate:static_cast<id>(m_statelessSessionDelegate.get()) delegateQueue:[NSOperationQueue mainQueue]];
734 #endif
735 }
736
737 NetworkDataTaskCocoa* NetworkSessionCocoa::dataTaskForIdentifier(NetworkDataTaskCocoa::TaskIdentifier taskIdentifier, WebCore::StoredCredentialsPolicy storedCredentialsPolicy)
738 {
739     ASSERT(RunLoop::isMain());
740     if (storedCredentialsPolicy == WebCore::StoredCredentialsPolicy::Use)
741         return m_dataTaskMapWithCredentials.get(taskIdentifier);
742     return m_dataTaskMapWithoutState.get(taskIdentifier);
743 }
744
745 NSURLSessionDownloadTask* NetworkSessionCocoa::downloadTaskWithResumeData(NSData* resumeData)
746 {
747     return [m_sessionWithCredentialStorage downloadTaskWithResumeData:resumeData];
748 }
749
750 void NetworkSessionCocoa::addDownloadID(NetworkDataTaskCocoa::TaskIdentifier taskIdentifier, DownloadID downloadID)
751 {
752 #ifndef NDEBUG
753     ASSERT(!m_downloadMap.contains(taskIdentifier));
754     for (auto idInMap : m_downloadMap.values())
755         ASSERT(idInMap != downloadID);
756 #endif
757     m_downloadMap.add(taskIdentifier, downloadID);
758 }
759
760 DownloadID NetworkSessionCocoa::downloadID(NetworkDataTaskCocoa::TaskIdentifier taskIdentifier)
761 {
762     ASSERT(m_downloadMap.get(taskIdentifier).downloadID());
763     return m_downloadMap.get(taskIdentifier);
764 }
765
766 DownloadID NetworkSessionCocoa::takeDownloadID(NetworkDataTaskCocoa::TaskIdentifier taskIdentifier)
767 {
768     auto downloadID = m_downloadMap.take(taskIdentifier);
769     return downloadID;
770 }
771
772 static bool certificatesMatch(SecTrustRef trust1, SecTrustRef trust2)
773 {
774     if (!trust1 || !trust2)
775         return false;
776
777     CFIndex count1 = SecTrustGetCertificateCount(trust1);
778     CFIndex count2 = SecTrustGetCertificateCount(trust2);
779     if (count1 != count2)
780         return false;
781
782     for (CFIndex i = 0; i < count1; i++) {
783         auto cert1 = SecTrustGetCertificateAtIndex(trust1, i);
784         auto cert2 = SecTrustGetCertificateAtIndex(trust2, i);
785         RELEASE_ASSERT(cert1);
786         RELEASE_ASSERT(cert2);
787         if (!CFEqual(cert1, cert2))
788             return false;
789     }
790
791     return true;
792 }
793
794 bool NetworkSessionCocoa::allowsSpecificHTTPSCertificateForHost(const WebCore::AuthenticationChallenge& challenge)
795 {
796     const String& host = challenge.protectionSpace().host();
797     NSArray *certificates = [NSURLRequest allowsSpecificHTTPSCertificateForHost:host];
798     if (!certificates)
799         return false;
800
801     bool requireServerCertificates = challenge.protectionSpace().authenticationScheme() == WebCore::ProtectionSpaceAuthenticationScheme::ProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested;
802     RetainPtr<SecPolicyRef> policy = adoptCF(SecPolicyCreateSSL(requireServerCertificates, host.createCFString().get()));
803
804     SecTrustRef trustRef = nullptr;
805     if (SecTrustCreateWithCertificates((CFArrayRef)certificates, policy.get(), &trustRef) != noErr)
806         return false;
807     RetainPtr<SecTrustRef> trust = adoptCF(trustRef);
808
809     return certificatesMatch(trust.get(), challenge.nsURLAuthenticationChallenge().protectionSpace.serverTrust);
810 }
811
812 }