1/* 2 * Copyright (C) 2004, 2005, 2006, 2007, 2009 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 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. 24 */ 25 26#include "config.h" 27 28#include "ResourceHandle.h" 29#include "ResourceHandleClient.h" 30#include "ResourceHandleInternal.h" 31 32#include "AuthenticationCF.h" 33#include "AuthenticationChallenge.h" 34#include "Base64.h" 35#include "CString.h" 36#include "CookieStorageWin.h" 37#include "CredentialStorage.h" 38#include "DocLoader.h" 39#include "FormDataStreamCFNet.h" 40#include "Frame.h" 41#include "FrameLoader.h" 42#include "LoaderRunLoopCF.h" 43#include "Logging.h" 44#include "MIMETypeRegistry.h" 45#include "ResourceError.h" 46#include "ResourceResponse.h" 47 48#include <wtf/HashMap.h> 49#include <wtf/Threading.h> 50 51#include <sys/types.h> 52#include <sys/stat.h> 53#include <process.h> // for _beginthread() 54 55#include <CFNetwork/CFNetwork.h> 56#include <WebKitSystemInterface/WebKitSystemInterface.h> 57 58namespace WebCore { 59 60static CFStringRef WebCoreSynchronousLoaderRunLoopMode = CFSTR("WebCoreSynchronousLoaderRunLoopMode"); 61 62class WebCoreSynchronousLoader { 63public: 64 static RetainPtr<CFDataRef> load(const ResourceRequest&, StoredCredentials, ResourceResponse&, ResourceError&); 65 66private: 67 WebCoreSynchronousLoader(ResourceResponse& response, ResourceError& error) 68 : m_isDone(false) 69 , m_response(response) 70 , m_error(error) 71 { 72 } 73 74 static CFURLRequestRef willSendRequest(CFURLConnectionRef, CFURLRequestRef, CFURLResponseRef, const void* clientInfo); 75 static void didReceiveResponse(CFURLConnectionRef, CFURLResponseRef, const void* clientInfo); 76 static void didReceiveData(CFURLConnectionRef, CFDataRef, CFIndex, const void* clientInfo); 77 static void didFinishLoading(CFURLConnectionRef, const void* clientInfo); 78 static void didFail(CFURLConnectionRef, CFErrorRef, const void* clientInfo); 79 static void didReceiveChallenge(CFURLConnectionRef, CFURLAuthChallengeRef, const void* clientInfo); 80 static Boolean shouldUseCredentialStorage(CFURLConnectionRef, const void* clientInfo); 81 82 bool m_isDone; 83 RetainPtr<CFURLRef> m_url; 84 RetainPtr<CFStringRef> m_user; 85 RetainPtr<CFStringRef> m_pass; 86 // Store the preemptively used initial credential so that if we get an authentication challenge, we won't use the same one again. 87 Credential m_initialCredential; 88 bool m_allowStoredCredentials; 89 ResourceResponse& m_response; 90 RetainPtr<CFMutableDataRef> m_data; 91 ResourceError& m_error; 92}; 93 94static HashSet<String>& allowsAnyHTTPSCertificateHosts() 95{ 96 static HashSet<String> hosts; 97 98 return hosts; 99} 100 101static HashMap<String, RetainPtr<CFDataRef> >& clientCerts() 102{ 103 static HashMap<String, RetainPtr<CFDataRef> > certs; 104 return certs; 105} 106 107static void setDefaultMIMEType(CFURLResponseRef response) 108{ 109 static CFStringRef defaultMIMETypeString = defaultMIMEType().createCFString(); 110 111 CFURLResponseSetMIMEType(response, defaultMIMETypeString); 112} 113 114static String encodeBasicAuthorization(const String& user, const String& password) 115{ 116 CString unencodedString = (user + ":" + password).utf8(); 117 Vector<char> unencoded(unencodedString.length()); 118 std::copy(unencodedString.data(), unencodedString.data() + unencodedString.length(), unencoded.begin()); 119 Vector<char> encoded; 120 base64Encode(unencoded, encoded); 121 return String(encoded.data(), encoded.size()); 122} 123 124CFURLRequestRef willSendRequest(CFURLConnectionRef conn, CFURLRequestRef cfRequest, CFURLResponseRef cfRedirectResponse, const void* clientInfo) 125{ 126 ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo)); 127 128 if (!cfRedirectResponse) { 129 CFRetain(cfRequest); 130 return cfRequest; 131 } 132 133 LOG(Network, "CFNet - willSendRequest(conn=%p, handle=%p) (%s)", conn, handle, handle->request().url().string().utf8().data()); 134 135 ResourceRequest request; 136 if (cfRedirectResponse) { 137 CFHTTPMessageRef httpMessage = CFURLResponseGetHTTPResponse(cfRedirectResponse); 138 if (httpMessage && CFHTTPMessageGetResponseStatusCode(httpMessage) == 307) { 139 RetainPtr<CFStringRef> originalMethod(AdoptCF, handle->request().httpMethod().createCFString()); 140 RetainPtr<CFStringRef> newMethod(AdoptCF, CFURLRequestCopyHTTPRequestMethod(cfRequest)); 141 if (CFStringCompareWithOptions(originalMethod.get(), newMethod.get(), CFRangeMake(0, CFStringGetLength(originalMethod.get())), kCFCompareCaseInsensitive)) { 142 RetainPtr<CFMutableURLRequestRef> mutableRequest(AdoptCF, CFURLRequestCreateMutableCopy(0, cfRequest)); 143 CFURLRequestSetHTTPRequestMethod(mutableRequest.get(), originalMethod.get()); 144 145 FormData* body = handle->request().httpBody(); 146 if (!equalIgnoringCase(handle->request().httpMethod(), "GET") && body && !body->isEmpty()) 147 WebCore::setHTTPBody(mutableRequest.get(), body); 148 149 String originalContentType = handle->request().httpContentType(); 150 RetainPtr<CFStringRef> originalContentTypeCF(AdoptCF, originalContentType.createCFString()); 151 if (!originalContentType.isEmpty()) 152 CFURLRequestSetHTTPHeaderFieldValue(mutableRequest.get(), CFSTR("Content-Type"), originalContentTypeCF.get()); 153 154 request = mutableRequest.get(); 155 } 156 } 157 } 158 if (request.isNull()) 159 request = cfRequest; 160 161 // Should not set Referer after a redirect from a secure resource to non-secure one. 162 if (!request.url().protocolIs("https") && protocolIs(request.httpReferrer(), "https")) 163 request.clearHTTPReferrer(); 164 165 handle->willSendRequest(request, cfRedirectResponse); 166 167 if (request.isNull()) 168 return 0; 169 170 cfRequest = request.cfURLRequest(); 171 172 CFRetain(cfRequest); 173 return cfRequest; 174} 175 176void didReceiveResponse(CFURLConnectionRef conn, CFURLResponseRef cfResponse, const void* clientInfo) 177{ 178 ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo)); 179 180 LOG(Network, "CFNet - didReceiveResponse(conn=%p, handle=%p) (%s)", conn, handle, handle->request().url().string().utf8().data()); 181 182 if (!handle->client()) 183 return; 184 185 if (!CFURLResponseGetMIMEType(cfResponse)) { 186 // We should never be applying the default MIMEType if we told the networking layer to do content sniffing for handle. 187 ASSERT(!handle->shouldContentSniff()); 188 setDefaultMIMEType(cfResponse); 189 } 190 191 handle->client()->didReceiveResponse(handle, cfResponse); 192} 193 194void didReceiveData(CFURLConnectionRef conn, CFDataRef data, CFIndex originalLength, const void* clientInfo) 195{ 196 ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo)); 197 const UInt8* bytes = CFDataGetBytePtr(data); 198 CFIndex length = CFDataGetLength(data); 199 200 LOG(Network, "CFNet - didReceiveData(conn=%p, handle=%p, bytes=%d) (%s)", conn, handle, length, handle->request().url().string().utf8().data()); 201 202 if (handle->client()) 203 handle->client()->didReceiveData(handle, (const char*)bytes, length, originalLength); 204} 205 206static void didSendBodyData(CFURLConnectionRef conn, CFIndex bytesWritten, CFIndex totalBytesWritten, CFIndex totalBytesExpectedToWrite, const void *clientInfo) 207{ 208 ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo)); 209 if (!handle || !handle->client()) 210 return; 211 handle->client()->didSendData(handle, totalBytesWritten, totalBytesExpectedToWrite); 212} 213 214static Boolean shouldUseCredentialStorageCallback(CFURLConnectionRef conn, const void* clientInfo) 215{ 216 ResourceHandle* handle = const_cast<ResourceHandle*>(static_cast<const ResourceHandle*>(clientInfo)); 217 218 LOG(Network, "CFNet - shouldUseCredentialStorage(conn=%p, handle=%p) (%s)", conn, handle, handle->request().url().string().utf8().data()); 219 220 if (!handle) 221 return false; 222 223 return handle->shouldUseCredentialStorage(); 224} 225 226void didFinishLoading(CFURLConnectionRef conn, const void* clientInfo) 227{ 228 ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo)); 229 230 LOG(Network, "CFNet - didFinishLoading(conn=%p, handle=%p) (%s)", conn, handle, handle->request().url().string().utf8().data()); 231 232 if (handle->client()) 233 handle->client()->didFinishLoading(handle); 234} 235 236void didFail(CFURLConnectionRef conn, CFErrorRef error, const void* clientInfo) 237{ 238 ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo)); 239 240 LOG(Network, "CFNet - didFail(conn=%p, handle=%p, error = %p) (%s)", conn, handle, error, handle->request().url().string().utf8().data()); 241 242 if (handle->client()) 243 handle->client()->didFail(handle, ResourceError(error)); 244} 245 246CFCachedURLResponseRef willCacheResponse(CFURLConnectionRef conn, CFCachedURLResponseRef cachedResponse, const void* clientInfo) 247{ 248 ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo)); 249 250 if (handle->client() && !handle->client()->shouldCacheResponse(handle, cachedResponse)) 251 return 0; 252 253 CacheStoragePolicy policy = static_cast<CacheStoragePolicy>(CFCachedURLResponseGetStoragePolicy(cachedResponse)); 254 255 if (handle->client()) 256 handle->client()->willCacheResponse(handle, policy); 257 258 if (static_cast<CFURLCacheStoragePolicy>(policy) != CFCachedURLResponseGetStoragePolicy(cachedResponse)) 259 cachedResponse = CFCachedURLResponseCreateWithUserInfo(kCFAllocatorDefault, 260 CFCachedURLResponseGetWrappedResponse(cachedResponse), 261 CFCachedURLResponseGetReceiverData(cachedResponse), 262 CFCachedURLResponseGetUserInfo(cachedResponse), 263 static_cast<CFURLCacheStoragePolicy>(policy)); 264 CFRetain(cachedResponse); 265 266 return cachedResponse; 267} 268 269void didReceiveChallenge(CFURLConnectionRef conn, CFURLAuthChallengeRef challenge, const void* clientInfo) 270{ 271 ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo)); 272 ASSERT(handle); 273 LOG(Network, "CFNet - didReceiveChallenge(conn=%p, handle=%p (%s)", conn, handle, handle->request().url().string().utf8().data()); 274 275 handle->didReceiveAuthenticationChallenge(AuthenticationChallenge(challenge, handle)); 276} 277 278void addHeadersFromHashMap(CFMutableURLRequestRef request, const HTTPHeaderMap& requestHeaders) 279{ 280 if (!requestHeaders.size()) 281 return; 282 283 HTTPHeaderMap::const_iterator end = requestHeaders.end(); 284 for (HTTPHeaderMap::const_iterator it = requestHeaders.begin(); it != end; ++it) { 285 CFStringRef key = it->first.createCFString(); 286 CFStringRef value = it->second.createCFString(); 287 CFURLRequestSetHTTPHeaderFieldValue(request, key, value); 288 CFRelease(key); 289 CFRelease(value); 290 } 291} 292 293ResourceHandleInternal::~ResourceHandleInternal() 294{ 295 if (m_connection) { 296 LOG(Network, "CFNet - Cancelling connection %p (%s)", m_connection, m_request.url().string().utf8().data()); 297 CFURLConnectionCancel(m_connection.get()); 298 } 299} 300 301ResourceHandle::~ResourceHandle() 302{ 303 LOG(Network, "CFNet - Destroying job %p (%s)", this, d->m_request.url().string().utf8().data()); 304} 305 306CFArrayRef arrayFromFormData(const FormData& d) 307{ 308 size_t size = d.elements().size(); 309 CFMutableArrayRef a = CFArrayCreateMutable(0, d.elements().size(), &kCFTypeArrayCallBacks); 310 for (size_t i = 0; i < size; ++i) { 311 const FormDataElement& e = d.elements()[i]; 312 if (e.m_type == FormDataElement::data) { 313 CFDataRef data = CFDataCreate(0, (const UInt8*)e.m_data.data(), e.m_data.size()); 314 CFArrayAppendValue(a, data); 315 CFRelease(data); 316 } else { 317 ASSERT(e.m_type == FormDataElement::encodedFile); 318 CFStringRef filename = e.m_filename.createCFString(); 319 CFArrayAppendValue(a, filename); 320 CFRelease(filename); 321 } 322 } 323 return a; 324} 325 326static CFURLRequestRef makeFinalRequest(const ResourceRequest& request, bool shouldContentSniff) 327{ 328 CFMutableURLRequestRef newRequest = CFURLRequestCreateMutableCopy(kCFAllocatorDefault, request.cfURLRequest()); 329 330 if (!shouldContentSniff) 331 wkSetCFURLRequestShouldContentSniff(newRequest, false); 332 333 RetainPtr<CFMutableDictionaryRef> sslProps; 334 335 if (allowsAnyHTTPSCertificateHosts().contains(request.url().host().lower())) { 336 sslProps.adoptCF(CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); 337 CFDictionaryAddValue(sslProps.get(), kCFStreamSSLAllowsAnyRoot, kCFBooleanTrue); 338 CFDictionaryAddValue(sslProps.get(), kCFStreamSSLAllowsExpiredRoots, kCFBooleanTrue); 339 CFDictionaryAddValue(sslProps.get(), kCFStreamSSLAllowsExpiredCertificates, kCFBooleanTrue); 340 CFDictionaryAddValue(sslProps.get(), kCFStreamSSLValidatesCertificateChain, kCFBooleanFalse); 341 } 342 343 HashMap<String, RetainPtr<CFDataRef> >::iterator clientCert = clientCerts().find(request.url().host().lower()); 344 if (clientCert != clientCerts().end()) { 345 if (!sslProps) 346 sslProps.adoptCF(CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); 347 wkSetClientCertificateInSSLProperties(sslProps.get(), (clientCert->second).get()); 348 } 349 350 if (sslProps) 351 CFURLRequestSetSSLProperties(newRequest, sslProps.get()); 352 353 if (CFHTTPCookieStorageRef cookieStorage = currentCookieStorage()) { 354 CFURLRequestSetHTTPCookieStorage(newRequest, cookieStorage); 355 CFURLRequestSetHTTPCookieStorageAcceptPolicy(newRequest, CFHTTPCookieStorageGetCookieAcceptPolicy(cookieStorage)); 356 } 357 358 return newRequest; 359} 360 361bool ResourceHandle::start(Frame* frame) 362{ 363 // If we are no longer attached to a Page, this must be an attempted load from an 364 // onUnload handler, so let's just block it. 365 if (!frame->page()) 366 return false; 367 368 if ((!d->m_user.isEmpty() || !d->m_pass.isEmpty()) && !d->m_request.url().protocolInHTTPFamily()) { 369 // Credentials for ftp can only be passed in URL, the didReceiveAuthenticationChallenge delegate call won't be made. 370 KURL urlWithCredentials(d->m_request.url()); 371 urlWithCredentials.setUser(d->m_user); 372 urlWithCredentials.setPass(d->m_pass); 373 d->m_request.setURL(urlWithCredentials); 374 } 375 376 // <rdar://problem/7174050> - For URLs that match the paths of those previously challenged for HTTP Basic authentication, 377 // try and reuse the credential preemptively, as allowed by RFC 2617. 378 if (!client() || client()->shouldUseCredentialStorage(this) && d->m_request.url().protocolInHTTPFamily()) { 379 if (d->m_user.isEmpty() && d->m_pass.isEmpty()) { 380 // <rdar://problem/7174050> - For URLs that match the paths of those previously challenged for HTTP Basic authentication, 381 // try and reuse the credential preemptively, as allowed by RFC 2617. 382 d->m_initialCredential = CredentialStorage::get(d->m_request.url()); 383 } else { 384 // If there is already a protection space known for the URL, update stored credentials before sending a request. 385 // This makes it possible to implement logout by sending an XMLHttpRequest with known incorrect credentials, and aborting it immediately 386 // (so that an authentication dialog doesn't pop up). 387 CredentialStorage::set(Credential(d->m_user, d->m_pass, CredentialPersistenceNone), d->m_request.url()); 388 } 389 } 390 391 if (!d->m_initialCredential.isEmpty()) { 392 String authHeader = "Basic " + encodeBasicAuthorization(d->m_initialCredential.user(), d->m_initialCredential.password()); 393 d->m_request.addHTTPHeaderField("Authorization", authHeader); 394 } 395 396 RetainPtr<CFURLRequestRef> request(AdoptCF, makeFinalRequest(d->m_request, d->m_shouldContentSniff)); 397 398 CFURLConnectionClient_V3 client = { 3, this, 0, 0, 0, WebCore::willSendRequest, didReceiveResponse, didReceiveData, NULL, didFinishLoading, didFail, willCacheResponse, didReceiveChallenge, didSendBodyData, shouldUseCredentialStorageCallback, 0}; 399 400 d->m_connection.adoptCF(CFURLConnectionCreate(0, request.get(), reinterpret_cast<CFURLConnectionClient*>(&client))); 401 402 CFURLConnectionScheduleWithCurrentMessageQueue(d->m_connection.get()); 403 CFURLConnectionScheduleDownloadWithRunLoop(d->m_connection.get(), loaderRunLoop(), kCFRunLoopDefaultMode); 404 CFURLConnectionStart(d->m_connection.get()); 405 406 LOG(Network, "CFNet - Starting URL %s (handle=%p, conn=%p)", d->m_request.url().string().utf8().data(), this, d->m_connection); 407 408 return true; 409} 410 411void ResourceHandle::cancel() 412{ 413 if (d->m_connection) { 414 CFURLConnectionCancel(d->m_connection.get()); 415 d->m_connection = 0; 416 } 417} 418 419PassRefPtr<SharedBuffer> ResourceHandle::bufferedData() 420{ 421 ASSERT_NOT_REACHED(); 422 return 0; 423} 424 425bool ResourceHandle::supportsBufferedData() 426{ 427 return false; 428} 429 430void ResourceHandle::willSendRequest(ResourceRequest& request, const ResourceResponse& redirectResponse) 431{ 432 const KURL& url = request.url(); 433 d->m_user = url.user(); 434 d->m_pass = url.pass(); 435 request.removeCredentials(); 436 437 client()->willSendRequest(this, request, redirectResponse); 438} 439 440bool ResourceHandle::shouldUseCredentialStorage() 441{ 442 LOG(Network, "CFNet - shouldUseCredentialStorage()"); 443 if (client()) 444 return client()->shouldUseCredentialStorage(this); 445 446 return false; 447} 448 449void ResourceHandle::didReceiveAuthenticationChallenge(const AuthenticationChallenge& challenge) 450{ 451 LOG(Network, "CFNet - didReceiveAuthenticationChallenge()"); 452 ASSERT(d->m_currentWebChallenge.isNull()); 453 // Since CFURLConnection networking relies on keeping a reference to the original CFURLAuthChallengeRef, 454 // we make sure that is actually present 455 ASSERT(challenge.cfURLAuthChallengeRef()); 456 ASSERT(challenge.authenticationClient() == this); // Should be already set. 457 458 if (!d->m_user.isNull() && !d->m_pass.isNull()) { 459 RetainPtr<CFStringRef> user(AdoptCF, d->m_user.createCFString()); 460 RetainPtr<CFStringRef> pass(AdoptCF, d->m_pass.createCFString()); 461 RetainPtr<CFURLCredentialRef> credential(AdoptCF, 462 CFURLCredentialCreate(kCFAllocatorDefault, user.get(), pass.get(), 0, kCFURLCredentialPersistenceNone)); 463 464 KURL urlToStore; 465 if (challenge.failureResponse().httpStatusCode() == 401) 466 urlToStore = d->m_request.url(); 467 CredentialStorage::set(core(credential.get()), challenge.protectionSpace(), urlToStore); 468 469 CFURLConnectionUseCredential(d->m_connection.get(), credential.get(), challenge.cfURLAuthChallengeRef()); 470 d->m_user = String(); 471 d->m_pass = String(); 472 // FIXME: Per the specification, the user shouldn't be asked for credentials if there were incorrect ones provided explicitly. 473 return; 474 } 475 476 if (!challenge.previousFailureCount() && (!client() || client()->shouldUseCredentialStorage(this))) { 477 Credential credential = CredentialStorage::get(challenge.protectionSpace()); 478 if (!credential.isEmpty() && credential != d->m_initialCredential) { 479 ASSERT(credential.persistence() == CredentialPersistenceNone); 480 RetainPtr<CFURLCredentialRef> cfCredential(AdoptCF, createCF(credential)); 481 CFURLConnectionUseCredential(d->m_connection.get(), cfCredential.get(), challenge.cfURLAuthChallengeRef()); 482 return; 483 } 484 } 485 486 d->m_currentWebChallenge = challenge; 487 488 if (client()) 489 client()->didReceiveAuthenticationChallenge(this, d->m_currentWebChallenge); 490} 491 492void ResourceHandle::receivedCredential(const AuthenticationChallenge& challenge, const Credential& credential) 493{ 494 LOG(Network, "CFNet - receivedCredential()"); 495 ASSERT(!challenge.isNull()); 496 ASSERT(challenge.cfURLAuthChallengeRef()); 497 if (challenge != d->m_currentWebChallenge) 498 return; 499 500 if (credential.persistence() == CredentialPersistenceForSession) { 501 // Manage per-session credentials internally, because once NSURLCredentialPersistencePerSession is used, there is no way 502 // to ignore it for a particular request (short of removing it altogether). 503 Credential webCredential(credential.user(), credential.password(), CredentialPersistenceNone); 504 RetainPtr<CFURLCredentialRef> cfCredential(AdoptCF, createCF(webCredential)); 505 506 KURL urlToStore; 507 if (challenge.failureResponse().httpStatusCode() == 401) 508 urlToStore = d->m_request.url(); 509 CredentialStorage::set(webCredential, challenge.protectionSpace(), urlToStore); 510 511 CFURLConnectionUseCredential(d->m_connection.get(), cfCredential.get(), challenge.cfURLAuthChallengeRef()); 512 } else { 513 RetainPtr<CFURLCredentialRef> cfCredential(AdoptCF, createCF(credential)); 514 CFURLConnectionUseCredential(d->m_connection.get(), cfCredential.get(), challenge.cfURLAuthChallengeRef()); 515 } 516 517 clearAuthentication(); 518} 519 520void ResourceHandle::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge& challenge) 521{ 522 LOG(Network, "CFNet - receivedRequestToContinueWithoutCredential()"); 523 ASSERT(!challenge.isNull()); 524 ASSERT(challenge.cfURLAuthChallengeRef()); 525 if (challenge != d->m_currentWebChallenge) 526 return; 527 528 CFURLConnectionUseCredential(d->m_connection.get(), 0, challenge.cfURLAuthChallengeRef()); 529 530 clearAuthentication(); 531} 532 533void ResourceHandle::receivedCancellation(const AuthenticationChallenge& challenge) 534{ 535 LOG(Network, "CFNet - receivedCancellation()"); 536 if (challenge != d->m_currentWebChallenge) 537 return; 538 539 if (client()) 540 client()->receivedCancellation(this, challenge); 541} 542 543CFURLConnectionRef ResourceHandle::connection() const 544{ 545 return d->m_connection.get(); 546} 547 548CFURLConnectionRef ResourceHandle::releaseConnectionForDownload() 549{ 550 LOG(Network, "CFNet - Job %p releasing connection %p for download", this, d->m_connection.get()); 551 return d->m_connection.releaseRef(); 552} 553 554void ResourceHandle::loadResourceSynchronously(const ResourceRequest& request, StoredCredentials storedCredentials, ResourceError& error, ResourceResponse& response, Vector<char>& vector, Frame*) 555{ 556 ASSERT(!request.isEmpty()); 557 558 RetainPtr<CFDataRef> data = WebCoreSynchronousLoader::load(request, storedCredentials, response, error); 559 560 if (!error.isNull()) { 561 response = ResourceResponse(request.url(), String(), 0, String(), String()); 562 563 CFErrorRef cfError = error; 564 CFStringRef domain = CFErrorGetDomain(cfError); 565 // FIXME: Return the actual response for failed authentication. 566 if (domain == kCFErrorDomainCFNetwork) 567 response.setHTTPStatusCode(CFErrorGetCode(cfError)); 568 else 569 response.setHTTPStatusCode(404); 570 } 571 572 if (data) { 573 ASSERT(vector.isEmpty()); 574 vector.append(CFDataGetBytePtr(data.get()), CFDataGetLength(data.get())); 575 } 576} 577 578void ResourceHandle::setHostAllowsAnyHTTPSCertificate(const String& host) 579{ 580 allowsAnyHTTPSCertificateHosts().add(host.lower()); 581} 582 583void ResourceHandle::setClientCertificate(const String& host, CFDataRef cert) 584{ 585 clientCerts().set(host.lower(), cert); 586} 587 588void ResourceHandle::setDefersLoading(bool defers) 589{ 590 if (!d->m_connection) 591 return; 592 593 if (defers) 594 CFURLConnectionHalt(d->m_connection.get()); 595 else 596 CFURLConnectionResume(d->m_connection.get()); 597} 598 599bool ResourceHandle::loadsBlocked() 600{ 601 return false; 602} 603 604bool ResourceHandle::willLoadFromCache(ResourceRequest& request, Frame* frame) 605{ 606 request.setCachePolicy(ReturnCacheDataDontLoad); 607 608 CFURLResponseRef cfResponse = 0; 609 CFErrorRef cfError = 0; 610 RetainPtr<CFURLRequestRef> cfRequest(AdoptCF, makeFinalRequest(request, true)); 611 RetainPtr<CFDataRef> data(AdoptCF, CFURLConnectionSendSynchronousRequest(cfRequest.get(), &cfResponse, &cfError, request.timeoutInterval())); 612 bool cached = cfResponse && !cfError; 613 614 if (cfError) 615 CFRelease(cfError); 616 if (cfResponse) 617 CFRelease(cfResponse); 618 619 return cached; 620} 621 622CFURLRequestRef WebCoreSynchronousLoader::willSendRequest(CFURLConnectionRef, CFURLRequestRef cfRequest, CFURLResponseRef cfRedirectResponse, const void* clientInfo) 623{ 624 WebCoreSynchronousLoader* loader = static_cast<WebCoreSynchronousLoader*>(const_cast<void*>(clientInfo)); 625 626 // FIXME: This needs to be fixed to follow the redirect correctly even for cross-domain requests. 627 if (loader->m_url && !protocolHostAndPortAreEqual(loader->m_url.get(), CFURLRequestGetURL(cfRequest))) { 628 RetainPtr<CFErrorRef> cfError(AdoptCF, CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainCFNetwork, kCFURLErrorBadServerResponse, 0)); 629 loader->m_error = cfError.get(); 630 loader->m_isDone = true; 631 return 0; 632 } 633 634 loader->m_url = CFURLRequestGetURL(cfRequest); 635 636 if (cfRedirectResponse) { 637 // Take user/pass out of the URL. 638 loader->m_user.adoptCF(CFURLCopyUserName(loader->m_url.get())); 639 loader->m_pass.adoptCF(CFURLCopyPassword(loader->m_url.get())); 640 if (loader->m_user || loader->m_pass) { 641 ResourceRequest requestWithoutCredentials = cfRequest; 642 requestWithoutCredentials.removeCredentials(); 643 cfRequest = requestWithoutCredentials.cfURLRequest(); 644 } 645 } 646 647 CFRetain(cfRequest); 648 return cfRequest; 649} 650 651void WebCoreSynchronousLoader::didReceiveResponse(CFURLConnectionRef, CFURLResponseRef cfResponse, const void* clientInfo) 652{ 653 WebCoreSynchronousLoader* loader = static_cast<WebCoreSynchronousLoader*>(const_cast<void*>(clientInfo)); 654 655 loader->m_response = cfResponse; 656} 657 658void WebCoreSynchronousLoader::didReceiveData(CFURLConnectionRef, CFDataRef data, CFIndex originalLength, const void* clientInfo) 659{ 660 WebCoreSynchronousLoader* loader = static_cast<WebCoreSynchronousLoader*>(const_cast<void*>(clientInfo)); 661 662 if (!loader->m_data) 663 loader->m_data.adoptCF(CFDataCreateMutable(kCFAllocatorDefault, 0)); 664 665 const UInt8* bytes = CFDataGetBytePtr(data); 666 CFIndex length = CFDataGetLength(data); 667 668 CFDataAppendBytes(loader->m_data.get(), bytes, length); 669} 670 671void WebCoreSynchronousLoader::didFinishLoading(CFURLConnectionRef, const void* clientInfo) 672{ 673 WebCoreSynchronousLoader* loader = static_cast<WebCoreSynchronousLoader*>(const_cast<void*>(clientInfo)); 674 675 loader->m_isDone = true; 676} 677 678void WebCoreSynchronousLoader::didFail(CFURLConnectionRef, CFErrorRef error, const void* clientInfo) 679{ 680 WebCoreSynchronousLoader* loader = static_cast<WebCoreSynchronousLoader*>(const_cast<void*>(clientInfo)); 681 682 loader->m_error = error; 683 loader->m_isDone = true; 684} 685 686void WebCoreSynchronousLoader::didReceiveChallenge(CFURLConnectionRef conn, CFURLAuthChallengeRef challenge, const void* clientInfo) 687{ 688 WebCoreSynchronousLoader* loader = static_cast<WebCoreSynchronousLoader*>(const_cast<void*>(clientInfo)); 689 690 if (loader->m_user && loader->m_pass) { 691 Credential credential(loader->m_user.get(), loader->m_pass.get(), CredentialPersistenceNone); 692 RetainPtr<CFURLCredentialRef> cfCredential(AdoptCF, createCF(credential)); 693 694 CFURLResponseRef urlResponse = (CFURLResponseRef)CFURLAuthChallengeGetFailureResponse(challenge); 695 CFHTTPMessageRef httpResponse = urlResponse ? CFURLResponseGetHTTPResponse(urlResponse) : 0; 696 KURL urlToStore; 697 if (httpResponse && CFHTTPMessageGetResponseStatusCode(httpResponse) == 401) 698 urlToStore = loader->m_url.get(); 699 700 CredentialStorage::set(credential, core(CFURLAuthChallengeGetProtectionSpace(challenge)), urlToStore); 701 702 CFURLConnectionUseCredential(conn, cfCredential.get(), challenge); 703 loader->m_user = 0; 704 loader->m_pass = 0; 705 return; 706 } 707 if (!CFURLAuthChallengeGetPreviousFailureCount(challenge) && loader->m_allowStoredCredentials) { 708 Credential credential = CredentialStorage::get(core(CFURLAuthChallengeGetProtectionSpace(challenge))); 709 if (!credential.isEmpty() && credential != loader->m_initialCredential) { 710 ASSERT(credential.persistence() == CredentialPersistenceNone); 711 RetainPtr<CFURLCredentialRef> cfCredential(AdoptCF, createCF(credential)); 712 CFURLConnectionUseCredential(conn, cfCredential.get(), challenge); 713 return; 714 } 715 } 716 // FIXME: The user should be asked for credentials, as in async case. 717 CFURLConnectionUseCredential(conn, 0, challenge); 718} 719 720Boolean WebCoreSynchronousLoader::shouldUseCredentialStorage(CFURLConnectionRef, const void* clientInfo) 721{ 722 WebCoreSynchronousLoader* loader = static_cast<WebCoreSynchronousLoader*>(const_cast<void*>(clientInfo)); 723 724 // FIXME: We should ask FrameLoaderClient whether using credential storage is globally forbidden. 725 return loader->m_allowStoredCredentials; 726} 727 728RetainPtr<CFDataRef> WebCoreSynchronousLoader::load(const ResourceRequest& request, StoredCredentials storedCredentials, ResourceResponse& response, ResourceError& error) 729{ 730 ASSERT(response.isNull()); 731 ASSERT(error.isNull()); 732 733 WebCoreSynchronousLoader loader(response, error); 734 735 KURL url = request.url(); 736 737 if (url.user().length()) 738 loader.m_user.adoptCF(url.user().createCFString()); 739 if (url.pass().length()) 740 loader.m_pass.adoptCF(url.pass().createCFString()); 741 loader.m_allowStoredCredentials = (storedCredentials == AllowStoredCredentials); 742 743 // Take user/pass out of the URL. 744 // Credentials for ftp can only be passed in URL, the didReceiveAuthenticationChallenge delegate call won't be made. 745 RetainPtr<CFURLRequestRef> cfRequest; 746 if ((loader.m_user || loader.m_pass) && url.protocolInHTTPFamily()) { 747 ResourceRequest requestWithoutCredentials(request); 748 requestWithoutCredentials.removeCredentials(); 749 cfRequest.adoptCF(makeFinalRequest(requestWithoutCredentials, ResourceHandle::shouldContentSniffURL(requestWithoutCredentials.url()))); 750 } else { 751 // <rdar://problem/7174050> - For URLs that match the paths of those previously challenged for HTTP Basic authentication, 752 // try and reuse the credential preemptively, as allowed by RFC 2617. 753 ResourceRequest requestWithInitialCredential(request); 754 if (loader.m_allowStoredCredentials && url.protocolInHTTPFamily()) 755 loader.m_initialCredential = CredentialStorage::get(url); 756 757 if (!loader.m_initialCredential.isEmpty()) { 758 String authHeader = "Basic " + encodeBasicAuthorization(loader.m_initialCredential.user(), loader.m_initialCredential.password()); 759 requestWithInitialCredential.addHTTPHeaderField("Authorization", authHeader); 760 } 761 762 cfRequest.adoptCF(makeFinalRequest(requestWithInitialCredential, ResourceHandle::shouldContentSniffURL(requestWithInitialCredential.url()))); 763 } 764 765 CFURLConnectionClient_V3 client = { 3, &loader, 0, 0, 0, willSendRequest, didReceiveResponse, didReceiveData, 0, didFinishLoading, didFail, 0, didReceiveChallenge, 0, shouldUseCredentialStorage, 0 }; 766 RetainPtr<CFURLConnectionRef> connection(AdoptCF, CFURLConnectionCreate(kCFAllocatorDefault, cfRequest.get(), reinterpret_cast<CFURLConnectionClient*>(&client))); 767 768 CFURLConnectionScheduleWithRunLoop(connection.get(), CFRunLoopGetCurrent(), WebCoreSynchronousLoaderRunLoopMode); 769 CFURLConnectionScheduleDownloadWithRunLoop(connection.get(), CFRunLoopGetCurrent(), WebCoreSynchronousLoaderRunLoopMode); 770 CFURLConnectionStart(connection.get()); 771 772 while (!loader.m_isDone) 773 CFRunLoopRunInMode(WebCoreSynchronousLoaderRunLoopMode, UINT_MAX, true); 774 775 CFURLConnectionCancel(connection.get()); 776 777 if (error.isNull() && loader.m_response.mimeType().isNull()) 778 setDefaultMIMEType(loader.m_response.cfURLResponse()); 779 780 return loader.m_data; 781} 782 783} // namespace WebCore 784