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