2
* Copyright (C) 2004, 2006, 2007, 2008, 2009, 2010, 2011, 2012 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"
33
#import "BlobRegistry.h"
34
#import "BlockExceptions.h"
35
#import "CookieStorage.h"
36
#import "CookieStorageCFNet.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/UnusedParam.h>
55
#import <wtf/text/Base64.h>
56
#import <wtf/text/CString.h>
58
using namespace WebCore;
60
@interface WebCoreResourceHandleAsDelegate : NSObject <NSURLConnectionDelegate> {
61
ResourceHandle* m_handle;
63
- (id)initWithHandle:(ResourceHandle*)handle;
67
// WebCoreNSURLConnectionDelegateProxy exists so that we can cast m_proxy to it in order
68
// to disambiguate the argument type in the -setDelegate: call. This avoids a spurious
69
// warning that the compiler would otherwise emit.
70
@interface WebCoreNSURLConnectionDelegateProxy : NSObject <NSURLConnectionDelegate>
71
- (void)setDelegate:(id<NSURLConnectionDelegate>)delegate;
74
@interface NSURLConnection (Details)
75
-(id)_initWithRequest:(NSURLRequest *)request delegate:(id)delegate usesCache:(BOOL)usesCacheFlag maxContentLength:(long long)maxContentLength startImmediately:(BOOL)startImmediately connectionProperties:(NSDictionary *)connectionProperties;
78
@interface NSURLRequest (Details)
79
- (id)_propertyForKey:(NSString *)key;
82
class WebCoreSynchronousLoaderClient : public ResourceHandleClient {
84
static PassOwnPtr<WebCoreSynchronousLoaderClient> create()
86
return adoptPtr(new WebCoreSynchronousLoaderClient);
89
virtual ~WebCoreSynchronousLoaderClient();
91
void setAllowStoredCredentials(bool allow) { m_allowStoredCredentials = allow; }
92
NSURLResponse *response() { return m_response; }
93
NSMutableData *data() { return m_data; }
94
NSError *error() { return m_error; }
95
bool isDone() { return m_isDone; }
98
WebCoreSynchronousLoaderClient()
99
: m_allowStoredCredentials(false)
107
virtual void willSendRequest(ResourceHandle*, ResourceRequest&, const ResourceResponse& /*redirectResponse*/);
108
virtual bool shouldUseCredentialStorage(ResourceHandle*);
109
virtual void didReceiveAuthenticationChallenge(ResourceHandle*, const AuthenticationChallenge&);
110
virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&);
111
virtual void didReceiveData(ResourceHandle*, const char*, int, int /*encodedDataLength*/);
112
virtual void didFinishLoading(ResourceHandle*, double /*finishTime*/);
113
virtual void didFail(ResourceHandle*, const ResourceError&);
114
#if USE(PROTECTION_SPACE_AUTH_CALLBACK)
115
virtual bool canAuthenticateAgainstProtectionSpace(ResourceHandle*, const ProtectionSpace&);
118
bool m_allowStoredCredentials;
119
NSURLResponse *m_response;
120
NSMutableData *m_data;
127
static void applyBasicAuthorizationHeader(ResourceRequest& request, const Credential& credential)
129
String authenticationHeader = "Basic " + base64Encode(String(credential.user() + ":" + credential.password()).utf8());
130
request.clearHTTPAuthorization(); // FIXME: Should addHTTPHeaderField be smart enough to not build comma-separated lists in headers like Authorization?
131
request.addHTTPHeaderField("Authorization", authenticationHeader);
134
ResourceHandleInternal::~ResourceHandleInternal()
138
ResourceHandle::~ResourceHandle()
141
d->m_currentWebChallenge.setAuthenticationClient(0);
143
LOG(Network, "Handle %p destroyed", this);
146
static bool shouldRelaxThirdPartyCookiePolicy(NetworkingContext* context, const KURL& url)
148
// If a URL already has cookies, then we'll relax the 3rd party cookie policy and accept new cookies.
150
RetainPtr<CFHTTPCookieStorageRef> cfCookieStorage = currentCFHTTPCookieStorage(context);
151
NSHTTPCookieAcceptPolicy cookieAcceptPolicy = static_cast<NSHTTPCookieAcceptPolicy>(wkGetHTTPCookieAcceptPolicy(cfCookieStorage.get()));
153
if (cookieAcceptPolicy != NSHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain)
156
NSArray *cookies = wkHTTPCookiesForURL(cfCookieStorage.get(), url);
158
return [cookies count];
161
void ResourceHandle::createNSURLConnection(id delegate, bool shouldUseCredentialStorage, bool shouldRelaxThirdPartyCookiePolicy, bool shouldContentSniff)
163
// Credentials for ftp can only be passed in URL, the connection:didReceiveAuthenticationChallenge: delegate call won't be made.
164
if ((!d->m_user.isEmpty() || !d->m_pass.isEmpty()) && !firstRequest().url().protocolIsInHTTPFamily()) {
165
KURL urlWithCredentials(firstRequest().url());
166
urlWithCredentials.setUser(d->m_user);
167
urlWithCredentials.setPass(d->m_pass);
168
firstRequest().setURL(urlWithCredentials);
171
if (shouldRelaxThirdPartyCookiePolicy)
172
firstRequest().setFirstPartyForCookies(firstRequest().url());
174
if (shouldUseCredentialStorage && firstRequest().url().protocolIsInHTTPFamily()) {
175
if (d->m_user.isEmpty() && d->m_pass.isEmpty()) {
176
// <rdar://problem/7174050> - For URLs that match the paths of those previously challenged for HTTP Basic authentication,
177
// try and reuse the credential preemptively, as allowed by RFC 2617.
178
d->m_initialCredential = CredentialStorage::get(firstRequest().url());
180
// If there is already a protection space known for the URL, update stored credentials before sending a request.
181
// This makes it possible to implement logout by sending an XMLHttpRequest with known incorrect credentials, and aborting it immediately
182
// (so that an authentication dialog doesn't pop up).
183
CredentialStorage::set(Credential(d->m_user, d->m_pass, CredentialPersistenceNone), firstRequest().url());
187
if (!d->m_initialCredential.isEmpty()) {
188
// FIXME: Support Digest authentication, and Proxy-Authorization.
189
applyBasicAuthorizationHeader(firstRequest(), d->m_initialCredential);
192
NSURLRequest *nsRequest = firstRequest().nsURLRequest();
193
if (!shouldContentSniff) {
194
NSMutableURLRequest *mutableRequest = [[nsRequest mutableCopy] autorelease];
195
wkSetNSURLRequestShouldContentSniff(mutableRequest, NO);
196
nsRequest = mutableRequest;
199
#if PLATFORM(IOS) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
200
ASSERT([NSURLConnection instancesRespondToSelector:@selector(_initWithRequest:delegate:usesCache:maxContentLength:startImmediately:connectionProperties:)]);
201
static bool supportsSettingConnectionProperties = true;
203
static bool supportsSettingConnectionProperties = [NSURLConnection instancesRespondToSelector:@selector(_initWithRequest:delegate:usesCache:maxContentLength:startImmediately:connectionProperties:)];
206
if (d->m_storageSession)
207
nsRequest = [wkCopyRequestWithStorageSession(d->m_storageSession.get(), nsRequest) autorelease];
209
if (supportsSettingConnectionProperties) {
210
NSDictionary *sessionID = shouldUseCredentialStorage ? [NSDictionary dictionary] : [NSDictionary dictionaryWithObject:@"WebKitPrivateSession" forKey:@"_kCFURLConnectionSessionID"];
211
NSDictionary *propertyDictionary = [NSDictionary dictionaryWithObject:sessionID forKey:@"kCFURLConnectionSocketStreamProperties"];
212
d->m_connection.adoptNS([[NSURLConnection alloc] _initWithRequest:nsRequest delegate:delegate usesCache:YES maxContentLength:0 startImmediately:NO connectionProperties:propertyDictionary]);
216
d->m_connection.adoptNS([[NSURLConnection alloc] initWithRequest:nsRequest delegate:delegate startImmediately:NO]);
221
bool ResourceHandle::start(NetworkingContext* context)
226
BEGIN_BLOCK_OBJC_EXCEPTIONS;
228
// If NetworkingContext is invalid then we are no longer attached to a Page,
229
// this must be an attempted load from an unload event handler, so let's just block it.
230
if (!context->isValid())
233
d->m_storageSession = context->storageSession();
236
d->m_proxy.adoptNS(wkCreateNSURLConnectionDelegateProxy());
237
[static_cast<WebCoreNSURLConnectionDelegateProxy*>(d->m_proxy.get()) setDelegate:ResourceHandle::delegate()];
239
bool shouldUseCredentialStorage = !client() || client()->shouldUseCredentialStorage(this);
241
d->m_needsSiteSpecificQuirks = context->needsSiteSpecificQuirks();
243
createNSURLConnection(
245
shouldUseCredentialStorage,
246
shouldRelaxThirdPartyCookiePolicy(context, firstRequest().url()),
247
d->m_shouldContentSniff || context->localFileContentSniffingEnabled());
249
bool scheduled = false;
250
if (SchedulePairHashSet* scheduledPairs = context->scheduledRunLoopPairs()) {
251
SchedulePairHashSet::iterator end = scheduledPairs->end();
252
for (SchedulePairHashSet::iterator it = scheduledPairs->begin(); it != end; ++it) {
253
if (NSRunLoop *runLoop = (*it)->nsRunLoop()) {
254
[connection() scheduleInRunLoop:runLoop forMode:(NSString *)(*it)->mode()];
260
if (NSOperationQueue *operationQueue = context->scheduledOperationQueue()) {
262
[connection() setDelegateQueue:operationQueue];
266
// Start the connection if we did schedule with at least one runloop.
267
// We can't start the connection until we have one runloop scheduled.
269
[connection() start];
271
d->m_startWhenScheduled = true;
273
LOG(Network, "Handle %p starting connection %p for %@", this, connection(), firstRequest().nsURLRequest());
275
if (d->m_connection) {
276
if (d->m_defersLoading)
277
wkSetNSURLConnectionDefersCallbacks(connection(), YES);
282
END_BLOCK_OBJC_EXCEPTIONS;
287
void ResourceHandle::cancel()
289
LOG(Network, "Handle %p cancel connection %p", this, d->m_connection.get());
291
// Leaks were seen on HTTP tests without this; can be removed once <rdar://problem/6886937> is fixed.
292
if (d->m_currentMacChallenge)
293
[[d->m_currentMacChallenge sender] cancelAuthenticationChallenge:d->m_currentMacChallenge];
295
[d->m_connection.get() cancel];
298
void ResourceHandle::platformSetDefersLoading(bool defers)
301
wkSetNSURLConnectionDefersCallbacks(d->m_connection.get(), defers);
304
void ResourceHandle::schedule(SchedulePair* pair)
306
NSRunLoop *runLoop = pair->nsRunLoop();
309
[d->m_connection.get() scheduleInRunLoop:runLoop forMode:(NSString *)pair->mode()];
310
if (d->m_startWhenScheduled) {
311
[d->m_connection.get() start];
312
d->m_startWhenScheduled = false;
316
void ResourceHandle::unschedule(SchedulePair* pair)
318
if (NSRunLoop *runLoop = pair->nsRunLoop())
319
[d->m_connection.get() unscheduleFromRunLoop:runLoop forMode:(NSString *)pair->mode()];
322
WebCoreResourceHandleAsDelegate *ResourceHandle::delegate()
324
if (!d->m_delegate) {
325
WebCoreResourceHandleAsDelegate *delegate = [[WebCoreResourceHandleAsDelegate alloc] initWithHandle:this];
326
d->m_delegate = delegate;
329
return d->m_delegate.get();
332
void ResourceHandle::releaseDelegate()
337
[d->m_proxy.get() setDelegate:nil];
338
[d->m_delegate.get() detachHandle];
342
id ResourceHandle::releaseProxy()
344
id proxy = [[d->m_proxy.get() retain] autorelease];
346
[proxy setDelegate:nil];
350
NSURLConnection *ResourceHandle::connection() const
352
return d->m_connection.get();
355
bool ResourceHandle::loadsBlocked()
360
bool ResourceHandle::willLoadFromCache(ResourceRequest& request, Frame*)
362
request.setCachePolicy(ReturnCacheDataDontLoad);
363
NSURLResponse *nsURLResponse = nil;
364
BEGIN_BLOCK_OBJC_EXCEPTIONS;
366
[NSURLConnection sendSynchronousRequest:request.nsURLRequest() returningResponse:&nsURLResponse error:nil];
368
END_BLOCK_OBJC_EXCEPTIONS;
370
return nsURLResponse;
373
CFStringRef ResourceHandle::synchronousLoadRunLoopMode()
375
return CFSTR("WebCoreSynchronousLoaderRunLoopMode");
378
void ResourceHandle::loadResourceSynchronously(NetworkingContext* context, const ResourceRequest& request, StoredCredentials storedCredentials, ResourceError& error, ResourceResponse& response, Vector<char>& data)
380
LOG(Network, "ResourceHandle::loadResourceSynchronously:%@ allowStoredCredentials:%u", request.nsURLRequest(), storedCredentials);
383
if (request.url().protocolIs("blob"))
384
if (blobRegistry().loadResourceSynchronously(request, error, response, data))
388
NSError *nsError = nil;
389
NSURLResponse *nsURLResponse = nil;
390
NSData *result = nil;
392
ASSERT(!request.isEmpty());
394
OwnPtr<WebCoreSynchronousLoaderClient> client = WebCoreSynchronousLoaderClient::create();
395
client->setAllowStoredCredentials(storedCredentials == AllowStoredCredentials);
397
RefPtr<ResourceHandle> handle = adoptRef(new ResourceHandle(request, client.get(), false /*defersLoading*/, true /*shouldContentSniff*/));
399
handle->d->m_storageSession = context->storageSession();
401
if (context && handle->d->m_scheduledFailureType != NoFailure) {
402
error = context->blockedError(request);
406
handle->createNSURLConnection(
407
handle->delegate(), // A synchronous request cannot turn into a download, so there is no need to proxy the delegate.
408
storedCredentials == AllowStoredCredentials,
409
shouldRelaxThirdPartyCookiePolicy(context, request.url()),
410
handle->shouldContentSniff() || context->localFileContentSniffingEnabled());
412
[handle->connection() scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:(NSString *)synchronousLoadRunLoopMode()];
413
[handle->connection() start];
415
while (!client->isDone())
416
[[NSRunLoop currentRunLoop] runMode:(NSString *)synchronousLoadRunLoopMode() beforeDate:[NSDate distantFuture]];
418
result = client->data();
419
nsURLResponse = client->response();
420
nsError = client->error();
422
[handle->connection() cancel];
426
response = nsURLResponse;
428
response = ResourceResponse(request.url(), String(), 0, String(), String());
429
if ([nsError domain] == NSURLErrorDomain)
430
switch ([nsError code]) {
431
case NSURLErrorUserCancelledAuthentication:
432
// FIXME: we should really return the actual HTTP response, but sendSynchronousRequest doesn't provide us with one.
433
response.setHTTPStatusCode(401);
436
response.setHTTPStatusCode([nsError code]);
439
response.setHTTPStatusCode(404);
442
data.resize([result length]);
443
memcpy(data.data(), [result bytes], [result length]);
448
void ResourceHandle::willSendRequest(ResourceRequest& request, const ResourceResponse& redirectResponse)
450
const KURL& url = request.url();
451
d->m_user = url.user();
452
d->m_pass = url.pass();
453
d->m_lastHTTPMethod = request.httpMethod();
454
request.removeCredentials();
456
if (!protocolHostAndPortAreEqual(request.url(), redirectResponse.url())) {
457
// If the network layer carries over authentication headers from the original request
458
// in a cross-origin redirect, we want to clear those headers here.
459
// As of Lion, CFNetwork no longer does this.
460
request.clearHTTPAuthorization();
462
// Only consider applying authentication credentials if this is actually a redirect and the redirect
463
// URL didn't include credentials of its own.
464
if (d->m_user.isEmpty() && d->m_pass.isEmpty() && !redirectResponse.isNull()) {
465
Credential credential = CredentialStorage::get(request.url());
466
if (!credential.isEmpty()) {
467
d->m_initialCredential = credential;
469
// FIXME: Support Digest authentication, and Proxy-Authorization.
470
applyBasicAuthorizationHeader(request, d->m_initialCredential);
475
if (d->m_storageSession)
476
request.setStorageSession(d->m_storageSession.get());
478
client()->willSendRequest(this, request, redirectResponse);
481
bool ResourceHandle::shouldUseCredentialStorage()
484
return client()->shouldUseCredentialStorage(this);
489
void ResourceHandle::didReceiveAuthenticationChallenge(const AuthenticationChallenge& challenge)
491
ASSERT(!d->m_currentMacChallenge);
492
ASSERT(d->m_currentWebChallenge.isNull());
493
// Since NSURLConnection networking relies on keeping a reference to the original NSURLAuthenticationChallenge,
494
// we make sure that is actually present
495
ASSERT(challenge.nsURLAuthenticationChallenge());
497
#if PLATFORM(IOS) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
498
// Proxy authentication is handled by CFNetwork internally. We can get here if the user cancels
499
// CFNetwork authentication dialog, and we shouldn't ask the client to display another one in that case.
500
if (challenge.protectionSpace().isProxy()) {
501
// Cannot use receivedRequestToContinueWithoutCredential(), because current challenge is not yet set.
502
[challenge.sender() continueWithoutCredentialForAuthenticationChallenge:challenge.nsURLAuthenticationChallenge()];
507
if (!d->m_user.isNull() && !d->m_pass.isNull()) {
508
NSURLCredential *credential = [[NSURLCredential alloc] initWithUser:d->m_user
510
persistence:NSURLCredentialPersistenceForSession];
511
d->m_currentMacChallenge = challenge.nsURLAuthenticationChallenge();
512
d->m_currentWebChallenge = challenge;
513
receivedCredential(challenge, core(credential));
514
[credential release];
515
// FIXME: Per the specification, the user shouldn't be asked for credentials if there were incorrect ones provided explicitly.
516
d->m_user = String();
517
d->m_pass = String();
521
if (!client() || client()->shouldUseCredentialStorage(this)) {
522
if (!d->m_initialCredential.isEmpty() || challenge.previousFailureCount()) {
523
// The stored credential wasn't accepted, stop using it.
524
// There is a race condition here, since a different credential might have already been stored by another ResourceHandle,
525
// but the observable effect should be very minor, if any.
526
CredentialStorage::remove(challenge.protectionSpace());
529
if (!challenge.previousFailureCount()) {
530
Credential credential = CredentialStorage::get(challenge.protectionSpace());
531
if (!credential.isEmpty() && credential != d->m_initialCredential) {
532
ASSERT(credential.persistence() == CredentialPersistenceNone);
533
if (challenge.failureResponse().httpStatusCode() == 401) {
534
// Store the credential back, possibly adding it as a default for this directory.
535
CredentialStorage::set(credential, challenge.protectionSpace(), challenge.failureResponse().url());
537
[challenge.sender() useCredential:mac(credential) forAuthenticationChallenge:mac(challenge)];
543
d->m_currentMacChallenge = challenge.nsURLAuthenticationChallenge();
544
d->m_currentWebChallenge = core(d->m_currentMacChallenge);
545
d->m_currentWebChallenge.setAuthenticationClient(this);
547
// FIXME: Several concurrent requests can return with the an authentication challenge for the same protection space.
548
// We should avoid making additional client calls for the same protection space when already waiting for the user,
549
// because typing the same credentials several times is annoying.
551
client()->didReceiveAuthenticationChallenge(this, d->m_currentWebChallenge);
554
void ResourceHandle::didCancelAuthenticationChallenge(const AuthenticationChallenge& challenge)
556
ASSERT(d->m_currentMacChallenge);
557
ASSERT(d->m_currentMacChallenge == challenge.nsURLAuthenticationChallenge());
558
ASSERT(!d->m_currentWebChallenge.isNull());
561
client()->didCancelAuthenticationChallenge(this, challenge);
564
#if USE(PROTECTION_SPACE_AUTH_CALLBACK)
565
bool ResourceHandle::canAuthenticateAgainstProtectionSpace(const ProtectionSpace& protectionSpace)
568
return client()->canAuthenticateAgainstProtectionSpace(this, protectionSpace);
574
void ResourceHandle::receivedCredential(const AuthenticationChallenge& challenge, const Credential& credential)
576
ASSERT(!challenge.isNull());
577
if (challenge != d->m_currentWebChallenge)
580
// FIXME: Support empty credentials. Currently, an empty credential cannot be stored in WebCore credential storage, as that's empty value for its map.
581
if (credential.isEmpty()) {
582
receivedRequestToContinueWithoutCredential(challenge);
586
if (credential.persistence() == CredentialPersistenceForSession && (!d->m_needsSiteSpecificQuirks || ![[[mac(challenge) protectionSpace] host] isEqualToString:@"gallery.me.com"])) {
587
// Manage per-session credentials internally, because once NSURLCredentialPersistenceForSession is used, there is no way
588
// to ignore it for a particular request (short of removing it altogether).
589
// <rdar://problem/6867598> gallery.me.com is temporarily whitelisted, so that QuickTime plug-in could see the credentials.
590
Credential webCredential(credential, CredentialPersistenceNone);
592
if (challenge.failureResponse().httpStatusCode() == 401)
593
urlToStore = challenge.failureResponse().url();
594
CredentialStorage::set(webCredential, core([d->m_currentMacChallenge protectionSpace]), urlToStore);
595
[[d->m_currentMacChallenge sender] useCredential:mac(webCredential) forAuthenticationChallenge:d->m_currentMacChallenge];
597
[[d->m_currentMacChallenge sender] useCredential:mac(credential) forAuthenticationChallenge:d->m_currentMacChallenge];
599
clearAuthentication();
602
void ResourceHandle::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge& challenge)
604
ASSERT(!challenge.isNull());
605
if (challenge != d->m_currentWebChallenge)
608
[[d->m_currentMacChallenge sender] continueWithoutCredentialForAuthenticationChallenge:d->m_currentMacChallenge];
610
clearAuthentication();
613
void ResourceHandle::receivedCancellation(const AuthenticationChallenge& challenge)
615
if (challenge != d->m_currentWebChallenge)
619
client()->receivedCancellation(this, challenge);
622
} // namespace WebCore
624
@implementation WebCoreResourceHandleAsDelegate
626
- (id)initWithHandle:(ResourceHandle*)handle
640
- (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)newRequest redirectResponse:(NSURLResponse *)redirectResponse
642
UNUSED_PARAM(connection);
644
// the willSendRequest call may cancel this load, in which case self could be deallocated
645
RetainPtr<WebCoreResourceHandleAsDelegate> protect(self);
647
if (!m_handle || !m_handle->client())
650
// See <rdar://problem/5380697> . This is a workaround for a behavior change in CFNetwork where willSendRequest gets called more often.
651
if (!redirectResponse)
655
if ([redirectResponse isKindOfClass:[NSHTTPURLResponse class]])
656
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"]);
658
LOG(Network, "Handle %p delegate connection:%p willSendRequest:%@ redirectResponse:non-HTTP", m_handle, connection, [newRequest description]);
661
if ([redirectResponse isKindOfClass:[NSHTTPURLResponse class]] && [(NSHTTPURLResponse *)redirectResponse statusCode] == 307) {
662
String lastHTTPMethod = m_handle->lastHTTPMethod();
663
if (!equalIgnoringCase(lastHTTPMethod, String([newRequest HTTPMethod]))) {
664
NSMutableURLRequest *mutableRequest = [newRequest mutableCopy];
665
[mutableRequest setHTTPMethod:lastHTTPMethod];
667
FormData* body = m_handle->firstRequest().httpBody();
668
if (!equalIgnoringCase(lastHTTPMethod, "GET") && body && !body->isEmpty())
669
WebCore::setHTTPBody(mutableRequest, body);
671
String originalContentType = m_handle->firstRequest().httpContentType();
672
if (!originalContentType.isEmpty())
673
[mutableRequest setValue:originalContentType forHTTPHeaderField:@"Content-Type"];
675
newRequest = [mutableRequest autorelease];
679
ResourceRequest request = newRequest;
681
// Should not set Referer after a redirect from a secure resource to non-secure one.
682
if (!request.url().protocolIs("https") && protocolIs(request.httpReferrer(), "https"))
683
request.clearHTTPReferrer();
685
m_handle->willSendRequest(request, redirectResponse);
687
return request.nsURLRequest();
690
- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection
692
UNUSED_PARAM(connection);
694
LOG(Network, "Handle %p delegate connectionShouldUseCredentialStorage:%p", m_handle, connection);
699
return m_handle->shouldUseCredentialStorage();
702
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
704
UNUSED_PARAM(connection);
706
LOG(Network, "Handle %p delegate connection:%p didReceiveAuthenticationChallenge:%p", m_handle, connection, challenge);
709
[[challenge sender] cancelAuthenticationChallenge:challenge];
712
m_handle->didReceiveAuthenticationChallenge(core(challenge));
715
- (void)connection:(NSURLConnection *)connection didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
717
UNUSED_PARAM(connection);
719
LOG(Network, "Handle %p delegate connection:%p didCancelAuthenticationChallenge:%p", m_handle, connection, challenge);
723
m_handle->didCancelAuthenticationChallenge(core(challenge));
726
#if USE(PROTECTION_SPACE_AUTH_CALLBACK)
727
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
730
UNUSED_PARAM(connection);
733
LOG(Network, "Handle %p delegate connection:%p canAuthenticateAgainstProtectionSpace:%p", m_handle, connection, protectionSpace);
738
return m_handle->canAuthenticateAgainstProtectionSpace(core(protectionSpace));
742
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)r
744
UNUSED_PARAM(connection);
746
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]);
748
if (!m_handle || !m_handle->client())
751
// Avoid MIME type sniffing if the response comes back as 304 Not Modified.
752
int statusCode = [r respondsToSelector:@selector(statusCode)] ? [(id)r statusCode] : 0;
753
if (statusCode != 304)
754
adjustMIMETypeIfNecessary([r _CFURLResponse]);
756
if ([m_handle->firstRequest().nsURLRequest() _propertyForKey:@"ForceHTMLMIMEType"])
757
[r _setMIMEType:@"text/html"];
759
m_handle->client()->didReceiveResponse(m_handle, r);
762
#if USE(NETWORK_CFDATA_ARRAY_CALLBACK)
763
- (void)connection:(NSURLConnection *)connection didReceiveDataArray:(NSArray *)dataArray
765
UNUSED_PARAM(connection);
766
LOG(Network, "Handle %p delegate connection:%p didReceiveDataArray:%p arraySize:%d", m_handle, connection, dataArray, [dataArray count]);
771
if (!m_handle || !m_handle->client())
774
m_handle->handleDataArray(reinterpret_cast<CFArrayRef>(dataArray));
775
// The call to didReceiveData above can cancel a load, and if so, the delegate (self) could have been deallocated by this point.
779
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data lengthReceived:(long long)lengthReceived
781
UNUSED_PARAM(connection);
782
UNUSED_PARAM(lengthReceived);
784
LOG(Network, "Handle %p delegate connection:%p didReceiveData:%p lengthReceived:%lld", m_handle, connection, data, lengthReceived);
786
if (!m_handle || !m_handle->client())
788
// FIXME: If we get more than 2B bytes in a single chunk, this code won't do the right thing.
789
// However, with today's computers and networking speeds, this won't happen in practice.
790
// Could be an issue with a giant local file.
792
// FIXME: https://bugs.webkit.org/show_bug.cgi?id=19793
793
// -1 means we do not provide any data about transfer size to inspector so it would use
794
// Content-Length headers or content size to show transfer size.
795
m_handle->client()->didReceiveData(m_handle, (const char*)[data bytes], [data length], -1);
798
- (void)connection:(NSURLConnection *)connection willStopBufferingData:(NSData *)data
800
UNUSED_PARAM(connection);
802
LOG(Network, "Handle %p delegate connection:%p willStopBufferingData:%p", m_handle, connection, data);
804
if (!m_handle || !m_handle->client())
806
// FIXME: If we get a resource with more than 2B bytes, this code won't do the right thing.
807
// However, with today's computers and networking speeds, this won't happen in practice.
808
// Could be an issue with a giant local file.
809
m_handle->client()->willStopBufferingData(m_handle, (const char*)[data bytes], static_cast<int>([data length]));
812
- (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite
814
UNUSED_PARAM(connection);
815
UNUSED_PARAM(bytesWritten);
817
LOG(Network, "Handle %p delegate connection:%p didSendBodyData:%d totalBytesWritten:%d totalBytesExpectedToWrite:%d", m_handle, connection, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
819
if (!m_handle || !m_handle->client())
821
m_handle->client()->didSendData(m_handle, totalBytesWritten, totalBytesExpectedToWrite);
824
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
826
UNUSED_PARAM(connection);
828
LOG(Network, "Handle %p delegate connectionDidFinishLoading:%p", m_handle, connection);
830
if (!m_handle || !m_handle->client())
833
m_handle->client()->didFinishLoading(m_handle, 0);
836
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
838
UNUSED_PARAM(connection);
840
LOG(Network, "Handle %p delegate connection:%p didFailWithError:%@", m_handle, connection, error);
842
if (!m_handle || !m_handle->client())
845
m_handle->client()->didFail(m_handle, error);
849
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse
851
LOG(Network, "Handle %p delegate connection:%p willCacheResponse:%p", m_handle, connection, cachedResponse);
853
UNUSED_PARAM(connection);
855
if (!m_handle || !m_handle->client())
858
// Workaround for <rdar://problem/6300990> Caching does not respect Vary HTTP header.
859
// FIXME: WebCore cache has issues with Vary, too (bug 58797, bug 71509).
860
if ([[cachedResponse response] isKindOfClass:[NSHTTPURLResponse class]]
861
&& [[(NSHTTPURLResponse *)[cachedResponse response] allHeaderFields] objectForKey:@"Vary"])
864
NSCachedURLResponse *newResponse = m_handle->client()->willCacheResponse(m_handle, cachedResponse);
865
if (newResponse != cachedResponse)
868
CacheStoragePolicy policy = static_cast<CacheStoragePolicy>([newResponse storagePolicy]);
870
m_handle->client()->willCacheResponse(m_handle, policy);
872
if (static_cast<NSURLCacheStoragePolicy>(policy) != [newResponse storagePolicy])
873
newResponse = [[[NSCachedURLResponse alloc] initWithResponse:[newResponse response]
874
data:[newResponse data]
875
userInfo:[newResponse userInfo]
876
storagePolicy:static_cast<NSURLCacheStoragePolicy>(policy)] autorelease];
884
WebCoreSynchronousLoaderClient::~WebCoreSynchronousLoaderClient()
886
[m_response release];
891
void WebCoreSynchronousLoaderClient::willSendRequest(ResourceHandle* handle, ResourceRequest& request, const ResourceResponse& /*redirectResponse*/)
893
// FIXME: This needs to be fixed to follow the redirect correctly even for cross-domain requests.
894
if (!protocolHostAndPortAreEqual(handle->firstRequest().url(), request.url())) {
896
m_error = [[NSError alloc] initWithDomain:NSURLErrorDomain code:NSURLErrorBadServerResponse userInfo:nil];
903
bool WebCoreSynchronousLoaderClient::shouldUseCredentialStorage(ResourceHandle*)
905
// FIXME: We should ask FrameLoaderClient whether using credential storage is globally forbidden.
906
return m_allowStoredCredentials;
909
#if USE(PROTECTION_SPACE_AUTH_CALLBACK)
910
bool WebCoreSynchronousLoaderClient::canAuthenticateAgainstProtectionSpace(ResourceHandle*, const ProtectionSpace&)
912
// FIXME: We should ask FrameLoaderClient. <http://webkit.org/b/65196>
917
void WebCoreSynchronousLoaderClient::didReceiveAuthenticationChallenge(ResourceHandle*, const AuthenticationChallenge& challenge)
919
// FIXME: The user should be asked for credentials, as in async case.
920
[challenge.sender() continueWithoutCredentialForAuthenticationChallenge:challenge.nsURLAuthenticationChallenge()];
923
void WebCoreSynchronousLoaderClient::didReceiveResponse(ResourceHandle*, const ResourceResponse& response)
925
[m_response release];
926
m_response = [response.nsURLResponse() copy];
929
void WebCoreSynchronousLoaderClient::didReceiveData(ResourceHandle*, const char* data, int length, int /*encodedDataLength*/)
932
m_data = [[NSMutableData alloc] init];
933
[m_data appendBytes:data length:length];
936
void WebCoreSynchronousLoaderClient::didFinishLoading(ResourceHandle*, double)
941
void WebCoreSynchronousLoaderClient::didFail(ResourceHandle*, const ResourceError& error)
945
m_error = [error copy];
950
#endif // !USE(CFNETWORK)