Clean up usesAsyncCallbacks handling in ResourceHandle
[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 = d->m_context->storageSession().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             d->m_context->storageSession().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 (d->m_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) && PLATFORM(COCOA)
256     setCollectsTimingData();
257 #endif
258
259     SchedulingBehavior schedulingBehavior = client()->loadingSynchronousXHR() ? SchedulingBehavior::Synchronous : SchedulingBehavior::Asynchronous;
260
261     createCFURLConnection(shouldUseCredentialStorage, d->m_shouldContentSniff, schedulingBehavior, client()->connectionProperties(this).get());
262
263     d->m_connectionDelegate->setupConnectionScheduling(d->m_connection.get());
264     CFURLConnectionStart(d->m_connection.get());
265
266     LOG(Network, "CFNet - Starting URL %s (handle=%p, conn=%p)", firstRequest().url().string().utf8().data(), this, d->m_connection.get());
267     
268     return true;
269 }
270
271 void ResourceHandle::cancel()
272 {
273     if (d->m_connection) {
274         CFURLConnectionCancel(d->m_connection.get());
275         d->m_connection = 0;
276     }
277 }
278
279 void ResourceHandle::willSendRequest(ResourceRequest& request, const ResourceResponse& redirectResponse)
280 {
281     const URL& url = request.url();
282     d->m_user = url.user();
283     d->m_pass = url.pass();
284     d->m_lastHTTPMethod = request.httpMethod();
285     request.removeCredentials();
286
287     if (!protocolHostAndPortAreEqual(request.url(), redirectResponse.url())) {
288         // The network layer might carry over some headers from the original request that
289         // we want to strip here because the redirect is cross-origin.
290         request.clearHTTPAuthorization();
291         request.clearHTTPOrigin();
292     } else {
293         // Only consider applying authentication credentials if this is actually a redirect and the redirect
294         // URL didn't include credentials of its own.
295         if (d->m_user.isEmpty() && d->m_pass.isEmpty() && !redirectResponse.isNull()) {
296             Credential credential = d->m_context->storageSession().credentialStorage().get(request.url());
297             if (!credential.isEmpty()) {
298                 d->m_initialCredential = credential;
299                 
300                 // FIXME: Support Digest authentication, and Proxy-Authorization.
301                 applyBasicAuthorizationHeader(request, d->m_initialCredential);
302             }
303         }
304     }
305
306     Ref<ResourceHandle> protect(*this);
307     if (d->m_usesAsyncCallbacks)
308         client()->willSendRequestAsync(this, request, redirectResponse);
309     else {
310         client()->willSendRequest(this, request, redirectResponse);
311
312         // Client call may not preserve the session, especially if the request is sent over IPC.
313         if (!request.isNull()) {
314             request.setStorageSession(d->m_storageSession.get());
315
316             d->m_currentRequest = request;
317         }
318     }
319 }
320
321 bool ResourceHandle::shouldUseCredentialStorage()
322 {
323     LOG(Network, "CFNet - shouldUseCredentialStorage()");
324     if (ResourceHandleClient* client = this->client()) {
325         ASSERT(!d->m_usesAsyncCallbacks);
326         return client->shouldUseCredentialStorage(this);
327     }
328     return false;
329 }
330
331 void ResourceHandle::didReceiveAuthenticationChallenge(const AuthenticationChallenge& challenge)
332 {
333     LOG(Network, "CFNet - didReceiveAuthenticationChallenge()");
334     ASSERT(d->m_currentWebChallenge.isNull());
335     // Since CFURLConnection networking relies on keeping a reference to the original CFURLAuthChallengeRef,
336     // we make sure that is actually present
337     ASSERT(challenge.cfURLAuthChallengeRef());
338     ASSERT(challenge.authenticationClient() == this); // Should be already set.
339
340 #if !PLATFORM(WIN)
341     // Proxy authentication is handled by CFNetwork internally. We can get here if the user cancels
342     // CFNetwork authentication dialog, and we shouldn't ask the client to display another one in that case.
343     if (challenge.protectionSpace().isProxy()) {
344         // Cannot use receivedRequestToContinueWithoutCredential(), because current challenge is not yet set.
345         CFURLConnectionUseCredential(d->m_connection.get(), 0, challenge.cfURLAuthChallengeRef());
346         return;
347     }
348 #endif
349
350     if (tryHandlePasswordBasedAuthentication(challenge))
351         return;
352
353     d->m_currentWebChallenge = challenge;
354     
355     if (client())
356         client()->didReceiveAuthenticationChallenge(this, d->m_currentWebChallenge);
357     else {
358         clearAuthentication();
359         CFURLConnectionPerformDefaultHandlingForChallenge(d->m_connection.get(), challenge.cfURLAuthChallengeRef());
360     }
361 }
362
363 bool ResourceHandle::tryHandlePasswordBasedAuthentication(const AuthenticationChallenge& challenge)
364 {
365     if (!challenge.protectionSpace().isPasswordBased())
366         return false;
367
368     if (!d->m_user.isNull() && !d->m_pass.isNull()) {
369         RetainPtr<CFURLCredentialRef> cfCredential = adoptCF(CFURLCredentialCreate(kCFAllocatorDefault, d->m_user.createCFString().get(), d->m_pass.createCFString().get(), 0, kCFURLCredentialPersistenceNone));
370 #if PLATFORM(COCOA)
371         Credential credential = Credential(cfCredential.get());
372 #else
373         Credential credential = core(cfCredential.get());
374 #endif
375         
376         URL urlToStore;
377         if (challenge.failureResponse().httpStatusCode() == 401)
378             urlToStore = challenge.failureResponse().url();
379         d->m_context->storageSession().credentialStorage().set(credential, challenge.protectionSpace(), urlToStore);
380         
381         CFURLConnectionUseCredential(d->m_connection.get(), cfCredential.get(), challenge.cfURLAuthChallengeRef());
382         d->m_user = String();
383         d->m_pass = String();
384         // FIXME: Per the specification, the user shouldn't be asked for credentials if there were incorrect ones provided explicitly.
385         return true;
386     }
387
388     if (!client() || client()->shouldUseCredentialStorage(this)) {
389         if (!d->m_initialCredential.isEmpty() || challenge.previousFailureCount()) {
390             // The stored credential wasn't accepted, stop using it.
391             // There is a race condition here, since a different credential might have already been stored by another ResourceHandle,
392             // but the observable effect should be very minor, if any.
393             d->m_context->storageSession().credentialStorage().remove(challenge.protectionSpace());
394         }
395
396         if (!challenge.previousFailureCount()) {
397             Credential credential = d->m_context->storageSession().credentialStorage().get(challenge.protectionSpace());
398             if (!credential.isEmpty() && credential != d->m_initialCredential) {
399                 ASSERT(credential.persistence() == CredentialPersistenceNone);
400                 if (challenge.failureResponse().httpStatusCode() == 401) {
401                     // Store the credential back, possibly adding it as a default for this directory.
402                     d->m_context->storageSession().credentialStorage().set(credential, challenge.protectionSpace(), challenge.failureResponse().url());
403                 }
404 #if PLATFORM(COCOA)
405                 CFURLConnectionUseCredential(d->m_connection.get(), credential.cfCredential(), challenge.cfURLAuthChallengeRef());
406 #else
407                 RetainPtr<CFURLCredentialRef> cfCredential = adoptCF(createCF(credential));
408                 CFURLConnectionUseCredential(d->m_connection.get(), cfCredential.get(), challenge.cfURLAuthChallengeRef());
409 #endif
410                 return true;
411             }
412         }
413     }
414
415     return false;
416 }
417
418 #if USE(PROTECTION_SPACE_AUTH_CALLBACK)
419 bool ResourceHandle::canAuthenticateAgainstProtectionSpace(const ProtectionSpace& protectionSpace)
420 {
421     ResourceHandleClient* client = this->client();
422     if (d->m_usesAsyncCallbacks) {
423         if (client)
424             client->canAuthenticateAgainstProtectionSpaceAsync(this, protectionSpace);
425         else
426             continueCanAuthenticateAgainstProtectionSpace(false);
427         return false; // Ignored by caller.
428     }
429
430     return client && client->canAuthenticateAgainstProtectionSpace(this, protectionSpace);
431 }
432 #endif
433
434 void ResourceHandle::receivedCredential(const AuthenticationChallenge& challenge, const Credential& credential)
435 {
436     LOG(Network, "CFNet - receivedCredential()");
437     ASSERT(!challenge.isNull());
438     ASSERT(challenge.cfURLAuthChallengeRef());
439     if (challenge != d->m_currentWebChallenge)
440         return;
441
442     // FIXME: Support empty credentials. Currently, an empty credential cannot be stored in WebCore credential storage, as that's empty value for its map.
443     if (credential.isEmpty()) {
444         receivedRequestToContinueWithoutCredential(challenge);
445         return;
446     }
447
448     if (credential.persistence() == CredentialPersistenceForSession && challenge.protectionSpace().authenticationScheme() != ProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested) {
449         // Manage per-session credentials internally, because once NSURLCredentialPersistencePerSession is used, there is no way
450         // to ignore it for a particular request (short of removing it altogether).
451         Credential webCredential(credential, CredentialPersistenceNone);
452
453         URL urlToStore;
454         if (challenge.failureResponse().httpStatusCode() == 401)
455             urlToStore = challenge.failureResponse().url();      
456         d->m_context->storageSession().credentialStorage().set(webCredential, challenge.protectionSpace(), urlToStore);
457
458         if (d->m_connection) {
459 #if PLATFORM(COCOA)
460             CFURLConnectionUseCredential(d->m_connection.get(), webCredential.cfCredential(), challenge.cfURLAuthChallengeRef());
461 #else
462             RetainPtr<CFURLCredentialRef> cfCredential = adoptCF(createCF(webCredential));
463             CFURLConnectionUseCredential(d->m_connection.get(), cfCredential.get(), challenge.cfURLAuthChallengeRef());
464 #endif
465         }
466     } else if (d->m_connection) {
467 #if PLATFORM(COCOA)
468         CFURLConnectionUseCredential(d->m_connection.get(), credential.cfCredential(), challenge.cfURLAuthChallengeRef());
469 #else
470         RetainPtr<CFURLCredentialRef> cfCredential = adoptCF(createCF(credential));
471         CFURLConnectionUseCredential(d->m_connection.get(), cfCredential.get(), challenge.cfURLAuthChallengeRef());
472 #endif
473     }
474
475     clearAuthentication();
476 }
477
478 void ResourceHandle::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge& challenge)
479 {
480     LOG(Network, "CFNet - receivedRequestToContinueWithoutCredential()");
481     ASSERT(!challenge.isNull());
482     ASSERT(challenge.cfURLAuthChallengeRef());
483     if (challenge != d->m_currentWebChallenge)
484         return;
485
486     if (d->m_connection)
487         CFURLConnectionUseCredential(d->m_connection.get(), 0, challenge.cfURLAuthChallengeRef());
488
489     clearAuthentication();
490 }
491
492 void ResourceHandle::receivedCancellation(const AuthenticationChallenge& challenge)
493 {
494     LOG(Network, "CFNet - receivedCancellation()");
495     if (challenge != d->m_currentWebChallenge)
496         return;
497
498     if (client())
499         client()->receivedCancellation(this, challenge);
500 }
501
502 void ResourceHandle::receivedRequestToPerformDefaultHandling(const AuthenticationChallenge& challenge)
503 {
504     LOG(Network, "CFNet - receivedRequestToPerformDefaultHandling()");
505     ASSERT(!challenge.isNull());
506     ASSERT(challenge.cfURLAuthChallengeRef());
507     if (challenge != d->m_currentWebChallenge)
508         return;
509
510     if (d->m_connection)
511         CFURLConnectionPerformDefaultHandlingForChallenge(d->m_connection.get(), challenge.cfURLAuthChallengeRef());
512
513     clearAuthentication();
514 }
515
516 void ResourceHandle::receivedChallengeRejection(const AuthenticationChallenge& challenge)
517 {
518     LOG(Network, "CFNet - receivedChallengeRejection()");
519     ASSERT(!challenge.isNull());
520     ASSERT(challenge.cfURLAuthChallengeRef());
521     if (challenge != d->m_currentWebChallenge)
522         return;
523
524     if (d->m_connection)
525         CFURLConnectionRejectChallenge(d->m_connection.get(), challenge.cfURLAuthChallengeRef());
526
527     clearAuthentication();
528 }
529
530 CFURLStorageSessionRef ResourceHandle::storageSession() const
531 {
532     return d->m_storageSession.get();
533 }
534
535 CFURLConnectionRef ResourceHandle::connection() const
536 {
537     return d->m_connection.get();
538 }
539
540 RetainPtr<CFURLConnectionRef> ResourceHandle::releaseConnectionForDownload()
541 {
542     LOG(Network, "CFNet - Job %p releasing connection %p for download", this, d->m_connection.get());
543     return WTF::move(d->m_connection);
544 }
545
546 CFStringRef ResourceHandle::synchronousLoadRunLoopMode()
547 {
548     return CFSTR("WebCoreSynchronousLoaderRunLoopMode");
549 }
550
551 void ResourceHandle::platformLoadResourceSynchronously(NetworkingContext* context, const ResourceRequest& request, StoredCredentials storedCredentials, ResourceError& error, ResourceResponse& response, Vector<char>& data)
552 {
553     LOG(Network, "ResourceHandle::platformLoadResourceSynchronously:%s allowStoredCredentials:%u", request.url().string().utf8().data(), storedCredentials);
554
555     ASSERT(!request.isEmpty());
556
557     ASSERT(response.isNull());
558     ASSERT(error.isNull());
559
560     SynchronousLoaderClient client;
561     client.setAllowStoredCredentials(storedCredentials == AllowStoredCredentials);
562
563     RefPtr<ResourceHandle> handle = adoptRef(new ResourceHandle(context, request, &client, false /*defersLoading*/, true /*shouldContentSniff*/));
564
565     handle->d->m_storageSession = context->storageSession().platformSession();
566
567     if (handle->d->m_scheduledFailureType != NoFailure) {
568         error = context->blockedError(request);
569         return;
570     }
571
572     handle->createCFURLConnection(storedCredentials == AllowStoredCredentials, ResourceHandle::shouldContentSniffURL(request.url()),
573         SchedulingBehavior::Synchronous, handle->client()->connectionProperties(handle.get()).get());
574
575     CFURLConnectionScheduleWithRunLoop(handle->connection(), CFRunLoopGetCurrent(), synchronousLoadRunLoopMode());
576     CFURLConnectionScheduleDownloadWithRunLoop(handle->connection(), CFRunLoopGetCurrent(), synchronousLoadRunLoopMode());
577     CFURLConnectionStart(handle->connection());
578
579     while (!client.isDone())
580         CFRunLoopRunInMode(synchronousLoadRunLoopMode(), UINT_MAX, true);
581
582     error = client.error();
583
584     CFURLConnectionCancel(handle->connection());
585
586     if (error.isNull())
587         response = client.response();
588
589     data.swap(client.mutableData());
590 }
591
592 void ResourceHandle::setHostAllowsAnyHTTPSCertificate(const String& host)
593 {
594     allowsAnyHTTPSCertificateHosts().add(host.lower());
595 }
596
597 void ResourceHandle::setClientCertificate(const String& host, CFDataRef cert)
598 {
599     clientCerts().set(host.lower(), cert);
600 }
601
602 void ResourceHandle::platformSetDefersLoading(bool defers)
603 {
604     if (!d->m_connection)
605         return;
606
607     if (defers)
608         CFURLConnectionHalt(d->m_connection.get());
609     else
610         CFURLConnectionResume(d->m_connection.get());
611 }
612
613 #if PLATFORM(COCOA)
614 void ResourceHandle::schedule(SchedulePair& pair)
615 {
616     CFRunLoopRef runLoop = pair.runLoop();
617     if (!runLoop)
618         return;
619
620     CFURLConnectionScheduleWithRunLoop(d->m_connection.get(), runLoop, pair.mode());
621     if (d->m_startWhenScheduled) {
622         CFURLConnectionStart(d->m_connection.get());
623         d->m_startWhenScheduled = false;
624     }
625 }
626
627 void ResourceHandle::unschedule(SchedulePair& pair)
628 {
629     CFRunLoopRef runLoop = pair.runLoop();
630     if (!runLoop)
631         return;
632
633     CFURLConnectionUnscheduleFromRunLoop(d->m_connection.get(), runLoop, pair.mode());
634 }
635 #endif
636
637 const ResourceRequest& ResourceHandle::currentRequest() const
638 {
639     return d->m_currentRequest;
640 }
641
642 void ResourceHandle::continueWillSendRequest(const ResourceRequest& request)
643 {
644     ResourceRequest requestResult = request;
645     if (!requestResult.isNull())
646         requestResult.setStorageSession(d->m_storageSession.get());
647     d->m_connectionDelegate->continueWillSendRequest(requestResult.cfURLRequest(UpdateHTTPBody));
648 }
649
650 void ResourceHandle::continueDidReceiveResponse()
651 {
652     d->m_connectionDelegate->continueDidReceiveResponse();
653 }
654
655 #if USE(PROTECTION_SPACE_AUTH_CALLBACK)
656 void ResourceHandle::continueCanAuthenticateAgainstProtectionSpace(bool canAuthenticate)
657 {
658     d->m_connectionDelegate->continueCanAuthenticateAgainstProtectionSpace(canAuthenticate);
659 }
660 #endif
661
662 void ResourceHandle::continueWillCacheResponse(CFCachedURLResponseRef response)
663 {
664     d->m_connectionDelegate->continueWillCacheResponse(response);
665 }
666 #endif // USE(CFNETWORK)
667
668 } // namespace WebCore