Add WKWebsiteDataStorePrivate._proxyConfiguration SPI
[WebKit-https.git] / Source / WebKit / NetworkProcess / cocoa / NetworkDataTaskCocoa.mm
1 /*
2  * Copyright (C) 2016-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 "NetworkDataTaskCocoa.h"
28
29 #import "AuthenticationManager.h"
30 #import "Download.h"
31 #import "DownloadProxyMessages.h"
32 #import "Logging.h"
33 #import "NetworkProcess.h"
34 #import "NetworkSessionCocoa.h"
35 #import "SessionTracker.h"
36 #import "WebCoreArgumentCoders.h"
37 #import <WebCore/AuthenticationChallenge.h>
38 #import <WebCore/FileSystem.h>
39 #import <WebCore/NetworkStorageSession.h>
40 #import <WebCore/NotImplemented.h>
41 #import <WebCore/ResourceRequest.h>
42 #import <pal/spi/cf/CFNetworkSPI.h>
43 #import <wtf/MainThread.h>
44 #import <wtf/ProcessPrivilege.h>
45 #import <wtf/text/Base64.h>
46
47 #if USE(APPLE_INTERNAL_SDK)
48 #import <WebKitAdditions/NetworkDataTaskCocoaAdditions.mm>
49 #endif
50
51 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101400) || (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 120000)
52 @interface NSURLSessionTask (Staging)
53 @property (nullable, readwrite, retain) NSURL *_siteForCookies;
54 @property (readwrite) BOOL _isTopLevelNavigation;
55 @end
56 #endif
57
58 namespace WebKit {
59
60 #if USE(CREDENTIAL_STORAGE_WITH_NETWORK_SESSION)
61 static void applyBasicAuthorizationHeader(WebCore::ResourceRequest& request, const WebCore::Credential& credential)
62 {
63     String authenticationHeader = "Basic " + base64Encode(String(credential.user() + ":" + credential.password()).utf8());
64     request.setHTTPHeaderField(WebCore::HTTPHeaderName::Authorization, authenticationHeader);
65 }
66 #endif
67
68 static float toNSURLSessionTaskPriority(WebCore::ResourceLoadPriority priority)
69 {
70     switch (priority) {
71     case WebCore::ResourceLoadPriority::VeryLow:
72         return 0;
73     case WebCore::ResourceLoadPriority::Low:
74         return 0.25;
75     case WebCore::ResourceLoadPriority::Medium:
76         return 0.5;
77     case WebCore::ResourceLoadPriority::High:
78         return 0.75;
79     case WebCore::ResourceLoadPriority::VeryHigh:
80         return 1;
81     }
82
83     ASSERT_NOT_REACHED();
84     return NSURLSessionTaskPriorityDefault;
85 }
86
87 void NetworkDataTaskCocoa::applySniffingPoliciesAndBindRequestToInferfaceIfNeeded(NSURLRequest*& nsRequest, bool shouldContentSniff, bool shouldContentEncodingSniff)
88 {
89 #if !PLATFORM(MAC)
90     UNUSED_PARAM(shouldContentEncodingSniff);
91 #elif __MAC_OS_X_VERSION_MIN_REQUIRED < 101302
92     shouldContentEncodingSniff = true;
93 #endif
94     auto& cocoaSession = static_cast<NetworkSessionCocoa&>(m_session.get());
95     if (shouldContentSniff
96         && shouldContentEncodingSniff
97         && cocoaSession.m_boundInterfaceIdentifier.isNull()
98         && !cocoaSession.m_proxyConfiguration)
99         return;
100
101     auto mutableRequest = adoptNS([nsRequest mutableCopy]);
102
103 #if PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101302
104     if (!shouldContentEncodingSniff)
105         [mutableRequest _setProperty:@(YES) forKey:(NSString *)kCFURLRequestContentDecoderSkipURLCheck];
106 #endif
107
108     if (!shouldContentSniff)
109         [mutableRequest _setProperty:@(NO) forKey:(NSString *)_kCFURLConnectionPropertyShouldSniff];
110
111     if (!cocoaSession.m_boundInterfaceIdentifier.isNull())
112         [mutableRequest setBoundInterfaceIdentifier:cocoaSession.m_boundInterfaceIdentifier];
113
114     if (cocoaSession.m_proxyConfiguration)
115         CFURLRequestSetProxySettings([mutableRequest _CFURLRequest], cocoaSession.m_proxyConfiguration.get());
116
117     nsRequest = mutableRequest.autorelease();
118 }
119
120 #if HAVE(CFNETWORK_STORAGE_PARTITIONING)
121 NSHTTPCookieStorage *NetworkDataTaskCocoa::statelessCookieStorage()
122 {
123     static NeverDestroyed<RetainPtr<NSHTTPCookieStorage>> statelessCookieStorage;
124     if (!statelessCookieStorage.get()) {
125 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101300)
126 #pragma clang diagnostic push
127 #pragma clang diagnostic ignored "-Wnonnull"
128 #endif
129         statelessCookieStorage.get() = adoptNS([[NSHTTPCookieStorage alloc] _initWithIdentifier:nil private:YES]);
130 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101300)
131 #pragma clang diagnostic pop
132 #endif
133         statelessCookieStorage.get().get().cookieAcceptPolicy = NSHTTPCookieAcceptPolicyNever;
134     }
135     ASSERT(statelessCookieStorage.get().get().cookies.count == 0);
136     return statelessCookieStorage.get().get();
137 }
138
139 void NetworkDataTaskCocoa::applyCookieBlockingPolicy(bool shouldBlock)
140 {
141     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
142
143     if (shouldBlock == m_hasBeenSetToUseStatelessCookieStorage)
144         return;
145
146     NSHTTPCookieStorage *storage = shouldBlock ? statelessCookieStorage(): m_session->networkStorageSession().nsCookieStorage();
147     [m_task performSelector:NSSelectorFromString(@"_setExplicitCookieStorage:") withObject:(NSObject*)storage._cookieStorage];
148     m_hasBeenSetToUseStatelessCookieStorage = shouldBlock;
149 }
150
151 void NetworkDataTaskCocoa::applyCookiePartitioningPolicy(const String& requiredStoragePartition, const String& currentStoragePartition)
152 {
153     // The need for a partion change is according to the following:
154     //      currentStoragePartition:  null  ""    abc
155     // requiredStoragePartition: ""   false false true
156     //                           abc  true  true  false
157     //                           xyz  true  true  true
158     auto shouldChangePartition = !((requiredStoragePartition.isEmpty() && currentStoragePartition.isEmpty()) || currentStoragePartition == requiredStoragePartition);
159     if (shouldChangePartition)
160         m_task.get()._storagePartitionIdentifier = requiredStoragePartition;
161 }
162 #endif
163
164 bool NetworkDataTaskCocoa::isThirdPartyRequest(const WebCore::ResourceRequest& request)
165 {
166     return !WebCore::registrableDomainsAreEqual(request.url(), request.firstPartyForCookies());
167 }
168
169 static void updateTaskWithFirstPartyForSameSiteCookies(NSURLSessionDataTask* task, const WebCore::ResourceRequest& request)
170 {
171     if (request.isSameSiteUnspecified())
172         return;
173 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101400) || (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 120000)
174     static NSURL *emptyURL = [[NSURL alloc] initWithString:@""];
175     if ([task respondsToSelector:@selector(set_siteForCookies:)])
176         task._siteForCookies = request.isSameSite() ? task.currentRequest.URL : emptyURL;
177     if ([task respondsToSelector:@selector(set_isTopLevelNavigation:)])
178         task._isTopLevelNavigation = request.isTopSite();
179 #else
180     UNUSED_PARAM(task);
181 #endif
182 }
183
184 NetworkDataTaskCocoa::NetworkDataTaskCocoa(NetworkSession& session, NetworkDataTaskClient& client, const WebCore::ResourceRequest& requestWithCredentials, uint64_t frameID, uint64_t pageID, WebCore::StoredCredentialsPolicy storedCredentialsPolicy, WebCore::ContentSniffingPolicy shouldContentSniff, WebCore::ContentEncodingSniffingPolicy shouldContentEncodingSniff, bool shouldClearReferrerOnHTTPSToHTTPRedirect, PreconnectOnly shouldPreconnectOnly, bool dataTaskIsForMainFrameNavigation)
185     : NetworkDataTask(session, client, requestWithCredentials, storedCredentialsPolicy, shouldClearReferrerOnHTTPSToHTTPRedirect, dataTaskIsForMainFrameNavigation)
186     , m_frameID(frameID)
187     , m_pageID(pageID)
188 {
189     if (m_scheduledFailureType != NoFailure)
190         return;
191
192     auto request = requestWithCredentials;
193     auto url = request.url();
194     if (storedCredentialsPolicy == WebCore::StoredCredentialsPolicy::Use && url.protocolIsInHTTPFamily()) {
195         m_user = url.user();
196         m_password = url.pass();
197         request.removeCredentials();
198         url = request.url();
199     
200 #if USE(CREDENTIAL_STORAGE_WITH_NETWORK_SESSION)
201         if (m_user.isEmpty() && m_password.isEmpty())
202             m_initialCredential = m_session->networkStorageSession().credentialStorage().get(m_partition, url);
203         else
204             m_session->networkStorageSession().credentialStorage().set(m_partition, WebCore::Credential(m_user, m_password, WebCore::CredentialPersistenceNone), url);
205 #endif
206     }
207
208 #if USE(CREDENTIAL_STORAGE_WITH_NETWORK_SESSION)
209     if (!m_initialCredential.isEmpty()) {
210         // FIXME: Support Digest authentication, and Proxy-Authorization.
211         applyBasicAuthorizationHeader(request, m_initialCredential);
212     }
213 #endif
214
215     bool shouldBlockCookies = false;
216 #if HAVE(CFNETWORK_STORAGE_PARTITIONING)
217     shouldBlockCookies = session.networkStorageSession().shouldBlockCookies(request);
218 #endif
219     if (shouldBlockCookies || (m_session->sessionID().isEphemeral() && isThirdPartyRequest(request)))
220         request.setExistingHTTPReferrerToOriginString();
221
222     NSURLRequest *nsRequest = request.nsURLRequest(WebCore::UpdateHTTPBody);
223     applySniffingPoliciesAndBindRequestToInferfaceIfNeeded(nsRequest, shouldContentSniff == WebCore::SniffContent && !url.isLocalFile(), shouldContentEncodingSniff == WebCore::ContentEncodingSniffingPolicy::Sniff);
224 #if ENABLE(WIFI_ASSERTIONS)
225     applyAdditionalProperties(request, *this, nsRequest);
226 #endif
227
228     auto& cocoaSession = static_cast<NetworkSessionCocoa&>(m_session.get());
229     if (storedCredentialsPolicy == WebCore::StoredCredentialsPolicy::Use) {
230         m_task = [cocoaSession.m_sessionWithCredentialStorage dataTaskWithRequest:nsRequest];
231         ASSERT(!cocoaSession.m_dataTaskMapWithCredentials.contains([m_task taskIdentifier]));
232         cocoaSession.m_dataTaskMapWithCredentials.add([m_task taskIdentifier], this);
233         LOG(NetworkSession, "%llu Creating stateless NetworkDataTask with URL %s", [m_task taskIdentifier], nsRequest.URL.absoluteString.UTF8String);
234     } else {
235         m_task = [cocoaSession.m_statelessSession dataTaskWithRequest:nsRequest];
236         ASSERT(!cocoaSession.m_dataTaskMapWithoutState.contains([m_task taskIdentifier]));
237         cocoaSession.m_dataTaskMapWithoutState.add([m_task taskIdentifier], this);
238         LOG(NetworkSession, "%llu Creating NetworkDataTask with URL %s", [m_task taskIdentifier], nsRequest.URL.absoluteString.UTF8String);
239     }
240
241     if (shouldPreconnectOnly == PreconnectOnly::Yes) {
242 #if ENABLE(SERVER_PRECONNECT)
243         m_task.get()._preconnect = true;
244 #else
245         ASSERT_NOT_REACHED();
246 #endif
247     }
248
249 #if HAVE(CFNETWORK_STORAGE_PARTITIONING)
250     if (shouldBlockCookies) {
251 #if HAVE(CFNETWORK_STORAGE_PARTITIONING) && !RELEASE_LOG_DISABLED
252         if (NetworkProcess::singleton().shouldLogCookieInformation())
253             RELEASE_LOG_IF(isAlwaysOnLoggingAllowed(), Network, "%p - NetworkDataTaskCocoa::logCookieInformation: pageID = %llu, frameID = %llu, taskID = %lu: Blocking cookies for URL %s", this, pageID, frameID, (unsigned long)[m_task taskIdentifier], nsRequest.URL.absoluteString.UTF8String);
254 #else
255         LOG(NetworkSession, "%llu Blocking cookies for URL %s", [m_task taskIdentifier], nsRequest.URL.absoluteString.UTF8String);
256 #endif
257         applyCookieBlockingPolicy(shouldBlockCookies);
258     } else {
259         auto storagePartition = session.networkStorageSession().cookieStoragePartition(request, m_frameID, m_pageID);
260         if (!storagePartition.isEmpty()) {
261 #if HAVE(CFNETWORK_STORAGE_PARTITIONING) && !RELEASE_LOG_DISABLED
262             if (NetworkProcess::singleton().shouldLogCookieInformation())
263                 RELEASE_LOG_IF(isAlwaysOnLoggingAllowed(), Network, "%p - NetworkDataTaskCocoa::logCookieInformation: pageID = %llu, frameID = %llu, taskID = %lu: Partitioning cookies for URL %s", this, pageID, frameID, (unsigned long)[m_task taskIdentifier], nsRequest.URL.absoluteString.UTF8String);
264 #else
265             LOG(NetworkSession, "%llu Partitioning cookies for URL %s", [m_task taskIdentifier], nsRequest.URL.absoluteString.UTF8String);
266 #endif
267             applyCookiePartitioningPolicy(storagePartition, emptyString());
268         }
269     }
270 #endif
271
272     if (WebCore::ResourceRequest::resourcePrioritiesEnabled())
273         m_task.get().priority = toNSURLSessionTaskPriority(request.priority());
274
275     updateTaskWithFirstPartyForSameSiteCookies(m_task.get(), request);
276 }
277
278 NetworkDataTaskCocoa::~NetworkDataTaskCocoa()
279 {
280     if (!m_task)
281         return;
282
283     auto& cocoaSession = static_cast<NetworkSessionCocoa&>(m_session.get());
284     if (m_storedCredentialsPolicy == WebCore::StoredCredentialsPolicy::Use) {
285         ASSERT(cocoaSession.m_dataTaskMapWithCredentials.get([m_task taskIdentifier]) == this);
286         cocoaSession.m_dataTaskMapWithCredentials.remove([m_task taskIdentifier]);
287     } else {
288         ASSERT(cocoaSession.m_dataTaskMapWithoutState.get([m_task taskIdentifier]) == this);
289         cocoaSession.m_dataTaskMapWithoutState.remove([m_task taskIdentifier]);
290     }
291 }
292
293 void NetworkDataTaskCocoa::didSendData(uint64_t totalBytesSent, uint64_t totalBytesExpectedToSend)
294 {
295     if (m_client)
296         m_client->didSendData(totalBytesSent, totalBytesExpectedToSend);
297 }
298
299 void NetworkDataTaskCocoa::didReceiveChallenge(const WebCore::AuthenticationChallenge& challenge, ChallengeCompletionHandler&& completionHandler)
300 {
301     if (tryPasswordBasedAuthentication(challenge, completionHandler))
302         return;
303
304     if (m_client)
305         m_client->didReceiveChallenge(challenge, WTFMove(completionHandler));
306     else {
307         ASSERT_NOT_REACHED();
308         completionHandler(AuthenticationChallengeDisposition::PerformDefaultHandling, { });
309     }
310 }
311
312 void NetworkDataTaskCocoa::didCompleteWithError(const WebCore::ResourceError& error, const WebCore::NetworkLoadMetrics& networkLoadMetrics)
313 {
314     if (m_client)
315         m_client->didCompleteWithError(error, networkLoadMetrics);
316 }
317
318 void NetworkDataTaskCocoa::didReceiveData(Ref<WebCore::SharedBuffer>&& data)
319 {
320     if (m_client)
321         m_client->didReceiveData(WTFMove(data));
322 }
323
324 void NetworkDataTaskCocoa::willPerformHTTPRedirection(WebCore::ResourceResponse&& redirectResponse, WebCore::ResourceRequest&& request, RedirectCompletionHandler&& completionHandler)
325 {
326     if (redirectResponse.httpStatusCode() == 307 || redirectResponse.httpStatusCode() == 308) {
327         ASSERT(m_lastHTTPMethod == request.httpMethod());
328         WebCore::FormData* body = m_firstRequest.httpBody();
329         if (body && !body->isEmpty() && !equalLettersIgnoringASCIICase(m_lastHTTPMethod, "get"))
330             request.setHTTPBody(body);
331         
332         String originalContentType = m_firstRequest.httpContentType();
333         if (!originalContentType.isEmpty())
334             request.setHTTPHeaderField(WebCore::HTTPHeaderName::ContentType, originalContentType);
335     }
336     
337     // Should not set Referer after a redirect from a secure resource to non-secure one.
338     if (m_shouldClearReferrerOnHTTPSToHTTPRedirect && !request.url().protocolIs("https") && WebCore::protocolIs(request.httpReferrer(), "https"))
339         request.clearHTTPReferrer();
340     
341     const auto& url = request.url();
342     m_user = url.user();
343     m_password = url.pass();
344     m_lastHTTPMethod = request.httpMethod();
345     request.removeCredentials();
346     
347     if (!protocolHostAndPortAreEqual(request.url(), redirectResponse.url())) {
348         // The network layer might carry over some headers from the original request that
349         // we want to strip here because the redirect is cross-origin.
350         request.clearHTTPAuthorization();
351         request.clearHTTPOrigin();
352 #if USE(CREDENTIAL_STORAGE_WITH_NETWORK_SESSION)
353     } else {
354         // Only consider applying authentication credentials if this is actually a redirect and the redirect
355         // URL didn't include credentials of its own.
356         if (m_user.isEmpty() && m_password.isEmpty() && !redirectResponse.isNull()) {
357             auto credential = m_session->networkStorageSession().credentialStorage().get(m_partition, request.url());
358             if (!credential.isEmpty()) {
359                 m_initialCredential = credential;
360
361                 // FIXME: Support Digest authentication, and Proxy-Authorization.
362                 applyBasicAuthorizationHeader(request, m_initialCredential);
363             }
364         }
365 #endif
366     }
367
368     if (isTopLevelNavigation())
369         request.setFirstPartyForCookies(request.url());
370
371     bool shouldBlockCookies = false;
372 #if HAVE(CFNETWORK_STORAGE_PARTITIONING)
373     shouldBlockCookies = m_session->networkStorageSession().shouldBlockCookies(request);
374 #if !RELEASE_LOG_DISABLED
375     if (NetworkProcess::singleton().shouldLogCookieInformation())
376         RELEASE_LOG_IF(isAlwaysOnLoggingAllowed(), Network, "%p - NetworkDataTaskCocoa::willPerformHTTPRedirection::logCookieInformation: pageID = %llu, frameID = %llu, taskID = %lu: %s cookies for redirect URL %s", this, m_pageID, m_frameID, (unsigned long)[m_task taskIdentifier], (shouldBlockCookies ? "Blocking" : "Not blocking"), request.url().string().utf8().data());
377 #else
378     LOG(NetworkSession, "%llu %s cookies for redirect URL %s", [m_task taskIdentifier], (shouldBlockCookies ? "Blocking" : "Not blocking"), request.url().string().utf8().data());
379 #endif
380 #endif
381
382     if (shouldBlockCookies || (m_session->sessionID().isEphemeral() && isThirdPartyRequest(request)))
383         request.setExistingHTTPReferrerToOriginString();
384
385 #if HAVE(CFNETWORK_STORAGE_PARTITIONING)
386     // Always apply the policy since blocking may need to be turned on or off in a redirect.
387     applyCookieBlockingPolicy(shouldBlockCookies);
388
389     if (!shouldBlockCookies) {
390         auto requiredStoragePartition = m_session->networkStorageSession().cookieStoragePartition(request, m_frameID, m_pageID);
391 #if !RELEASE_LOG_DISABLED
392         if (NetworkProcess::singleton().shouldLogCookieInformation())
393             RELEASE_LOG_IF(isAlwaysOnLoggingAllowed(), Network, "%p - NetworkDataTaskCocoa::willPerformHTTPRedirection::logCookieInformation: pageID = %llu, frameID = %llu, taskID = %lu: %s cookies for redirect URL %s", this, m_pageID, m_frameID, (unsigned long)[m_task taskIdentifier], (requiredStoragePartition.isEmpty() ? "Not partitioning" : "Partitioning"), request.url().string().utf8().data());
394 #else
395         LOG(NetworkSession, "%llu %s cookies for redirect URL %s", [m_task taskIdentifier], (requiredStoragePartition.isEmpty() ? "Not partitioning" : "Partitioning"), request.url().string().utf8().data());
396 #endif
397         applyCookiePartitioningPolicy(requiredStoragePartition, m_task.get()._storagePartitionIdentifier);
398     }
399 #endif
400
401     updateTaskWithFirstPartyForSameSiteCookies(m_task.get(), request);
402
403     if (m_client)
404         m_client->willPerformHTTPRedirection(WTFMove(redirectResponse), WTFMove(request), WTFMove(completionHandler));
405     else {
406         ASSERT_NOT_REACHED();
407         completionHandler({ });
408     }
409 }
410
411 void NetworkDataTaskCocoa::setPendingDownloadLocation(const WTF::String& filename, SandboxExtension::Handle&& sandboxExtensionHandle, bool allowOverwrite)
412 {
413     NetworkDataTask::setPendingDownloadLocation(filename, { }, allowOverwrite);
414
415     ASSERT(!m_sandboxExtension);
416     m_sandboxExtension = SandboxExtension::create(WTFMove(sandboxExtensionHandle));
417     if (m_sandboxExtension)
418         m_sandboxExtension->consume();
419
420     m_task.get()._pathToDownloadTaskFile = m_pendingDownloadLocation;
421
422     if (allowOverwrite && WebCore::FileSystem::fileExists(m_pendingDownloadLocation))
423         WebCore::FileSystem::deleteFile(filename);
424 }
425
426 bool NetworkDataTaskCocoa::tryPasswordBasedAuthentication(const WebCore::AuthenticationChallenge& challenge, ChallengeCompletionHandler& completionHandler)
427 {
428     if (!challenge.protectionSpace().isPasswordBased())
429         return false;
430     
431     if (!m_user.isNull() && !m_password.isNull()) {
432         auto persistence = m_storedCredentialsPolicy == WebCore::StoredCredentialsPolicy::Use ? WebCore::CredentialPersistenceForSession : WebCore::CredentialPersistenceNone;
433         completionHandler(AuthenticationChallengeDisposition::UseCredential, WebCore::Credential(m_user, m_password, persistence));
434         m_user = String();
435         m_password = String();
436         return true;
437     }
438
439 #if USE(CREDENTIAL_STORAGE_WITH_NETWORK_SESSION)
440     if (m_storedCredentialsPolicy == WebCore::StoredCredentialsPolicy::Use) {
441         if (!m_initialCredential.isEmpty() || challenge.previousFailureCount()) {
442             // The stored credential wasn't accepted, stop using it.
443             // There is a race condition here, since a different credential might have already been stored by another ResourceHandle,
444             // but the observable effect should be very minor, if any.
445             m_session->networkStorageSession().credentialStorage().remove(m_partition, challenge.protectionSpace());
446         }
447
448         if (!challenge.previousFailureCount()) {
449             auto credential = m_session->networkStorageSession().credentialStorage().get(m_partition, challenge.protectionSpace());
450             if (!credential.isEmpty() && credential != m_initialCredential) {
451                 ASSERT(credential.persistence() == WebCore::CredentialPersistenceNone);
452                 if (challenge.failureResponse().httpStatusCode() == 401) {
453                     // Store the credential back, possibly adding it as a default for this directory.
454                     m_session->networkStorageSession().credentialStorage().set(m_partition, credential, challenge.protectionSpace(), challenge.failureResponse().url());
455                 }
456                 completionHandler(AuthenticationChallengeDisposition::UseCredential, credential);
457                 return true;
458             }
459         }
460     }
461 #endif
462
463     if (!challenge.proposedCredential().isEmpty() && !challenge.previousFailureCount()) {
464         completionHandler(AuthenticationChallengeDisposition::UseCredential, challenge.proposedCredential());
465         return true;
466     }
467     
468     return false;
469 }
470
471 void NetworkDataTaskCocoa::transferSandboxExtensionToDownload(Download& download)
472 {
473     download.setSandboxExtension(WTFMove(m_sandboxExtension));
474 }
475
476 String NetworkDataTaskCocoa::suggestedFilename() const
477 {
478     if (!m_suggestedFilename.isEmpty())
479         return m_suggestedFilename;
480     return m_task.get().response.suggestedFilename;
481 }
482
483 void NetworkDataTaskCocoa::cancel()
484 {
485     [m_task cancel];
486 }
487
488 void NetworkDataTaskCocoa::resume()
489 {
490     if (m_scheduledFailureType != NoFailure)
491         m_failureTimer.startOneShot(0_s);
492     [m_task resume];
493 }
494
495 void NetworkDataTaskCocoa::suspend()
496 {
497     if (m_failureTimer.isActive())
498         m_failureTimer.stop();
499     [m_task suspend];
500 }
501
502 NetworkDataTask::State NetworkDataTaskCocoa::state() const
503 {
504     switch ([m_task state]) {
505     case NSURLSessionTaskStateRunning:
506         return State::Running;
507     case NSURLSessionTaskStateSuspended:
508         return State::Suspended;
509     case NSURLSessionTaskStateCanceling:
510         return State::Canceling;
511     case NSURLSessionTaskStateCompleted:
512         return State::Completed;
513     }
514
515     ASSERT_NOT_REACHED();
516     return State::Completed;
517 }
518
519 WebCore::Credential serverTrustCredential(const WebCore::AuthenticationChallenge& challenge)
520 {
521     return WebCore::Credential([NSURLCredential credentialForTrust:challenge.nsURLAuthenticationChallenge().protectionSpace.serverTrust]);
522 }
523
524 bool NetworkDataTaskCocoa::isAlwaysOnLoggingAllowed() const
525 {
526     if (NetworkProcess::singleton().sessionIsControlledByAutomation(m_session->sessionID()))
527         return true;
528
529     return m_session->sessionID().isAlwaysOnLoggingAllowed();
530 }
531
532 String NetworkDataTaskCocoa::description() const
533 {
534     return String([m_task description]);
535 }
536
537 }