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.
70extern "C" {
71__declspec(dllimport) CFURLConnectionRef CFURLConnectionCreateWithProperties(
72  CFAllocatorRef           alloc,
73  CFURLRequestRef          request,
74  CFURLConnectionClient *  client,
75  CFDictionaryRef properties);
76}
77#endif
78
79namespace WebCore {
80
81#if USE(CFNETWORK)
82
83static HashSet<String>& allowsAnyHTTPSCertificateHosts()
84{
85    DEPRECATED_DEFINE_STATIC_LOCAL(HashSet<String>, hosts, ());
86    return hosts;
87}
88
89static 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
96static 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
103ResourceHandleInternal::~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
114ResourceHandle::~ResourceHandle()
115{
116    LOG(Network, "CFNet - Destroying job %p (%s)", this, d->m_firstRequest.url().string().utf8().data());
117}
118
119void 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#if PLATFORM(WIN)
182    if (CFHTTPCookieStorageRef cookieStorage = overridenCookieStorage()) {
183        // Overridden cookie storage doesn't come from a session, so the request does not have it yet.
184        CFURLRequestSetHTTPCookieStorage(request.get(), cookieStorage);
185    }
186#endif
187
188    CFMutableDictionaryRef streamProperties  = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
189
190    if (!shouldUseCredentialStorage) {
191        // Avoid using existing connections, because they may be already authenticated.
192        CFDictionarySetValue(streamProperties, CFSTR("_kCFURLConnectionSessionID"), CFSTR("WebKitPrivateSession"));
193    }
194
195    if (schedulingBehavior == SchedulingBehavior::Synchronous) {
196        // Synchronous requests should not be subject to regular connection count limit to avoid deadlocks.
197        // If we are using all available connections for async requests, and make a sync request, then prior
198        // requests may get stuck waiting for delegate calls while we are in nested run loop, and the sync
199        // request won't start because there are no available connections.
200        // Connections are grouped by their socket stream properties, with each group having a separate count.
201        CFDictionarySetValue(streamProperties, CFSTR("_WebKitSynchronousRequest"), kCFBooleanTrue);
202    }
203
204#if PLATFORM(IOS) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090)
205    RetainPtr<CFDataRef> sourceApplicationAuditData = d->m_context->sourceApplicationAuditData();
206    if (sourceApplicationAuditData)
207        CFDictionarySetValue(streamProperties, CFSTR("kCFStreamPropertySourceApplication"), sourceApplicationAuditData.get());
208#endif
209
210    static const CFStringRef kCFURLConnectionSocketStreamProperties = CFSTR("kCFURLConnectionSocketStreamProperties");
211    RetainPtr<CFMutableDictionaryRef> propertiesDictionary;
212    if (clientProperties)
213        propertiesDictionary = adoptCF(CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, clientProperties));
214    else
215        propertiesDictionary = adoptCF(CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
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    d->m_connection = adoptCF(CFURLConnectionCreateWithProperties(0, request.get(), reinterpret_cast<CFURLConnectionClient*>(&client), propertiesDictionary.get()));
236}
237
238bool ResourceHandle::start()
239{
240    if (!d->m_context)
241        return false;
242
243    // If NetworkingContext is invalid then we are no longer attached to a Page,
244    // this must be an attempted load from an unload handler, so let's just block it.
245    if (!d->m_context->isValid())
246        return false;
247
248    d->m_storageSession = d->m_context->storageSession().platformSession();
249
250    bool shouldUseCredentialStorage = !client() || client()->shouldUseCredentialStorage(this);
251
252    createCFURLConnection(shouldUseCredentialStorage, d->m_shouldContentSniff, SchedulingBehavior::Asynchronous, client()->connectionProperties(this).get());
253
254    d->m_connectionDelegate->setupConnectionScheduling(d->m_connection.get());
255    CFURLConnectionStart(d->m_connection.get());
256
257    LOG(Network, "CFNet - Starting URL %s (handle=%p, conn=%p)", firstRequest().url().string().utf8().data(), this, d->m_connection.get());
258
259#if ENABLE(WEB_TIMING)
260    setCollectsTimingData();
261#endif
262
263    return true;
264}
265
266void ResourceHandle::cancel()
267{
268    if (d->m_connection) {
269        CFURLConnectionCancel(d->m_connection.get());
270        d->m_connection = 0;
271    }
272}
273
274void ResourceHandle::willSendRequest(ResourceRequest& request, const ResourceResponse& redirectResponse)
275{
276    const URL& url = request.url();
277    d->m_user = url.user();
278    d->m_pass = url.pass();
279    d->m_lastHTTPMethod = request.httpMethod();
280    request.removeCredentials();
281
282    if (!protocolHostAndPortAreEqual(request.url(), redirectResponse.url())) {
283        // If the network layer carries over authentication headers from the original request
284        // in a cross-origin redirect, we want to clear those headers here.
285        request.clearHTTPAuthorization();
286    } else {
287        // Only consider applying authentication credentials if this is actually a redirect and the redirect
288        // URL didn't include credentials of its own.
289        if (d->m_user.isEmpty() && d->m_pass.isEmpty() && !redirectResponse.isNull()) {
290            Credential credential = CredentialStorage::get(request.url());
291            if (!credential.isEmpty()) {
292                d->m_initialCredential = credential;
293
294                // FIXME: Support Digest authentication, and Proxy-Authorization.
295                applyBasicAuthorizationHeader(request, d->m_initialCredential);
296            }
297        }
298    }
299
300    Ref<ResourceHandle> protect(*this);
301    if (client()->usesAsyncCallbacks())
302        client()->willSendRequestAsync(this, request, redirectResponse);
303    else {
304        client()->willSendRequest(this, request, redirectResponse);
305
306        // Client call may not preserve the session, especially if the request is sent over IPC.
307        if (!request.isNull()) {
308            request.setStorageSession(d->m_storageSession.get());
309
310            d->m_currentRequest = request;
311        }
312    }
313}
314
315bool ResourceHandle::shouldUseCredentialStorage()
316{
317    LOG(Network, "CFNet - shouldUseCredentialStorage()");
318    if (ResourceHandleClient* client = this->client()) {
319        ASSERT(!client->usesAsyncCallbacks());
320        return client->shouldUseCredentialStorage(this);
321    }
322    return false;
323}
324
325void ResourceHandle::didReceiveAuthenticationChallenge(const AuthenticationChallenge& challenge)
326{
327    LOG(Network, "CFNet - didReceiveAuthenticationChallenge()");
328    ASSERT(d->m_currentWebChallenge.isNull());
329    // Since CFURLConnection networking relies on keeping a reference to the original CFURLAuthChallengeRef,
330    // we make sure that is actually present
331    ASSERT(challenge.cfURLAuthChallengeRef());
332    ASSERT(challenge.authenticationClient() == this); // Should be already set.
333
334#if !PLATFORM(WIN)
335    // Proxy authentication is handled by CFNetwork internally. We can get here if the user cancels
336    // CFNetwork authentication dialog, and we shouldn't ask the client to display another one in that case.
337    if (challenge.protectionSpace().isProxy()) {
338        // Cannot use receivedRequestToContinueWithoutCredential(), because current challenge is not yet set.
339        CFURLConnectionUseCredential(d->m_connection.get(), 0, challenge.cfURLAuthChallengeRef());
340        return;
341    }
342#endif
343
344    if (!d->m_user.isNull() && !d->m_pass.isNull()) {
345        RetainPtr<CFURLCredentialRef> credential = adoptCF(CFURLCredentialCreate(kCFAllocatorDefault, d->m_user.createCFString().get(), d->m_pass.createCFString().get(), 0, kCFURLCredentialPersistenceNone));
346
347        URL urlToStore;
348        if (challenge.failureResponse().httpStatusCode() == 401)
349            urlToStore = challenge.failureResponse().url();
350        CredentialStorage::set(core(credential.get()), challenge.protectionSpace(), urlToStore);
351
352        CFURLConnectionUseCredential(d->m_connection.get(), credential.get(), challenge.cfURLAuthChallengeRef());
353        d->m_user = String();
354        d->m_pass = String();
355        // FIXME: Per the specification, the user shouldn't be asked for credentials if there were incorrect ones provided explicitly.
356        return;
357    }
358
359    if (!client() || client()->shouldUseCredentialStorage(this)) {
360        if (!d->m_initialCredential.isEmpty() || challenge.previousFailureCount()) {
361            // The stored credential wasn't accepted, stop using it.
362            // There is a race condition here, since a different credential might have already been stored by another ResourceHandle,
363            // but the observable effect should be very minor, if any.
364            CredentialStorage::remove(challenge.protectionSpace());
365        }
366
367        if (!challenge.previousFailureCount()) {
368            Credential credential = CredentialStorage::get(challenge.protectionSpace());
369            if (!credential.isEmpty() && credential != d->m_initialCredential) {
370                ASSERT(credential.persistence() == CredentialPersistenceNone);
371                if (challenge.failureResponse().httpStatusCode() == 401) {
372                    // Store the credential back, possibly adding it as a default for this directory.
373                    CredentialStorage::set(credential, challenge.protectionSpace(), challenge.failureResponse().url());
374                }
375                RetainPtr<CFURLCredentialRef> cfCredential = adoptCF(createCF(credential));
376                CFURLConnectionUseCredential(d->m_connection.get(), cfCredential.get(), challenge.cfURLAuthChallengeRef());
377                return;
378            }
379        }
380    }
381
382    d->m_currentWebChallenge = challenge;
383
384    if (client())
385        client()->didReceiveAuthenticationChallenge(this, d->m_currentWebChallenge);
386}
387
388#if USE(PROTECTION_SPACE_AUTH_CALLBACK)
389bool ResourceHandle::canAuthenticateAgainstProtectionSpace(const ProtectionSpace& protectionSpace)
390{
391    if (ResourceHandleClient* client = this->client()) {
392        if (client->usesAsyncCallbacks())
393            client->canAuthenticateAgainstProtectionSpaceAsync(this, protectionSpace);
394        else
395            return client->canAuthenticateAgainstProtectionSpace(this, protectionSpace);
396    }
397    return false;
398}
399#endif
400
401void ResourceHandle::receivedCredential(const AuthenticationChallenge& challenge, const Credential& credential)
402{
403    LOG(Network, "CFNet - receivedCredential()");
404    ASSERT(!challenge.isNull());
405    ASSERT(challenge.cfURLAuthChallengeRef());
406    if (challenge != d->m_currentWebChallenge)
407        return;
408
409    // FIXME: Support empty credentials. Currently, an empty credential cannot be stored in WebCore credential storage, as that's empty value for its map.
410    if (credential.isEmpty()) {
411        receivedRequestToContinueWithoutCredential(challenge);
412        return;
413    }
414
415    if (credential.persistence() == CredentialPersistenceForSession) {
416        // Manage per-session credentials internally, because once NSURLCredentialPersistencePerSession is used, there is no way
417        // to ignore it for a particular request (short of removing it altogether).
418        Credential webCredential(credential.user(), credential.password(), CredentialPersistenceNone);
419        RetainPtr<CFURLCredentialRef> cfCredential = adoptCF(createCF(webCredential));
420
421        URL urlToStore;
422        if (challenge.failureResponse().httpStatusCode() == 401)
423            urlToStore = challenge.failureResponse().url();
424        CredentialStorage::set(webCredential, challenge.protectionSpace(), urlToStore);
425
426        CFURLConnectionUseCredential(d->m_connection.get(), cfCredential.get(), challenge.cfURLAuthChallengeRef());
427    } else {
428        RetainPtr<CFURLCredentialRef> cfCredential = adoptCF(createCF(credential));
429        CFURLConnectionUseCredential(d->m_connection.get(), cfCredential.get(), challenge.cfURLAuthChallengeRef());
430    }
431
432    clearAuthentication();
433}
434
435void ResourceHandle::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge& challenge)
436{
437    LOG(Network, "CFNet - receivedRequestToContinueWithoutCredential()");
438    ASSERT(!challenge.isNull());
439    ASSERT(challenge.cfURLAuthChallengeRef());
440    if (challenge != d->m_currentWebChallenge)
441        return;
442
443    CFURLConnectionUseCredential(d->m_connection.get(), 0, challenge.cfURLAuthChallengeRef());
444
445    clearAuthentication();
446}
447
448void ResourceHandle::receivedCancellation(const AuthenticationChallenge& challenge)
449{
450    LOG(Network, "CFNet - receivedCancellation()");
451    if (challenge != d->m_currentWebChallenge)
452        return;
453
454    if (client())
455        client()->receivedCancellation(this, challenge);
456}
457
458void ResourceHandle::receivedRequestToPerformDefaultHandling(const AuthenticationChallenge& challenge)
459{
460    LOG(Network, "CFNet - receivedRequestToPerformDefaultHandling()");
461    ASSERT(!challenge.isNull());
462    ASSERT(challenge.cfURLAuthChallengeRef());
463    if (challenge != d->m_currentWebChallenge)
464        return;
465
466    CFURLConnectionPerformDefaultHandlingForChallenge(d->m_connection.get(), challenge.cfURLAuthChallengeRef());
467
468    clearAuthentication();
469}
470
471void ResourceHandle::receivedChallengeRejection(const AuthenticationChallenge& challenge)
472{
473    LOG(Network, "CFNet - receivedChallengeRejection()");
474    ASSERT(!challenge.isNull());
475    ASSERT(challenge.cfURLAuthChallengeRef());
476    if (challenge != d->m_currentWebChallenge)
477        return;
478
479    CFURLConnectionRejectChallenge(d->m_connection.get(), challenge.cfURLAuthChallengeRef());
480
481    clearAuthentication();
482}
483
484CFURLStorageSessionRef ResourceHandle::storageSession() const
485{
486    return d->m_storageSession.get();
487}
488
489CFURLConnectionRef ResourceHandle::connection() const
490{
491    return d->m_connection.get();
492}
493
494RetainPtr<CFURLConnectionRef> ResourceHandle::releaseConnectionForDownload()
495{
496    LOG(Network, "CFNet - Job %p releasing connection %p for download", this, d->m_connection.get());
497    return WTF::move(d->m_connection);
498}
499
500CFStringRef ResourceHandle::synchronousLoadRunLoopMode()
501{
502    return CFSTR("WebCoreSynchronousLoaderRunLoopMode");
503}
504
505void ResourceHandle::platformLoadResourceSynchronously(NetworkingContext* context, const ResourceRequest& request, StoredCredentials storedCredentials, ResourceError& error, ResourceResponse& response, Vector<char>& data)
506{
507    LOG(Network, "ResourceHandle::platformLoadResourceSynchronously:%s allowStoredCredentials:%u", request.url().string().utf8().data(), storedCredentials);
508
509    ASSERT(!request.isEmpty());
510
511    ASSERT(response.isNull());
512    ASSERT(error.isNull());
513
514    OwnPtr<SynchronousLoaderClient> client = SynchronousLoaderClient::create();
515    client->setAllowStoredCredentials(storedCredentials == AllowStoredCredentials);
516
517    RefPtr<ResourceHandle> handle = adoptRef(new ResourceHandle(context, request, client.get(), false /*defersLoading*/, true /*shouldContentSniff*/));
518
519    handle->d->m_storageSession = context->storageSession().platformSession();
520
521    if (handle->d->m_scheduledFailureType != NoFailure) {
522        error = context->blockedError(request);
523        return;
524    }
525
526    handle->createCFURLConnection(storedCredentials == AllowStoredCredentials, ResourceHandle::shouldContentSniffURL(request.url()),
527        SchedulingBehavior::Synchronous, handle->client()->connectionProperties(handle.get()).get());
528
529    CFURLConnectionScheduleWithRunLoop(handle->connection(), CFRunLoopGetCurrent(), synchronousLoadRunLoopMode());
530    CFURLConnectionScheduleDownloadWithRunLoop(handle->connection(), CFRunLoopGetCurrent(), synchronousLoadRunLoopMode());
531    CFURLConnectionStart(handle->connection());
532
533    while (!client->isDone())
534        CFRunLoopRunInMode(synchronousLoadRunLoopMode(), UINT_MAX, true);
535
536    error = client->error();
537
538    CFURLConnectionCancel(handle->connection());
539
540    if (error.isNull())
541        response = client->response();
542
543    data.swap(client->mutableData());
544}
545
546void ResourceHandle::setHostAllowsAnyHTTPSCertificate(const String& host)
547{
548    allowsAnyHTTPSCertificateHosts().add(host.lower());
549}
550
551void ResourceHandle::setClientCertificate(const String& host, CFDataRef cert)
552{
553    clientCerts().set(host.lower(), cert);
554}
555
556void ResourceHandle::platformSetDefersLoading(bool defers)
557{
558    if (!d->m_connection)
559        return;
560
561    if (defers)
562        CFURLConnectionHalt(d->m_connection.get());
563    else
564        CFURLConnectionResume(d->m_connection.get());
565}
566
567bool ResourceHandle::loadsBlocked()
568{
569    return false;
570}
571
572#if PLATFORM(COCOA)
573void ResourceHandle::schedule(SchedulePair& pair)
574{
575    CFRunLoopRef runLoop = pair.runLoop();
576    if (!runLoop)
577        return;
578
579    CFURLConnectionScheduleWithRunLoop(d->m_connection.get(), runLoop, pair.mode());
580    if (d->m_startWhenScheduled) {
581        CFURLConnectionStart(d->m_connection.get());
582        d->m_startWhenScheduled = false;
583    }
584}
585
586void ResourceHandle::unschedule(SchedulePair& pair)
587{
588    CFRunLoopRef runLoop = pair.runLoop();
589    if (!runLoop)
590        return;
591
592    CFURLConnectionUnscheduleFromRunLoop(d->m_connection.get(), runLoop, pair.mode());
593}
594#endif
595
596const ResourceRequest& ResourceHandle::currentRequest() const
597{
598    return d->m_currentRequest;
599}
600
601void ResourceHandle::continueWillSendRequest(const ResourceRequest& request)
602{
603    ResourceRequest requestResult = request;
604    if (!requestResult.isNull())
605        requestResult.setStorageSession(d->m_storageSession.get());
606    d->m_connectionDelegate->continueWillSendRequest(requestResult.cfURLRequest(UpdateHTTPBody));
607}
608
609void ResourceHandle::continueDidReceiveResponse()
610{
611    d->m_connectionDelegate->continueDidReceiveResponse();
612}
613
614#if USE(PROTECTION_SPACE_AUTH_CALLBACK)
615void ResourceHandle::continueCanAuthenticateAgainstProtectionSpace(bool canAuthenticate)
616{
617    d->m_connectionDelegate->continueCanAuthenticateAgainstProtectionSpace(canAuthenticate);
618}
619#endif
620
621void ResourceHandle::continueWillCacheResponse(CFCachedURLResponseRef response)
622{
623    d->m_connectionDelegate->continueWillCacheResponse(response);
624}
625#endif // USE(CFNETWORK)
626
627} // namespace WebCore
628