1/*
2 * Copyright (C) 2004, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 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 INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#import "config.h"
27#import "WebCoreResourceHandleAsDelegate.h"
28
29#if !USE(CFNETWORK)
30
31#import "AuthenticationChallenge.h"
32#import "AuthenticationMac.h"
33#import "Logging.h"
34#import "ResourceHandle.h"
35#import "ResourceHandleClient.h"
36#import "ResourceRequest.h"
37#import "ResourceResponse.h"
38#import "SharedBuffer.h"
39#import "WebCoreURLResponse.h"
40
41@interface NSURLRequest (Details)
42- (id)_propertyForKey:(NSString *)key;
43@end
44
45using namespace WebCore;
46
47@implementation WebCoreResourceHandleAsDelegate
48
49- (id)initWithHandle:(ResourceHandle*)handle
50{
51    self = [self init];
52    if (!self)
53        return nil;
54    m_handle = handle;
55    return self;
56}
57
58- (void)detachHandle
59{
60    m_handle = 0;
61}
62
63- (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)newRequest redirectResponse:(NSURLResponse *)redirectResponse
64{
65    UNUSED_PARAM(connection);
66
67    if (!m_handle)
68        return nil;
69
70    redirectResponse = synthesizeRedirectResponseIfNecessary(connection, newRequest, redirectResponse);
71
72    // See <rdar://problem/5380697>. This is a workaround for a behavior change in CFNetwork where willSendRequest gets called more often.
73    if (!redirectResponse)
74        return newRequest;
75
76#if !LOG_DISABLED
77    if ([redirectResponse isKindOfClass:[NSHTTPURLResponse class]])
78        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"]);
79    else
80        LOG(Network, "Handle %p delegate connection:%p willSendRequest:%@ redirectResponse:non-HTTP", m_handle, connection, [newRequest description]);
81#endif
82
83    ResourceRequest request = newRequest;
84
85    m_handle->willSendRequest(request, redirectResponse);
86
87    return request.nsURLRequest(UpdateHTTPBody);
88}
89
90- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection
91{
92    UNUSED_PARAM(connection);
93
94    LOG(Network, "Handle %p delegate connectionShouldUseCredentialStorage:%p", m_handle, connection);
95
96#if PLATFORM(IOS)
97    return NO;
98#else
99    if (!m_handle)
100        return NO;
101
102    return m_handle->shouldUseCredentialStorage();
103#endif
104}
105
106- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
107{
108    UNUSED_PARAM(connection);
109
110    LOG(Network, "Handle %p delegate connection:%p didReceiveAuthenticationChallenge:%p", m_handle, connection, challenge);
111
112    if (!m_handle) {
113        [[challenge sender] cancelAuthenticationChallenge:challenge];
114        return;
115    }
116    m_handle->didReceiveAuthenticationChallenge(core(challenge));
117}
118
119- (void)connection:(NSURLConnection *)connection didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
120{
121    // FIXME: We probably don't need to implement this (see <rdar://problem/8960124>).
122
123    UNUSED_PARAM(connection);
124
125    LOG(Network, "Handle %p delegate connection:%p didCancelAuthenticationChallenge:%p", m_handle, connection, challenge);
126
127    if (!m_handle)
128        return;
129    m_handle->didCancelAuthenticationChallenge(core(challenge));
130}
131
132#if USE(PROTECTION_SPACE_AUTH_CALLBACK)
133- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
134{
135    UNUSED_PARAM(connection);
136
137    LOG(Network, "Handle %p delegate connection:%p canAuthenticateAgainstProtectionSpace:%@://%@:%u realm:%@ method:%@ %@%@", m_handle, connection, [protectionSpace protocol], [protectionSpace host], [protectionSpace port], [protectionSpace realm], [protectionSpace authenticationMethod], [protectionSpace isProxy] ? @"proxy:" : @"", [protectionSpace isProxy] ? [protectionSpace proxyType] : @"");
138
139    if (!m_handle)
140        return NO;
141
142    return m_handle->canAuthenticateAgainstProtectionSpace(ProtectionSpace(protectionSpace));
143}
144#endif
145
146- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)r
147{
148#if !PLATFORM(IOS)
149    UNUSED_PARAM(connection);
150#endif
151
152    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]);
153
154    if (!m_handle || !m_handle->client())
155        return;
156
157    // Avoid MIME type sniffing if the response comes back as 304 Not Modified.
158    int statusCode = [r respondsToSelector:@selector(statusCode)] ? [(id)r statusCode] : 0;
159    if (statusCode != 304)
160        adjustMIMETypeIfNecessary([r _CFURLResponse]);
161
162#if !PLATFORM(IOS)
163    if ([m_handle->firstRequest().nsURLRequest(DoNotUpdateHTTPBody) _propertyForKey:@"ForceHTMLMIMEType"])
164        [r _setMIMEType:@"text/html"];
165#endif
166
167#if USE(QUICK_LOOK)
168    m_handle->setQuickLookHandle(QuickLookHandle::create(m_handle, connection, r, self));
169    if (m_handle->quickLookHandle())
170        r = m_handle->quickLookHandle()->nsResponse();
171#endif
172
173    ResourceResponse resourceResponse(r);
174#if ENABLE(WEB_TIMING)
175    ResourceHandle::getConnectionTimingData(connection, resourceResponse.resourceLoadTiming());
176#else
177    UNUSED_PARAM(connection);
178#endif
179
180    m_handle->client()->didReceiveResponse(m_handle, resourceResponse);
181}
182
183#if USE(NETWORK_CFDATA_ARRAY_CALLBACK)
184- (void)connection:(NSURLConnection *)connection didReceiveDataArray:(NSArray *)dataArray
185{
186    UNUSED_PARAM(connection);
187    LOG(Network, "Handle %p delegate connection:%p didReceiveDataArray:%p arraySize:%d", m_handle, connection, dataArray, [dataArray count]);
188
189    if (!dataArray)
190        return;
191
192    if (!m_handle || !m_handle->client())
193        return;
194
195#if USE(QUICK_LOOK)
196    if (m_handle->quickLookHandle() && m_handle->quickLookHandle()->didReceiveDataArray(reinterpret_cast<CFArrayRef>(dataArray)))
197        return;
198#endif
199
200    m_handle->client()->didReceiveBuffer(m_handle, SharedBuffer::wrapCFDataArray(reinterpret_cast<CFArrayRef>(dataArray)), -1);
201    // The call to didReceiveData above can cancel a load, and if so, the delegate (self) could have been deallocated by this point.
202}
203#endif
204
205- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data lengthReceived:(long long)lengthReceived
206{
207    UNUSED_PARAM(connection);
208    UNUSED_PARAM(lengthReceived);
209
210    LOG(Network, "Handle %p delegate connection:%p didReceiveData:%p lengthReceived:%lld", m_handle, connection, data, lengthReceived);
211
212#if PLATFORM(IOS)
213    if ([data length] == 0) // <rdar://problem/5532931>
214        return;
215#endif
216
217    if (!m_handle || !m_handle->client())
218        return;
219    // FIXME: If we get more than 2B bytes in a single chunk, this code won't do the right thing.
220    // However, with today's computers and networking speeds, this won't happen in practice.
221    // Could be an issue with a giant local file.
222
223#if USE(QUICK_LOOK)
224    if (m_handle->quickLookHandle() && m_handle->quickLookHandle()->didReceiveData(reinterpret_cast<CFDataRef>(data)))
225        return;
226#endif
227
228    // FIXME: https://bugs.webkit.org/show_bug.cgi?id=19793
229    // -1 means we do not provide any data about transfer size to inspector so it would use
230    // Content-Length headers or content size to show transfer size.
231    m_handle->client()->didReceiveBuffer(m_handle, SharedBuffer::wrapNSData(data), -1);
232}
233
234- (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite
235{
236    UNUSED_PARAM(connection);
237    UNUSED_PARAM(bytesWritten);
238
239    LOG(Network, "Handle %p delegate connection:%p didSendBodyData:%d totalBytesWritten:%d totalBytesExpectedToWrite:%d", m_handle, connection, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
240
241    if (!m_handle || !m_handle->client())
242        return;
243    m_handle->client()->didSendData(m_handle, totalBytesWritten, totalBytesExpectedToWrite);
244}
245
246- (void)connectionDidFinishLoading:(NSURLConnection *)connection
247{
248    UNUSED_PARAM(connection);
249
250    LOG(Network, "Handle %p delegate connectionDidFinishLoading:%p", m_handle, connection);
251
252    if (!m_handle || !m_handle->client())
253        return;
254
255#if USE(QUICK_LOOK)
256    if (m_handle->quickLookHandle() && m_handle->quickLookHandle()->didFinishLoading())
257        return;
258#endif
259
260    m_handle->client()->didFinishLoading(m_handle, 0);
261}
262
263- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
264{
265    UNUSED_PARAM(connection);
266
267    LOG(Network, "Handle %p delegate connection:%p didFailWithError:%@", m_handle, connection, error);
268
269    if (!m_handle || !m_handle->client())
270        return;
271
272#if USE(QUICK_LOOK)
273    if (m_handle->quickLookHandle())
274        m_handle->quickLookHandle()->didFail();
275#endif
276
277    m_handle->client()->didFail(m_handle, error);
278}
279
280
281- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse
282{
283    LOG(Network, "Handle %p delegate connection:%p willCacheResponse:%p", m_handle, connection, cachedResponse);
284
285    UNUSED_PARAM(connection);
286
287    if (!m_handle || !m_handle->client())
288        return nil;
289
290    return m_handle->client()->willCacheResponse(m_handle, cachedResponse);
291}
292
293@end
294
295#endif // !USE(CFNETWORK)
296
297