Add WTF::move()
[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 #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
238 bool 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
266 void ResourceHandle::cancel()
267 {
268     if (d->m_connection) {
269         CFURLConnectionCancel(d->m_connection.get());
270         d->m_connection = 0;
271     }
272 }
273
274 void 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
315 bool 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
325 void 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)
389 bool 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
401 void 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
435 void 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
448 void 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
458 void 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
471 void 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
484 CFURLStorageSessionRef ResourceHandle::storageSession() const
485 {
486     return d->m_storageSession.get();
487 }
488
489 CFURLConnectionRef ResourceHandle::connection() const
490 {
491     return d->m_connection.get();
492 }
493
494 RetainPtr<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
500 CFStringRef ResourceHandle::synchronousLoadRunLoopMode()
501 {
502     return CFSTR("WebCoreSynchronousLoaderRunLoopMode");
503 }
504
505 void 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
546 void ResourceHandle::setHostAllowsAnyHTTPSCertificate(const String& host)
547 {
548     allowsAnyHTTPSCertificateHosts().add(host.lower());
549 }
550
551 void ResourceHandle::setClientCertificate(const String& host, CFDataRef cert)
552 {
553     clientCerts().set(host.lower(), cert);
554 }
555
556 void 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
567 bool ResourceHandle::loadsBlocked()
568 {
569     return false;
570 }
571
572 #if PLATFORM(COCOA)
573 void 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
586 void 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
596 const ResourceRequest& ResourceHandle::currentRequest() const
597 {
598     return d->m_currentRequest;
599 }
600
601 void 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
609 void ResourceHandle::continueDidReceiveResponse()
610 {
611     d->m_connectionDelegate->continueDidReceiveResponse();
612 }
613
614 #if USE(PROTECTION_SPACE_AUTH_CALLBACK)
615 void ResourceHandle::continueCanAuthenticateAgainstProtectionSpace(bool canAuthenticate)
616 {
617     d->m_connectionDelegate->continueCanAuthenticateAgainstProtectionSpace(canAuthenticate);
618 }
619 #endif
620
621 void ResourceHandle::continueWillCacheResponse(CFCachedURLResponseRef response)
622 {
623     d->m_connectionDelegate->continueWillCacheResponse(response);
624 }
625 #endif // USE(CFNETWORK)
626
627 #if USE(NETWORK_CFDATA_ARRAY_CALLBACK)
628 void ResourceHandle::handleDataArray(CFArrayRef dataArray)
629 {
630     ASSERT(client());
631     if (client()->supportsDataArray()) {
632         client()->didReceiveDataArray(this, dataArray);
633         return;
634     }
635
636     CFIndex count = CFArrayGetCount(dataArray);
637     ASSERT(count);
638     if (count == 1) {
639         CFDataRef data = static_cast<CFDataRef>(CFArrayGetValueAtIndex(dataArray, 0));
640         client()->didReceiveBuffer(this, SharedBuffer::wrapCFData(data), -1);
641         return;
642     }
643
644     RefPtr<SharedBuffer> sharedBuffer = SharedBuffer::create();
645     for (CFIndex index = 0; index < count; index++)
646         sharedBuffer->append(static_cast<CFDataRef>(CFArrayGetValueAtIndex(dataArray, index)));
647
648     client()->didReceiveBuffer(this, sharedBuffer, -1);
649 }
650 #endif
651
652 } // namespace WebCore