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