2 * Copyright (C) 2004, 2005, 2006, 2007, 2009, 2010, 2011, 2013 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
28 #include "ResourceHandleInternal.h"
30 #include "AuthenticationCF.h"
31 #include "AuthenticationChallenge.h"
32 #include "CredentialStorage.h"
33 #include "CachedResourceLoader.h"
34 #include "FormDataStreamCFNet.h"
36 #include "FrameLoader.h"
37 #include "HTTPHeaderNames.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>
48 #include <sys/types.h>
49 #include <wtf/HashMap.h>
51 #include <wtf/Threading.h>
52 #include <wtf/text/Base64.h>
53 #include <wtf/text/CString.h>
56 #include "ResourceHandleCFURLConnectionDelegateWithOperationQueue.h"
57 #include "WebCoreSystemInterface.h"
59 #include "WebCoreURLResponse.h"
60 #include <CFNetwork/CFURLConnectionPriv.h>
61 #include <CFNetwork/CFURLRequestPriv.h>
66 #include <WebKitSystemInterface/WebKitSystemInterface.h>
69 // FIXME: Remove this declaration once it's in WebKitSupportLibrary.
71 __declspec(dllimport) CFURLConnectionRef CFURLConnectionCreateWithProperties(
73 CFURLRequestRef request,
74 CFURLConnectionClient * client,
75 CFDictionaryRef properties);
83 static HashSet<String>& allowsAnyHTTPSCertificateHosts()
85 DEPRECATED_DEFINE_STATIC_LOCAL(HashSet<String>, hosts, ());
89 static HashMap<String, RetainPtr<CFDataRef>>& clientCerts()
91 typedef HashMap<String, RetainPtr<CFDataRef>> CertsMap;
92 DEPRECATED_DEFINE_STATIC_LOCAL(CertsMap, certs, ());
96 static void applyBasicAuthorizationHeader(ResourceRequest& request, const Credential& credential)
98 String authenticationHeader = "Basic " + base64Encode(String(credential.user() + ":" + credential.password()).utf8());
100 request.setHTTPHeaderField(HTTPHeaderName::Authorization, authenticationHeader);
103 ResourceHandleInternal::~ResourceHandleInternal()
105 if (m_connectionDelegate)
106 m_connectionDelegate->releaseHandle();
109 LOG(Network, "CFNet - Cancelling connection %p (%s)", m_connection.get(), m_firstRequest.url().string().utf8().data());
110 CFURLConnectionCancel(m_connection.get());
114 ResourceHandle::~ResourceHandle()
116 LOG(Network, "CFNet - Destroying job %p (%s)", this, d->m_firstRequest.url().string().utf8().data());
119 void ResourceHandle::createCFURLConnection(bool shouldUseCredentialStorage, bool shouldContentSniff, SchedulingBehavior schedulingBehavior, CFDictionaryRef clientProperties)
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);
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());
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());
144 if (!d->m_initialCredential.isEmpty()) {
145 // FIXME: Support Digest authentication, and Proxy-Authorization.
146 applyBasicAuthorizationHeader(firstRequest(), d->m_initialCredential);
149 RetainPtr<CFMutableURLRequestRef> request = adoptCF(CFURLRequestCreateMutableCopy(kCFAllocatorDefault, firstRequest().cfURLRequest(UpdateHTTPBody)));
150 wkSetRequestStorageSession(d->m_storageSession.get(), request.get());
152 if (!shouldContentSniff)
153 wkSetCFURLRequestShouldContentSniff(request.get(), false);
155 RetainPtr<CFMutableDictionaryRef> sslProps;
158 sslProps = adoptCF(ResourceHandle::createSSLPropertiesFromNSURLRequest(firstRequest()));
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);
168 HashMap<String, RetainPtr<CFDataRef>>::iterator clientCert = clientCerts().find(firstRequest().url().host().lower());
169 if (clientCert != clientCerts().end()) {
171 sslProps = adoptCF(CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
173 wkSetClientCertificateInSSLProperties(sslProps.get(), (clientCert->value).get());
176 #endif // PLATFORM(IOS)
179 CFURLRequestSetSSLProperties(request.get(), sslProps.get());
181 CFMutableDictionaryRef streamProperties = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
183 if (!shouldUseCredentialStorage) {
184 // Avoid using existing connections, because they may be already authenticated.
185 CFDictionarySetValue(streamProperties, CFSTR("_kCFURLConnectionSessionID"), CFSTR("WebKitPrivateSession"));
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);
198 RetainPtr<CFDataRef> sourceApplicationAuditData = d->m_context->sourceApplicationAuditData();
199 if (sourceApplicationAuditData)
200 CFDictionarySetValue(streamProperties, CFSTR("kCFStreamPropertySourceApplication"), sourceApplicationAuditData.get());
203 static const CFStringRef kCFURLConnectionSocketStreamProperties = CFSTR("kCFURLConnectionSocketStreamProperties");
204 RetainPtr<CFMutableDictionaryRef> propertiesDictionary;
205 if (clientProperties)
206 propertiesDictionary = adoptCF(CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, clientProperties));
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());
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);
222 if (d->m_usesAsyncCallbacks)
223 d->m_connectionDelegate = adoptRef(new ResourceHandleCFURLConnectionDelegateWithOperationQueue(this));
225 d->m_connectionDelegate = adoptRef(new SynchronousResourceHandleCFURLConnectionDelegate(this));
227 d->m_connectionDelegate = adoptRef(new SynchronousResourceHandleCFURLConnectionDelegate(this));
229 d->m_connectionDelegate->setupRequest(request.get());
231 CFURLConnectionClient_V6 client = d->m_connectionDelegate->makeConnectionClient();
232 if (shouldUseCredentialStorage)
233 client.shouldUseCredentialStorage = 0;
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
241 bool ResourceHandle::start()
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())
251 d->m_storageSession = d->m_context->storageSession().platformSession();
253 bool shouldUseCredentialStorage = !client() || client()->shouldUseCredentialStorage(this);
255 #if ENABLE(WEB_TIMING) && PLATFORM(COCOA)
256 setCollectsTimingData();
259 SchedulingBehavior schedulingBehavior = client()->loadingSynchronousXHR() ? SchedulingBehavior::Synchronous : SchedulingBehavior::Asynchronous;
261 createCFURLConnection(shouldUseCredentialStorage, d->m_shouldContentSniff, schedulingBehavior, client()->connectionProperties(this).get());
263 d->m_connectionDelegate->setupConnectionScheduling(d->m_connection.get());
264 CFURLConnectionStart(d->m_connection.get());
266 LOG(Network, "CFNet - Starting URL %s (handle=%p, conn=%p)", firstRequest().url().string().utf8().data(), this, d->m_connection.get());
271 void ResourceHandle::cancel()
273 if (d->m_connection) {
274 CFURLConnectionCancel(d->m_connection.get());
279 void ResourceHandle::willSendRequest(ResourceRequest& request, const ResourceResponse& redirectResponse)
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();
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();
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;
300 // FIXME: Support Digest authentication, and Proxy-Authorization.
301 applyBasicAuthorizationHeader(request, d->m_initialCredential);
306 Ref<ResourceHandle> protect(*this);
307 if (d->m_usesAsyncCallbacks)
308 client()->willSendRequestAsync(this, request, redirectResponse);
310 client()->willSendRequest(this, request, redirectResponse);
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());
316 d->m_currentRequest = request;
321 bool ResourceHandle::shouldUseCredentialStorage()
323 LOG(Network, "CFNet - shouldUseCredentialStorage()");
324 if (ResourceHandleClient* client = this->client()) {
325 ASSERT(!d->m_usesAsyncCallbacks);
326 return client->shouldUseCredentialStorage(this);
331 void ResourceHandle::didReceiveAuthenticationChallenge(const AuthenticationChallenge& challenge)
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.
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());
350 if (tryHandlePasswordBasedAuthentication(challenge))
353 d->m_currentWebChallenge = challenge;
356 client()->didReceiveAuthenticationChallenge(this, d->m_currentWebChallenge);
358 clearAuthentication();
359 CFURLConnectionPerformDefaultHandlingForChallenge(d->m_connection.get(), challenge.cfURLAuthChallengeRef());
363 bool ResourceHandle::tryHandlePasswordBasedAuthentication(const AuthenticationChallenge& challenge)
365 if (!challenge.protectionSpace().isPasswordBased())
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));
371 Credential credential = Credential(cfCredential.get());
373 Credential credential = core(cfCredential.get());
377 if (challenge.failureResponse().httpStatusCode() == 401)
378 urlToStore = challenge.failureResponse().url();
379 d->m_context->storageSession().credentialStorage().set(credential, challenge.protectionSpace(), urlToStore);
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.
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());
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());
405 CFURLConnectionUseCredential(d->m_connection.get(), credential.cfCredential(), challenge.cfURLAuthChallengeRef());
407 RetainPtr<CFURLCredentialRef> cfCredential = adoptCF(createCF(credential));
408 CFURLConnectionUseCredential(d->m_connection.get(), cfCredential.get(), challenge.cfURLAuthChallengeRef());
418 #if USE(PROTECTION_SPACE_AUTH_CALLBACK)
419 bool ResourceHandle::canAuthenticateAgainstProtectionSpace(const ProtectionSpace& protectionSpace)
421 ResourceHandleClient* client = this->client();
422 if (d->m_usesAsyncCallbacks) {
424 client->canAuthenticateAgainstProtectionSpaceAsync(this, protectionSpace);
426 continueCanAuthenticateAgainstProtectionSpace(false);
427 return false; // Ignored by caller.
430 return client && client->canAuthenticateAgainstProtectionSpace(this, protectionSpace);
434 void ResourceHandle::receivedCredential(const AuthenticationChallenge& challenge, const Credential& credential)
436 LOG(Network, "CFNet - receivedCredential()");
437 ASSERT(!challenge.isNull());
438 ASSERT(challenge.cfURLAuthChallengeRef());
439 if (challenge != d->m_currentWebChallenge)
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);
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);
454 if (challenge.failureResponse().httpStatusCode() == 401)
455 urlToStore = challenge.failureResponse().url();
456 d->m_context->storageSession().credentialStorage().set(webCredential, challenge.protectionSpace(), urlToStore);
458 if (d->m_connection) {
460 CFURLConnectionUseCredential(d->m_connection.get(), webCredential.cfCredential(), challenge.cfURLAuthChallengeRef());
462 RetainPtr<CFURLCredentialRef> cfCredential = adoptCF(createCF(webCredential));
463 CFURLConnectionUseCredential(d->m_connection.get(), cfCredential.get(), challenge.cfURLAuthChallengeRef());
466 } else if (d->m_connection) {
468 CFURLConnectionUseCredential(d->m_connection.get(), credential.cfCredential(), challenge.cfURLAuthChallengeRef());
470 RetainPtr<CFURLCredentialRef> cfCredential = adoptCF(createCF(credential));
471 CFURLConnectionUseCredential(d->m_connection.get(), cfCredential.get(), challenge.cfURLAuthChallengeRef());
475 clearAuthentication();
478 void ResourceHandle::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge& challenge)
480 LOG(Network, "CFNet - receivedRequestToContinueWithoutCredential()");
481 ASSERT(!challenge.isNull());
482 ASSERT(challenge.cfURLAuthChallengeRef());
483 if (challenge != d->m_currentWebChallenge)
487 CFURLConnectionUseCredential(d->m_connection.get(), 0, challenge.cfURLAuthChallengeRef());
489 clearAuthentication();
492 void ResourceHandle::receivedCancellation(const AuthenticationChallenge& challenge)
494 LOG(Network, "CFNet - receivedCancellation()");
495 if (challenge != d->m_currentWebChallenge)
499 client()->receivedCancellation(this, challenge);
502 void ResourceHandle::receivedRequestToPerformDefaultHandling(const AuthenticationChallenge& challenge)
504 LOG(Network, "CFNet - receivedRequestToPerformDefaultHandling()");
505 ASSERT(!challenge.isNull());
506 ASSERT(challenge.cfURLAuthChallengeRef());
507 if (challenge != d->m_currentWebChallenge)
511 CFURLConnectionPerformDefaultHandlingForChallenge(d->m_connection.get(), challenge.cfURLAuthChallengeRef());
513 clearAuthentication();
516 void ResourceHandle::receivedChallengeRejection(const AuthenticationChallenge& challenge)
518 LOG(Network, "CFNet - receivedChallengeRejection()");
519 ASSERT(!challenge.isNull());
520 ASSERT(challenge.cfURLAuthChallengeRef());
521 if (challenge != d->m_currentWebChallenge)
525 CFURLConnectionRejectChallenge(d->m_connection.get(), challenge.cfURLAuthChallengeRef());
527 clearAuthentication();
530 CFURLStorageSessionRef ResourceHandle::storageSession() const
532 return d->m_storageSession.get();
535 CFURLConnectionRef ResourceHandle::connection() const
537 return d->m_connection.get();
540 RetainPtr<CFURLConnectionRef> ResourceHandle::releaseConnectionForDownload()
542 LOG(Network, "CFNet - Job %p releasing connection %p for download", this, d->m_connection.get());
543 return WTF::move(d->m_connection);
546 CFStringRef ResourceHandle::synchronousLoadRunLoopMode()
548 return CFSTR("WebCoreSynchronousLoaderRunLoopMode");
551 void ResourceHandle::platformLoadResourceSynchronously(NetworkingContext* context, const ResourceRequest& request, StoredCredentials storedCredentials, ResourceError& error, ResourceResponse& response, Vector<char>& data)
553 LOG(Network, "ResourceHandle::platformLoadResourceSynchronously:%s allowStoredCredentials:%u", request.url().string().utf8().data(), storedCredentials);
555 ASSERT(!request.isEmpty());
557 ASSERT(response.isNull());
558 ASSERT(error.isNull());
560 SynchronousLoaderClient client;
561 client.setAllowStoredCredentials(storedCredentials == AllowStoredCredentials);
563 RefPtr<ResourceHandle> handle = adoptRef(new ResourceHandle(context, request, &client, false /*defersLoading*/, true /*shouldContentSniff*/));
565 handle->d->m_storageSession = context->storageSession().platformSession();
567 if (handle->d->m_scheduledFailureType != NoFailure) {
568 error = context->blockedError(request);
572 handle->createCFURLConnection(storedCredentials == AllowStoredCredentials, ResourceHandle::shouldContentSniffURL(request.url()),
573 SchedulingBehavior::Synchronous, handle->client()->connectionProperties(handle.get()).get());
575 CFURLConnectionScheduleWithRunLoop(handle->connection(), CFRunLoopGetCurrent(), synchronousLoadRunLoopMode());
576 CFURLConnectionScheduleDownloadWithRunLoop(handle->connection(), CFRunLoopGetCurrent(), synchronousLoadRunLoopMode());
577 CFURLConnectionStart(handle->connection());
579 while (!client.isDone())
580 CFRunLoopRunInMode(synchronousLoadRunLoopMode(), UINT_MAX, true);
582 error = client.error();
584 CFURLConnectionCancel(handle->connection());
587 response = client.response();
589 data.swap(client.mutableData());
592 void ResourceHandle::setHostAllowsAnyHTTPSCertificate(const String& host)
594 allowsAnyHTTPSCertificateHosts().add(host.lower());
597 void ResourceHandle::setClientCertificate(const String& host, CFDataRef cert)
599 clientCerts().set(host.lower(), cert);
602 void ResourceHandle::platformSetDefersLoading(bool defers)
604 if (!d->m_connection)
608 CFURLConnectionHalt(d->m_connection.get());
610 CFURLConnectionResume(d->m_connection.get());
614 void ResourceHandle::schedule(SchedulePair& pair)
616 CFRunLoopRef runLoop = pair.runLoop();
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;
627 void ResourceHandle::unschedule(SchedulePair& pair)
629 CFRunLoopRef runLoop = pair.runLoop();
633 CFURLConnectionUnscheduleFromRunLoop(d->m_connection.get(), runLoop, pair.mode());
637 const ResourceRequest& ResourceHandle::currentRequest() const
639 return d->m_currentRequest;
642 void ResourceHandle::continueWillSendRequest(const ResourceRequest& request)
644 ResourceRequest requestResult = request;
645 if (!requestResult.isNull())
646 requestResult.setStorageSession(d->m_storageSession.get());
647 d->m_connectionDelegate->continueWillSendRequest(requestResult.cfURLRequest(UpdateHTTPBody));
650 void ResourceHandle::continueDidReceiveResponse()
652 d->m_connectionDelegate->continueDidReceiveResponse();
655 #if USE(PROTECTION_SPACE_AUTH_CALLBACK)
656 void ResourceHandle::continueCanAuthenticateAgainstProtectionSpace(bool canAuthenticate)
658 d->m_connectionDelegate->continueCanAuthenticateAgainstProtectionSpace(canAuthenticate);
662 void ResourceHandle::continueWillCacheResponse(CFCachedURLResponseRef response)
664 d->m_connectionDelegate->continueWillCacheResponse(response);
666 #endif // USE(CFNETWORK)
668 } // namespace WebCore