2 * Copyright (C) 2004, 2005, 2006, 2007, 2009, 2010, 2011 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 COMPUTER, 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 COMPUTER, 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"
33 #include "CookieStorageCFNet.h"
34 #include "CredentialStorage.h"
35 #include "CachedResourceLoader.h"
36 #include "FormDataStreamCFNet.h"
38 #include "FrameLoader.h"
39 #include "LoaderRunLoopCF.h"
41 #include "MIMETypeRegistry.h"
42 #include "ResourceError.h"
43 #include "ResourceHandleClient.h"
44 #include "ResourceResponse.h"
45 #include "SharedBuffer.h"
46 #include <CFNetwork/CFNetwork.h>
48 #include <sys/types.h>
49 #include <wtf/HashMap.h>
50 #include <wtf/Threading.h>
51 #include <wtf/text/CString.h>
54 #include "WebCoreSystemInterface.h"
56 #include "WebCoreURLResponse.h"
57 #include <CFNetwork/CFURLConnectionPriv.h>
58 #include <CFNetwork/CFURLRequestPriv.h>
63 #include <WebKitSystemInterface/WebKitSystemInterface.h>
66 // FIXME: Remove this declaration once it's in WebKitSupportLibrary.
68 __declspec(dllimport) CFURLConnectionRef CFURLConnectionCreateWithProperties(
70 CFURLRequestRef request,
71 CFURLConnectionClient * client,
72 CFDictionaryRef properties);
80 class WebCoreSynchronousLoaderClient : public ResourceHandleClient {
82 static PassOwnPtr<WebCoreSynchronousLoaderClient> create(ResourceResponse& response, ResourceError& error)
84 return adoptPtr(new WebCoreSynchronousLoaderClient(response, error));
87 void setAllowStoredCredentials(bool allow) { m_allowStoredCredentials = allow; }
88 bool isDone() { return m_isDone; }
90 CFMutableDataRef data() { return m_data.get(); }
93 WebCoreSynchronousLoaderClient(ResourceResponse& response, ResourceError& error)
94 : m_allowStoredCredentials(false)
95 , m_response(response)
101 virtual void willSendRequest(ResourceHandle*, ResourceRequest&, const ResourceResponse& /*redirectResponse*/);
102 virtual bool shouldUseCredentialStorage(ResourceHandle*);
103 virtual void didReceiveAuthenticationChallenge(ResourceHandle*, const AuthenticationChallenge&);
104 virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&);
105 virtual void didReceiveData(ResourceHandle*, const char*, int, int /*encodedDataLength*/);
106 virtual void didFinishLoading(ResourceHandle*, double /*finishTime*/);
107 virtual void didFail(ResourceHandle*, const ResourceError&);
108 #if USE(PROTECTION_SPACE_AUTH_CALLBACK)
109 virtual bool canAuthenticateAgainstProtectionSpace(ResourceHandle*, const ProtectionSpace&);
112 bool m_allowStoredCredentials;
113 ResourceResponse& m_response;
114 RetainPtr<CFMutableDataRef> m_data;
115 ResourceError& m_error;
119 static HashSet<String>& allowsAnyHTTPSCertificateHosts()
121 DEFINE_STATIC_LOCAL(HashSet<String>, hosts, ());
125 static HashMap<String, RetainPtr<CFDataRef> >& clientCerts()
127 typedef HashMap<String, RetainPtr<CFDataRef> > CertsMap;
128 DEFINE_STATIC_LOCAL(CertsMap, certs, ());
132 static void setDefaultMIMEType(CFURLResponseRef response)
134 static CFStringRef defaultMIMETypeString = defaultMIMEType().createCFString();
136 CFURLResponseSetMIMEType(response, defaultMIMETypeString);
139 static void applyBasicAuthorizationHeader(ResourceRequest& request, const Credential& credential)
141 String authenticationHeader = "Basic " + base64Encode(String(credential.user() + ":" + credential.password()).utf8());
142 request.clearHTTPAuthorization(); // FIXME: Should addHTTPHeaderField be smart enough to not build comma-separated lists in headers like Authorization?
143 request.addHTTPHeaderField("Authorization", authenticationHeader);
146 static CFURLRequestRef willSendRequest(CFURLConnectionRef conn, CFURLRequestRef cfRequest, CFURLResponseRef cfRedirectResponse, const void* clientInfo)
151 ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo));
153 if (!cfRedirectResponse) {
158 LOG(Network, "CFNet - willSendRequest(conn=%p, handle=%p) (%s)", conn, handle, handle->firstRequest().url().string().utf8().data());
160 ResourceRequest request;
161 if (cfRedirectResponse) {
162 CFHTTPMessageRef httpMessage = CFURLResponseGetHTTPResponse(cfRedirectResponse);
163 if (httpMessage && CFHTTPMessageGetResponseStatusCode(httpMessage) == 307) {
164 RetainPtr<CFStringRef> lastHTTPMethod(AdoptCF, handle->lastHTTPMethod().createCFString());
165 RetainPtr<CFStringRef> newMethod(AdoptCF, CFURLRequestCopyHTTPRequestMethod(cfRequest));
166 if (CFStringCompareWithOptions(lastHTTPMethod.get(), newMethod.get(), CFRangeMake(0, CFStringGetLength(lastHTTPMethod.get())), kCFCompareCaseInsensitive)) {
167 RetainPtr<CFMutableURLRequestRef> mutableRequest(AdoptCF, CFURLRequestCreateMutableCopy(0, cfRequest));
168 #if USE(CFURLSTORAGESESSIONS)
169 wkSetRequestStorageSession(ResourceHandle::currentStorageSession(), mutableRequest.get());
171 CFURLRequestSetHTTPRequestMethod(mutableRequest.get(), lastHTTPMethod.get());
173 FormData* body = handle->firstRequest().httpBody();
174 if (!equalIgnoringCase(handle->firstRequest().httpMethod(), "GET") && body && !body->isEmpty())
175 WebCore::setHTTPBody(mutableRequest.get(), body);
177 String originalContentType = handle->firstRequest().httpContentType();
178 RetainPtr<CFStringRef> originalContentTypeCF(AdoptCF, originalContentType.createCFString());
179 if (!originalContentType.isEmpty())
180 CFURLRequestSetHTTPHeaderFieldValue(mutableRequest.get(), CFSTR("Content-Type"), originalContentTypeCF.get());
182 request = mutableRequest.get();
186 if (request.isNull())
189 // Should not set Referer after a redirect from a secure resource to non-secure one.
190 if (!request.url().protocolIs("https") && protocolIs(request.httpReferrer(), "https"))
191 request.clearHTTPReferrer();
193 handle->willSendRequest(request, cfRedirectResponse);
195 if (request.isNull())
198 cfRequest = request.cfURLRequest();
204 static void didReceiveResponse(CFURLConnectionRef conn, CFURLResponseRef cfResponse, const void* clientInfo)
209 ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo));
211 LOG(Network, "CFNet - didReceiveResponse(conn=%p, handle=%p) (%s)", conn, handle, handle->firstRequest().url().string().utf8().data());
213 if (!handle->client())
217 // Avoid MIME type sniffing if the response comes back as 304 Not Modified.
218 CFHTTPMessageRef msg = wkGetCFURLResponseHTTPResponse(cfResponse);
219 int statusCode = msg ? CFHTTPMessageGetResponseStatusCode(msg) : 0;
221 if (statusCode != 304)
222 adjustMIMETypeIfNecessary(cfResponse);
224 if (_CFURLRequestCopyProtocolPropertyForKey(handle->firstRequest().cfURLRequest(), CFSTR("ForceHTMLMIMEType")))
225 wkSetCFURLResponseMIMEType(cfResponse, CFSTR("text/html"));
227 if (!CFURLResponseGetMIMEType(cfResponse)) {
228 // We should never be applying the default MIMEType if we told the networking layer to do content sniffing for handle.
229 ASSERT(!handle->shouldContentSniff());
230 setDefaultMIMEType(cfResponse);
234 handle->client()->didReceiveResponse(handle, cfResponse);
237 #if HAVE(NETWORK_CFDATA_ARRAY_CALLBACK)
238 static void didReceiveDataArray(CFURLConnectionRef conn, CFArrayRef dataArray, const void* clientInfo)
243 ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo));
244 if (!handle->client())
247 LOG(Network, "CFNet - didReceiveDataArray(conn=%p, handle=%p, arrayLength=%ld) (%s)", conn, handle, CFArrayGetCount(dataArray), handle->firstRequest().url().string().utf8().data());
249 handle->handleDataArray(dataArray);
253 static void didReceiveData(CFURLConnectionRef conn, CFDataRef data, CFIndex originalLength, const void* clientInfo)
258 ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo));
259 const UInt8* bytes = CFDataGetBytePtr(data);
260 CFIndex length = CFDataGetLength(data);
262 LOG(Network, "CFNet - didReceiveData(conn=%p, handle=%p, bytes=%ld) (%s)", conn, handle, length, handle->firstRequest().url().string().utf8().data());
264 if (handle->client())
265 handle->client()->didReceiveData(handle, (const char*)bytes, length, originalLength);
268 static void didSendBodyData(CFURLConnectionRef, CFIndex, CFIndex totalBytesWritten, CFIndex totalBytesExpectedToWrite, const void *clientInfo)
270 ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo));
271 if (!handle || !handle->client())
273 handle->client()->didSendData(handle, totalBytesWritten, totalBytesExpectedToWrite);
276 static Boolean shouldUseCredentialStorageCallback(CFURLConnectionRef conn, const void* clientInfo)
281 ResourceHandle* handle = const_cast<ResourceHandle*>(static_cast<const ResourceHandle*>(clientInfo));
283 LOG(Network, "CFNet - shouldUseCredentialStorage(conn=%p, handle=%p) (%s)", conn, handle, handle->firstRequest().url().string().utf8().data());
288 return handle->shouldUseCredentialStorage();
291 static void didFinishLoading(CFURLConnectionRef conn, const void* clientInfo)
296 ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo));
298 LOG(Network, "CFNet - didFinishLoading(conn=%p, handle=%p) (%s)", conn, handle, handle->firstRequest().url().string().utf8().data());
300 if (handle->client())
301 handle->client()->didFinishLoading(handle, 0);
304 static void didFail(CFURLConnectionRef conn, CFErrorRef error, const void* clientInfo)
309 ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo));
311 LOG(Network, "CFNet - didFail(conn=%p, handle=%p, error = %p) (%s)", conn, handle, error, handle->firstRequest().url().string().utf8().data());
313 if (handle->client())
314 handle->client()->didFail(handle, ResourceError(error));
317 static CFCachedURLResponseRef willCacheResponse(CFURLConnectionRef, CFCachedURLResponseRef cachedResponse, const void* clientInfo)
319 ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo));
322 if (handle->client() && !handle->client()->shouldCacheResponse(handle, cachedResponse))
325 CFCachedURLResponseRef newResponse = handle->client()->willCacheResponse(handle, cachedResponse);
326 if (newResponse != cachedResponse)
330 CacheStoragePolicy policy = static_cast<CacheStoragePolicy>(CFCachedURLResponseGetStoragePolicy(cachedResponse));
332 if (handle->client())
333 handle->client()->willCacheResponse(handle, policy);
335 if (static_cast<CFURLCacheStoragePolicy>(policy) != CFCachedURLResponseGetStoragePolicy(cachedResponse)) {
336 cachedResponse = CFCachedURLResponseCreateWithUserInfo(kCFAllocatorDefault,
337 CFCachedURLResponseGetWrappedResponse(cachedResponse),
338 CFCachedURLResponseGetReceiverData(cachedResponse),
339 CFCachedURLResponseGetUserInfo(cachedResponse),
340 static_cast<CFURLCacheStoragePolicy>(policy));
342 CFRetain(cachedResponse);
344 return cachedResponse;
347 static void didReceiveChallenge(CFURLConnectionRef conn, CFURLAuthChallengeRef challenge, const void* clientInfo)
352 ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo));
354 LOG(Network, "CFNet - didReceiveChallenge(conn=%p, handle=%p (%s)", conn, handle, handle->firstRequest().url().string().utf8().data());
356 handle->didReceiveAuthenticationChallenge(AuthenticationChallenge(challenge, handle));
359 #if USE(PROTECTION_SPACE_AUTH_CALLBACK)
360 static Boolean canRespondToProtectionSpace(CFURLConnectionRef conn, CFURLProtectionSpaceRef protectionSpace, const void* clientInfo)
365 ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo));
368 LOG(Network, "CFNet - canRespondToProtectionSpace(conn=%p, handle=%p (%s)", conn, handle, handle->firstRequest().url().string().utf8().data());
370 return handle->canAuthenticateAgainstProtectionSpace(core(protectionSpace));
374 ResourceHandleInternal::~ResourceHandleInternal()
377 LOG(Network, "CFNet - Cancelling connection %p (%s)", m_connection.get(), m_firstRequest.url().string().utf8().data());
378 CFURLConnectionCancel(m_connection.get());
382 ResourceHandle::~ResourceHandle()
384 LOG(Network, "CFNet - Destroying job %p (%s)", this, d->m_firstRequest.url().string().utf8().data());
387 static CFURLRequestRef makeFinalRequest(const ResourceRequest& request, bool shouldContentSniff)
389 CFMutableURLRequestRef newRequest = CFURLRequestCreateMutableCopy(kCFAllocatorDefault, request.cfURLRequest());
390 #if USE(CFURLSTORAGESESSIONS)
391 wkSetRequestStorageSession(ResourceHandle::currentStorageSession(), newRequest);
394 if (!shouldContentSniff)
395 wkSetCFURLRequestShouldContentSniff(newRequest, false);
397 RetainPtr<CFMutableDictionaryRef> sslProps;
399 if (allowsAnyHTTPSCertificateHosts().contains(request.url().host().lower())) {
400 sslProps.adoptCF(CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
401 CFDictionaryAddValue(sslProps.get(), kCFStreamSSLAllowsAnyRoot, kCFBooleanTrue);
402 CFDictionaryAddValue(sslProps.get(), kCFStreamSSLAllowsExpiredRoots, kCFBooleanTrue);
403 CFDictionaryAddValue(sslProps.get(), kCFStreamSSLAllowsExpiredCertificates, kCFBooleanTrue);
404 CFDictionaryAddValue(sslProps.get(), kCFStreamSSLValidatesCertificateChain, kCFBooleanFalse);
407 HashMap<String, RetainPtr<CFDataRef> >::iterator clientCert = clientCerts().find(request.url().host().lower());
408 if (clientCert != clientCerts().end()) {
410 sslProps.adoptCF(CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
412 wkSetClientCertificateInSSLProperties(sslProps.get(), (clientCert->second).get());
417 CFURLRequestSetSSLProperties(newRequest, sslProps.get());
419 if (RetainPtr<CFHTTPCookieStorageRef> cookieStorage = currentCFHTTPCookieStorage()) {
420 CFURLRequestSetHTTPCookieStorage(newRequest, cookieStorage.get());
421 CFHTTPCookieStorageAcceptPolicy policy = CFHTTPCookieStorageGetCookieAcceptPolicy(cookieStorage.get());
422 CFURLRequestSetHTTPCookieStorageAcceptPolicy(newRequest, policy);
424 // If a URL already has cookies, then we'll relax the 3rd party cookie policy and accept new cookies.
425 if (policy == CFHTTPCookieStorageAcceptPolicyOnlyFromMainDocumentDomain) {
426 CFURLRef url = CFURLRequestGetURL(newRequest);
427 RetainPtr<CFArrayRef> cookies(AdoptCF, CFHTTPCookieStorageCopyCookiesForURL(cookieStorage.get(), url, false));
428 if (CFArrayGetCount(cookies.get()))
429 CFURLRequestSetMainDocumentURL(newRequest, url);
436 static CFDictionaryRef createConnectionProperties(bool shouldUseCredentialStorage)
438 static const CFStringRef webKitPrivateSessionCF = CFSTR("WebKitPrivateSession");
439 static const CFStringRef _kCFURLConnectionSessionID = CFSTR("_kCFURLConnectionSessionID");
440 static const CFStringRef kCFURLConnectionSocketStreamProperties = CFSTR("kCFURLConnectionSocketStreamProperties");
442 CFDictionaryRef sessionID = shouldUseCredentialStorage ?
443 CFDictionaryCreate(0, 0, 0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks) :
444 CFDictionaryCreate(0, (const void**)&_kCFURLConnectionSessionID, (const void**)&webKitPrivateSessionCF, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
446 CFDictionaryRef propertiesDictionary = CFDictionaryCreate(0, (const void**)&kCFURLConnectionSocketStreamProperties, (const void**)&sessionID, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
448 CFRelease(sessionID);
449 return propertiesDictionary;
452 void ResourceHandle::createCFURLConnection(bool shouldUseCredentialStorage, bool shouldContentSniff)
454 if ((!d->m_user.isEmpty() || !d->m_pass.isEmpty()) && !firstRequest().url().protocolInHTTPFamily()) {
455 // Credentials for ftp can only be passed in URL, the didReceiveAuthenticationChallenge delegate call won't be made.
456 KURL urlWithCredentials(firstRequest().url());
457 urlWithCredentials.setUser(d->m_user);
458 urlWithCredentials.setPass(d->m_pass);
459 firstRequest().setURL(urlWithCredentials);
462 // <rdar://problem/7174050> - For URLs that match the paths of those previously challenged for HTTP Basic authentication,
463 // try and reuse the credential preemptively, as allowed by RFC 2617.
464 if (shouldUseCredentialStorage && firstRequest().url().protocolInHTTPFamily()) {
465 if (d->m_user.isEmpty() && d->m_pass.isEmpty()) {
466 // <rdar://problem/7174050> - For URLs that match the paths of those previously challenged for HTTP Basic authentication,
467 // try and reuse the credential preemptively, as allowed by RFC 2617.
468 d->m_initialCredential = CredentialStorage::get(firstRequest().url());
470 // If there is already a protection space known for the URL, update stored credentials before sending a request.
471 // This makes it possible to implement logout by sending an XMLHttpRequest with known incorrect credentials, and aborting it immediately
472 // (so that an authentication dialog doesn't pop up).
473 CredentialStorage::set(Credential(d->m_user, d->m_pass, CredentialPersistenceNone), firstRequest().url());
477 if (!d->m_initialCredential.isEmpty()) {
478 // FIXME: Support Digest authentication, and Proxy-Authorization.
479 applyBasicAuthorizationHeader(firstRequest(), d->m_initialCredential);
482 RetainPtr<CFURLRequestRef> request(AdoptCF, makeFinalRequest(firstRequest(), shouldContentSniff));
484 #if HAVE(NETWORK_CFDATA_ARRAY_CALLBACK) && USE(PROTECTION_SPACE_AUTH_CALLBACK)
485 CFURLConnectionClient_V6 client = { 6, this, 0, 0, 0, WebCore::willSendRequest, didReceiveResponse, didReceiveData, 0, didFinishLoading, didFail, willCacheResponse, didReceiveChallenge, didSendBodyData, shouldUseCredentialStorageCallback, 0, canRespondToProtectionSpace, 0, didReceiveDataArray};
487 CFURLConnectionClient_V3 client = { 3, this, 0, 0, 0, WebCore::willSendRequest, didReceiveResponse, didReceiveData, 0, didFinishLoading, didFail, willCacheResponse, didReceiveChallenge, didSendBodyData, shouldUseCredentialStorageCallback, 0};
489 RetainPtr<CFDictionaryRef> connectionProperties(AdoptCF, createConnectionProperties(shouldUseCredentialStorage));
491 d->m_connection.adoptCF(CFURLConnectionCreateWithProperties(0, request.get(), reinterpret_cast<CFURLConnectionClient*>(&client), connectionProperties.get()));
494 bool ResourceHandle::start(NetworkingContext* context)
499 // If NetworkingContext is invalid then we are no longer attached to a Page,
500 // this must be an attempted load from an unload handler, so let's just block it.
501 if (!context->isValid())
504 bool shouldUseCredentialStorage = !client() || client()->shouldUseCredentialStorage(this);
506 createCFURLConnection(shouldUseCredentialStorage, d->m_shouldContentSniff);
509 CFURLConnectionScheduleWithCurrentMessageQueue(d->m_connection.get());
511 CFURLConnectionScheduleWithRunLoop(d->m_connection.get(), CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
513 CFURLConnectionScheduleDownloadWithRunLoop(d->m_connection.get(), loaderRunLoop(), kCFRunLoopDefaultMode);
514 CFURLConnectionStart(d->m_connection.get());
516 LOG(Network, "CFNet - Starting URL %s (handle=%p, conn=%p)", firstRequest().url().string().utf8().data(), this, d->m_connection.get());
521 void ResourceHandle::cancel()
523 if (d->m_connection) {
524 CFURLConnectionCancel(d->m_connection.get());
529 void ResourceHandle::willSendRequest(ResourceRequest& request, const ResourceResponse& redirectResponse)
531 const KURL& url = request.url();
532 d->m_user = url.user();
533 d->m_pass = url.pass();
534 d->m_lastHTTPMethod = request.httpMethod();
535 request.removeCredentials();
537 if (!protocolHostAndPortAreEqual(request.url(), redirectResponse.url())) {
538 // If the network layer carries over authentication headers from the original request
539 // in a cross-origin redirect, we want to clear those headers here.
540 request.clearHTTPAuthorization();
542 // Only consider applying authentication credentials if this is actually a redirect and the redirect
543 // URL didn't include credentials of its own.
544 if (d->m_user.isEmpty() && d->m_pass.isEmpty() && !redirectResponse.isNull()) {
545 Credential credential = CredentialStorage::get(request.url());
546 if (!credential.isEmpty()) {
547 d->m_initialCredential = credential;
549 // FIXME: Support Digest authentication, and Proxy-Authorization.
550 applyBasicAuthorizationHeader(request, d->m_initialCredential);
555 #if USE(CFURLSTORAGESESSIONS)
556 request.setStorageSession(ResourceHandle::currentStorageSession());
559 client()->willSendRequest(this, request, redirectResponse);
562 bool ResourceHandle::shouldUseCredentialStorage()
564 LOG(Network, "CFNet - shouldUseCredentialStorage()");
566 return client()->shouldUseCredentialStorage(this);
571 void ResourceHandle::didReceiveAuthenticationChallenge(const AuthenticationChallenge& challenge)
573 LOG(Network, "CFNet - didReceiveAuthenticationChallenge()");
574 ASSERT(d->m_currentWebChallenge.isNull());
575 // Since CFURLConnection networking relies on keeping a reference to the original CFURLAuthChallengeRef,
576 // we make sure that is actually present
577 ASSERT(challenge.cfURLAuthChallengeRef());
578 ASSERT(challenge.authenticationClient() == this); // Should be already set.
581 // Proxy authentication is handled by CFNetwork internally. We can get here if the user cancels
582 // CFNetwork authentication dialog, and we shouldn't ask the client to display another one in that case.
583 if (challenge.protectionSpace().isProxy()) {
584 // Cannot use receivedRequestToContinueWithoutCredential(), because current challenge is not yet set.
585 CFURLConnectionUseCredential(d->m_connection.get(), 0, challenge.cfURLAuthChallengeRef());
590 if (!d->m_user.isNull() && !d->m_pass.isNull()) {
591 RetainPtr<CFStringRef> user(AdoptCF, d->m_user.createCFString());
592 RetainPtr<CFStringRef> pass(AdoptCF, d->m_pass.createCFString());
593 RetainPtr<CFURLCredentialRef> credential(AdoptCF,
594 CFURLCredentialCreate(kCFAllocatorDefault, user.get(), pass.get(), 0, kCFURLCredentialPersistenceNone));
597 if (challenge.failureResponse().httpStatusCode() == 401)
598 urlToStore = firstRequest().url();
599 CredentialStorage::set(core(credential.get()), challenge.protectionSpace(), urlToStore);
601 CFURLConnectionUseCredential(d->m_connection.get(), credential.get(), challenge.cfURLAuthChallengeRef());
602 d->m_user = String();
603 d->m_pass = String();
604 // FIXME: Per the specification, the user shouldn't be asked for credentials if there were incorrect ones provided explicitly.
608 if (!client() || client()->shouldUseCredentialStorage(this)) {
609 if (!d->m_initialCredential.isEmpty() || challenge.previousFailureCount()) {
610 // The stored credential wasn't accepted, stop using it.
611 // There is a race condition here, since a different credential might have already been stored by another ResourceHandle,
612 // but the observable effect should be very minor, if any.
613 CredentialStorage::remove(challenge.protectionSpace());
616 if (!challenge.previousFailureCount()) {
617 Credential credential = CredentialStorage::get(challenge.protectionSpace());
618 if (!credential.isEmpty() && credential != d->m_initialCredential) {
619 ASSERT(credential.persistence() == CredentialPersistenceNone);
620 if (challenge.failureResponse().httpStatusCode() == 401) {
621 // Store the credential back, possibly adding it as a default for this directory.
622 CredentialStorage::set(credential, challenge.protectionSpace(), firstRequest().url());
624 RetainPtr<CFURLCredentialRef> cfCredential(AdoptCF, createCF(credential));
625 CFURLConnectionUseCredential(d->m_connection.get(), cfCredential.get(), challenge.cfURLAuthChallengeRef());
631 d->m_currentWebChallenge = challenge;
634 client()->didReceiveAuthenticationChallenge(this, d->m_currentWebChallenge);
637 #if USE(PROTECTION_SPACE_AUTH_CALLBACK)
638 bool ResourceHandle::canAuthenticateAgainstProtectionSpace(const ProtectionSpace& protectionSpace)
641 return client()->canAuthenticateAgainstProtectionSpace(this, protectionSpace);
647 void ResourceHandle::receivedCredential(const AuthenticationChallenge& challenge, const Credential& credential)
649 LOG(Network, "CFNet - receivedCredential()");
650 ASSERT(!challenge.isNull());
651 ASSERT(challenge.cfURLAuthChallengeRef());
652 if (challenge != d->m_currentWebChallenge)
655 // FIXME: Support empty credentials. Currently, an empty credential cannot be stored in WebCore credential storage, as that's empty value for its map.
656 if (credential.isEmpty()) {
657 receivedRequestToContinueWithoutCredential(challenge);
661 if (credential.persistence() == CredentialPersistenceForSession) {
662 // Manage per-session credentials internally, because once NSURLCredentialPersistencePerSession is used, there is no way
663 // to ignore it for a particular request (short of removing it altogether).
664 Credential webCredential(credential.user(), credential.password(), CredentialPersistenceNone);
665 RetainPtr<CFURLCredentialRef> cfCredential(AdoptCF, createCF(webCredential));
668 if (challenge.failureResponse().httpStatusCode() == 401)
669 urlToStore = firstRequest().url();
670 CredentialStorage::set(webCredential, challenge.protectionSpace(), urlToStore);
672 CFURLConnectionUseCredential(d->m_connection.get(), cfCredential.get(), challenge.cfURLAuthChallengeRef());
674 RetainPtr<CFURLCredentialRef> cfCredential(AdoptCF, createCF(credential));
675 CFURLConnectionUseCredential(d->m_connection.get(), cfCredential.get(), challenge.cfURLAuthChallengeRef());
678 clearAuthentication();
681 void ResourceHandle::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge& challenge)
683 LOG(Network, "CFNet - receivedRequestToContinueWithoutCredential()");
684 ASSERT(!challenge.isNull());
685 ASSERT(challenge.cfURLAuthChallengeRef());
686 if (challenge != d->m_currentWebChallenge)
689 CFURLConnectionUseCredential(d->m_connection.get(), 0, challenge.cfURLAuthChallengeRef());
691 clearAuthentication();
694 void ResourceHandle::receivedCancellation(const AuthenticationChallenge& challenge)
696 LOG(Network, "CFNet - receivedCancellation()");
697 if (challenge != d->m_currentWebChallenge)
701 client()->receivedCancellation(this, challenge);
704 CFURLConnectionRef ResourceHandle::connection() const
706 return d->m_connection.get();
709 CFURLConnectionRef ResourceHandle::releaseConnectionForDownload()
711 LOG(Network, "CFNet - Job %p releasing connection %p for download", this, d->m_connection.get());
712 return d->m_connection.leakRef();
715 CFStringRef ResourceHandle::synchronousLoadRunLoopMode()
717 return CFSTR("WebCoreSynchronousLoaderRunLoopMode");
720 void ResourceHandle::loadResourceSynchronously(NetworkingContext* context, const ResourceRequest& request, StoredCredentials storedCredentials, ResourceError& error, ResourceResponse& response, Vector<char>& vector)
722 LOG(Network, "ResourceHandle::loadResourceSynchronously:%s allowStoredCredentials:%u", request.url().string().utf8().data(), storedCredentials);
724 ASSERT(!request.isEmpty());
726 ASSERT(response.isNull());
727 ASSERT(error.isNull());
729 OwnPtr<WebCoreSynchronousLoaderClient> client = WebCoreSynchronousLoaderClient::create(response, error);
730 client->setAllowStoredCredentials(storedCredentials == AllowStoredCredentials);
732 RefPtr<ResourceHandle> handle = adoptRef(new ResourceHandle(request, client.get(), false /*defersLoading*/, true /*shouldContentSniff*/));
734 if (context && handle->d->m_scheduledFailureType != NoFailure) {
735 error = context->blockedError(request);
739 handle->createCFURLConnection(storedCredentials == AllowStoredCredentials, ResourceHandle::shouldContentSniffURL(request.url()));
741 CFURLConnectionScheduleWithRunLoop(handle->connection(), CFRunLoopGetCurrent(), synchronousLoadRunLoopMode());
742 CFURLConnectionScheduleDownloadWithRunLoop(handle->connection(), CFRunLoopGetCurrent(), synchronousLoadRunLoopMode());
743 CFURLConnectionStart(handle->connection());
745 while (!client->isDone())
746 CFRunLoopRunInMode(synchronousLoadRunLoopMode(), UINT_MAX, true);
748 CFURLConnectionCancel(handle->connection());
750 if (error.isNull() && response.mimeType().isNull())
751 setDefaultMIMEType(response.cfURLResponse());
753 RetainPtr<CFDataRef> data = client->data();
755 if (!error.isNull()) {
756 response = ResourceResponse(request.url(), String(), 0, String(), String());
758 CFErrorRef cfError = error;
759 CFStringRef domain = CFErrorGetDomain(cfError);
760 // FIXME: Return the actual response for failed authentication.
761 if (domain == kCFErrorDomainCFNetwork)
762 response.setHTTPStatusCode(CFErrorGetCode(cfError));
764 response.setHTTPStatusCode(404);
768 ASSERT(vector.isEmpty());
769 vector.append(CFDataGetBytePtr(data.get()), CFDataGetLength(data.get()));
773 void ResourceHandle::setHostAllowsAnyHTTPSCertificate(const String& host)
775 allowsAnyHTTPSCertificateHosts().add(host.lower());
778 void ResourceHandle::setClientCertificate(const String& host, CFDataRef cert)
780 clientCerts().set(host.lower(), cert);
783 void ResourceHandle::platformSetDefersLoading(bool defers)
785 if (!d->m_connection)
789 CFURLConnectionHalt(d->m_connection.get());
791 CFURLConnectionResume(d->m_connection.get());
794 bool ResourceHandle::loadsBlocked()
799 bool ResourceHandle::willLoadFromCache(ResourceRequest& request, Frame*)
801 request.setCachePolicy(ReturnCacheDataDontLoad);
803 CFURLResponseRef cfResponse = 0;
804 CFErrorRef cfError = 0;
805 RetainPtr<CFURLRequestRef> cfRequest(AdoptCF, makeFinalRequest(request, true));
806 RetainPtr<CFDataRef> data(AdoptCF, CFURLConnectionSendSynchronousRequest(cfRequest.get(), &cfResponse, &cfError, request.timeoutInterval()));
807 bool cached = cfResponse && !cfError;
812 CFRelease(cfResponse);
818 void ResourceHandle::schedule(SchedulePair* pair)
820 CFRunLoopRef runLoop = pair->runLoop();
824 CFURLConnectionScheduleWithRunLoop(d->m_connection.get(), runLoop, pair->mode());
825 if (d->m_startWhenScheduled) {
826 CFURLConnectionStart(d->m_connection.get());
827 d->m_startWhenScheduled = false;
831 void ResourceHandle::unschedule(SchedulePair* pair)
833 CFRunLoopRef runLoop = pair->runLoop();
837 CFURLConnectionUnscheduleFromRunLoop(d->m_connection.get(), runLoop, pair->mode());
841 void WebCoreSynchronousLoaderClient::willSendRequest(ResourceHandle* handle, ResourceRequest& request, const ResourceResponse& /*redirectResponse*/)
843 // FIXME: This needs to be fixed to follow the redirect correctly even for cross-domain requests.
844 if (!protocolHostAndPortAreEqual(handle->firstRequest().url(), request.url())) {
845 ASSERT(!m_error.cfError());
846 RetainPtr<CFErrorRef> cfError(AdoptCF, CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainCFNetwork, kCFURLErrorBadServerResponse, 0));
847 m_error = cfError.get();
849 CFURLRequestRef nullRequest = 0;
850 request = nullRequest;
854 void WebCoreSynchronousLoaderClient::didReceiveResponse(ResourceHandle*, const ResourceResponse& response)
856 m_response = response;
859 void WebCoreSynchronousLoaderClient::didReceiveData(ResourceHandle*, const char* data, int length, int /*encodedDataLength*/)
862 m_data.adoptCF(CFDataCreateMutable(kCFAllocatorDefault, 0));
863 CFDataAppendBytes(m_data.get(), reinterpret_cast<const UInt8*>(data), length);
866 void WebCoreSynchronousLoaderClient::didFinishLoading(ResourceHandle*, double)
871 void WebCoreSynchronousLoaderClient::didFail(ResourceHandle*, const ResourceError& error)
877 void WebCoreSynchronousLoaderClient::didReceiveAuthenticationChallenge(ResourceHandle* handle, const AuthenticationChallenge& challenge)
879 // FIXME: The user should be asked for credentials, as in async case.
880 CFURLConnectionUseCredential(handle->connection(), 0, challenge.cfURLAuthChallengeRef());
883 bool WebCoreSynchronousLoaderClient::shouldUseCredentialStorage(ResourceHandle*)
885 // FIXME: We should ask FrameLoaderClient whether using credential storage is globally forbidden.
886 return m_allowStoredCredentials;
889 #if USE(PROTECTION_SPACE_AUTH_CALLBACK)
890 bool WebCoreSynchronousLoaderClient::canAuthenticateAgainstProtectionSpace(ResourceHandle*, const ProtectionSpace&)
892 // FIXME: We should ask FrameLoaderClient. <http://webkit.org/b/65196>
897 #endif // USE(CFNETWORK)
899 #if HAVE(NETWORK_CFDATA_ARRAY_CALLBACK)
900 void ResourceHandle::handleDataArray(CFArrayRef dataArray)
903 if (client()->supportsDataArray()) {
904 client()->didReceiveDataArray(this, dataArray);
908 CFIndex count = CFArrayGetCount(dataArray);
911 CFDataRef data = static_cast<CFDataRef>(CFArrayGetValueAtIndex(dataArray, 0));
912 CFIndex length = CFDataGetLength(data);
913 client()->didReceiveData(this, reinterpret_cast<const char*>(CFDataGetBytePtr(data)), length, static_cast<int>(length));
917 CFIndex totalSize = 0;
919 for (index = 0; index < count; index++)
920 totalSize += CFDataGetLength(static_cast<CFDataRef>(CFArrayGetValueAtIndex(dataArray, index)));
922 RetainPtr<CFMutableDataRef> mergedData(AdoptCF, CFDataCreateMutable(kCFAllocatorDefault, totalSize));
923 for (index = 0; index < count; index++) {
924 CFDataRef data = static_cast<CFDataRef>(CFArrayGetValueAtIndex(dataArray, index));
925 CFDataAppendBytes(mergedData.get(), CFDataGetBytePtr(data), CFDataGetLength(data));
928 client()->didReceiveData(this, reinterpret_cast<const char*>(CFDataGetBytePtr(mergedData.get())), totalSize, static_cast<int>(totalSize));
932 #if USE(CFURLSTORAGESESSIONS)
934 RetainPtr<CFURLStorageSessionRef> ResourceHandle::createPrivateBrowsingStorageSession(CFStringRef identifier)
937 return RetainPtr<CFURLStorageSessionRef>(AdoptCF, wkCreatePrivateStorageSession(identifier, defaultStorageSession()));
939 return RetainPtr<CFURLStorageSessionRef>(AdoptCF, wkCreatePrivateStorageSession(identifier));
943 CFURLStorageSessionRef ResourceHandle::currentStorageSession()
945 if (CFURLStorageSessionRef privateStorageSession = privateBrowsingStorageSession())
946 return privateStorageSession;
947 return defaultStorageSession();
950 static RetainPtr<CFURLStorageSessionRef>& defaultCFURLStorageSession()
952 DEFINE_STATIC_LOCAL(RetainPtr<CFURLStorageSessionRef>, storageSession, ());
953 return storageSession;
956 void ResourceHandle::setDefaultStorageSession(CFURLStorageSessionRef storageSession)
958 defaultCFURLStorageSession() = storageSession;
961 CFURLStorageSessionRef ResourceHandle::defaultStorageSession()
963 return defaultCFURLStorageSession().get();
966 static RetainPtr<CFURLStorageSessionRef>& privateStorageSession()
968 DEFINE_STATIC_LOCAL(RetainPtr<CFURLStorageSessionRef>, storageSession, ());
969 return storageSession;
972 static String& privateBrowsingStorageSessionIdentifierBase()
974 DEFINE_STATIC_LOCAL(String, base, ());
978 void ResourceHandle::setPrivateBrowsingEnabled(bool enabled)
981 privateStorageSession() = nullptr;
985 if (privateStorageSession())
988 String base = privateBrowsingStorageSessionIdentifierBase().isNull() ? privateBrowsingStorageSessionIdentifierDefaultBase() : privateBrowsingStorageSessionIdentifierBase();
989 RetainPtr<CFStringRef> cfIdentifier(AdoptCF, String(base + ".PrivateBrowsing").createCFString());
991 privateStorageSession() = createPrivateBrowsingStorageSession(cfIdentifier.get());
994 CFURLStorageSessionRef ResourceHandle::privateBrowsingStorageSession()
996 return privateStorageSession().get();
999 void ResourceHandle::setPrivateBrowsingStorageSessionIdentifierBase(const String& identifier)
1001 privateBrowsingStorageSessionIdentifierBase() = identifier;
1006 String ResourceHandle::privateBrowsingStorageSessionIdentifierDefaultBase()
1008 return String(reinterpret_cast<CFStringRef>(CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(), kCFBundleIdentifierKey)));
1011 #endif // PLATFORM(WIN)
1013 #endif // USE(CFURLSTORAGESESSIONS)
1015 } // namespace WebCore