Remove WebCoreSystemInterface
[WebKit-https.git] / Source / WebCore / platform / network / cf / ResourceHandleCFNet.cpp
1 /*
2  * Copyright (C) 2004, 2005, 2006, 2007, 2009, 2010, 2011, 2013 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. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27
28 #include "ResourceHandleInternal.h"
29
30 #include "AuthenticationCF.h"
31 #include "AuthenticationChallenge.h"
32 #include "CachedResourceLoader.h"
33 #include "CredentialStorage.h"
34 #include "FormDataStreamCFNet.h"
35 #include "Frame.h"
36 #include "FrameLoader.h"
37 #include "HTTPHeaderNames.h"
38 #include "Logging.h"
39 #include "NetworkStorageSession.h"
40 #include "NetworkingContext.h"
41 #include "ResourceError.h"
42 #include "ResourceHandleClient.h"
43 #include "ResourceResponse.h"
44 #include "SharedBuffer.h"
45 #include "SynchronousLoaderClient.h"
46 #include "SynchronousResourceHandleCFURLConnectionDelegate.h"
47 #include <CFNetwork/CFNetwork.h>
48 #include <pal/spi/cf/CFNetworkSPI.h>
49 #include <sys/stat.h>
50 #include <sys/types.h>
51 #include <wtf/HashMap.h>
52 #include <wtf/NeverDestroyed.h>
53 #include <wtf/Ref.h>
54 #include <wtf/Threading.h>
55 #include <wtf/text/Base64.h>
56 #include <wtf/text/CString.h>
57
58 #if PLATFORM(COCOA)
59 #include "ResourceHandleCFURLConnectionDelegateWithOperationQueue.h"
60 #if USE(CFURLCONNECTION)
61 #include "WebCoreURLResponse.h"
62 #include <CFNetwork/CFURLConnectionPriv.h>
63 #include <CFNetwork/CFURLRequestPriv.h>
64 #endif
65 #endif
66
67 #if PLATFORM(WIN)
68 #include <WebKitSystemInterface/WebKitSystemInterface.h>
69 #include <process.h>
70
71 // FIXME: Remove this declaration once it's in WebKitSupportLibrary.
72 extern "C" {
73 __declspec(dllimport) CFURLConnectionRef CFURLConnectionCreateWithProperties(
74   CFAllocatorRef           alloc,
75   CFURLRequestRef          request,
76   CFURLConnectionClient *  client,
77   CFDictionaryRef properties);
78 }
79 #endif
80
81 namespace WebCore {
82
83 #if USE(CFURLCONNECTION)
84
85 static HashSet<String, ASCIICaseInsensitiveHash>& allowsAnyHTTPSCertificateHosts()
86 {
87     static NeverDestroyed<HashSet<String, ASCIICaseInsensitiveHash>> hosts;
88     return hosts;
89 }
90
91 static HashMap<String, RetainPtr<CFDataRef>, ASCIICaseInsensitiveHash>& clientCertificates()
92 {
93     static NeverDestroyed<HashMap<String, RetainPtr<CFDataRef>, ASCIICaseInsensitiveHash>> certs;
94     return certs;
95 }
96
97 static void applyBasicAuthorizationHeader(ResourceRequest& request, const Credential& credential)
98 {
99     String authenticationHeader = "Basic " + base64Encode(String(credential.user() + ":" + credential.password()).utf8());
100
101     request.setHTTPHeaderField(HTTPHeaderName::Authorization, authenticationHeader);
102 }
103
104 ResourceHandleInternal::~ResourceHandleInternal()
105 {
106     if (m_connectionDelegate)
107         m_connectionDelegate->releaseHandle();
108
109     if (m_connection) {
110         LOG(Network, "CFNet - Cancelling connection %p (%s)", m_connection.get(), m_firstRequest.url().string().utf8().data());
111         CFURLConnectionCancel(m_connection.get());
112     }
113 }
114
115 ResourceHandle::~ResourceHandle()
116 {
117     LOG(Network, "CFNet - Destroying job %p (%s)", this, d->m_firstRequest.url().string().utf8().data());
118 }
119     
120 static inline CFStringRef shouldSniffConnectionProperty()
121 {
122 #if PLATFORM(WIN)
123     return CFSTR("_kCFURLConnectionPropertyShouldSniff");
124 #else
125     return _kCFURLConnectionPropertyShouldSniff;
126 #endif
127 }
128
129 void ResourceHandle::createCFURLConnection(bool shouldUseCredentialStorage, bool shouldContentSniff, SchedulingBehavior schedulingBehavior, CFDictionaryRef clientProperties)
130 {
131     if ((!d->m_user.isEmpty() || !d->m_pass.isEmpty()) && !firstRequest().url().protocolIsInHTTPFamily()) {
132         // Credentials for ftp can only be passed in URL, the didReceiveAuthenticationChallenge delegate call won't be made.
133         URL urlWithCredentials(firstRequest().url());
134         urlWithCredentials.setUser(d->m_user);
135         urlWithCredentials.setPass(d->m_pass);
136         firstRequest().setURL(urlWithCredentials);
137     }
138
139     String partition = firstRequest().cachePartition();
140
141     // <rdar://problem/7174050> - For URLs that match the paths of those previously challenged for HTTP Basic authentication,
142     // try and reuse the credential preemptively, as allowed by RFC 2617.
143     if (shouldUseCredentialStorage && firstRequest().url().protocolIsInHTTPFamily()) {
144         if (d->m_user.isEmpty() && d->m_pass.isEmpty()) {
145             // <rdar://problem/7174050> - For URLs that match the paths of those previously challenged for HTTP Basic authentication, 
146             // try and reuse the credential preemptively, as allowed by RFC 2617.
147             d->m_initialCredential = d->m_context->storageSession().credentialStorage().get(partition, firstRequest().url());
148         } else {
149             // If there is already a protection space known for the URL, update stored credentials before sending a request.
150             // This makes it possible to implement logout by sending an XMLHttpRequest with known incorrect credentials, and aborting it immediately
151             // (so that an authentication dialog doesn't pop up).
152             d->m_context->storageSession().credentialStorage().set(partition, Credential(d->m_user, d->m_pass, CredentialPersistenceNone), firstRequest().url());
153         }
154     }
155         
156     if (!d->m_initialCredential.isEmpty()) {
157         // FIXME: Support Digest authentication, and Proxy-Authorization.
158         applyBasicAuthorizationHeader(firstRequest(), d->m_initialCredential);
159     }
160
161     auto request = adoptCF(CFURLRequestCreateMutableCopy(kCFAllocatorDefault, firstRequest().cfURLRequest(UpdateHTTPBody)));
162     if (auto storageSession = d->m_storageSession.get())
163         _CFURLRequestSetStorageSession(request.get(), storageSession);
164     
165     if (!shouldContentSniff)
166         _CFURLRequestSetProtocolProperty(request.get(), shouldSniffConnectionProperty(), kCFBooleanFalse);
167
168     RetainPtr<CFMutableDictionaryRef> sslProps;
169
170 #if PLATFORM(IOS)
171     sslProps = adoptCF(ResourceHandle::createSSLPropertiesFromNSURLRequest(firstRequest()));
172 #else
173     if (allowsAnyHTTPSCertificateHosts().contains(firstRequest().url().host())) {
174         sslProps = adoptCF(CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
175 #pragma clang diagnostic push
176 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
177         CFDictionaryAddValue(sslProps.get(), kCFStreamSSLAllowsAnyRoot, kCFBooleanTrue);
178         CFDictionaryAddValue(sslProps.get(), kCFStreamSSLAllowsExpiredRoots, kCFBooleanTrue);
179         CFDictionaryAddValue(sslProps.get(), kCFStreamSSLAllowsExpiredCertificates, kCFBooleanTrue);
180 #pragma clang diagnostic pop
181 #if !PLATFORM(WIN) // <rdar://problem/33993462> - Disabling validation of certificate chain breaks SSL on Windows.
182         CFDictionaryAddValue(sslProps.get(), kCFStreamSSLValidatesCertificateChain, kCFBooleanFalse);
183 #endif
184     }
185
186     auto clientCert = clientCertificates().find(firstRequest().url().host());
187     if (clientCert != clientCertificates().end()) {
188         if (!sslProps)
189             sslProps = adoptCF(CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
190 #if PLATFORM(WIN)
191         wkSetClientCertificateInSSLProperties(sslProps.get(), (clientCert->value).get());
192 #endif
193     }
194 #endif // PLATFORM(IOS)
195
196     if (sslProps)
197         CFURLRequestSetSSLProperties(request.get(), sslProps.get());
198
199     CFMutableDictionaryRef streamProperties  = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
200
201     if (!shouldUseCredentialStorage) {
202         // Avoid using existing connections, because they may be already authenticated.
203         CFDictionarySetValue(streamProperties, CFSTR("_kCFURLConnectionSessionID"), CFSTR("WebKitPrivateSession"));
204     }
205
206     if (schedulingBehavior == SchedulingBehavior::Synchronous) {
207         // Synchronous requests should not be subject to regular connection count limit to avoid deadlocks.
208         // If we are using all available connections for async requests, and make a sync request, then prior
209         // requests may get stuck waiting for delegate calls while we are in nested run loop, and the sync
210         // request won't start because there are no available connections.
211         // Connections are grouped by their socket stream properties, with each group having a separate count.
212         CFDictionarySetValue(streamProperties, CFSTR("_WebKitSynchronousRequest"), kCFBooleanTrue);
213     }
214
215 #if PLATFORM(COCOA)
216     RetainPtr<CFDataRef> sourceApplicationAuditData = d->m_context->sourceApplicationAuditData();
217     if (sourceApplicationAuditData)
218         CFDictionarySetValue(streamProperties, CFSTR("kCFStreamPropertySourceApplication"), sourceApplicationAuditData.get());
219 #endif
220
221     static const CFStringRef kCFURLConnectionSocketStreamProperties = CFSTR("kCFURLConnectionSocketStreamProperties");
222     RetainPtr<CFMutableDictionaryRef> propertiesDictionary;
223     if (clientProperties)
224         propertiesDictionary = adoptCF(CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, clientProperties));
225     else
226         propertiesDictionary = adoptCF(CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
227
228 #if HAVE(TIMINGDATAOPTIONS)
229     int64_t value = static_cast<int64_t>(_TimingDataOptionsEnableW3CNavigationTiming);
230     auto enableW3CNavigationTiming = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &value));
231     auto timingDataOptionsDictionary = adoptCF(CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
232     CFDictionaryAddValue(timingDataOptionsDictionary.get(), CFSTR("_kCFURLConnectionPropertyTimingDataOptions"), enableW3CNavigationTiming.get());
233     CFDictionaryAddValue(propertiesDictionary.get(), CFSTR("kCFURLConnectionURLConnectionProperties"), timingDataOptionsDictionary.get());
234 #endif
235
236     // FIXME: This code is different from iOS code in ResourceHandleMac.mm in that here we ignore stream properties that were present in client properties.
237     CFDictionaryAddValue(propertiesDictionary.get(), kCFURLConnectionSocketStreamProperties, streamProperties);
238     CFRelease(streamProperties);
239
240 #if PLATFORM(COCOA)
241     if (d->m_usesAsyncCallbacks)
242         d->m_connectionDelegate = adoptRef(new ResourceHandleCFURLConnectionDelegateWithOperationQueue(this));
243     else
244         d->m_connectionDelegate = adoptRef(new SynchronousResourceHandleCFURLConnectionDelegate(this));
245 #else
246     d->m_connectionDelegate = adoptRef(new SynchronousResourceHandleCFURLConnectionDelegate(this));
247 #endif
248     d->m_connectionDelegate->setupRequest(request.get());
249
250     CFURLConnectionClient_V6 client = d->m_connectionDelegate->makeConnectionClient();
251     if (shouldUseCredentialStorage)
252         client.shouldUseCredentialStorage = 0;
253
254 #pragma clang diagnostic push
255 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
256     d->m_connection = adoptCF(CFURLConnectionCreateWithProperties(0, request.get(), reinterpret_cast<CFURLConnectionClient*>(&client), propertiesDictionary.get()));
257 #pragma clang diagnostic pop
258 }
259
260 bool ResourceHandle::start()
261 {
262     if (!d->m_context)
263         return false;
264
265     // If NetworkingContext is invalid then we are no longer attached to a Page,
266     // this must be an attempted load from an unload handler, so let's just block it.
267     if (!d->m_context->isValid())
268         return false;
269
270     d->m_storageSession = d->m_context->storageSession().platformSession();
271
272     bool shouldUseCredentialStorage = !client() || client()->shouldUseCredentialStorage(this);
273
274 #if PLATFORM(COCOA) && !HAVE(TIMINGDATAOPTIONS)
275     setCollectsTimingData();
276 #endif
277
278     SchedulingBehavior schedulingBehavior = client()->loadingSynchronousXHR() ? SchedulingBehavior::Synchronous : SchedulingBehavior::Asynchronous;
279
280     createCFURLConnection(shouldUseCredentialStorage, d->m_shouldContentSniff, schedulingBehavior, client()->connectionProperties(this).get());
281
282     d->m_connectionDelegate->setupConnectionScheduling(d->m_connection.get());
283     CFURLConnectionStart(d->m_connection.get());
284
285     LOG(Network, "CFNet - Starting URL %s (handle=%p, conn=%p)", firstRequest().url().string().utf8().data(), this, d->m_connection.get());
286
287     return true;
288 }
289
290 void ResourceHandle::cancel()
291 {
292     if (d->m_connection) {
293         CFURLConnectionCancel(d->m_connection.get());
294         d->m_connection = 0;
295     }
296 }
297
298 ResourceRequest ResourceHandle::willSendRequest(ResourceRequest&& request, ResourceResponse&& redirectResponse)
299 {
300     const URL& url = request.url();
301     d->m_user = url.user();
302     d->m_pass = url.pass();
303     d->m_lastHTTPMethod = request.httpMethod();
304     request.removeCredentials();
305
306     String partition = firstRequest().cachePartition();
307
308     if (!protocolHostAndPortAreEqual(request.url(), redirectResponse.url())) {
309         // The network layer might carry over some headers from the original request that
310         // we want to strip here because the redirect is cross-origin.
311         request.clearHTTPAuthorization();
312         request.clearHTTPOrigin();
313     } else {
314         // Only consider applying authentication credentials if this is actually a redirect and the redirect
315         // URL didn't include credentials of its own.
316         if (d->m_user.isEmpty() && d->m_pass.isEmpty() && !redirectResponse.isNull()) {
317             Credential credential = d->m_context->storageSession().credentialStorage().get(partition, request.url());
318             if (!credential.isEmpty()) {
319                 d->m_initialCredential = credential;
320                 
321                 // FIXME: Support Digest authentication, and Proxy-Authorization.
322                 applyBasicAuthorizationHeader(request, d->m_initialCredential);
323             }
324         }
325     }
326
327     Ref<ResourceHandle> protectedThis(*this);
328     if (d->m_usesAsyncCallbacks) {
329         client()->willSendRequestAsync(this, WTFMove(request), WTFMove(redirectResponse));
330         return { };
331     }
332     
333     auto newRequest = client()->willSendRequest(this, WTFMove(request), WTFMove(redirectResponse));
334
335     // Client call may not preserve the session, especially if the request is sent over IPC.
336     if (!newRequest.isNull()) {
337         newRequest.setStorageSession(d->m_storageSession.get());
338
339         d->m_currentRequest = newRequest;
340     }
341     return newRequest;
342 }
343
344 bool ResourceHandle::shouldUseCredentialStorage()
345 {
346     LOG(Network, "CFNet - shouldUseCredentialStorage()");
347     if (ResourceHandleClient* client = this->client()) {
348         ASSERT(!d->m_usesAsyncCallbacks);
349         return client->shouldUseCredentialStorage(this);
350     }
351     return false;
352 }
353
354 void ResourceHandle::didReceiveAuthenticationChallenge(const AuthenticationChallenge& challenge)
355 {
356     LOG(Network, "CFNet - didReceiveAuthenticationChallenge()");
357     ASSERT(d->m_currentWebChallenge.isNull());
358     // Since CFURLConnection networking relies on keeping a reference to the original CFURLAuthChallengeRef,
359     // we make sure that is actually present
360     ASSERT(challenge.cfURLAuthChallengeRef());
361     ASSERT(challenge.authenticationClient() == this); // Should be already set.
362
363 #if !PLATFORM(WIN)
364     // Proxy authentication is handled by CFNetwork internally. We can get here if the user cancels
365     // CFNetwork authentication dialog, and we shouldn't ask the client to display another one in that case.
366     if (challenge.protectionSpace().isProxy()) {
367         // Cannot use receivedRequestToContinueWithoutCredential(), because current challenge is not yet set.
368         CFURLConnectionUseCredential(d->m_connection.get(), 0, challenge.cfURLAuthChallengeRef());
369         return;
370     }
371 #endif
372
373     if (tryHandlePasswordBasedAuthentication(challenge))
374         return;
375
376     d->m_currentWebChallenge = challenge;
377     
378     if (client())
379         client()->didReceiveAuthenticationChallenge(this, d->m_currentWebChallenge);
380     else {
381         clearAuthentication();
382         CFURLConnectionPerformDefaultHandlingForChallenge(d->m_connection.get(), challenge.cfURLAuthChallengeRef());
383     }
384 }
385
386 bool ResourceHandle::tryHandlePasswordBasedAuthentication(const AuthenticationChallenge& challenge)
387 {
388     if (!challenge.protectionSpace().isPasswordBased())
389         return false;
390
391     String partition = firstRequest().cachePartition();
392
393     if (!d->m_user.isNull() && !d->m_pass.isNull()) {
394         RetainPtr<CFURLCredentialRef> cfCredential = adoptCF(CFURLCredentialCreate(kCFAllocatorDefault, d->m_user.createCFString().get(), d->m_pass.createCFString().get(), 0, kCFURLCredentialPersistenceNone));
395 #if PLATFORM(COCOA)
396         Credential credential = Credential(cfCredential.get());
397 #else
398         Credential credential = core(cfCredential.get());
399 #endif
400         
401         URL urlToStore;
402         if (challenge.failureResponse().httpStatusCode() == 401)
403             urlToStore = challenge.failureResponse().url();
404         d->m_context->storageSession().credentialStorage().set(partition, credential, challenge.protectionSpace(), urlToStore);
405         
406         CFURLConnectionUseCredential(d->m_connection.get(), cfCredential.get(), challenge.cfURLAuthChallengeRef());
407         d->m_user = String();
408         d->m_pass = String();
409         // FIXME: Per the specification, the user shouldn't be asked for credentials if there were incorrect ones provided explicitly.
410         return true;
411     }
412
413     if (!client() || client()->shouldUseCredentialStorage(this)) {
414         if (!d->m_initialCredential.isEmpty() || challenge.previousFailureCount()) {
415             // The stored credential wasn't accepted, stop using it.
416             // There is a race condition here, since a different credential might have already been stored by another ResourceHandle,
417             // but the observable effect should be very minor, if any.
418             d->m_context->storageSession().credentialStorage().remove(partition, challenge.protectionSpace());
419         }
420
421         if (!challenge.previousFailureCount()) {
422             Credential credential = d->m_context->storageSession().credentialStorage().get(partition, challenge.protectionSpace());
423             if (!credential.isEmpty() && credential != d->m_initialCredential) {
424                 ASSERT(credential.persistence() == CredentialPersistenceNone);
425                 if (challenge.failureResponse().httpStatusCode() == 401) {
426                     // Store the credential back, possibly adding it as a default for this directory.
427                     d->m_context->storageSession().credentialStorage().set(partition, credential, challenge.protectionSpace(), challenge.failureResponse().url());
428                 }
429 #if PLATFORM(COCOA)
430                 CFURLConnectionUseCredential(d->m_connection.get(), credential.cfCredential(), challenge.cfURLAuthChallengeRef());
431 #else
432                 RetainPtr<CFURLCredentialRef> cfCredential = adoptCF(createCF(credential));
433                 CFURLConnectionUseCredential(d->m_connection.get(), cfCredential.get(), challenge.cfURLAuthChallengeRef());
434 #endif
435                 return true;
436             }
437         }
438     }
439
440     return false;
441 }
442
443 #if USE(PROTECTION_SPACE_AUTH_CALLBACK)
444 bool ResourceHandle::canAuthenticateAgainstProtectionSpace(const ProtectionSpace& protectionSpace)
445 {
446     ResourceHandleClient* client = this->client();
447     if (d->m_usesAsyncCallbacks) {
448         if (client)
449             client->canAuthenticateAgainstProtectionSpaceAsync(this, protectionSpace);
450         else
451             continueCanAuthenticateAgainstProtectionSpace(false);
452         return false; // Ignored by caller.
453     }
454
455     return client && client->canAuthenticateAgainstProtectionSpace(this, protectionSpace);
456 }
457 #endif
458
459 void ResourceHandle::receivedCredential(const AuthenticationChallenge& challenge, const Credential& credential)
460 {
461     LOG(Network, "CFNet - receivedCredential()");
462     ASSERT(!challenge.isNull());
463     ASSERT(challenge.cfURLAuthChallengeRef());
464     if (challenge != d->m_currentWebChallenge)
465         return;
466
467     // FIXME: Support empty credentials. Currently, an empty credential cannot be stored in WebCore credential storage, as that's empty value for its map.
468     if (credential.isEmpty()) {
469         receivedRequestToContinueWithoutCredential(challenge);
470         return;
471     }
472
473     if (credential.persistence() == CredentialPersistenceForSession && challenge.protectionSpace().authenticationScheme() != ProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested) {
474         // Manage per-session credentials internally, because once NSURLCredentialPersistencePerSession is used, there is no way
475         // to ignore it for a particular request (short of removing it altogether).
476         Credential webCredential(credential, CredentialPersistenceNone);
477
478         URL urlToStore;
479         if (challenge.failureResponse().httpStatusCode() == 401)
480             urlToStore = challenge.failureResponse().url(); 
481
482         d->m_context->storageSession().credentialStorage().set(firstRequest().cachePartition(), webCredential, challenge.protectionSpace(), urlToStore);
483
484         if (d->m_connection) {
485 #if PLATFORM(COCOA)
486             CFURLConnectionUseCredential(d->m_connection.get(), webCredential.cfCredential(), challenge.cfURLAuthChallengeRef());
487 #else
488             RetainPtr<CFURLCredentialRef> cfCredential = adoptCF(createCF(webCredential));
489             CFURLConnectionUseCredential(d->m_connection.get(), cfCredential.get(), challenge.cfURLAuthChallengeRef());
490 #endif
491         }
492     } else if (d->m_connection) {
493 #if PLATFORM(COCOA)
494         CFURLConnectionUseCredential(d->m_connection.get(), credential.cfCredential(), challenge.cfURLAuthChallengeRef());
495 #else
496         RetainPtr<CFURLCredentialRef> cfCredential = adoptCF(createCF(credential));
497         CFURLConnectionUseCredential(d->m_connection.get(), cfCredential.get(), challenge.cfURLAuthChallengeRef());
498 #endif
499     }
500
501     clearAuthentication();
502 }
503
504 void ResourceHandle::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge& challenge)
505 {
506     LOG(Network, "CFNet - receivedRequestToContinueWithoutCredential()");
507     ASSERT(!challenge.isNull());
508     ASSERT(challenge.cfURLAuthChallengeRef());
509     if (challenge != d->m_currentWebChallenge)
510         return;
511
512     if (d->m_connection)
513         CFURLConnectionUseCredential(d->m_connection.get(), 0, challenge.cfURLAuthChallengeRef());
514
515     clearAuthentication();
516 }
517
518 void ResourceHandle::receivedCancellation(const AuthenticationChallenge& challenge)
519 {
520     LOG(Network, "CFNet - receivedCancellation()");
521     if (challenge != d->m_currentWebChallenge)
522         return;
523
524     if (client())
525         client()->receivedCancellation(this, challenge);
526 }
527
528 void ResourceHandle::receivedRequestToPerformDefaultHandling(const AuthenticationChallenge& challenge)
529 {
530     LOG(Network, "CFNet - receivedRequestToPerformDefaultHandling()");
531     ASSERT(!challenge.isNull());
532     ASSERT(challenge.cfURLAuthChallengeRef());
533     if (challenge != d->m_currentWebChallenge)
534         return;
535
536     if (d->m_connection)
537         CFURLConnectionPerformDefaultHandlingForChallenge(d->m_connection.get(), challenge.cfURLAuthChallengeRef());
538
539     clearAuthentication();
540 }
541
542 void ResourceHandle::receivedChallengeRejection(const AuthenticationChallenge& challenge)
543 {
544     LOG(Network, "CFNet - receivedChallengeRejection()");
545     ASSERT(!challenge.isNull());
546     ASSERT(challenge.cfURLAuthChallengeRef());
547     if (challenge != d->m_currentWebChallenge)
548         return;
549
550     if (d->m_connection)
551         CFURLConnectionRejectChallenge(d->m_connection.get(), challenge.cfURLAuthChallengeRef());
552
553     clearAuthentication();
554 }
555
556 CFURLStorageSessionRef ResourceHandle::storageSession() const
557 {
558     return d->m_storageSession.get();
559 }
560
561 CFURLConnectionRef ResourceHandle::connection() const
562 {
563     return d->m_connection.get();
564 }
565
566 RetainPtr<CFURLConnectionRef> ResourceHandle::releaseConnectionForDownload()
567 {
568     LOG(Network, "CFNet - Job %p releasing connection %p for download", this, d->m_connection.get());
569     return WTFMove(d->m_connection);
570 }
571
572 CFStringRef ResourceHandle::synchronousLoadRunLoopMode()
573 {
574     return CFSTR("WebCoreSynchronousLoaderRunLoopMode");
575 }
576
577 void ResourceHandle::platformLoadResourceSynchronously(NetworkingContext* context, const ResourceRequest& request, StoredCredentialsPolicy storedCredentialsPolicy, ResourceError& error, ResourceResponse& response, Vector<char>& data)
578 {
579     LOG(Network, "ResourceHandle::platformLoadResourceSynchronously:%s sstoredCredentialsPolicy:%u", request.url().string().utf8().data(), static_cast<unsigned>(storedCredentialsPolicy));
580
581     ASSERT(!request.isEmpty());
582
583     ASSERT(response.isNull());
584     ASSERT(error.isNull());
585
586     SynchronousLoaderClient client;
587     client.setAllowStoredCredentials(storedCredentialsPolicy == StoredCredentialsPolicy::Use);
588
589     RefPtr<ResourceHandle> handle = adoptRef(new ResourceHandle(context, request, &client, false /*defersLoading*/, true /*shouldContentSniff*/));
590
591     handle->d->m_storageSession = context->storageSession().platformSession();
592
593     if (handle->d->m_scheduledFailureType != NoFailure) {
594         error = context->blockedError(request);
595         return;
596     }
597
598     handle->createCFURLConnection(storedCredentialsPolicy == StoredCredentialsPolicy::Use, ResourceHandle::shouldContentSniffURL(request.url()),
599         SchedulingBehavior::Synchronous, handle->client()->connectionProperties(handle.get()).get());
600
601     CFURLConnectionScheduleWithRunLoop(handle->connection(), CFRunLoopGetCurrent(), synchronousLoadRunLoopMode());
602     CFURLConnectionScheduleDownloadWithRunLoop(handle->connection(), CFRunLoopGetCurrent(), synchronousLoadRunLoopMode());
603     CFURLConnectionStart(handle->connection());
604
605     while (!client.isDone())
606         CFRunLoopRunInMode(synchronousLoadRunLoopMode(), UINT_MAX, true);
607
608     error = client.error();
609
610     CFURLConnectionCancel(handle->connection());
611
612     if (error.isNull())
613         response = client.response();
614
615     data.swap(client.mutableData());
616 }
617
618 void ResourceHandle::setHostAllowsAnyHTTPSCertificate(const String& host)
619 {
620     allowsAnyHTTPSCertificateHosts().add(host);
621 }
622
623 void ResourceHandle::setClientCertificate(const String& host, CFDataRef certificate)
624 {
625     clientCertificates().set(host, certificate);
626 }
627
628 void ResourceHandle::platformSetDefersLoading(bool defers)
629 {
630     if (!d->m_connection)
631         return;
632
633     if (defers)
634         CFURLConnectionHalt(d->m_connection.get());
635     else
636         CFURLConnectionResume(d->m_connection.get());
637 }
638
639 #if PLATFORM(COCOA)
640 void ResourceHandle::schedule(SchedulePair& pair)
641 {
642     CFRunLoopRef runLoop = pair.runLoop();
643     if (!runLoop)
644         return;
645
646     CFURLConnectionScheduleWithRunLoop(d->m_connection.get(), runLoop, pair.mode());
647     if (d->m_startWhenScheduled) {
648         CFURLConnectionStart(d->m_connection.get());
649         d->m_startWhenScheduled = false;
650     }
651 }
652
653 void ResourceHandle::unschedule(SchedulePair& pair)
654 {
655     CFRunLoopRef runLoop = pair.runLoop();
656     if (!runLoop)
657         return;
658
659     CFURLConnectionUnscheduleFromRunLoop(d->m_connection.get(), runLoop, pair.mode());
660 }
661 #endif
662
663 const ResourceRequest& ResourceHandle::currentRequest() const
664 {
665     return d->m_currentRequest;
666 }
667
668 void ResourceHandle::continueWillSendRequest(ResourceRequest&& request)
669 {
670     if (!request.isNull())
671         request.setStorageSession(d->m_storageSession.get());
672     d->m_connectionDelegate->continueWillSendRequest(request.cfURLRequest(UpdateHTTPBody));
673 }
674
675 void ResourceHandle::continueDidReceiveResponse()
676 {
677     d->m_connectionDelegate->continueDidReceiveResponse();
678 }
679
680 #if USE(PROTECTION_SPACE_AUTH_CALLBACK)
681 void ResourceHandle::continueCanAuthenticateAgainstProtectionSpace(bool canAuthenticate)
682 {
683     d->m_connectionDelegate->continueCanAuthenticateAgainstProtectionSpace(canAuthenticate);
684 }
685 #endif
686
687 void ResourceHandle::continueWillCacheResponse(CFCachedURLResponseRef response)
688 {
689     d->m_connectionDelegate->continueWillCacheResponse(response);
690 }
691 #endif // USE(CFURLCONNECTION)
692
693 } // namespace WebCore