4360c8f707dcd91ae7500ee504d4cb96be0269ff
[WebKit-https.git] / WebCore / platform / network / mac / ResourceHandleMac.mm
1 /*
2  * Copyright (C) 2004, 2006 Apple Computer, 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 "ResourceHandle.h"
28 #import "ResourceHandleInternal.h"
29
30 #import "AuthenticationMac.h"
31 #import "BlockExceptions.h"
32 #import "DocLoader.h"
33 #import "Frame.h"
34 #import "FrameLoader.h"
35 #import "ResourceError.h"
36 #import "ResourceResponse.h"
37 #import "SharedBuffer.h"
38 #import "SubresourceLoader.h"
39 #import "AuthenticationChallenge.h"
40 #import "WebCoreSystemInterface.h"
41
42 using namespace WebCore;
43
44 @interface WebCoreResourceHandleAsDelegate : NSObject <NSURLAuthenticationChallengeSender>
45 {
46     ResourceHandle* m_handle;
47 #ifndef BUILDING_ON_TIGER
48     NSURL *m_url;
49 #endif
50 }
51 - (id)initWithHandle:(ResourceHandle*)handle;
52 - (void)detachHandle;
53 @end
54
55 @interface NSURLConnection (NSURLConnectionTigerPrivate)
56 - (NSData *)_bufferedData;
57 @end
58
59 @interface NSURLProtocol (WebFoundationSecret) 
60 + (void)_removePropertyForKey:(NSString *)key inRequest:(NSMutableURLRequest *)request;
61 @end
62
63 #ifndef BUILDING_ON_TIGER
64 @interface WebCoreSynchronousLoader : NSObject {
65     NSURL *m_url;
66     NSURLResponse *m_response;
67     NSMutableData *m_data;
68     NSError *m_error;
69     BOOL m_isDone;
70 }
71 + (NSData *)loadRequest:(NSURLRequest *)request returningResponse:(NSURLResponse **)response error:(NSError **)error;
72 @end
73
74 static NSString *WebCoreSynchronousLoaderRunLoopMode = @"WebCoreSynchronousLoaderRunLoopMode";
75 #endif
76
77 namespace WebCore {
78    
79 static unsigned inNSURLConnectionCallback;
80 static bool NSURLConnectionSupportsBufferedData;
81     
82 #ifndef NDEBUG
83 static bool isInitializingConnection;
84 #endif
85     
86 ResourceHandleInternal::~ResourceHandleInternal()
87 {
88 }
89
90 ResourceHandle::~ResourceHandle()
91 {
92     releaseDelegate();
93 }
94
95 bool ResourceHandle::start(Frame* frame)
96 {
97     if (!frame)
98         return false;
99
100     BEGIN_BLOCK_OBJC_EXCEPTIONS;
101
102     // If we are no longer attached to a Page, this must be an attempted load from an
103     // onUnload handler, so let's just block it.
104     if (!frame->page())
105         return false;
106   
107 #ifndef NDEBUG
108     isInitializingConnection = YES;
109 #endif
110     id delegate;
111     
112     if (d->m_mightDownloadFromHandle) {
113         ASSERT(!d->m_proxy);
114         d->m_proxy = wkCreateNSURLConnectionDelegateProxy();
115         [d->m_proxy.get() setDelegate:ResourceHandle::delegate()];
116         [d->m_proxy.get() release];
117         
118         delegate = d->m_proxy.get();
119     } else 
120         delegate = ResourceHandle::delegate();
121     
122     NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:d->m_request.nsURLRequest() delegate:delegate];
123 #ifndef NDEBUG
124     isInitializingConnection = NO;
125 #endif
126     d->m_connection = connection;
127     [connection release];
128     if (d->m_defersLoading)
129         wkSetNSURLConnectionDefersCallbacks(d->m_connection.get(), YES);
130     
131     if (d->m_connection)
132         return true;
133
134     END_BLOCK_OBJC_EXCEPTIONS;
135
136     return false;
137 }
138
139 void ResourceHandle::cancel()
140 {
141     [d->m_connection.get() cancel];
142 }
143
144 void ResourceHandle::setDefersLoading(bool defers)
145 {
146     d->m_defersLoading = defers;
147     wkSetNSURLConnectionDefersCallbacks(d->m_connection.get(), defers);
148 }
149
150 WebCoreResourceHandleAsDelegate *ResourceHandle::delegate()
151 {
152     if (!d->m_delegate) {
153         WebCoreResourceHandleAsDelegate *delegate = [[WebCoreResourceHandleAsDelegate alloc] initWithHandle:this];
154         d->m_delegate = delegate;
155         [delegate release];
156     }
157     return d->m_delegate.get();
158 }
159
160 void ResourceHandle::releaseDelegate()
161 {
162     if (!d->m_delegate)
163         return;
164     if (d->m_proxy)
165         [d->m_proxy.get() setDelegate:nil];
166     [d->m_delegate.get() detachHandle];
167     d->m_delegate = nil;
168 }
169
170 bool ResourceHandle::supportsBufferedData()
171 {
172     static bool initialized = false;
173     if (!initialized) {
174         NSURLConnectionSupportsBufferedData = [NSURLConnection instancesRespondToSelector:@selector(_bufferedData)];
175         initialized = true;
176     }
177
178     return NSURLConnectionSupportsBufferedData;
179 }
180
181 PassRefPtr<SharedBuffer> ResourceHandle::bufferedData()
182 {
183     if (ResourceHandle::supportsBufferedData())
184         return SharedBuffer::wrapNSData([d->m_connection.get() _bufferedData]);
185
186     return 0;
187 }
188
189 id ResourceHandle::releaseProxy()
190 {
191     id proxy = [[d->m_proxy.get() retain] autorelease];
192     d->m_proxy = nil;
193     [proxy setDelegate:nil];
194     return proxy;
195 }
196
197 NSURLConnection *ResourceHandle::connection() const
198 {
199     return d->m_connection.get();
200 }
201
202 bool ResourceHandle::loadsBlocked()
203 {
204     return inNSURLConnectionCallback != 0;
205 }
206
207 bool ResourceHandle::willLoadFromCache(ResourceRequest& request)
208 {
209     request.setCachePolicy(ReturnCacheDataDontLoad);
210     NSURLResponse *nsURLResponse = nil;
211     BEGIN_BLOCK_OBJC_EXCEPTIONS;
212     
213    [NSURLConnection sendSynchronousRequest:request.nsURLRequest() returningResponse:&nsURLResponse error:nil];
214     
215     END_BLOCK_OBJC_EXCEPTIONS;
216     
217     return nsURLResponse;
218 }
219
220 void ResourceHandle::loadResourceSynchronously(const ResourceRequest& request, ResourceError& error, ResourceResponse& response, Vector<char>& data)
221 {
222     NSError *nsError = nil;
223     
224     NSURLResponse *nsURLResponse = nil;
225     NSData *result = nil;
226
227     ASSERT(!request.isEmpty());
228     
229     BEGIN_BLOCK_OBJC_EXCEPTIONS;
230     
231 #ifndef BUILDING_ON_TIGER
232     result = [WebCoreSynchronousLoader loadRequest:request.nsURLRequest() returningResponse:&nsURLResponse error:&nsError];
233 #else
234     result = [NSURLConnection sendSynchronousRequest:request.nsURLRequest() returningResponse:&nsURLResponse error:&nsError];
235 #endif
236     END_BLOCK_OBJC_EXCEPTIONS;
237
238     if (nsError == nil)
239         response = nsURLResponse;
240     else {
241         response = ResourceResponse(request.url(), String(), 0, String(), String());
242         if ([nsError domain] == NSURLErrorDomain)
243             switch ([nsError code]) {
244                 case NSURLErrorUserCancelledAuthentication:
245                     // FIXME: we should really return the actual HTTP response, but sendSynchronousRequest doesn't provide us with one.
246                     response.setHTTPStatusCode(401);
247                     break;
248                 default:
249                     response.setHTTPStatusCode([nsError code]);
250             }
251         else
252             response.setHTTPStatusCode(404);
253     }
254     
255     data.resize([result length]);
256     memcpy(data.data(), [result bytes], [result length]);
257     
258     error = nsError;
259 }
260
261 void ResourceHandle::didReceiveAuthenticationChallenge(const AuthenticationChallenge& challenge)
262 {
263     ASSERT(!d->m_currentMacChallenge);
264     ASSERT(d->m_currentWebChallenge.isNull());
265     // Since NSURLConnection networking relies on keeping a reference to the original NSURLAuthenticationChallenge,
266     // we make sure that is actually present
267     ASSERT(challenge.nsURLAuthenticationChallenge());
268         
269     d->m_currentMacChallenge = challenge.nsURLAuthenticationChallenge();
270     NSURLAuthenticationChallenge *webChallenge = [[NSURLAuthenticationChallenge alloc] initWithAuthenticationChallenge:d->m_currentMacChallenge 
271                                                                                        sender:(id<NSURLAuthenticationChallengeSender>)delegate()];
272     d->m_currentWebChallenge = core(webChallenge);
273     [webChallenge release];
274
275     if (client())
276         client()->didReceiveAuthenticationChallenge(this, d->m_currentWebChallenge);
277 }
278
279 void ResourceHandle::didCancelAuthenticationChallenge(const AuthenticationChallenge& challenge)
280 {
281     ASSERT(d->m_currentMacChallenge);
282     ASSERT(!d->m_currentWebChallenge.isNull());
283     ASSERT(d->m_currentWebChallenge == challenge);
284
285     if (client())
286         client()->didCancelAuthenticationChallenge(this, d->m_currentWebChallenge);
287 }
288
289 void ResourceHandle::receivedCredential(const AuthenticationChallenge& challenge, const Credential& credential)
290 {
291     ASSERT(!challenge.isNull());
292     if (challenge != d->m_currentWebChallenge)
293         return;
294
295     [[d->m_currentMacChallenge sender] useCredential:mac(credential) forAuthenticationChallenge:d->m_currentMacChallenge];
296
297     clearAuthentication();
298 }
299
300 void ResourceHandle::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge& challenge)
301 {
302     ASSERT(!challenge.isNull());
303     if (challenge != d->m_currentWebChallenge)
304         return;
305
306     [[d->m_currentMacChallenge sender] continueWithoutCredentialForAuthenticationChallenge:d->m_currentMacChallenge];
307
308     clearAuthentication();
309 }
310
311 void ResourceHandle::receivedCancellation(const AuthenticationChallenge& challenge)
312 {
313     if (challenge != d->m_currentWebChallenge)
314         return;
315
316     if (client())
317         client()->receivedCancellation(this, challenge);
318 }
319
320 } // namespace WebCore
321
322 @implementation WebCoreResourceHandleAsDelegate
323
324 - (id)initWithHandle:(ResourceHandle*)handle
325 {
326     self = [self init];
327     if (!self)
328         return nil;
329     m_handle = handle;
330     return self;
331 }
332
333 #ifndef BUILDING_ON_TIGER
334 - (void)dealloc
335 {
336     [m_url release];
337     [super dealloc];
338 }
339 #endif
340
341 - (void)detachHandle
342 {
343     m_handle = 0;
344 }
345
346 - (NSURLRequest *)connection:(NSURLConnection *)con willSendRequest:(NSURLRequest *)newRequest redirectResponse:(NSURLResponse *)redirectResponse
347 {
348     // the willSendRequest call may cancel this load, in which case self could be deallocated
349     RetainPtr<WebCoreResourceHandleAsDelegate> protect(self);
350
351     if (!m_handle || !m_handle->client())
352         return nil;
353     ++inNSURLConnectionCallback;
354     ResourceRequest request = newRequest;
355     m_handle->client()->willSendRequest(m_handle, request, redirectResponse);
356     --inNSURLConnectionCallback;
357 #ifndef BUILDING_ON_TIGER
358     NSURL *copy = [[request.nsURLRequest() URL] copy];
359     [m_url release];
360     m_url = copy;
361 #endif
362     
363     return request.nsURLRequest();
364 }
365
366 - (void)connection:(NSURLConnection *)con didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
367 {
368 #ifndef BUILDING_ON_TIGER
369     if ([challenge previousFailureCount] == 0) {
370         NSString *user = [m_url user];
371         NSString *password = [m_url password];
372
373         if (user && password) {
374             NSURLCredential *credential = [NSURLCredential credentialWithUser:user
375                                                                      password:password
376                                                                   persistence:NSURLCredentialPersistenceForSession];
377             [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
378             return;
379         }
380     }
381 #endif
382     
383     if (!m_handle)
384         return;
385     ++inNSURLConnectionCallback;
386     m_handle->didReceiveAuthenticationChallenge(core(challenge));
387     --inNSURLConnectionCallback;
388 }
389
390 - (void)connection:(NSURLConnection *)con didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
391 {
392     if (!m_handle)
393         return;
394     ++inNSURLConnectionCallback;
395     m_handle->didCancelAuthenticationChallenge(core(challenge));
396     --inNSURLConnectionCallback;
397 }
398
399 - (void)connection:(NSURLConnection *)con didReceiveResponse:(NSURLResponse *)r
400 {
401     if (!m_handle || !m_handle->client())
402         return;
403     ++inNSURLConnectionCallback;
404     m_handle->client()->didReceiveResponse(m_handle, r);
405     --inNSURLConnectionCallback;
406 }
407
408 - (void)connection:(NSURLConnection *)con didReceiveData:(NSData *)data lengthReceived:(long long)lengthReceived
409 {
410     if (!m_handle || !m_handle->client())
411         return;
412     // FIXME: If we get more than 2B bytes in a single chunk, this code won't do the right thing.
413     // However, with today's computers and networking speeds, this won't happen in practice.
414     // Could be an issue with a giant local file.
415     ++inNSURLConnectionCallback;
416     m_handle->client()->didReceiveData(m_handle, (const char*)[data bytes], [data length], static_cast<int>(lengthReceived));
417     --inNSURLConnectionCallback;
418 }
419
420 - (void)connection:(NSURLConnection *)con willStopBufferingData:(NSData *)data
421 {
422     if (!m_handle || !m_handle->client())
423         return;
424     // FIXME: If we get a resource with more than 2B bytes, this code won't do the right thing.
425     // However, with today's computers and networking speeds, this won't happen in practice.
426     // Could be an issue with a giant local file.
427     ++inNSURLConnectionCallback;
428     m_handle->client()->willStopBufferingData(m_handle, (const char*)[data bytes], static_cast<int>([data length]));
429     --inNSURLConnectionCallback;
430 }
431
432 - (void)connectionDidFinishLoading:(NSURLConnection *)con
433 {
434     if (!m_handle || !m_handle->client())
435         return;
436     ++inNSURLConnectionCallback;
437     m_handle->client()->didFinishLoading(m_handle);
438     --inNSURLConnectionCallback;
439 }
440
441 - (void)connection:(NSURLConnection *)con didFailWithError:(NSError *)error
442 {
443     if (!m_handle || !m_handle->client())
444         return;
445     ++inNSURLConnectionCallback;
446     m_handle->client()->didFail(m_handle, error);
447     --inNSURLConnectionCallback;
448 }
449
450 - (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse
451 {
452 #ifndef NDEBUG
453     if (isInitializingConnection)
454         LOG_ERROR("connection:willCacheResponse: was called inside of [NSURLConnection initWithRequest:delegate:] (4067625)");
455 #endif
456     if (!m_handle || !m_handle->client())
457         return nil;
458     ++inNSURLConnectionCallback;
459     
460     NSCachedURLResponse * newResponse = m_handle->client()->willCacheResponse(m_handle, cachedResponse);
461     if (newResponse != cachedResponse)
462         return newResponse;
463     
464     CacheStoragePolicy policy = static_cast<CacheStoragePolicy>([newResponse storagePolicy]);
465         
466     m_handle->client()->willCacheResponse(m_handle, policy);
467
468     if (static_cast<NSURLCacheStoragePolicy>(policy) != [newResponse storagePolicy])
469         newResponse = [[[NSCachedURLResponse alloc] initWithResponse:[newResponse response]
470                                                                    data:[newResponse data]
471                                                                userInfo:[newResponse userInfo]
472                                                           storagePolicy:static_cast<NSURLCacheStoragePolicy>(policy)] autorelease];
473
474     --inNSURLConnectionCallback;
475     return newResponse;
476 }
477
478 - (void)useCredential:(NSURLCredential *)credential forAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
479 {
480     if (!m_handle)
481         return;
482     m_handle->receivedCredential(core(challenge), core(credential));
483 }
484
485 - (void)continueWithoutCredentialForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
486 {
487     if (!m_handle)
488         return;
489     m_handle->receivedRequestToContinueWithoutCredential(core(challenge));
490 }
491
492 - (void)cancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
493 {
494     if (!m_handle)
495         return;
496     m_handle->receivedCancellation(core(challenge));
497 }
498
499 @end
500
501 #ifndef BUILDING_ON_TIGER
502 @implementation WebCoreSynchronousLoader
503
504 - (BOOL)_isDone
505 {
506     return m_isDone;
507 }
508
509 - (void)dealloc
510 {
511     [m_url release];
512     [m_response release];
513     [m_data release];
514     [m_error release];
515     
516     [super dealloc];
517 }
518
519 - (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)newRequest redirectResponse:(NSURLResponse *)redirectResponse
520 {
521     NSURL *copy = [[newRequest URL] copy];
522     [m_url release];
523     m_url = copy;
524
525     return newRequest;
526 }
527
528 - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
529 {
530     if ([challenge previousFailureCount] == 0) {
531         NSString *user = [m_url user];
532         NSString *password = [m_url password];
533         
534         if (user && password) {
535             NSURLCredential *credential = [NSURLCredential credentialWithUser:user
536                                                                      password:password
537                                                                   persistence:NSURLCredentialPersistenceForSession];
538             [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
539             return;
540         }
541     }
542     
543     [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];
544 }
545
546 - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
547 {
548     NSURLResponse *r = [response copy];
549     
550     [m_response release];
551     m_response = r;
552 }
553
554 - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
555 {
556     if (!m_data)
557         m_data = [[NSMutableData alloc] init];
558     
559     [m_data appendData:data];
560 }
561
562 - (void)connectionDidFinishLoading:(NSURLConnection *)connection
563 {
564     m_isDone = YES;
565 }
566
567 - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
568 {
569     ASSERT(!m_error);
570     
571     m_error = [error retain];
572     m_isDone = YES;
573 }
574
575 - (NSData *)_data
576 {
577     return [[m_data retain] autorelease];
578 }
579
580 - (NSURLResponse *)_response
581 {
582     return [[m_response retain] autorelease];
583 }
584
585 - (NSError *)_error
586 {
587     return [[m_error retain] autorelease];
588 }
589
590 + (NSData *)loadRequest:(NSURLRequest *)request returningResponse:(NSURLResponse **)response error:(NSError **)error
591 {
592     WebCoreSynchronousLoader *delegate = [[WebCoreSynchronousLoader alloc] init];
593     
594     NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:delegate startImmediately:NO];
595     [connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:WebCoreSynchronousLoaderRunLoopMode];
596     [connection start];
597     
598     while (![delegate _isDone])
599         [[NSRunLoop currentRunLoop] runMode:WebCoreSynchronousLoaderRunLoopMode beforeDate:[NSDate distantFuture]];
600
601     NSData *data = [delegate _data];
602     *response = [delegate _response];
603     *error = [delegate _error];
604     
605     [connection cancel];
606     
607     [connection release];
608     [delegate release];
609     
610     return data;
611 }
612
613 @end
614 #endif