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