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