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