2 * Copyright (C) 2004, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #import "ResourceHandleInternal.h"
31 #import "AuthenticationChallenge.h"
32 #import "AuthenticationMac.h"
34 #import "BlobRegistry.h"
35 #import "BlockExceptions.h"
36 #import "CookieStorage.h"
37 #import "CredentialStorage.h"
38 #import "CachedResourceLoader.h"
39 #import "EmptyProtocolDefinitions.h"
40 #import "FormDataStreamMac.h"
42 #import "FrameLoader.h"
44 #import "MIMETypeRegistry.h"
46 #import "ResourceError.h"
47 #import "ResourceResponse.h"
48 #import "SchedulePair.h"
50 #import "SharedBuffer.h"
51 #import "SubresourceLoader.h"
52 #import "WebCoreSystemInterface.h"
53 #import "WebCoreURLResponse.h"
54 #import <wtf/text/CString.h>
55 #import <wtf/UnusedParam.h>
57 #ifdef BUILDING_ON_TIGER
58 typedef int NSInteger;
61 using namespace WebCore;
63 @interface WebCoreResourceHandleAsDelegate : NSObject <NSURLConnectionDelegate> {
64 ResourceHandle* m_handle;
66 - (id)initWithHandle:(ResourceHandle*)handle;
70 // WebCoreNSURLConnectionDelegateProxy exists so that we can cast m_proxy to it in order
71 // to disambiguate the argument type in the -setDelegate: call. This avoids a spurious
72 // warning that the compiler would otherwise emit.
73 @interface WebCoreNSURLConnectionDelegateProxy : NSObject <NSURLConnectionDelegate>
74 - (void)setDelegate:(id<NSURLConnectionDelegate>)delegate;
77 @interface NSURLConnection (NSURLConnectionTigerPrivate)
78 - (NSData *)_bufferedData;
81 @interface NSURLConnection (Details)
82 -(id)_initWithRequest:(NSURLRequest *)request delegate:(id)delegate usesCache:(BOOL)usesCacheFlag maxContentLength:(long long)maxContentLength startImmediately:(BOOL)startImmediately connectionProperties:(NSDictionary *)connectionProperties;
85 @interface NSURLRequest (Details)
86 - (id)_propertyForKey:(NSString *)key;
89 #ifndef BUILDING_ON_TIGER
91 class WebCoreSynchronousLoaderClient : public ResourceHandleClient {
93 static PassOwnPtr<WebCoreSynchronousLoaderClient> create()
95 return adoptPtr(new WebCoreSynchronousLoaderClient);
98 virtual ~WebCoreSynchronousLoaderClient();
100 void setAllowStoredCredentials(bool allow) { m_allowStoredCredentials = allow; }
101 NSURLResponse *response() { return m_response; }
102 NSMutableData *data() { return m_data; }
103 NSError *error() { return m_error; }
104 bool isDone() { return m_isDone; }
107 WebCoreSynchronousLoaderClient()
108 : m_allowStoredCredentials(false)
116 virtual void willSendRequest(ResourceHandle*, ResourceRequest&, const ResourceResponse& /*redirectResponse*/);
117 virtual bool shouldUseCredentialStorage(ResourceHandle*);
118 virtual void didReceiveAuthenticationChallenge(ResourceHandle*, const AuthenticationChallenge&);
119 virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&);
120 virtual void didReceiveData(ResourceHandle*, const char*, int, int /*lengthReceived*/);
121 virtual void didFinishLoading(ResourceHandle*, double /*finishTime*/);
122 virtual void didFail(ResourceHandle*, const ResourceError&);
123 #if USE(PROTECTION_SPACE_AUTH_CALLBACK)
124 virtual bool canAuthenticateAgainstProtectionSpace(ResourceHandle*, const ProtectionSpace&);
127 bool m_allowStoredCredentials;
128 NSURLResponse *m_response;
129 NSMutableData *m_data;
134 static NSString *WebCoreSynchronousLoaderRunLoopMode = @"WebCoreSynchronousLoaderRunLoopMode";
140 #ifdef BUILDING_ON_TIGER
141 static unsigned inNSURLConnectionCallback;
145 static bool isInitializingConnection;
148 class CallbackGuard {
152 #ifdef BUILDING_ON_TIGER
153 ++inNSURLConnectionCallback;
158 #ifdef BUILDING_ON_TIGER
159 ASSERT(inNSURLConnectionCallback > 0);
160 --inNSURLConnectionCallback;
165 #ifndef BUILDING_ON_TIGER
166 static String encodeBasicAuthorization(const String& user, const String& password)
168 return base64Encode((user + ":" + password).utf8());
172 ResourceHandleInternal::~ResourceHandleInternal()
176 ResourceHandle::~ResourceHandle()
179 d->m_currentWebChallenge.setAuthenticationClient(0);
181 LOG(Network, "Handle %p destroyed", this);
184 static const double MaxFoundationVersionWithoutdidSendBodyDataDelegate = 677.21;
185 bool ResourceHandle::didSendBodyDataDelegateExists()
187 return NSFoundationVersionNumber > MaxFoundationVersionWithoutdidSendBodyDataDelegate;
190 static bool shouldRelaxThirdPartyCookiePolicy(const KURL& url)
192 // If a URL already has cookies, then we'll relax the 3rd party cookie policy and accept new cookies.
194 NSHTTPCookieStorage *sharedStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
196 NSHTTPCookieAcceptPolicy cookieAcceptPolicy;
197 #if USE(CFURLSTORAGESESSIONS)
198 CFHTTPCookieStorageRef cfPrivateBrowsingStorage = privateBrowsingCookieStorage().get();
199 if (cfPrivateBrowsingStorage)
200 cookieAcceptPolicy = wkGetHTTPCookieAcceptPolicy(cfPrivateBrowsingStorage);
203 cookieAcceptPolicy = [sharedStorage cookieAcceptPolicy];
205 if (cookieAcceptPolicy != NSHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain)
209 #if USE(CFURLSTORAGESESSIONS)
210 if (cfPrivateBrowsingStorage)
211 cookies = wkHTTPCookiesForURL(cfPrivateBrowsingStorage, url);
214 cookies = [sharedStorage cookiesForURL:url];
216 return [cookies count];
219 void ResourceHandle::createNSURLConnection(id delegate, bool shouldUseCredentialStorage, bool shouldContentSniff)
221 // Credentials for ftp can only be passed in URL, the connection:didReceiveAuthenticationChallenge: delegate call won't be made.
222 if ((!d->m_user.isEmpty() || !d->m_pass.isEmpty())
223 #ifndef BUILDING_ON_TIGER
224 && !firstRequest().url().protocolInHTTPFamily() // On Tiger, always pass credentials in URL, so that they get stored even if the request gets cancelled right away.
227 KURL urlWithCredentials(firstRequest().url());
228 urlWithCredentials.setUser(d->m_user);
229 urlWithCredentials.setPass(d->m_pass);
230 firstRequest().setURL(urlWithCredentials);
233 if (shouldRelaxThirdPartyCookiePolicy(firstRequest().url()))
234 firstRequest().setFirstPartyForCookies(firstRequest().url());
236 #if !defined(BUILDING_ON_TIGER)
237 if (shouldUseCredentialStorage && firstRequest().url().protocolInHTTPFamily()) {
238 if (d->m_user.isEmpty() && d->m_pass.isEmpty()) {
239 // <rdar://problem/7174050> - For URLs that match the paths of those previously challenged for HTTP Basic authentication,
240 // try and reuse the credential preemptively, as allowed by RFC 2617.
241 d->m_initialCredential = CredentialStorage::get(firstRequest().url());
243 // If there is already a protection space known for the URL, update stored credentials before sending a request.
244 // This makes it possible to implement logout by sending an XMLHttpRequest with known incorrect credentials, and aborting it immediately
245 // (so that an authentication dialog doesn't pop up).
246 CredentialStorage::set(Credential(d->m_user, d->m_pass, CredentialPersistenceNone), firstRequest().url());
250 if (!d->m_initialCredential.isEmpty()) {
251 // FIXME: Support Digest authentication, and Proxy-Authorization.
252 String authHeader = "Basic " + encodeBasicAuthorization(d->m_initialCredential.user(), d->m_initialCredential.password());
253 firstRequest().addHTTPHeaderField("Authorization", authHeader);
256 NSURLRequest *nsRequest = firstRequest().nsURLRequest();
257 if (!shouldContentSniff) {
258 NSMutableURLRequest *mutableRequest = [[nsRequest copy] autorelease];
259 wkSetNSURLRequestShouldContentSniff(mutableRequest, NO);
260 nsRequest = mutableRequest;
263 #if !defined(BUILDING_ON_LEOPARD)
264 ASSERT([NSURLConnection instancesRespondToSelector:@selector(_initWithRequest:delegate:usesCache:maxContentLength:startImmediately:connectionProperties:)]);
265 static bool supportsSettingConnectionProperties = true;
267 static bool supportsSettingConnectionProperties = [NSURLConnection instancesRespondToSelector:@selector(_initWithRequest:delegate:usesCache:maxContentLength:startImmediately:connectionProperties:)];
270 #if USE(CFURLSTORAGESESSIONS)
271 if (CFURLStorageSessionRef storageSession = privateBrowsingStorageSession())
272 nsRequest = [wkCopyRequestWithStorageSession(storageSession, nsRequest) autorelease];
275 if (supportsSettingConnectionProperties) {
276 NSDictionary *sessionID = shouldUseCredentialStorage ? [NSDictionary dictionary] : [NSDictionary dictionaryWithObject:@"WebKitPrivateSession" forKey:@"_kCFURLConnectionSessionID"];
277 NSDictionary *propertyDictionary = [NSDictionary dictionaryWithObject:sessionID forKey:@"kCFURLConnectionSocketStreamProperties"];
278 d->m_connection.adoptNS([[NSURLConnection alloc] _initWithRequest:nsRequest delegate:delegate usesCache:YES maxContentLength:0 startImmediately:NO connectionProperties:propertyDictionary]);
282 d->m_connection.adoptNS([[NSURLConnection alloc] initWithRequest:nsRequest delegate:delegate startImmediately:NO]);
286 // Building on Tiger. Don't use WebCore credential storage, don't try to disable content sniffing.
287 UNUSED_PARAM(shouldUseCredentialStorage);
288 UNUSED_PARAM(shouldContentSniff);
289 d->m_connection.adoptNS([[NSURLConnection alloc] initWithRequest:firstRequest().nsURLRequest() delegate:delegate]);
293 bool ResourceHandle::start(NetworkingContext* context)
298 BEGIN_BLOCK_OBJC_EXCEPTIONS;
300 // If NetworkingContext is invalid then we are no longer attached to a Page,
301 // this must be an attempted load from an unload event handler, so let's just block it.
302 if (!context->isValid())
306 isInitializingConnection = YES;
310 d->m_proxy.adoptNS(wkCreateNSURLConnectionDelegateProxy());
311 [static_cast<WebCoreNSURLConnectionDelegateProxy*>(d->m_proxy.get()) setDelegate:ResourceHandle::delegate()];
313 bool shouldUseCredentialStorage = !client() || client()->shouldUseCredentialStorage(this);
315 if (!ResourceHandle::didSendBodyDataDelegateExists())
316 associateStreamWithResourceHandle([firstRequest().nsURLRequest() HTTPBodyStream], this);
318 #ifdef BUILDING_ON_TIGER
319 // A conditional request sent by WebCore (e.g. to update appcache) can be for a resource that is not cacheable by NSURLConnection,
320 // which can get confused and fail to load it in this case.
321 if (firstRequest().isConditional())
322 firstRequest().setCachePolicy(ReloadIgnoringCacheData);
325 d->m_needsSiteSpecificQuirks = context->needsSiteSpecificQuirks();
327 createNSURLConnection(
329 shouldUseCredentialStorage,
330 d->m_shouldContentSniff || context->localFileContentSniffingEnabled());
332 #ifndef BUILDING_ON_TIGER
333 bool scheduled = false;
334 if (SchedulePairHashSet* scheduledPairs = context->scheduledRunLoopPairs()) {
335 SchedulePairHashSet::iterator end = scheduledPairs->end();
336 for (SchedulePairHashSet::iterator it = scheduledPairs->begin(); it != end; ++it) {
337 if (NSRunLoop *runLoop = (*it)->nsRunLoop()) {
338 [connection() scheduleInRunLoop:runLoop forMode:(NSString *)(*it)->mode()];
344 // Start the connection if we did schedule with at least one runloop.
345 // We can't start the connection until we have one runloop scheduled.
347 [connection() start];
349 d->m_startWhenScheduled = true;
353 isInitializingConnection = NO;
356 LOG(Network, "Handle %p starting connection %p for %@", this, connection(), firstRequest().nsURLRequest());
358 if (d->m_connection) {
359 if (d->m_defersLoading)
360 wkSetNSURLConnectionDefersCallbacks(connection(), YES);
365 END_BLOCK_OBJC_EXCEPTIONS;
370 void ResourceHandle::cancel()
372 LOG(Network, "Handle %p cancel connection %p", this, d->m_connection.get());
374 // Leaks were seen on HTTP tests without this; can be removed once <rdar://problem/6886937> is fixed.
375 if (d->m_currentMacChallenge)
376 [[d->m_currentMacChallenge sender] cancelAuthenticationChallenge:d->m_currentMacChallenge];
378 if (!ResourceHandle::didSendBodyDataDelegateExists())
379 disassociateStreamWithResourceHandle([firstRequest().nsURLRequest() HTTPBodyStream]);
380 [d->m_connection.get() cancel];
383 void ResourceHandle::platformSetDefersLoading(bool defers)
386 wkSetNSURLConnectionDefersCallbacks(d->m_connection.get(), defers);
389 void ResourceHandle::schedule(SchedulePair* pair)
391 #ifndef BUILDING_ON_TIGER
392 NSRunLoop *runLoop = pair->nsRunLoop();
395 [d->m_connection.get() scheduleInRunLoop:runLoop forMode:(NSString *)pair->mode()];
396 if (d->m_startWhenScheduled) {
397 [d->m_connection.get() start];
398 d->m_startWhenScheduled = false;
405 void ResourceHandle::unschedule(SchedulePair* pair)
407 #ifndef BUILDING_ON_TIGER
408 if (NSRunLoop *runLoop = pair->nsRunLoop())
409 [d->m_connection.get() unscheduleFromRunLoop:runLoop forMode:(NSString *)pair->mode()];
415 WebCoreResourceHandleAsDelegate *ResourceHandle::delegate()
417 if (!d->m_delegate) {
418 WebCoreResourceHandleAsDelegate *delegate = [[WebCoreResourceHandleAsDelegate alloc] initWithHandle:this];
419 d->m_delegate = delegate;
422 return d->m_delegate.get();
425 void ResourceHandle::releaseDelegate()
430 [d->m_proxy.get() setDelegate:nil];
431 [d->m_delegate.get() detachHandle];
435 bool ResourceHandle::supportsBufferedData()
437 static bool supportsBufferedData = [NSURLConnection instancesRespondToSelector:@selector(_bufferedData)];
438 return supportsBufferedData;
441 PassRefPtr<SharedBuffer> ResourceHandle::bufferedData()
443 if (ResourceHandle::supportsBufferedData())
444 return SharedBuffer::wrapNSData([d->m_connection.get() _bufferedData]);
449 id ResourceHandle::releaseProxy()
451 id proxy = [[d->m_proxy.get() retain] autorelease];
453 [proxy setDelegate:nil];
457 NSURLConnection *ResourceHandle::connection() const
459 return d->m_connection.get();
462 bool ResourceHandle::loadsBlocked()
464 #ifndef BUILDING_ON_TIGER
467 // On Tiger, if we're in an NSURLConnection callback, that blocks all other NSURLConnection callbacks.
468 // On Leopard and newer, it blocks only callbacks on that same NSURLConnection object, which is not
469 // a problem in practice.
470 return inNSURLConnectionCallback != 0;
474 bool ResourceHandle::willLoadFromCache(ResourceRequest& request, Frame*)
476 #ifndef BUILDING_ON_TIGER
477 request.setCachePolicy(ReturnCacheDataDontLoad);
478 NSURLResponse *nsURLResponse = nil;
479 BEGIN_BLOCK_OBJC_EXCEPTIONS;
481 [NSURLConnection sendSynchronousRequest:request.nsURLRequest() returningResponse:&nsURLResponse error:nil];
483 END_BLOCK_OBJC_EXCEPTIONS;
485 return nsURLResponse;
487 // <rdar://problem/6803217> - Re-enable after <rdar://problem/6786454> is resolved.
488 UNUSED_PARAM(request);
493 void ResourceHandle::loadResourceSynchronously(NetworkingContext* context, const ResourceRequest& request, StoredCredentials storedCredentials, ResourceError& error, ResourceResponse& response, Vector<char>& data)
495 LOG(Network, "ResourceHandle::loadResourceSynchronously:%@ allowStoredCredentials:%u", request.nsURLRequest(), storedCredentials);
498 if (request.url().protocolIs("blob"))
499 if (blobRegistry().loadResourceSynchronously(request, error, response, data))
503 NSError *nsError = nil;
504 NSURLResponse *nsURLResponse = nil;
505 NSData *result = nil;
507 ASSERT(!request.isEmpty());
509 #ifndef BUILDING_ON_TIGER
510 OwnPtr<WebCoreSynchronousLoaderClient> client = WebCoreSynchronousLoaderClient::create();
511 client->setAllowStoredCredentials(storedCredentials == AllowStoredCredentials);
513 RefPtr<ResourceHandle> handle = adoptRef(new ResourceHandle(request, client.get(), false /*defersLoading*/, true /*shouldContentSniff*/));
515 if (context && handle->d->m_scheduledFailureType != NoFailure) {
516 error = context->blockedError(request);
520 handle->createNSURLConnection(
521 handle->delegate(), // A synchronous request cannot turn into a download, so there is no need to proxy the delegate.
522 storedCredentials == AllowStoredCredentials,
523 handle->shouldContentSniff() || (context && context->localFileContentSniffingEnabled()));
525 [handle->connection() scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:WebCoreSynchronousLoaderRunLoopMode];
526 [handle->connection() start];
528 while (!client->isDone())
529 [[NSRunLoop currentRunLoop] runMode:WebCoreSynchronousLoaderRunLoopMode beforeDate:[NSDate distantFuture]];
531 result = client->data();
532 nsURLResponse = client->response();
533 nsError = client->error();
535 [handle->connection() cancel];
538 UNUSED_PARAM(storedCredentials);
539 UNUSED_PARAM(context);
540 NSURLRequest *firstRequest = request.nsURLRequest();
542 if (shouldRelaxThirdPartyCookiePolicy([firstRequest URL])) {
543 NSMutableURLRequest *mutableRequest = [[firstRequest mutableCopy] autorelease];
544 [mutableRequest setMainDocumentURL:[mutableRequest URL]];
545 firstRequest = mutableRequest;
548 BEGIN_BLOCK_OBJC_EXCEPTIONS;
549 result = [NSURLConnection sendSynchronousRequest:firstRequest returningResponse:&nsURLResponse error:&nsError];
550 END_BLOCK_OBJC_EXCEPTIONS;
554 response = nsURLResponse;
556 response = ResourceResponse(request.url(), String(), 0, String(), String());
557 if ([nsError domain] == NSURLErrorDomain)
558 switch ([nsError code]) {
559 case NSURLErrorUserCancelledAuthentication:
560 // FIXME: we should really return the actual HTTP response, but sendSynchronousRequest doesn't provide us with one.
561 response.setHTTPStatusCode(401);
564 response.setHTTPStatusCode([nsError code]);
567 response.setHTTPStatusCode(404);
570 data.resize([result length]);
571 memcpy(data.data(), [result bytes], [result length]);
576 void ResourceHandle::willSendRequest(ResourceRequest& request, const ResourceResponse& redirectResponse)
578 const KURL& url = request.url();
579 d->m_user = url.user();
580 d->m_pass = url.pass();
581 d->m_lastHTTPMethod = request.httpMethod();
582 request.removeCredentials();
583 if (!protocolHostAndPortAreEqual(request.url(), redirectResponse.url()))
584 request.clearHTTPAuthorization();
586 #if USE(CFURLSTORAGESESSIONS)
587 if (CFURLStorageSessionRef storageSession = privateBrowsingStorageSession())
588 request.setStorageSession(storageSession);
591 client()->willSendRequest(this, request, redirectResponse);
594 bool ResourceHandle::shouldUseCredentialStorage()
597 return client()->shouldUseCredentialStorage(this);
602 void ResourceHandle::didReceiveAuthenticationChallenge(const AuthenticationChallenge& challenge)
604 ASSERT(!d->m_currentMacChallenge);
605 ASSERT(d->m_currentWebChallenge.isNull());
606 // Since NSURLConnection networking relies on keeping a reference to the original NSURLAuthenticationChallenge,
607 // we make sure that is actually present
608 ASSERT(challenge.nsURLAuthenticationChallenge());
610 if (!d->m_user.isNull() && !d->m_pass.isNull()) {
611 NSURLCredential *credential = [[NSURLCredential alloc] initWithUser:d->m_user
613 persistence:NSURLCredentialPersistenceForSession];
614 d->m_currentMacChallenge = challenge.nsURLAuthenticationChallenge();
615 d->m_currentWebChallenge = challenge;
616 receivedCredential(challenge, core(credential));
617 [credential release];
618 // FIXME: Per the specification, the user shouldn't be asked for credentials if there were incorrect ones provided explicitly.
619 d->m_user = String();
620 d->m_pass = String();
624 #ifndef BUILDING_ON_TIGER
625 if (!client() || client()->shouldUseCredentialStorage(this)) {
626 if (!d->m_initialCredential.isEmpty() || challenge.previousFailureCount()) {
627 // The stored credential wasn't accepted, stop using it.
628 // There is a race condition here, since a different credential might have already been stored by another ResourceHandle,
629 // but the observable effect should be very minor, if any.
630 CredentialStorage::remove(challenge.protectionSpace());
633 if (!challenge.previousFailureCount()) {
634 Credential credential = CredentialStorage::get(challenge.protectionSpace());
635 if (!credential.isEmpty() && credential != d->m_initialCredential) {
636 ASSERT(credential.persistence() == CredentialPersistenceNone);
637 if (challenge.failureResponse().httpStatusCode() == 401) {
638 // Store the credential back, possibly adding it as a default for this directory.
639 CredentialStorage::set(credential, challenge.protectionSpace(), firstRequest().url());
641 [challenge.sender() useCredential:mac(credential) forAuthenticationChallenge:mac(challenge)];
648 d->m_currentMacChallenge = challenge.nsURLAuthenticationChallenge();
649 d->m_currentWebChallenge = core(d->m_currentMacChallenge);
650 d->m_currentWebChallenge.setAuthenticationClient(this);
653 client()->didReceiveAuthenticationChallenge(this, d->m_currentWebChallenge);
656 void ResourceHandle::didCancelAuthenticationChallenge(const AuthenticationChallenge& challenge)
658 ASSERT(d->m_currentMacChallenge);
659 ASSERT(d->m_currentMacChallenge == challenge.nsURLAuthenticationChallenge());
660 ASSERT(!d->m_currentWebChallenge.isNull());
663 client()->didCancelAuthenticationChallenge(this, challenge);
666 #if USE(PROTECTION_SPACE_AUTH_CALLBACK)
667 bool ResourceHandle::canAuthenticateAgainstProtectionSpace(const ProtectionSpace& protectionSpace)
670 return client()->canAuthenticateAgainstProtectionSpace(this, protectionSpace);
676 void ResourceHandle::receivedCredential(const AuthenticationChallenge& challenge, const Credential& credential)
678 ASSERT(!challenge.isNull());
679 if (challenge != d->m_currentWebChallenge)
682 // FIXME: Support empty credentials. Currently, an empty credential cannot be stored in WebCore credential storage, as that's empty value for its map.
683 if (credential.isEmpty()) {
684 receivedRequestToContinueWithoutCredential(challenge);
688 #ifdef BUILDING_ON_TIGER
689 if (credential.persistence() == CredentialPersistenceNone) {
690 // NSURLCredentialPersistenceNone doesn't work on Tiger, so we have to use session persistence.
691 Credential webCredential(credential.user(), credential.password(), CredentialPersistenceForSession);
692 [[d->m_currentMacChallenge sender] useCredential:mac(webCredential) forAuthenticationChallenge:d->m_currentMacChallenge];
695 if (credential.persistence() == CredentialPersistenceForSession && (!d->m_needsSiteSpecificQuirks || ![[[mac(challenge) protectionSpace] host] isEqualToString:@"gallery.me.com"])) {
696 // Manage per-session credentials internally, because once NSURLCredentialPersistenceForSession is used, there is no way
697 // to ignore it for a particular request (short of removing it altogether).
698 // <rdar://problem/6867598> gallery.me.com is temporarily whitelisted, so that QuickTime plug-in could see the credentials.
699 Credential webCredential(credential, CredentialPersistenceNone);
701 if (challenge.failureResponse().httpStatusCode() == 401)
702 urlToStore = firstRequest().url();
703 CredentialStorage::set(webCredential, core([d->m_currentMacChallenge protectionSpace]), urlToStore);
704 [[d->m_currentMacChallenge sender] useCredential:mac(webCredential) forAuthenticationChallenge:d->m_currentMacChallenge];
707 [[d->m_currentMacChallenge sender] useCredential:mac(credential) forAuthenticationChallenge:d->m_currentMacChallenge];
709 clearAuthentication();
712 void ResourceHandle::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge& challenge)
714 ASSERT(!challenge.isNull());
715 if (challenge != d->m_currentWebChallenge)
718 [[d->m_currentMacChallenge sender] continueWithoutCredentialForAuthenticationChallenge:d->m_currentMacChallenge];
720 clearAuthentication();
723 void ResourceHandle::receivedCancellation(const AuthenticationChallenge& challenge)
725 if (challenge != d->m_currentWebChallenge)
729 client()->receivedCancellation(this, challenge);
732 #if USE(CFURLSTORAGESESSIONS)
734 RetainPtr<CFURLStorageSessionRef> ResourceHandle::createPrivateBrowsingStorageSession(CFStringRef identifier)
736 return RetainPtr<CFURLStorageSessionRef>(AdoptCF, wkCreatePrivateStorageSession(identifier));
739 String ResourceHandle::privateBrowsingStorageSessionIdentifierDefaultBase()
741 return String([[NSBundle mainBundle] bundleIdentifier]);
746 } // namespace WebCore
748 @implementation WebCoreResourceHandleAsDelegate
750 - (id)initWithHandle:(ResourceHandle*)handle
764 - (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)newRequest redirectResponse:(NSURLResponse *)redirectResponse
766 UNUSED_PARAM(connection);
768 // the willSendRequest call may cancel this load, in which case self could be deallocated
769 RetainPtr<WebCoreResourceHandleAsDelegate> protect(self);
771 if (!m_handle || !m_handle->client())
774 // See <rdar://problem/5380697> . This is a workaround for a behavior change in CFNetwork where willSendRequest gets called more often.
775 if (!redirectResponse)
779 if ([redirectResponse isKindOfClass:[NSHTTPURLResponse class]])
780 LOG(Network, "Handle %p delegate connection:%p willSendRequest:%@ redirectResponse:%d, Location:<%@>", m_handle, connection, [newRequest description], static_cast<int>([(id)redirectResponse statusCode]), [[(id)redirectResponse allHeaderFields] objectForKey:@"Location"]);
782 LOG(Network, "Handle %p delegate connection:%p willSendRequest:%@ redirectResponse:non-HTTP", m_handle, connection, [newRequest description]);
785 if ([redirectResponse isKindOfClass:[NSHTTPURLResponse class]] && [(NSHTTPURLResponse *)redirectResponse statusCode] == 307) {
786 String lastHTTPMethod = m_handle->lastHTTPMethod();
787 if (!equalIgnoringCase(lastHTTPMethod, String([newRequest HTTPMethod]))) {
788 NSMutableURLRequest *mutableRequest = [newRequest mutableCopy];
789 [mutableRequest setHTTPMethod:lastHTTPMethod];
791 FormData* body = m_handle->firstRequest().httpBody();
792 if (!equalIgnoringCase(lastHTTPMethod, "GET") && body && !body->isEmpty())
793 WebCore::setHTTPBody(mutableRequest, body);
795 String originalContentType = m_handle->firstRequest().httpContentType();
796 if (!originalContentType.isEmpty())
797 [mutableRequest setValue:originalContentType forHTTPHeaderField:@"Content-Type"];
799 newRequest = [mutableRequest autorelease];
804 ResourceRequest request = newRequest;
806 // Should not set Referer after a redirect from a secure resource to non-secure one.
807 if (!request.url().protocolIs("https") && protocolIs(request.httpReferrer(), "https"))
808 request.clearHTTPReferrer();
810 m_handle->willSendRequest(request, redirectResponse);
812 if (!ResourceHandle::didSendBodyDataDelegateExists()) {
813 // The client may change the request's body stream, in which case we have to re-associate
814 // the handle with the new stream so upload progress callbacks continue to work correctly.
815 NSInputStream* oldBodyStream = [newRequest HTTPBodyStream];
816 NSInputStream* newBodyStream = [request.nsURLRequest() HTTPBodyStream];
817 if (oldBodyStream != newBodyStream) {
818 disassociateStreamWithResourceHandle(oldBodyStream);
819 associateStreamWithResourceHandle(newBodyStream, m_handle);
823 return request.nsURLRequest();
826 - (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection
828 UNUSED_PARAM(connection);
830 LOG(Network, "Handle %p delegate connectionShouldUseCredentialStorage:%p", m_handle, connection);
836 return m_handle->shouldUseCredentialStorage();
839 - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
841 UNUSED_PARAM(connection);
843 LOG(Network, "Handle %p delegate connection:%p didReceiveAuthenticationChallenge:%p", m_handle, connection, challenge);
848 m_handle->didReceiveAuthenticationChallenge(core(challenge));
851 - (void)connection:(NSURLConnection *)connection didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
853 UNUSED_PARAM(connection);
855 LOG(Network, "Handle %p delegate connection:%p didCancelAuthenticationChallenge:%p", m_handle, connection, challenge);
860 m_handle->didCancelAuthenticationChallenge(core(challenge));
863 #if USE(PROTECTION_SPACE_AUTH_CALLBACK)
864 - (BOOL)connection:(NSURLConnection *)unusedConnection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
866 UNUSED_PARAM(unusedConnection);
872 return m_handle->canAuthenticateAgainstProtectionSpace(core(protectionSpace));
876 - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)r
878 UNUSED_PARAM(connection);
880 LOG(Network, "Handle %p delegate connection:%p didReceiveResponse:%p (HTTP status %d, reported MIMEType '%s')", m_handle, connection, r, [r respondsToSelector:@selector(statusCode)] ? [(id)r statusCode] : 0, [[r MIMEType] UTF8String]);
882 if (!m_handle || !m_handle->client())
886 // Avoid MIME type sniffing if the response comes back as 304 Not Modified.
887 int statusCode = [r respondsToSelector:@selector(statusCode)] ? [(id)r statusCode] : 0;
888 if (statusCode != 304)
889 [r adjustMIMETypeIfNecessary];
891 if ([m_handle->firstRequest().nsURLRequest() _propertyForKey:@"ForceHTMLMIMEType"])
892 [r _setMIMEType:@"text/html"];
895 const KURL& url = [r URL];
896 if (url.isLocalFile()) {
897 // FIXME: Workaround for <rdar://problem/6917571>: The WML file extension ".wml" is not mapped to
898 // the right MIME type, work around that CFNetwork problem, to unbreak WML support for local files.
899 const String& path = url.path();
901 DEFINE_STATIC_LOCAL(const String, wmlExt, (".wml"));
902 if (path.endsWith(wmlExt, false)) {
903 static NSString* defaultMIMETypeString = [(NSString*) defaultMIMEType() retain];
904 if ([[r MIMEType] isEqualToString:defaultMIMETypeString])
905 [r _setMIMEType:@"text/vnd.wap.wml"];
910 m_handle->client()->didReceiveResponse(m_handle, r);
913 - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data lengthReceived:(long long)lengthReceived
915 UNUSED_PARAM(connection);
917 LOG(Network, "Handle %p delegate connection:%p didReceiveData:%p lengthReceived:%lld", m_handle, connection, data, lengthReceived);
919 if (!m_handle || !m_handle->client())
921 // FIXME: If we get more than 2B bytes in a single chunk, this code won't do the right thing.
922 // However, with today's computers and networking speeds, this won't happen in practice.
923 // Could be an issue with a giant local file.
925 m_handle->client()->didReceiveData(m_handle, (const char*)[data bytes], [data length], static_cast<int>(lengthReceived));
928 - (void)connection:(NSURLConnection *)connection willStopBufferingData:(NSData *)data
930 UNUSED_PARAM(connection);
932 LOG(Network, "Handle %p delegate connection:%p willStopBufferingData:%p", m_handle, connection, data);
934 if (!m_handle || !m_handle->client())
936 // FIXME: If we get a resource with more than 2B bytes, this code won't do the right thing.
937 // However, with today's computers and networking speeds, this won't happen in practice.
938 // Could be an issue with a giant local file.
940 m_handle->client()->willStopBufferingData(m_handle, (const char*)[data bytes], static_cast<int>([data length]));
943 - (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite
945 UNUSED_PARAM(connection);
946 UNUSED_PARAM(bytesWritten);
948 LOG(Network, "Handle %p delegate connection:%p didSendBodyData:%d totalBytesWritten:%d totalBytesExpectedToWrite:%d", m_handle, connection, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
950 if (!m_handle || !m_handle->client())
953 m_handle->client()->didSendData(m_handle, totalBytesWritten, totalBytesExpectedToWrite);
956 - (void)connectionDidFinishLoading:(NSURLConnection *)connection
958 UNUSED_PARAM(connection);
960 LOG(Network, "Handle %p delegate connectionDidFinishLoading:%p", m_handle, connection);
962 if (!m_handle || !m_handle->client())
966 if (!ResourceHandle::didSendBodyDataDelegateExists())
967 disassociateStreamWithResourceHandle([m_handle->firstRequest().nsURLRequest() HTTPBodyStream]);
969 m_handle->client()->didFinishLoading(m_handle, 0);
972 - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
974 UNUSED_PARAM(connection);
976 LOG(Network, "Handle %p delegate connection:%p didFailWithError:%@", m_handle, connection, error);
978 if (!m_handle || !m_handle->client())
982 if (!ResourceHandle::didSendBodyDataDelegateExists())
983 disassociateStreamWithResourceHandle([m_handle->firstRequest().nsURLRequest() HTTPBodyStream]);
985 m_handle->client()->didFail(m_handle, error);
988 #ifdef BUILDING_ON_TIGER
989 - (void)_callConnectionWillCacheResponseWithInfo:(NSMutableDictionary *)info
991 NSURLConnection *connection = [info objectForKey:@"connection"];
992 NSCachedURLResponse *cachedResponse = [info objectForKey:@"cachedResponse"];
993 NSCachedURLResponse *result = [self connection:connection willCacheResponse:cachedResponse];
995 [info setObject:result forKey:@"result"];
999 - (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse
1001 LOG(Network, "Handle %p delegate connection:%p willCacheResponse:%p", m_handle, connection, cachedResponse);
1003 #ifdef BUILDING_ON_TIGER
1004 // On Tiger CFURLConnection can sometimes call the connection:willCacheResponse: delegate method on
1005 // a secondary thread instead of the main thread. If this happens perform the work on the main thread.
1006 if (!pthread_main_np()) {
1007 NSMutableDictionary *info = [[NSMutableDictionary alloc] init];
1009 [info setObject:connection forKey:@"connection"];
1011 [info setObject:cachedResponse forKey:@"cachedResponse"];
1013 // Include synchronous url connection's mode as an acceptable run loopmode
1014 // <rdar://problem/5511842>
1015 NSArray *modes = [[NSArray alloc] initWithObjects:(NSString *)kCFRunLoopCommonModes, @"NSSynchronousURLConnection_PrivateMode", nil];
1016 [self performSelectorOnMainThread:@selector(_callConnectionWillCacheResponseWithInfo:) withObject:info waitUntilDone:YES modes:modes];
1019 NSCachedURLResponse *result = [[info valueForKey:@"result"] retain];
1022 return [result autorelease];
1025 UNUSED_PARAM(connection);
1029 if (isInitializingConnection)
1030 LOG_ERROR("connection:willCacheResponse: was called inside of [NSURLConnection initWithRequest:delegate:] (4067625)");
1033 if (!m_handle || !m_handle->client())
1036 CallbackGuard guard;
1038 NSCachedURLResponse *newResponse = m_handle->client()->willCacheResponse(m_handle, cachedResponse);
1039 if (newResponse != cachedResponse)
1042 CacheStoragePolicy policy = static_cast<CacheStoragePolicy>([newResponse storagePolicy]);
1044 m_handle->client()->willCacheResponse(m_handle, policy);
1046 if (static_cast<NSURLCacheStoragePolicy>(policy) != [newResponse storagePolicy])
1047 newResponse = [[[NSCachedURLResponse alloc] initWithResponse:[newResponse response]
1048 data:[newResponse data]
1049 userInfo:[newResponse userInfo]
1050 storagePolicy:static_cast<NSURLCacheStoragePolicy>(policy)] autorelease];
1057 #ifndef BUILDING_ON_TIGER
1059 WebCoreSynchronousLoaderClient::~WebCoreSynchronousLoaderClient()
1061 [m_response release];
1066 void WebCoreSynchronousLoaderClient::willSendRequest(ResourceHandle* handle, ResourceRequest& request, const ResourceResponse& /*redirectResponse*/)
1068 // FIXME: This needs to be fixed to follow the redirect correctly even for cross-domain requests.
1069 if (!protocolHostAndPortAreEqual(handle->firstRequest().url(), request.url())) {
1071 m_error = [[NSError alloc] initWithDomain:NSURLErrorDomain code:NSURLErrorBadServerResponse userInfo:nil];
1078 bool WebCoreSynchronousLoaderClient::shouldUseCredentialStorage(ResourceHandle*)
1080 // FIXME: We should ask FrameLoaderClient whether using credential storage is globally forbidden.
1081 return m_allowStoredCredentials;
1084 #if USE(PROTECTION_SPACE_AUTH_CALLBACK)
1085 bool WebCoreSynchronousLoaderClient::canAuthenticateAgainstProtectionSpace(ResourceHandle*, const ProtectionSpace&)
1087 // FIXME: We should ask FrameLoaderClient.
1092 void WebCoreSynchronousLoaderClient::didReceiveAuthenticationChallenge(ResourceHandle*, const AuthenticationChallenge& challenge)
1094 // FIXME: The user should be asked for credentials, as in async case.
1095 [challenge.sender() continueWithoutCredentialForAuthenticationChallenge:challenge.nsURLAuthenticationChallenge()];
1098 void WebCoreSynchronousLoaderClient::didReceiveResponse(ResourceHandle*, const ResourceResponse& response)
1100 [m_response release];
1101 m_response = [response.nsURLResponse() copy];
1104 void WebCoreSynchronousLoaderClient::didReceiveData(ResourceHandle*, const char* data, int length, int /*lengthReceived*/)
1107 m_data = [[NSMutableData alloc] init];
1108 [m_data appendBytes:data length:length];
1111 void WebCoreSynchronousLoaderClient::didFinishLoading(ResourceHandle*, double)
1116 void WebCoreSynchronousLoaderClient::didFail(ResourceHandle*, const ResourceError& error)
1120 m_error = [error copy];
1124 #endif // BUILDING_ON_TIGER
1126 #endif // !USE(CFNETWORK)