• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (C) 2004, 2006-2009 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#import "config.h"
27#import "ResourceHandleInternal.h"
28
29#import "AuthenticationChallenge.h"
30#import "AuthenticationMac.h"
31#import "Base64.h"
32#import "BlockExceptions.h"
33#import "CString.h"
34#import "CredentialStorage.h"
35#import "DocLoader.h"
36#import "FormDataStreamMac.h"
37#import "Frame.h"
38#import "FrameLoader.h"
39#import "Logging.h"
40#import "MIMETypeRegistry.h"
41#import "Page.h"
42#import "ResourceError.h"
43#import "ResourceResponse.h"
44#import "SchedulePair.h"
45#import "Settings.h"
46#import "SharedBuffer.h"
47#import "SubresourceLoader.h"
48#import "WebCoreSystemInterface.h"
49#import "WebCoreURLResponse.h"
50#import <wtf/UnusedParam.h>
51
52#ifdef BUILDING_ON_TIGER
53typedef int NSInteger;
54#endif
55
56using namespace WebCore;
57
58@interface WebCoreResourceHandleAsDelegate : NSObject
59{
60    ResourceHandle* m_handle;
61}
62- (id)initWithHandle:(ResourceHandle*)handle;
63- (void)detachHandle;
64@end
65
66@interface NSURLConnection (NSURLConnectionTigerPrivate)
67- (NSData *)_bufferedData;
68@end
69
70@interface NSURLRequest (Details)
71- (id)_propertyForKey:(NSString *)key;
72@end
73
74#ifndef BUILDING_ON_TIGER
75
76@interface WebCoreSynchronousLoader : NSObject {
77    NSURL *m_url;
78    NSString *m_user;
79    NSString *m_pass;
80    // Store the preemptively used initial credential so that if we get an authentication challenge, we won't use the same one again.
81    Credential m_initialCredential;
82    BOOL m_allowStoredCredentials;
83    NSURLResponse *m_response;
84    NSMutableData *m_data;
85    NSError *m_error;
86    BOOL m_isDone;
87}
88+ (NSData *)loadRequest:(NSURLRequest *)request allowStoredCredentials:(BOOL)allowStoredCredentials returningResponse:(NSURLResponse **)response error:(NSError **)error;
89@end
90
91static NSString *WebCoreSynchronousLoaderRunLoopMode = @"WebCoreSynchronousLoaderRunLoopMode";
92
93#endif
94
95namespace WebCore {
96
97#ifdef BUILDING_ON_TIGER
98static unsigned inNSURLConnectionCallback;
99#endif
100
101#ifndef NDEBUG
102static bool isInitializingConnection;
103#endif
104
105class CallbackGuard {
106public:
107    CallbackGuard()
108    {
109#ifdef BUILDING_ON_TIGER
110        ++inNSURLConnectionCallback;
111#endif
112    }
113    ~CallbackGuard()
114    {
115#ifdef BUILDING_ON_TIGER
116        ASSERT(inNSURLConnectionCallback > 0);
117        --inNSURLConnectionCallback;
118#endif
119    }
120};
121
122#ifndef BUILDING_ON_TIGER
123static String encodeBasicAuthorization(const String& user, const String& password)
124{
125    CString unencodedString = (user + ":" + password).utf8();
126    Vector<char> unencoded(unencodedString.length());
127    std::copy(unencodedString.data(), unencodedString.data() + unencodedString.length(), unencoded.begin());
128    Vector<char> encoded;
129    base64Encode(unencoded, encoded);
130    return String(encoded.data(), encoded.size());
131}
132#endif
133
134ResourceHandleInternal::~ResourceHandleInternal()
135{
136}
137
138ResourceHandle::~ResourceHandle()
139{
140    releaseDelegate();
141    d->m_currentWebChallenge.setAuthenticationClient(0);
142
143    LOG(Network, "Handle %p destroyed", this);
144}
145
146static const double MaxFoundationVersionWithoutdidSendBodyDataDelegate = 677.21;
147bool ResourceHandle::didSendBodyDataDelegateExists()
148{
149    return NSFoundationVersionNumber > MaxFoundationVersionWithoutdidSendBodyDataDelegate;
150}
151
152bool ResourceHandle::start(Frame* frame)
153{
154    if (!frame)
155        return false;
156
157    BEGIN_BLOCK_OBJC_EXCEPTIONS;
158
159    // If we are no longer attached to a Page, this must be an attempted load from an
160    // onUnload handler, so let's just block it.
161    Page* page = frame->page();
162    if (!page)
163        return false;
164
165#ifndef NDEBUG
166    isInitializingConnection = YES;
167#endif
168
169    id delegate;
170
171    if (d->m_mightDownloadFromHandle) {
172        ASSERT(!d->m_proxy);
173        d->m_proxy = wkCreateNSURLConnectionDelegateProxy();
174        [d->m_proxy.get() setDelegate:ResourceHandle::delegate()];
175        [d->m_proxy.get() release];
176
177        delegate = d->m_proxy.get();
178    } else
179        delegate = ResourceHandle::delegate();
180
181    if ((!d->m_user.isEmpty() || !d->m_pass.isEmpty())
182#ifndef BUILDING_ON_TIGER
183     && !d->m_request.url().protocolInHTTPFamily() // On Tiger, always pass credentials in URL, so that they get stored even if the request gets cancelled right away.
184#endif
185    ) {
186        // Credentials for ftp can only be passed in URL, the connection:didReceiveAuthenticationChallenge: delegate call won't be made.
187        KURL urlWithCredentials(d->m_request.url());
188        urlWithCredentials.setUser(d->m_user);
189        urlWithCredentials.setPass(d->m_pass);
190        d->m_request.setURL(urlWithCredentials);
191    }
192
193#ifndef BUILDING_ON_TIGER
194    if ((!client() || client()->shouldUseCredentialStorage(this)) && d->m_request.url().protocolInHTTPFamily()) {
195        if (d->m_user.isEmpty() && d->m_pass.isEmpty()) {
196            // <rdar://problem/7174050> - For URLs that match the paths of those previously challenged for HTTP Basic authentication,
197            // try and reuse the credential preemptively, as allowed by RFC 2617.
198            d->m_initialCredential = CredentialStorage::get(d->m_request.url());
199        } else {
200            // If there is already a protection space known for the URL, update stored credentials before sending a request.
201            // This makes it possible to implement logout by sending an XMLHttpRequest with known incorrect credentials, and aborting it immediately
202            // (so that an authentication dialog doesn't pop up).
203            CredentialStorage::set(Credential(d->m_user, d->m_pass, CredentialPersistenceNone), d->m_request.url());
204        }
205    }
206
207    if (!d->m_initialCredential.isEmpty()) {
208        // FIXME: Support Digest authentication, and Proxy-Authorization.
209        String authHeader = "Basic " + encodeBasicAuthorization(d->m_initialCredential.user(), d->m_initialCredential.password());
210        d->m_request.addHTTPHeaderField("Authorization", authHeader);
211    }
212#endif
213
214    if (!ResourceHandle::didSendBodyDataDelegateExists())
215        associateStreamWithResourceHandle([d->m_request.nsURLRequest() HTTPBodyStream], this);
216
217#ifdef BUILDING_ON_TIGER
218    // A conditional request sent by WebCore (e.g. to update appcache) can be for a resource that is not cacheable by NSURLConnection,
219    // which can get confused and fail to load it in this case.
220    if (d->m_request.isConditional())
221        d->m_request.setCachePolicy(ReloadIgnoringCacheData);
222#endif
223
224    d->m_needsSiteSpecificQuirks = frame->settings() && frame->settings()->needsSiteSpecificQuirks();
225
226    NSURLConnection *connection;
227
228    if (d->m_shouldContentSniff || frame->settings()->localFileContentSniffingEnabled())
229#ifdef BUILDING_ON_TIGER
230        connection = [[NSURLConnection alloc] initWithRequest:d->m_request.nsURLRequest() delegate:delegate];
231#else
232        connection = [[NSURLConnection alloc] initWithRequest:d->m_request.nsURLRequest() delegate:delegate startImmediately:NO];
233#endif
234    else {
235        NSMutableURLRequest *request = [d->m_request.nsURLRequest() mutableCopy];
236        wkSetNSURLRequestShouldContentSniff(request, NO);
237#ifdef BUILDING_ON_TIGER
238        connection = [[NSURLConnection alloc] initWithRequest:request delegate:delegate];
239#else
240        connection = [[NSURLConnection alloc] initWithRequest:request delegate:delegate startImmediately:NO];
241#endif
242        [request release];
243    }
244
245#ifndef BUILDING_ON_TIGER
246    bool scheduled = false;
247    if (SchedulePairHashSet* scheduledPairs = page->scheduledRunLoopPairs()) {
248        SchedulePairHashSet::iterator end = scheduledPairs->end();
249        for (SchedulePairHashSet::iterator it = scheduledPairs->begin(); it != end; ++it) {
250            if (NSRunLoop *runLoop = (*it)->nsRunLoop()) {
251                [connection scheduleInRunLoop:runLoop forMode:(NSString *)(*it)->mode()];
252                scheduled = true;
253            }
254        }
255    }
256
257    // Start the connection if we did schedule with at least one runloop.
258    // We can't start the connection until we have one runloop scheduled.
259    if (scheduled)
260        [connection start];
261    else
262        d->m_startWhenScheduled = true;
263#endif
264
265#ifndef NDEBUG
266    isInitializingConnection = NO;
267#endif
268
269    LOG(Network, "Handle %p starting connection %p for %@", this, connection, d->m_request.nsURLRequest());
270
271    d->m_connection = connection;
272
273    if (d->m_connection) {
274        [connection release];
275
276        if (d->m_defersLoading)
277            wkSetNSURLConnectionDefersCallbacks(d->m_connection.get(), YES);
278
279        return true;
280    }
281
282    END_BLOCK_OBJC_EXCEPTIONS;
283
284    return false;
285}
286
287void ResourceHandle::cancel()
288{
289    LOG(Network, "Handle %p cancel connection %p", this, d->m_connection.get());
290
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];
294
295    if (!ResourceHandle::didSendBodyDataDelegateExists())
296        disassociateStreamWithResourceHandle([d->m_request.nsURLRequest() HTTPBodyStream]);
297    [d->m_connection.get() cancel];
298}
299
300void ResourceHandle::setDefersLoading(bool defers)
301{
302    LOG(Network, "Handle %p setDefersLoading(%s)", this, defers ? "true" : "false");
303
304    d->m_defersLoading = defers;
305    if (d->m_connection)
306        wkSetNSURLConnectionDefersCallbacks(d->m_connection.get(), defers);
307}
308
309void ResourceHandle::schedule(SchedulePair* pair)
310{
311#ifndef BUILDING_ON_TIGER
312    NSRunLoop *runLoop = pair->nsRunLoop();
313    if (!runLoop)
314        return;
315    [d->m_connection.get() scheduleInRunLoop:runLoop forMode:(NSString *)pair->mode()];
316    if (d->m_startWhenScheduled) {
317        [d->m_connection.get() start];
318        d->m_startWhenScheduled = false;
319    }
320#else
321    UNUSED_PARAM(pair);
322#endif
323}
324
325void ResourceHandle::unschedule(SchedulePair* pair)
326{
327#ifndef BUILDING_ON_TIGER
328    if (NSRunLoop *runLoop = pair->nsRunLoop())
329        [d->m_connection.get() unscheduleFromRunLoop:runLoop forMode:(NSString *)pair->mode()];
330#else
331    UNUSED_PARAM(pair);
332#endif
333}
334
335WebCoreResourceHandleAsDelegate *ResourceHandle::delegate()
336{
337    if (!d->m_delegate) {
338        WebCoreResourceHandleAsDelegate *delegate = [[WebCoreResourceHandleAsDelegate alloc] initWithHandle:this];
339        d->m_delegate = delegate;
340        [delegate release];
341    }
342    return d->m_delegate.get();
343}
344
345void ResourceHandle::releaseDelegate()
346{
347    if (!d->m_delegate)
348        return;
349    if (d->m_proxy)
350        [d->m_proxy.get() setDelegate:nil];
351    [d->m_delegate.get() detachHandle];
352    d->m_delegate = nil;
353}
354
355bool ResourceHandle::supportsBufferedData()
356{
357    static bool supportsBufferedData = [NSURLConnection instancesRespondToSelector:@selector(_bufferedData)];
358    return supportsBufferedData;
359}
360
361PassRefPtr<SharedBuffer> ResourceHandle::bufferedData()
362{
363    if (ResourceHandle::supportsBufferedData())
364        return SharedBuffer::wrapNSData([d->m_connection.get() _bufferedData]);
365
366    return 0;
367}
368
369id ResourceHandle::releaseProxy()
370{
371    id proxy = [[d->m_proxy.get() retain] autorelease];
372    d->m_proxy = nil;
373    [proxy setDelegate:nil];
374    return proxy;
375}
376
377NSURLConnection *ResourceHandle::connection() const
378{
379    return d->m_connection.get();
380}
381
382bool ResourceHandle::loadsBlocked()
383{
384#ifndef BUILDING_ON_TIGER
385    return false;
386#else
387    // On Tiger, if we're in an NSURLConnection callback, that blocks all other NSURLConnection callbacks.
388    // On Leopard and newer, it blocks only callbacks on that same NSURLConnection object, which is not
389    // a problem in practice.
390    return inNSURLConnectionCallback != 0;
391#endif
392}
393
394bool ResourceHandle::willLoadFromCache(ResourceRequest& request, Frame*)
395{
396#ifndef BUILDING_ON_TIGER
397    request.setCachePolicy(ReturnCacheDataDontLoad);
398    NSURLResponse *nsURLResponse = nil;
399    BEGIN_BLOCK_OBJC_EXCEPTIONS;
400
401   [NSURLConnection sendSynchronousRequest:request.nsURLRequest() returningResponse:&nsURLResponse error:nil];
402
403    END_BLOCK_OBJC_EXCEPTIONS;
404
405    return nsURLResponse;
406#else
407    // <rdar://problem/6803217> - Re-enable after <rdar://problem/6786454> is resolved.
408    UNUSED_PARAM(request);
409    return false;
410#endif
411}
412
413void ResourceHandle::loadResourceSynchronously(const ResourceRequest& request, StoredCredentials storedCredentials, ResourceError& error, ResourceResponse& response, Vector<char>& data, Frame*)
414{
415    NSError *nsError = nil;
416
417    NSURLResponse *nsURLResponse = nil;
418    NSData *result = nil;
419
420    ASSERT(!request.isEmpty());
421
422    NSURLRequest *nsRequest;
423    if (!shouldContentSniffURL(request.url())) {
424        NSMutableURLRequest *mutableRequest = [[request.nsURLRequest() mutableCopy] autorelease];
425        wkSetNSURLRequestShouldContentSniff(mutableRequest, NO);
426        nsRequest = mutableRequest;
427    } else
428        nsRequest = request.nsURLRequest();
429
430    BEGIN_BLOCK_OBJC_EXCEPTIONS;
431
432#ifndef BUILDING_ON_TIGER
433    result = [WebCoreSynchronousLoader loadRequest:nsRequest allowStoredCredentials:(storedCredentials == AllowStoredCredentials) returningResponse:&nsURLResponse error:&nsError];
434#else
435    UNUSED_PARAM(storedCredentials);
436    result = [NSURLConnection sendSynchronousRequest:nsRequest returningResponse:&nsURLResponse error:&nsError];
437#endif
438    END_BLOCK_OBJC_EXCEPTIONS;
439
440    if (nsError == nil)
441        response = nsURLResponse;
442    else {
443        response = ResourceResponse(request.url(), String(), 0, String(), String());
444        if ([nsError domain] == NSURLErrorDomain)
445            switch ([nsError code]) {
446                case NSURLErrorUserCancelledAuthentication:
447                    // FIXME: we should really return the actual HTTP response, but sendSynchronousRequest doesn't provide us with one.
448                    response.setHTTPStatusCode(401);
449                    break;
450                default:
451                    response.setHTTPStatusCode([nsError code]);
452            }
453        else
454            response.setHTTPStatusCode(404);
455    }
456
457    data.resize([result length]);
458    memcpy(data.data(), [result bytes], [result length]);
459
460    error = nsError;
461}
462
463void ResourceHandle::willSendRequest(ResourceRequest& request, const ResourceResponse& redirectResponse)
464{
465    const KURL& url = request.url();
466    d->m_user = url.user();
467    d->m_pass = url.pass();
468    request.removeCredentials();
469
470    client()->willSendRequest(this, request, redirectResponse);
471}
472
473bool ResourceHandle::shouldUseCredentialStorage()
474{
475    if (client())
476        return client()->shouldUseCredentialStorage(this);
477
478    return false;
479}
480
481void ResourceHandle::didReceiveAuthenticationChallenge(const AuthenticationChallenge& challenge)
482{
483    ASSERT(!d->m_currentMacChallenge);
484    ASSERT(d->m_currentWebChallenge.isNull());
485    // Since NSURLConnection networking relies on keeping a reference to the original NSURLAuthenticationChallenge,
486    // we make sure that is actually present
487    ASSERT(challenge.nsURLAuthenticationChallenge());
488
489    if (!d->m_user.isNull() && !d->m_pass.isNull()) {
490        NSURLCredential *credential = [[NSURLCredential alloc] initWithUser:d->m_user
491                                                                   password:d->m_pass
492                                                                persistence:NSURLCredentialPersistenceForSession];
493        d->m_currentMacChallenge = challenge.nsURLAuthenticationChallenge();
494        d->m_currentWebChallenge = challenge;
495        receivedCredential(challenge, core(credential));
496        [credential release];
497        // FIXME: Per the specification, the user shouldn't be asked for credentials if there were incorrect ones provided explicitly.
498        d->m_user = String();
499        d->m_pass = String();
500        return;
501    }
502
503#ifndef BUILDING_ON_TIGER
504    if (!challenge.previousFailureCount() && (!client() || client()->shouldUseCredentialStorage(this))) {
505        Credential credential = CredentialStorage::get(challenge.protectionSpace());
506        if (!credential.isEmpty() && credential != d->m_initialCredential) {
507            ASSERT(credential.persistence() == CredentialPersistenceNone);
508            [challenge.sender() useCredential:mac(credential) forAuthenticationChallenge:mac(challenge)];
509            return;
510        }
511    }
512#endif
513
514    d->m_currentMacChallenge = challenge.nsURLAuthenticationChallenge();
515    d->m_currentWebChallenge = core(d->m_currentMacChallenge);
516    d->m_currentWebChallenge.setAuthenticationClient(this);
517
518    if (client())
519        client()->didReceiveAuthenticationChallenge(this, d->m_currentWebChallenge);
520}
521
522void ResourceHandle::didCancelAuthenticationChallenge(const AuthenticationChallenge& challenge)
523{
524    ASSERT(d->m_currentMacChallenge);
525    ASSERT(d->m_currentMacChallenge == challenge.nsURLAuthenticationChallenge());
526    ASSERT(!d->m_currentWebChallenge.isNull());
527
528    if (client())
529        client()->didCancelAuthenticationChallenge(this, challenge);
530}
531
532void ResourceHandle::receivedCredential(const AuthenticationChallenge& challenge, const Credential& credential)
533{
534    ASSERT(!challenge.isNull());
535    if (challenge != d->m_currentWebChallenge)
536        return;
537
538#ifdef BUILDING_ON_TIGER
539    if (credential.persistence() == CredentialPersistenceNone) {
540        // NSURLCredentialPersistenceNone doesn't work on Tiger, so we have to use session persistence.
541        Credential webCredential(credential.user(), credential.password(), CredentialPersistenceForSession);
542        [[d->m_currentMacChallenge sender] useCredential:mac(webCredential) forAuthenticationChallenge:d->m_currentMacChallenge];
543    } else
544#else
545    if (credential.persistence() == CredentialPersistenceForSession && (!d->m_needsSiteSpecificQuirks || ![[[mac(challenge) protectionSpace] host] isEqualToString:@"gallery.me.com"])) {
546        // Manage per-session credentials internally, because once NSURLCredentialPersistenceForSession is used, there is no way
547        // to ignore it for a particular request (short of removing it altogether).
548        // <rdar://problem/6867598> gallery.me.com is temporarily whitelisted, so that QuickTime plug-in could see the credentials.
549        Credential webCredential(credential, CredentialPersistenceNone);
550        KURL urlToStore;
551        if (challenge.failureResponse().httpStatusCode() == 401)
552            urlToStore = d->m_request.url();
553        CredentialStorage::set(webCredential, core([d->m_currentMacChallenge protectionSpace]), urlToStore);
554        [[d->m_currentMacChallenge sender] useCredential:mac(webCredential) forAuthenticationChallenge:d->m_currentMacChallenge];
555    } else
556#endif
557        [[d->m_currentMacChallenge sender] useCredential:mac(credential) forAuthenticationChallenge:d->m_currentMacChallenge];
558
559    clearAuthentication();
560}
561
562void ResourceHandle::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge& challenge)
563{
564    ASSERT(!challenge.isNull());
565    if (challenge != d->m_currentWebChallenge)
566        return;
567
568    [[d->m_currentMacChallenge sender] continueWithoutCredentialForAuthenticationChallenge:d->m_currentMacChallenge];
569
570    clearAuthentication();
571}
572
573void ResourceHandle::receivedCancellation(const AuthenticationChallenge& challenge)
574{
575    if (challenge != d->m_currentWebChallenge)
576        return;
577
578    if (client())
579        client()->receivedCancellation(this, challenge);
580}
581
582} // namespace WebCore
583
584@implementation WebCoreResourceHandleAsDelegate
585
586- (id)initWithHandle:(ResourceHandle*)handle
587{
588    self = [self init];
589    if (!self)
590        return nil;
591    m_handle = handle;
592    return self;
593}
594
595- (void)detachHandle
596{
597    m_handle = 0;
598}
599
600- (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)newRequest redirectResponse:(NSURLResponse *)redirectResponse
601{
602    UNUSED_PARAM(connection);
603
604    // the willSendRequest call may cancel this load, in which case self could be deallocated
605    RetainPtr<WebCoreResourceHandleAsDelegate> protect(self);
606
607    if (!m_handle || !m_handle->client())
608        return nil;
609
610    // See <rdar://problem/5380697> .  This is a workaround for a behavior change in CFNetwork where willSendRequest gets called more often.
611    if (!redirectResponse)
612        return newRequest;
613
614#if !LOG_DISABLED
615    if ([redirectResponse isKindOfClass:[NSHTTPURLResponse class]])
616        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"]);
617    else
618        LOG(Network, "Handle %p delegate connection:%p willSendRequest:%@ redirectResponse:non-HTTP", m_handle, connection, [newRequest description]);
619#endif
620
621    if ([redirectResponse isKindOfClass:[NSHTTPURLResponse class]] && [(NSHTTPURLResponse *)redirectResponse statusCode] == 307) {
622        String originalMethod = m_handle->request().httpMethod();
623        if (!equalIgnoringCase(originalMethod, String([newRequest HTTPMethod]))) {
624            NSMutableURLRequest *mutableRequest = [newRequest mutableCopy];
625            [mutableRequest setHTTPMethod:originalMethod];
626
627            FormData* body = m_handle->request().httpBody();
628            if (!equalIgnoringCase(originalMethod, "GET") && body && !body->isEmpty())
629                WebCore::setHTTPBody(mutableRequest, body);
630
631            String originalContentType = m_handle->request().httpContentType();
632            if (!originalContentType.isEmpty())
633                [mutableRequest setValue:originalContentType forHTTPHeaderField:@"Content-Type"];
634
635            newRequest = [mutableRequest autorelease];
636        }
637    }
638
639    CallbackGuard guard;
640    ResourceRequest request = newRequest;
641
642    // Should not set Referer after a redirect from a secure resource to non-secure one.
643    if (!request.url().protocolIs("https") && protocolIs(request.httpReferrer(), "https"))
644        request.clearHTTPReferrer();
645
646    m_handle->willSendRequest(request, redirectResponse);
647
648    if (!ResourceHandle::didSendBodyDataDelegateExists()) {
649        // The client may change the request's body stream, in which case we have to re-associate
650        // the handle with the new stream so upload progress callbacks continue to work correctly.
651        NSInputStream* oldBodyStream = [newRequest HTTPBodyStream];
652        NSInputStream* newBodyStream = [request.nsURLRequest() HTTPBodyStream];
653        if (oldBodyStream != newBodyStream) {
654            disassociateStreamWithResourceHandle(oldBodyStream);
655            associateStreamWithResourceHandle(newBodyStream, m_handle);
656        }
657    }
658
659    return request.nsURLRequest();
660}
661
662- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection
663{
664    UNUSED_PARAM(connection);
665
666    LOG(Network, "Handle %p delegate connectionShouldUseCredentialStorage:%p", m_handle, connection);
667
668    if (!m_handle)
669        return NO;
670
671    CallbackGuard guard;
672    return m_handle->shouldUseCredentialStorage();
673}
674
675- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
676{
677    UNUSED_PARAM(connection);
678
679    LOG(Network, "Handle %p delegate connection:%p didReceiveAuthenticationChallenge:%p", m_handle, connection, challenge);
680
681    if (!m_handle)
682        return;
683    CallbackGuard guard;
684    m_handle->didReceiveAuthenticationChallenge(core(challenge));
685}
686
687- (void)connection:(NSURLConnection *)connection didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
688{
689    UNUSED_PARAM(connection);
690
691    LOG(Network, "Handle %p delegate connection:%p didCancelAuthenticationChallenge:%p", m_handle, connection, challenge);
692
693    if (!m_handle)
694        return;
695    CallbackGuard guard;
696    m_handle->didCancelAuthenticationChallenge(core(challenge));
697}
698
699- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)r
700{
701    UNUSED_PARAM(connection);
702
703    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]);
704
705    if (!m_handle || !m_handle->client())
706        return;
707    CallbackGuard guard;
708
709    [r adjustMIMETypeIfNecessary];
710
711    if ([m_handle->request().nsURLRequest() _propertyForKey:@"ForceHTMLMIMEType"])
712        [r _setMIMEType:@"text/html"];
713
714#if ENABLE(WML)
715    const KURL& url = [r URL];
716    if (url.isLocalFile()) {
717        // FIXME: Workaround for <rdar://problem/6917571>: The WML file extension ".wml" is not mapped to
718        // the right MIME type, work around that CFNetwork problem, to unbreak WML support for local files.
719        const String& path = url.path();
720
721        DEFINE_STATIC_LOCAL(const String, wmlExt, (".wml"));
722        if (path.endsWith(wmlExt, false)) {
723            static NSString* defaultMIMETypeString = [(NSString*) defaultMIMEType() retain];
724            if ([[r MIMEType] isEqualToString:defaultMIMETypeString])
725                [r _setMIMEType:@"text/vnd.wap.wml"];
726        }
727    }
728#endif
729
730    m_handle->client()->didReceiveResponse(m_handle, r);
731}
732
733- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data lengthReceived:(long long)lengthReceived
734{
735    UNUSED_PARAM(connection);
736
737    LOG(Network, "Handle %p delegate connection:%p didReceiveData:%p lengthReceived:%lld", m_handle, connection, data, lengthReceived);
738
739    if (!m_handle || !m_handle->client())
740        return;
741    // FIXME: If we get more than 2B bytes in a single chunk, this code won't do the right thing.
742    // However, with today's computers and networking speeds, this won't happen in practice.
743    // Could be an issue with a giant local file.
744    CallbackGuard guard;
745    m_handle->client()->didReceiveData(m_handle, (const char*)[data bytes], [data length], static_cast<int>(lengthReceived));
746}
747
748- (void)connection:(NSURLConnection *)connection willStopBufferingData:(NSData *)data
749{
750    UNUSED_PARAM(connection);
751
752    LOG(Network, "Handle %p delegate connection:%p willStopBufferingData:%p", m_handle, connection, data);
753
754    if (!m_handle || !m_handle->client())
755        return;
756    // FIXME: If we get a resource with more than 2B bytes, this code won't do the right thing.
757    // However, with today's computers and networking speeds, this won't happen in practice.
758    // Could be an issue with a giant local file.
759    CallbackGuard guard;
760    m_handle->client()->willStopBufferingData(m_handle, (const char*)[data bytes], static_cast<int>([data length]));
761}
762
763- (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite
764{
765    UNUSED_PARAM(connection);
766    UNUSED_PARAM(bytesWritten);
767
768    LOG(Network, "Handle %p delegate connection:%p didSendBodyData:%d totalBytesWritten:%d totalBytesExpectedToWrite:%d", m_handle, connection, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
769
770    if (!m_handle || !m_handle->client())
771        return;
772    CallbackGuard guard;
773    m_handle->client()->didSendData(m_handle, totalBytesWritten, totalBytesExpectedToWrite);
774}
775
776- (void)connectionDidFinishLoading:(NSURLConnection *)connection
777{
778    UNUSED_PARAM(connection);
779
780    LOG(Network, "Handle %p delegate connectionDidFinishLoading:%p", m_handle, connection);
781
782    if (!m_handle || !m_handle->client())
783        return;
784    CallbackGuard guard;
785
786    if (!ResourceHandle::didSendBodyDataDelegateExists())
787        disassociateStreamWithResourceHandle([m_handle->request().nsURLRequest() HTTPBodyStream]);
788
789    m_handle->client()->didFinishLoading(m_handle);
790}
791
792- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
793{
794    UNUSED_PARAM(connection);
795
796    LOG(Network, "Handle %p delegate connection:%p didFailWithError:%@", m_handle, connection, error);
797
798    if (!m_handle || !m_handle->client())
799        return;
800    CallbackGuard guard;
801
802    if (!ResourceHandle::didSendBodyDataDelegateExists())
803        disassociateStreamWithResourceHandle([m_handle->request().nsURLRequest() HTTPBodyStream]);
804
805    m_handle->client()->didFail(m_handle, error);
806}
807
808#ifdef BUILDING_ON_TIGER
809- (void)_callConnectionWillCacheResponseWithInfo:(NSMutableDictionary *)info
810{
811    NSURLConnection *connection = [info objectForKey:@"connection"];
812    NSCachedURLResponse *cachedResponse = [info objectForKey:@"cachedResponse"];
813    NSCachedURLResponse *result = [self connection:connection willCacheResponse:cachedResponse];
814    if (result)
815        [info setObject:result forKey:@"result"];
816}
817#endif
818
819- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse
820{
821    LOG(Network, "Handle %p delegate connection:%p willCacheResponse:%p", m_handle, connection, cachedResponse);
822
823#ifdef BUILDING_ON_TIGER
824    // On Tiger CFURLConnection can sometimes call the connection:willCacheResponse: delegate method on
825    // a secondary thread instead of the main thread. If this happens perform the work on the main thread.
826    if (!pthread_main_np()) {
827        NSMutableDictionary *info = [[NSMutableDictionary alloc] init];
828        if (connection)
829            [info setObject:connection forKey:@"connection"];
830        if (cachedResponse)
831            [info setObject:cachedResponse forKey:@"cachedResponse"];
832
833        // Include synchronous url connection's mode as an acceptable run loopmode
834        // <rdar://problem/5511842>
835        NSArray *modes = [[NSArray alloc] initWithObjects:(NSString *)kCFRunLoopCommonModes, @"NSSynchronousURLConnection_PrivateMode", nil];
836        [self performSelectorOnMainThread:@selector(_callConnectionWillCacheResponseWithInfo:) withObject:info waitUntilDone:YES modes:modes];
837        [modes release];
838
839        NSCachedURLResponse *result = [[info valueForKey:@"result"] retain];
840        [info release];
841
842        return [result autorelease];
843    }
844#else
845    UNUSED_PARAM(connection);
846#endif
847
848#ifndef NDEBUG
849    if (isInitializingConnection)
850        LOG_ERROR("connection:willCacheResponse: was called inside of [NSURLConnection initWithRequest:delegate:] (4067625)");
851#endif
852
853    if (!m_handle || !m_handle->client())
854        return nil;
855
856    CallbackGuard guard;
857
858    NSCachedURLResponse *newResponse = m_handle->client()->willCacheResponse(m_handle, cachedResponse);
859    if (newResponse != cachedResponse)
860        return newResponse;
861
862    CacheStoragePolicy policy = static_cast<CacheStoragePolicy>([newResponse storagePolicy]);
863
864    m_handle->client()->willCacheResponse(m_handle, policy);
865
866    if (static_cast<NSURLCacheStoragePolicy>(policy) != [newResponse storagePolicy])
867        newResponse = [[[NSCachedURLResponse alloc] initWithResponse:[newResponse response]
868                                                                data:[newResponse data]
869                                                            userInfo:[newResponse userInfo]
870                                                       storagePolicy:static_cast<NSURLCacheStoragePolicy>(policy)] autorelease];
871
872    return newResponse;
873}
874
875@end
876
877#ifndef BUILDING_ON_TIGER
878
879@implementation WebCoreSynchronousLoader
880
881- (BOOL)_isDone
882{
883    return m_isDone;
884}
885
886- (void)dealloc
887{
888    [m_url release];
889    [m_user release];
890    [m_pass release];
891    [m_response release];
892    [m_data release];
893    [m_error release];
894
895    [super dealloc];
896}
897
898- (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)newRequest redirectResponse:(NSURLResponse *)redirectResponse
899{
900    UNUSED_PARAM(connection);
901
902    LOG(Network, "WebCoreSynchronousLoader delegate connection:%p willSendRequest:%@ redirectResponse:%p", connection, [newRequest description], redirectResponse);
903
904    // FIXME: This needs to be fixed to follow the redirect correctly even for cross-domain requests.
905    if (m_url && !protocolHostAndPortAreEqual(m_url, [newRequest URL])) {
906        m_error = [[NSError alloc] initWithDomain:NSURLErrorDomain code:NSURLErrorBadServerResponse userInfo:nil];
907        m_isDone = YES;
908        return nil;
909    }
910
911    NSURL *copy = [[newRequest URL] copy];
912    [m_url release];
913    m_url = copy;
914
915    if (redirectResponse) {
916        // Take user/pass out of the URL.
917        [m_user release];
918        [m_pass release];
919        m_user = [[m_url user] copy];
920        m_pass = [[m_url password] copy];
921        if (m_user || m_pass) {
922            ResourceRequest requestWithoutCredentials = newRequest;
923            requestWithoutCredentials.removeCredentials();
924            return requestWithoutCredentials.nsURLRequest();
925        }
926    }
927
928    return newRequest;
929}
930
931- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection
932{
933    UNUSED_PARAM(connection);
934
935    LOG(Network, "WebCoreSynchronousLoader delegate connectionShouldUseCredentialStorage:%p", connection);
936
937    // FIXME: We should ask FrameLoaderClient whether using credential storage is globally forbidden.
938    return m_allowStoredCredentials;
939}
940
941- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
942{
943    UNUSED_PARAM(connection);
944
945    LOG(Network, "WebCoreSynchronousLoader delegate connection:%p didReceiveAuthenticationChallenge:%p", connection, challenge);
946
947    if (m_user && m_pass) {
948        NSURLCredential *credential = [[NSURLCredential alloc] initWithUser:m_user
949                                                                   password:m_pass
950                                                                persistence:NSURLCredentialPersistenceNone];
951        KURL urlToStore;
952        if ([[challenge failureResponse] isKindOfClass:[NSHTTPURLResponse class]] && [(NSHTTPURLResponse*)[challenge failureResponse] statusCode] == 401)
953            urlToStore = m_url;
954        CredentialStorage::set(core(credential), core([challenge protectionSpace]), urlToStore);
955
956        [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
957        [credential release];
958        [m_user release];
959        [m_pass release];
960        m_user = 0;
961        m_pass = 0;
962        return;
963    }
964    if ([challenge previousFailureCount] == 0 && m_allowStoredCredentials) {
965        Credential credential = CredentialStorage::get(core([challenge protectionSpace]));
966        if (!credential.isEmpty() && credential != m_initialCredential) {
967            ASSERT(credential.persistence() == CredentialPersistenceNone);
968            [[challenge sender] useCredential:mac(credential) forAuthenticationChallenge:challenge];
969            return;
970        }
971    }
972    // FIXME: The user should be asked for credentials, as in async case.
973    [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];
974}
975
976- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
977{
978    UNUSED_PARAM(connection);
979
980    LOG(Network, "WebCoreSynchronousLoader delegate connection:%p didReceiveResponse:%p (HTTP status %d, reported MIMEType '%s')", connection, response, [response respondsToSelector:@selector(statusCode)] ? [(id)response statusCode] : 0, [[response MIMEType] UTF8String]);
981
982    NSURLResponse *r = [response copy];
983
984    [m_response release];
985    m_response = r;
986}
987
988- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
989{
990    UNUSED_PARAM(connection);
991
992    LOG(Network, "WebCoreSynchronousLoader delegate connection:%p didReceiveData:%p", connection, data);
993
994    if (!m_data)
995        m_data = [[NSMutableData alloc] init];
996
997    [m_data appendData:data];
998}
999
1000- (void)connectionDidFinishLoading:(NSURLConnection *)connection
1001{
1002    UNUSED_PARAM(connection);
1003
1004    LOG(Network, "WebCoreSynchronousLoader delegate connectionDidFinishLoading:%p", connection);
1005
1006    m_isDone = YES;
1007}
1008
1009- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
1010{
1011    UNUSED_PARAM(connection);
1012
1013    LOG(Network, "WebCoreSynchronousLoader delegate connection:%p didFailWithError:%@", connection, error);
1014
1015    ASSERT(!m_error);
1016
1017    m_error = [error retain];
1018    m_isDone = YES;
1019}
1020
1021- (NSData *)_data
1022{
1023    return [[m_data retain] autorelease];
1024}
1025
1026- (NSURLResponse *)_response
1027{
1028    return [[m_response retain] autorelease];
1029}
1030
1031- (NSError *)_error
1032{
1033    return [[m_error retain] autorelease];
1034}
1035
1036+ (NSData *)loadRequest:(NSURLRequest *)request allowStoredCredentials:(BOOL)allowStoredCredentials returningResponse:(NSURLResponse **)response error:(NSError **)error
1037{
1038    LOG(Network, "WebCoreSynchronousLoader loadRequest:%@ allowStoredCredentials:%u", request, allowStoredCredentials);
1039
1040    WebCoreSynchronousLoader *delegate = [[WebCoreSynchronousLoader alloc] init];
1041
1042    KURL url([request URL]);
1043    delegate->m_user = [nsStringNilIfEmpty(url.user()) retain];
1044    delegate->m_pass = [nsStringNilIfEmpty(url.pass()) retain];
1045    delegate->m_allowStoredCredentials = allowStoredCredentials;
1046
1047    NSURLConnection *connection;
1048
1049    // Take user/pass out of the URL.
1050    // Credentials for ftp can only be passed in URL, the connection:didReceiveAuthenticationChallenge: delegate call won't be made.
1051    if ((delegate->m_user || delegate->m_pass) && url.protocolInHTTPFamily()) {
1052        ResourceRequest requestWithoutCredentials = request;
1053        requestWithoutCredentials.removeCredentials();
1054        connection = [[NSURLConnection alloc] initWithRequest:requestWithoutCredentials.nsURLRequest() delegate:delegate startImmediately:NO];
1055    } else {
1056        // <rdar://problem/7174050> - For URLs that match the paths of those previously challenged for HTTP Basic authentication,
1057        // try and reuse the credential preemptively, as allowed by RFC 2617.
1058        ResourceRequest requestWithInitialCredentials = request;
1059        if (allowStoredCredentials && url.protocolInHTTPFamily())
1060            delegate->m_initialCredential = CredentialStorage::get(url);
1061
1062        if (!delegate->m_initialCredential.isEmpty()) {
1063            String authHeader = "Basic " + encodeBasicAuthorization(delegate->m_initialCredential.user(), delegate->m_initialCredential.password());
1064            requestWithInitialCredentials.addHTTPHeaderField("Authorization", authHeader);
1065        }
1066        connection = [[NSURLConnection alloc] initWithRequest:requestWithInitialCredentials.nsURLRequest() delegate:delegate startImmediately:NO];
1067    }
1068
1069    [connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:WebCoreSynchronousLoaderRunLoopMode];
1070    [connection start];
1071
1072    while (![delegate _isDone])
1073        [[NSRunLoop currentRunLoop] runMode:WebCoreSynchronousLoaderRunLoopMode beforeDate:[NSDate distantFuture]];
1074
1075    NSData *data = [delegate _data];
1076    *response = [delegate _response];
1077    *error = [delegate _error];
1078
1079    [connection cancel];
1080
1081    [connection release];
1082    [delegate release];
1083
1084    LOG(Network, "WebCoreSynchronousLoader done");
1085
1086    return data;
1087}
1088
1089@end
1090
1091#endif
1092