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 "WebCoreResourceHandleAsOperationQueueDelegate.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#import <wtf/MainThread.h> 41 42@interface NSURLRequest (Details) 43- (id)_propertyForKey:(NSString *)key; 44@end 45 46using namespace WebCore; 47 48@implementation WebCoreResourceHandleAsOperationQueueDelegate 49 50- (id)initWithHandle:(ResourceHandle*)handle 51{ 52 self = [self init]; 53 if (!self) 54 return nil; 55 56 m_handle = handle; 57 m_semaphore = dispatch_semaphore_create(0); 58 59 return self; 60} 61 62- (void)detachHandle 63{ 64 m_handle = 0; 65 66 m_requestResult = nullptr; 67 m_cachedResponseResult = nullptr; 68 m_boolResult = NO; 69 dispatch_semaphore_signal(m_semaphore); // OK to signal even if we are not waiting. 70} 71 72- (void)dealloc 73{ 74 dispatch_release(m_semaphore); 75 [super dealloc]; 76} 77 78- (void)continueWillSendRequest:(NSURLRequest *)newRequest 79{ 80 m_requestResult = newRequest; 81 dispatch_semaphore_signal(m_semaphore); 82} 83 84- (void)continueDidReceiveResponse 85{ 86 dispatch_semaphore_signal(m_semaphore); 87} 88 89- (void)continueCanAuthenticateAgainstProtectionSpace:(BOOL)canAuthenticate 90{ 91 m_boolResult = canAuthenticate; 92 dispatch_semaphore_signal(m_semaphore); 93} 94 95- (void)continueWillCacheResponse:(NSCachedURLResponse *)response 96{ 97 m_cachedResponseResult = response; 98 dispatch_semaphore_signal(m_semaphore); 99} 100 101- (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)newRequest redirectResponse:(NSURLResponse *)redirectResponse 102{ 103 ASSERT(!isMainThread()); 104 UNUSED_PARAM(connection); 105 106 redirectResponse = synthesizeRedirectResponseIfNecessary(connection, newRequest, redirectResponse); 107 108 // See <rdar://problem/5380697>. This is a workaround for a behavior change in CFNetwork where willSendRequest gets called more often. 109 if (!redirectResponse) 110 return newRequest; 111 112#if !LOG_DISABLED 113 if ([redirectResponse isKindOfClass:[NSHTTPURLResponse class]]) 114 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"]); 115 else 116 LOG(Network, "Handle %p delegate connection:%p willSendRequest:%@ redirectResponse:non-HTTP", m_handle, connection, [newRequest description]); 117#endif 118 119 RetainPtr<id> protector(self); 120 121 dispatch_async(dispatch_get_main_queue(), ^{ 122 if (!m_handle) { 123 m_requestResult = nullptr; 124 dispatch_semaphore_signal(m_semaphore); 125 return; 126 } 127 128 ResourceRequest request = newRequest; 129 130 m_handle->willSendRequest(request, redirectResponse); 131 }); 132 133 dispatch_semaphore_wait(m_semaphore, DISPATCH_TIME_FOREVER); 134 return m_requestResult.autorelease(); 135} 136 137- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge 138{ 139 ASSERT(!isMainThread()); 140 UNUSED_PARAM(connection); 141 142 LOG(Network, "Handle %p delegate connection:%p didReceiveAuthenticationChallenge:%p", m_handle, connection, challenge); 143 144 dispatch_async(dispatch_get_main_queue(), ^{ 145 if (!m_handle) { 146 [[challenge sender] cancelAuthenticationChallenge:challenge]; 147 return; 148 } 149 m_handle->didReceiveAuthenticationChallenge(core(challenge)); 150 }); 151} 152 153- (void)connection:(NSURLConnection *)connection didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge 154{ 155 // FIXME: We probably don't need to implement this (see <rdar://problem/8960124>). 156 157 ASSERT(!isMainThread()); 158 UNUSED_PARAM(connection); 159 160 LOG(Network, "Handle %p delegate connection:%p didCancelAuthenticationChallenge:%p", m_handle, connection, challenge); 161 162 dispatch_async(dispatch_get_main_queue(), ^{ 163 if (!m_handle) 164 return; 165 m_handle->didCancelAuthenticationChallenge(core(challenge)); 166 }); 167} 168 169#if USE(PROTECTION_SPACE_AUTH_CALLBACK) 170- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace 171{ 172 ASSERT(!isMainThread()); 173 UNUSED_PARAM(connection); 174 175 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] : @""); 176 177 RetainPtr<id> protector(self); 178 179 dispatch_async(dispatch_get_main_queue(), ^{ 180 if (!m_handle) { 181 m_boolResult = NO; 182 dispatch_semaphore_signal(m_semaphore); 183 return; 184 } 185 m_handle->canAuthenticateAgainstProtectionSpace(ProtectionSpace(protectionSpace)); 186 }); 187 188 dispatch_semaphore_wait(m_semaphore, DISPATCH_TIME_FOREVER); 189 return m_boolResult; 190} 191#endif 192 193- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)r 194{ 195 ASSERT(!isMainThread()); 196 197 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]); 198 199 RetainPtr<id> protector(self); 200 201 dispatch_async(dispatch_get_main_queue(), ^{ 202 if (!m_handle) { 203 dispatch_semaphore_signal(m_semaphore); 204 return; 205 } 206 207 // Avoid MIME type sniffing if the response comes back as 304 Not Modified. 208 int statusCode = [r respondsToSelector:@selector(statusCode)] ? [(id)r statusCode] : 0; 209 if (statusCode != 304) 210 adjustMIMETypeIfNecessary([r _CFURLResponse]); 211 212 if ([m_handle->firstRequest().nsURLRequest(DoNotUpdateHTTPBody) _propertyForKey:@"ForceHTMLMIMEType"]) 213 [r _setMIMEType:@"text/html"]; 214 215 ResourceResponse resourceResponse(r); 216#if ENABLE(WEB_TIMING) 217 ResourceHandle::getConnectionTimingData(connection, resourceResponse.resourceLoadTiming()); 218#else 219 UNUSED_PARAM(connection); 220#endif 221 m_handle->client()->didReceiveResponseAsync(m_handle, resourceResponse); 222 }); 223 224 dispatch_semaphore_wait(m_semaphore, DISPATCH_TIME_FOREVER); 225} 226 227#if USE(NETWORK_CFDATA_ARRAY_CALLBACK) 228- (void)connection:(NSURLConnection *)connection didReceiveDataArray:(NSArray *)dataArray 229{ 230 ASSERT(!isMainThread()); 231 UNUSED_PARAM(connection); 232 233 LOG(Network, "Handle %p delegate connection:%p didReceiveDataArray:%p arraySize:%d", m_handle, connection, dataArray, [dataArray count]); 234 235 dispatch_async(dispatch_get_main_queue(), ^{ 236 if (!dataArray) 237 return; 238 239 if (!m_handle || !m_handle->client()) 240 return; 241 242 m_handle->client()->didReceiveBuffer(m_handle, SharedBuffer::wrapCFDataArray(reinterpret_cast<CFArrayRef>(dataArray)), -1); 243 // The call to didReceiveData above can cancel a load, and if so, the delegate (self) could have been deallocated by this point. 244 }); 245} 246#endif 247 248- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data lengthReceived:(long long)lengthReceived 249{ 250 ASSERT(!isMainThread()); 251 UNUSED_PARAM(connection); 252 UNUSED_PARAM(lengthReceived); 253 254 LOG(Network, "Handle %p delegate connection:%p didReceiveData:%p lengthReceived:%lld", m_handle, connection, data, lengthReceived); 255 256 dispatch_async(dispatch_get_main_queue(), ^{ 257 if (!m_handle || !m_handle->client()) 258 return; 259 // FIXME: If we get more than 2B bytes in a single chunk, this code won't do the right thing. 260 // However, with today's computers and networking speeds, this won't happen in practice. 261 // Could be an issue with a giant local file. 262 263 // FIXME: https://bugs.webkit.org/show_bug.cgi?id=19793 264 // -1 means we do not provide any data about transfer size to inspector so it would use 265 // Content-Length headers or content size to show transfer size. 266 m_handle->client()->didReceiveBuffer(m_handle, SharedBuffer::wrapNSData(data), -1); 267 }); 268} 269 270- (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite 271{ 272 ASSERT(!isMainThread()); 273 UNUSED_PARAM(connection); 274 UNUSED_PARAM(bytesWritten); 275 276 LOG(Network, "Handle %p delegate connection:%p didSendBodyData:%d totalBytesWritten:%d totalBytesExpectedToWrite:%d", m_handle, connection, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite); 277 278 dispatch_async(dispatch_get_main_queue(), ^{ 279 if (!m_handle || !m_handle->client()) 280 return; 281 m_handle->client()->didSendData(m_handle, totalBytesWritten, totalBytesExpectedToWrite); 282 }); 283} 284 285- (void)connectionDidFinishLoading:(NSURLConnection *)connection 286{ 287 ASSERT(!isMainThread()); 288 UNUSED_PARAM(connection); 289 290 LOG(Network, "Handle %p delegate connectionDidFinishLoading:%p", m_handle, connection); 291 292 dispatch_async(dispatch_get_main_queue(), ^{ 293 if (!m_handle || !m_handle->client()) 294 return; 295 296 m_handle->client()->didFinishLoading(m_handle, 0); 297 }); 298} 299 300- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error 301{ 302 ASSERT(!isMainThread()); 303 UNUSED_PARAM(connection); 304 305 LOG(Network, "Handle %p delegate connection:%p didFailWithError:%@", m_handle, connection, error); 306 307 dispatch_async(dispatch_get_main_queue(), ^{ 308 if (!m_handle || !m_handle->client()) 309 return; 310 311 m_handle->client()->didFail(m_handle, error); 312 }); 313} 314 315 316- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse 317{ 318 ASSERT(!isMainThread()); 319 UNUSED_PARAM(connection); 320 321 LOG(Network, "Handle %p delegate connection:%p willCacheResponse:%p", m_handle, connection, cachedResponse); 322 323 RetainPtr<id> protector(self); 324 325 dispatch_async(dispatch_get_main_queue(), ^{ 326 if (!m_handle || !m_handle->client()) { 327 m_cachedResponseResult = nullptr; 328 dispatch_semaphore_signal(m_semaphore); 329 return; 330 } 331 332 m_handle->client()->willCacheResponseAsync(m_handle, cachedResponse); 333 }); 334 335 dispatch_semaphore_wait(m_semaphore, DISPATCH_TIME_FOREVER); 336 return m_cachedResponseResult.autorelease(); 337} 338 339@end 340 341@implementation WebCoreResourceHandleWithCredentialStorageAsOperationQueueDelegate 342 343- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection 344{ 345 ASSERT(!isMainThread()); 346 UNUSED_PARAM(connection); 347 return NO; 348} 349 350@end 351 352#endif // !USE(CFNETWORK) 353 354