1/* 2 * Copyright (C) 2004, 2006, 2007, 2008, 2009, 2010, 2011, 2012 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#import "config.h" 27#import "ResourceHandleInternal.h" 28 29#if !USE(CFNETWORK) 30 31#import "AuthenticationChallenge.h" 32#import "AuthenticationMac.h" 33#import "BlockExceptions.h" 34#import "CookieStorage.h" 35#import "CredentialStorage.h" 36#import "CachedResourceLoader.h" 37#import "EmptyProtocolDefinitions.h" 38#import "FormDataStreamMac.h" 39#import "Frame.h" 40#import "FrameLoader.h" 41#import "Logging.h" 42#import "MIMETypeRegistry.h" 43#import "NetworkingContext.h" 44#import "Page.h" 45#import "ResourceError.h" 46#import "ResourceResponse.h" 47#import "Settings.h" 48#import "SharedBuffer.h" 49#import "SubresourceLoader.h" 50#import "WebCoreResourceHandleAsDelegate.h" 51#import "WebCoreResourceHandleAsOperationQueueDelegate.h" 52#import "SynchronousLoaderClient.h" 53#import "WebCoreSystemInterface.h" 54#import "WebCoreURLResponse.h" 55#import <wtf/SchedulePair.h> 56#import <wtf/text/Base64.h> 57#import <wtf/text/CString.h> 58 59using namespace WebCore; 60 61@interface NSURLConnection (Details) 62-(id)_initWithRequest:(NSURLRequest *)request delegate:(id)delegate usesCache:(BOOL)usesCacheFlag maxContentLength:(long long)maxContentLength startImmediately:(BOOL)startImmediately connectionProperties:(NSDictionary *)connectionProperties; 63@end 64 65namespace WebCore { 66 67static void applyBasicAuthorizationHeader(ResourceRequest& request, const Credential& credential) 68{ 69 String authenticationHeader = "Basic " + base64Encode(String(credential.user() + ":" + credential.password()).utf8()); 70 request.clearHTTPAuthorization(); // FIXME: Should addHTTPHeaderField be smart enough to not build comma-separated lists in headers like Authorization? 71 request.addHTTPHeaderField("Authorization", authenticationHeader); 72} 73 74static NSOperationQueue *operationQueueForAsyncClients() 75{ 76 static NSOperationQueue *queue; 77 if (!queue) { 78 queue = [[NSOperationQueue alloc] init]; 79 // Default concurrent operation count depends on current system workload, but delegate methods are mostly idling in IPC, so we can run as many as needed. 80 [queue setMaxConcurrentOperationCount:NSIntegerMax]; 81 } 82 return queue; 83} 84 85ResourceHandleInternal::~ResourceHandleInternal() 86{ 87} 88 89ResourceHandle::~ResourceHandle() 90{ 91 releaseDelegate(); 92 d->m_currentWebChallenge.setAuthenticationClient(0); 93 94 LOG(Network, "Handle %p destroyed", this); 95} 96 97void ResourceHandle::createNSURLConnection(id delegate, bool shouldUseCredentialStorage, bool shouldContentSniff) 98{ 99 // Credentials for ftp can only be passed in URL, the connection:didReceiveAuthenticationChallenge: delegate call won't be made. 100 if ((!d->m_user.isEmpty() || !d->m_pass.isEmpty()) && !firstRequest().url().protocolIsInHTTPFamily()) { 101 KURL urlWithCredentials(firstRequest().url()); 102 urlWithCredentials.setUser(d->m_user); 103 urlWithCredentials.setPass(d->m_pass); 104 firstRequest().setURL(urlWithCredentials); 105 } 106 107 if (shouldUseCredentialStorage && firstRequest().url().protocolIsInHTTPFamily()) { 108 if (d->m_user.isEmpty() && d->m_pass.isEmpty()) { 109 // <rdar://problem/7174050> - For URLs that match the paths of those previously challenged for HTTP Basic authentication, 110 // try and reuse the credential preemptively, as allowed by RFC 2617. 111 d->m_initialCredential = CredentialStorage::get(firstRequest().url()); 112 } else { 113 // If there is already a protection space known for the URL, update stored credentials before sending a request. 114 // This makes it possible to implement logout by sending an XMLHttpRequest with known incorrect credentials, and aborting it immediately 115 // (so that an authentication dialog doesn't pop up). 116 CredentialStorage::set(Credential(d->m_user, d->m_pass, CredentialPersistenceNone), firstRequest().url()); 117 } 118 } 119 120 if (!d->m_initialCredential.isEmpty()) { 121 // FIXME: Support Digest authentication, and Proxy-Authorization. 122 applyBasicAuthorizationHeader(firstRequest(), d->m_initialCredential); 123 } 124 125 NSURLRequest *nsRequest = firstRequest().nsURLRequest(UpdateHTTPBody); 126 if (!shouldContentSniff) { 127 NSMutableURLRequest *mutableRequest = [[nsRequest mutableCopy] autorelease]; 128 wkSetNSURLRequestShouldContentSniff(mutableRequest, NO); 129 nsRequest = mutableRequest; 130 } 131 132 if (d->m_storageSession) 133 nsRequest = [wkCopyRequestWithStorageSession(d->m_storageSession.get(), nsRequest) autorelease]; 134 135 ASSERT([NSURLConnection instancesRespondToSelector:@selector(_initWithRequest:delegate:usesCache:maxContentLength:startImmediately:connectionProperties:)]); 136 137 NSMutableDictionary *streamProperties = [NSMutableDictionary dictionary]; 138 139 if (!shouldUseCredentialStorage) 140 [streamProperties setObject:@"WebKitPrivateSession" forKey:@"_kCFURLConnectionSessionID"]; 141 142#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090 143 RetainPtr<CFDataRef> sourceApplicationAuditData = d->m_context->sourceApplicationAuditData(); 144 if (sourceApplicationAuditData) 145 [streamProperties setObject:(NSData *)sourceApplicationAuditData.get() forKey:@"kCFStreamPropertySourceApplication"]; 146#endif 147 148 NSDictionary *propertyDictionary = [NSDictionary dictionaryWithObject:streamProperties forKey:@"kCFURLConnectionSocketStreamProperties"]; 149 d->m_connection = adoptNS([[NSURLConnection alloc] _initWithRequest:nsRequest delegate:delegate usesCache:YES maxContentLength:0 startImmediately:NO connectionProperties:propertyDictionary]); 150} 151 152bool ResourceHandle::start() 153{ 154 if (!d->m_context) 155 return false; 156 157 BEGIN_BLOCK_OBJC_EXCEPTIONS; 158 159 // If NetworkingContext is invalid then we are no longer attached to a Page, 160 // this must be an attempted load from an unload event handler, so let's just block it. 161 if (!d->m_context->isValid()) 162 return false; 163 164 d->m_storageSession = d->m_context->storageSession().platformSession(); 165 166 // FIXME: Do not use the sync version of shouldUseCredentialStorage when the client returns true from usesAsyncCallbacks. 167 bool shouldUseCredentialStorage = !client() || client()->shouldUseCredentialStorage(this); 168 169 d->m_needsSiteSpecificQuirks = d->m_context->needsSiteSpecificQuirks(); 170 171 createNSURLConnection( 172 ResourceHandle::delegate(), 173 shouldUseCredentialStorage, 174 d->m_shouldContentSniff || d->m_context->localFileContentSniffingEnabled()); 175 176 bool scheduled = false; 177 if (SchedulePairHashSet* scheduledPairs = d->m_context->scheduledRunLoopPairs()) { 178 SchedulePairHashSet::iterator end = scheduledPairs->end(); 179 for (SchedulePairHashSet::iterator it = scheduledPairs->begin(); it != end; ++it) { 180 if (NSRunLoop *runLoop = (*it)->nsRunLoop()) { 181 [connection() scheduleInRunLoop:runLoop forMode:(NSString *)(*it)->mode()]; 182 scheduled = true; 183 } 184 } 185 } 186 187 if (client() && client()->usesAsyncCallbacks()) { 188 ASSERT(!scheduled); 189 [connection() setDelegateQueue:operationQueueForAsyncClients()]; 190 scheduled = true; 191 } 192 193 // Start the connection if we did schedule with at least one runloop. 194 // We can't start the connection until we have one runloop scheduled. 195 if (scheduled) 196 [connection() start]; 197 else 198 d->m_startWhenScheduled = true; 199 200 LOG(Network, "Handle %p starting connection %p for %@", this, connection(), firstRequest().nsURLRequest(DoNotUpdateHTTPBody)); 201 202 if (d->m_connection) { 203 if (d->m_defersLoading) 204 wkSetNSURLConnectionDefersCallbacks(connection(), YES); 205 206 return true; 207 } 208 209 END_BLOCK_OBJC_EXCEPTIONS; 210 211 return false; 212} 213 214void ResourceHandle::cancel() 215{ 216 LOG(Network, "Handle %p cancel connection %p", this, d->m_connection.get()); 217 218 // Leaks were seen on HTTP tests without this; can be removed once <rdar://problem/6886937> is fixed. 219 if (d->m_currentMacChallenge) 220 [[d->m_currentMacChallenge sender] cancelAuthenticationChallenge:d->m_currentMacChallenge]; 221 222 [d->m_connection.get() cancel]; 223} 224 225void ResourceHandle::platformSetDefersLoading(bool defers) 226{ 227 if (d->m_connection) 228 wkSetNSURLConnectionDefersCallbacks(d->m_connection.get(), defers); 229} 230 231void ResourceHandle::schedule(SchedulePair* pair) 232{ 233 NSRunLoop *runLoop = pair->nsRunLoop(); 234 if (!runLoop) 235 return; 236 [d->m_connection.get() scheduleInRunLoop:runLoop forMode:(NSString *)pair->mode()]; 237 if (d->m_startWhenScheduled) { 238 [d->m_connection.get() start]; 239 d->m_startWhenScheduled = false; 240 } 241} 242 243void ResourceHandle::unschedule(SchedulePair* pair) 244{ 245 if (NSRunLoop *runLoop = pair->nsRunLoop()) 246 [d->m_connection.get() unscheduleFromRunLoop:runLoop forMode:(NSString *)pair->mode()]; 247} 248 249id ResourceHandle::delegate() 250{ 251 if (!d->m_delegate) { 252 id <NSURLConnectionDelegate> delegate = (client() && client()->usesAsyncCallbacks()) ? 253 [[WebCoreResourceHandleAsOperationQueueDelegate alloc] initWithHandle:this] 254 : [[WebCoreResourceHandleAsDelegate alloc] initWithHandle:this]; 255 d->m_delegate = delegate; 256 [delegate release]; 257 } 258 return d->m_delegate.get(); 259} 260 261void ResourceHandle::releaseDelegate() 262{ 263 if (!d->m_delegate) 264 return; 265 [d->m_delegate.get() detachHandle]; 266 d->m_delegate = nil; 267} 268 269NSURLConnection *ResourceHandle::connection() const 270{ 271 return d->m_connection.get(); 272} 273 274bool ResourceHandle::loadsBlocked() 275{ 276 return false; 277} 278 279CFStringRef ResourceHandle::synchronousLoadRunLoopMode() 280{ 281 return CFSTR("WebCoreSynchronousLoaderRunLoopMode"); 282} 283 284void ResourceHandle::platformLoadResourceSynchronously(NetworkingContext* context, const ResourceRequest& request, StoredCredentials storedCredentials, ResourceError& error, ResourceResponse& response, Vector<char>& data) 285{ 286 LOG(Network, "ResourceHandle::platformLoadResourceSynchronously:%@ allowStoredCredentials:%u", request.nsURLRequest(DoNotUpdateHTTPBody), storedCredentials); 287 288 ASSERT(!request.isEmpty()); 289 290 OwnPtr<SynchronousLoaderClient> client = SynchronousLoaderClient::create(); 291 client->setAllowStoredCredentials(storedCredentials == AllowStoredCredentials); 292 293 RefPtr<ResourceHandle> handle = adoptRef(new ResourceHandle(context, request, client.get(), false /*defersLoading*/, true /*shouldContentSniff*/)); 294 295 handle->d->m_storageSession = context->storageSession().platformSession(); 296 297 if (context && handle->d->m_scheduledFailureType != NoFailure) { 298 error = context->blockedError(request); 299 return; 300 } 301 302 handle->createNSURLConnection( 303 handle->delegate(), 304 storedCredentials == AllowStoredCredentials, 305 handle->shouldContentSniff() || context->localFileContentSniffingEnabled()); 306 307 [handle->connection() scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:(NSString *)synchronousLoadRunLoopMode()]; 308 [handle->connection() start]; 309 310 while (!client->isDone()) 311 [[NSRunLoop currentRunLoop] runMode:(NSString *)synchronousLoadRunLoopMode() beforeDate:[NSDate distantFuture]]; 312 313 error = client->error(); 314 315 [handle->connection() cancel]; 316 317 if (error.isNull()) 318 response = client->response(); 319 else { 320 // FIXME: We might not ever need to manufacture a response: This might all be dead code. 321 // When exploring removal of this code, we should substitute appropriate ASSERTs. 322 response = ResourceResponse(request.url(), String(), 0, String(), String()); 323 if (error.domain() == String(NSURLErrorDomain)) 324 switch (error.errorCode()) { 325 case NSURLErrorUserCancelledAuthentication: 326 // FIXME: We don't need to manufacture a 401 response if we say continueWithoutCredentialForAuthenticationChallenge: 327 // in which case we'll get the real failure response. A reading of SynchronousLoaderClient.mm suggests we already do this. 328 response.setHTTPStatusCode(401); 329 break; 330 default: 331 response.setHTTPStatusCode(error.errorCode()); 332 } 333 else { 334 // FIXME: This is wrong. We shouldn't need to ever make up a 404. 335 response.setHTTPStatusCode(404); 336 } 337 } 338 339 data.swap(client->mutableData()); 340} 341 342void ResourceHandle::willSendRequest(ResourceRequest& request, const ResourceResponse& redirectResponse) 343{ 344 ASSERT(!redirectResponse.isNull()); 345 346 if (redirectResponse.httpStatusCode() == 307) { 347 String lastHTTPMethod = d->m_lastHTTPMethod; 348 if (!equalIgnoringCase(lastHTTPMethod, request.httpMethod())) { 349 request.setHTTPMethod(lastHTTPMethod); 350 351 FormData* body = d->m_firstRequest.httpBody(); 352 if (!equalIgnoringCase(lastHTTPMethod, "GET") && body && !body->isEmpty()) 353 request.setHTTPBody(body); 354 355 String originalContentType = d->m_firstRequest.httpContentType(); 356 if (!originalContentType.isEmpty()) 357 request.setHTTPHeaderField("Content-Type", originalContentType); 358 } 359 } 360 361 // Should not set Referer after a redirect from a secure resource to non-secure one. 362 if (!request.url().protocolIs("https") && protocolIs(request.httpReferrer(), "https") && d->m_context->shouldClearReferrerOnHTTPSToHTTPRedirect()) 363 request.clearHTTPReferrer(); 364 365 const KURL& url = request.url(); 366 d->m_user = url.user(); 367 d->m_pass = url.pass(); 368 d->m_lastHTTPMethod = request.httpMethod(); 369 request.removeCredentials(); 370 371 if (!protocolHostAndPortAreEqual(request.url(), redirectResponse.url())) { 372 // If the network layer carries over authentication headers from the original request 373 // in a cross-origin redirect, we want to clear those headers here. 374 // As of Lion, CFNetwork no longer does this. 375 request.clearHTTPAuthorization(); 376 } else { 377 // Only consider applying authentication credentials if this is actually a redirect and the redirect 378 // URL didn't include credentials of its own. 379 if (d->m_user.isEmpty() && d->m_pass.isEmpty() && !redirectResponse.isNull()) { 380 Credential credential = CredentialStorage::get(request.url()); 381 if (!credential.isEmpty()) { 382 d->m_initialCredential = credential; 383 384 // FIXME: Support Digest authentication, and Proxy-Authorization. 385 applyBasicAuthorizationHeader(request, d->m_initialCredential); 386 } 387 } 388 } 389 390 if (client()->usesAsyncCallbacks()) { 391 client()->willSendRequestAsync(this, request, redirectResponse); 392 } else { 393 RefPtr<ResourceHandle> protect(this); 394 client()->willSendRequest(this, request, redirectResponse); 395 396 // Client call may not preserve the session, especially if the request is sent over IPC. 397 if (!request.isNull()) 398 request.setStorageSession(d->m_storageSession.get()); 399 } 400} 401 402void ResourceHandle::continueWillSendRequest(const ResourceRequest& request) 403{ 404 ASSERT(client()); 405 ASSERT(client()->usesAsyncCallbacks()); 406 407 // Client call may not preserve the session, especially if the request is sent over IPC. 408 ResourceRequest newRequest = request; 409 if (!newRequest.isNull()) 410 newRequest.setStorageSession(d->m_storageSession.get()); 411 [(id)delegate() continueWillSendRequest:newRequest.nsURLRequest(UpdateHTTPBody)]; 412} 413 414void ResourceHandle::continueDidReceiveResponse() 415{ 416 ASSERT(client()); 417 ASSERT(client()->usesAsyncCallbacks()); 418 419 [delegate() continueDidReceiveResponse]; 420} 421 422bool ResourceHandle::shouldUseCredentialStorage() 423{ 424 if (client()->usesAsyncCallbacks()) { 425 if (client()) 426 client()->shouldUseCredentialStorageAsync(this); 427 else 428 continueShouldUseCredentialStorage(false); 429 return false; // Ignored by caller. 430 } else 431 return client() && client()->shouldUseCredentialStorage(this); 432} 433 434void ResourceHandle::continueShouldUseCredentialStorage(bool useCredentialStorage) 435{ 436 ASSERT(client()); 437 ASSERT(client()->usesAsyncCallbacks()); 438 439 [(id)delegate() continueShouldUseCredentialStorage:useCredentialStorage]; 440} 441 442void ResourceHandle::didReceiveAuthenticationChallenge(const AuthenticationChallenge& challenge) 443{ 444 ASSERT(!d->m_currentMacChallenge); 445 ASSERT(d->m_currentWebChallenge.isNull()); 446 // Since NSURLConnection networking relies on keeping a reference to the original NSURLAuthenticationChallenge, 447 // we make sure that is actually present 448 ASSERT(challenge.nsURLAuthenticationChallenge()); 449 450 // Proxy authentication is handled by CFNetwork internally. We can get here if the user cancels 451 // CFNetwork authentication dialog, and we shouldn't ask the client to display another one in that case. 452 if (challenge.protectionSpace().isProxy()) { 453 // Cannot use receivedRequestToContinueWithoutCredential(), because current challenge is not yet set. 454 [challenge.sender() continueWithoutCredentialForAuthenticationChallenge:challenge.nsURLAuthenticationChallenge()]; 455 return; 456 } 457 458 if (!d->m_user.isNull() && !d->m_pass.isNull()) { 459 NSURLCredential *credential = [[NSURLCredential alloc] initWithUser:d->m_user 460 password:d->m_pass 461 persistence:NSURLCredentialPersistenceForSession]; 462 d->m_currentMacChallenge = challenge.nsURLAuthenticationChallenge(); 463 d->m_currentWebChallenge = challenge; 464 receivedCredential(challenge, core(credential)); 465 [credential release]; 466 // FIXME: Per the specification, the user shouldn't be asked for credentials if there were incorrect ones provided explicitly. 467 d->m_user = String(); 468 d->m_pass = String(); 469 return; 470 } 471 472 // FIXME: Do not use the sync version of shouldUseCredentialStorage when the client returns true from usesAsyncCallbacks. 473 if (!client() || client()->shouldUseCredentialStorage(this)) { 474 if (!d->m_initialCredential.isEmpty() || challenge.previousFailureCount()) { 475 // The stored credential wasn't accepted, stop using it. 476 // There is a race condition here, since a different credential might have already been stored by another ResourceHandle, 477 // but the observable effect should be very minor, if any. 478 CredentialStorage::remove(challenge.protectionSpace()); 479 } 480 481 if (!challenge.previousFailureCount()) { 482 Credential credential = CredentialStorage::get(challenge.protectionSpace()); 483 if (!credential.isEmpty() && credential != d->m_initialCredential) { 484 ASSERT(credential.persistence() == CredentialPersistenceNone); 485 if (challenge.failureResponse().httpStatusCode() == 401) { 486 // Store the credential back, possibly adding it as a default for this directory. 487 CredentialStorage::set(credential, challenge.protectionSpace(), challenge.failureResponse().url()); 488 } 489 [challenge.sender() useCredential:mac(credential) forAuthenticationChallenge:mac(challenge)]; 490 return; 491 } 492 } 493 } 494 495 d->m_currentMacChallenge = challenge.nsURLAuthenticationChallenge(); 496 d->m_currentWebChallenge = core(d->m_currentMacChallenge); 497 d->m_currentWebChallenge.setAuthenticationClient(this); 498 499 // FIXME: Several concurrent requests can return with the an authentication challenge for the same protection space. 500 // We should avoid making additional client calls for the same protection space when already waiting for the user, 501 // because typing the same credentials several times is annoying. 502 if (client()) 503 client()->didReceiveAuthenticationChallenge(this, d->m_currentWebChallenge); 504} 505 506void ResourceHandle::didCancelAuthenticationChallenge(const AuthenticationChallenge& challenge) 507{ 508 ASSERT(d->m_currentMacChallenge); 509 ASSERT(d->m_currentMacChallenge == challenge.nsURLAuthenticationChallenge()); 510 ASSERT(!d->m_currentWebChallenge.isNull()); 511 512 if (client()) 513 client()->didCancelAuthenticationChallenge(this, challenge); 514} 515 516#if USE(PROTECTION_SPACE_AUTH_CALLBACK) 517bool ResourceHandle::canAuthenticateAgainstProtectionSpace(const ProtectionSpace& protectionSpace) 518{ 519 if (client()->usesAsyncCallbacks()) { 520 if (client()) 521 client()->canAuthenticateAgainstProtectionSpaceAsync(this, protectionSpace); 522 else 523 continueCanAuthenticateAgainstProtectionSpace(false); 524 return false; // Ignored by caller. 525 } else 526 return client() && client()->canAuthenticateAgainstProtectionSpace(this, protectionSpace); 527} 528 529void ResourceHandle::continueCanAuthenticateAgainstProtectionSpace(bool result) 530{ 531 ASSERT(client()); 532 ASSERT(client()->usesAsyncCallbacks()); 533 534 [(id)delegate() continueCanAuthenticateAgainstProtectionSpace:result]; 535} 536#endif 537 538void ResourceHandle::receivedCredential(const AuthenticationChallenge& challenge, const Credential& credential) 539{ 540 LOG(Network, "Handle %p receivedCredential", this); 541 542 ASSERT(!challenge.isNull()); 543 if (challenge != d->m_currentWebChallenge) 544 return; 545 546 // FIXME: Support empty credentials. Currently, an empty credential cannot be stored in WebCore credential storage, as that's empty value for its map. 547 if (credential.isEmpty()) { 548 receivedRequestToContinueWithoutCredential(challenge); 549 return; 550 } 551 552 if (credential.persistence() == CredentialPersistenceForSession && (!d->m_needsSiteSpecificQuirks || ![[[mac(challenge) protectionSpace] host] isEqualToString:@"gallery.me.com"])) { 553 // Manage per-session credentials internally, because once NSURLCredentialPersistenceForSession is used, there is no way 554 // to ignore it for a particular request (short of removing it altogether). 555 // <rdar://problem/6867598> gallery.me.com is temporarily whitelisted, so that QuickTime plug-in could see the credentials. 556 Credential webCredential(credential, CredentialPersistenceNone); 557 KURL urlToStore; 558 if (challenge.failureResponse().httpStatusCode() == 401) 559 urlToStore = challenge.failureResponse().url(); 560 CredentialStorage::set(webCredential, core([d->m_currentMacChallenge protectionSpace]), urlToStore); 561 [[d->m_currentMacChallenge sender] useCredential:mac(webCredential) forAuthenticationChallenge:d->m_currentMacChallenge]; 562 } else 563 [[d->m_currentMacChallenge sender] useCredential:mac(credential) forAuthenticationChallenge:d->m_currentMacChallenge]; 564 565 clearAuthentication(); 566} 567 568void ResourceHandle::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge& challenge) 569{ 570 LOG(Network, "Handle %p receivedRequestToContinueWithoutCredential", this); 571 572 ASSERT(!challenge.isNull()); 573 if (challenge != d->m_currentWebChallenge) 574 return; 575 576 [[d->m_currentMacChallenge sender] continueWithoutCredentialForAuthenticationChallenge:d->m_currentMacChallenge]; 577 578 clearAuthentication(); 579} 580 581void ResourceHandle::receivedCancellation(const AuthenticationChallenge& challenge) 582{ 583 LOG(Network, "Handle %p receivedCancellation", this); 584 585 if (challenge != d->m_currentWebChallenge) 586 return; 587 588 if (client()) 589 client()->receivedCancellation(this, challenge); 590} 591 592void ResourceHandle::continueWillCacheResponse(NSCachedURLResponse *response) 593{ 594 ASSERT(client()); 595 ASSERT(client()->usesAsyncCallbacks()); 596 597 [(id)delegate() continueWillCacheResponse:response]; 598} 599 600 601} // namespace WebCore 602 603#endif // !USE(CFNETWORK) 604