1/* 2 * Copyright (C) 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. ``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 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#include "config.h" 27#include "ResourceHandleCFURLConnectionDelegateWithOperationQueue.h" 28 29#if USE(CFNETWORK) 30 31#include "AuthenticationCF.h" 32#include "AuthenticationChallenge.h" 33#include "Logging.h" 34#include "ResourceHandle.h" 35#include "ResourceHandleClient.h" 36#include "ResourceResponse.h" 37#include "SharedBuffer.h" 38#include "WebCoreSystemInterface.h" 39#include "WebCoreURLResponse.h" 40#include <wtf/MainThread.h> 41#include <wtf/text/CString.h> 42#include <wtf/text/WTFString.h> 43 44namespace WebCore { 45 46ResourceHandleCFURLConnectionDelegateWithOperationQueue::ResourceHandleCFURLConnectionDelegateWithOperationQueue(ResourceHandle* handle) 47 : ResourceHandleCFURLConnectionDelegate(handle) 48 , m_queue(dispatch_queue_create("com.apple.WebCore/CFNetwork", DISPATCH_QUEUE_SERIAL)) 49 , m_semaphore(dispatch_semaphore_create(0)) 50{ 51 dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); 52 dispatch_set_target_queue(m_queue, backgroundQueue); 53} 54 55ResourceHandleCFURLConnectionDelegateWithOperationQueue::~ResourceHandleCFURLConnectionDelegateWithOperationQueue() 56{ 57 dispatch_release(m_semaphore); 58 dispatch_release(m_queue); 59} 60 61bool ResourceHandleCFURLConnectionDelegateWithOperationQueue::hasHandle() const 62{ 63 return !!m_handle; 64} 65 66void ResourceHandleCFURLConnectionDelegateWithOperationQueue::setupRequest(CFMutableURLRequestRef request) 67{ 68#if PLATFORM(IOS) 69 CFURLRequestSetShouldStartSynchronously(request, 1); 70#endif 71 CFURLRef requestURL = CFURLRequestGetURL(request); 72 if (!requestURL) 73 return; 74 m_originalScheme = adoptCF(CFURLCopyScheme(requestURL)); 75} 76 77void ResourceHandleCFURLConnectionDelegateWithOperationQueue::setupConnectionScheduling(CFURLConnectionRef connection) 78{ 79 CFURLConnectionSetDelegateDispatchQueue(connection, m_queue); 80} 81 82CFURLRequestRef ResourceHandleCFURLConnectionDelegateWithOperationQueue::willSendRequest(CFURLRequestRef cfRequest, CFURLResponseRef originalRedirectResponse) 83{ 84 // If the protocols of the new request and the current request match, this is not an HSTS redirect and we don't need to synthesize a redirect response. 85 if (!originalRedirectResponse) { 86 RetainPtr<CFStringRef> newScheme = adoptCF(CFURLCopyScheme(CFURLRequestGetURL(cfRequest))); 87 if (CFStringCompare(newScheme.get(), m_originalScheme.get(), kCFCompareCaseInsensitive) == kCFCompareEqualTo) { 88 CFRetain(cfRequest); 89 return cfRequest; 90 } 91 } 92 93 ASSERT(!isMainThread()); 94 95 RefPtr<ResourceHandleCFURLConnectionDelegateWithOperationQueue> protector(this); 96 97 dispatch_async(dispatch_get_main_queue(), ^{ 98 if (!protector->hasHandle()) { 99 continueWillSendRequest(nullptr); 100 return; 101 } 102 103 LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::willSendRequest(handle=%p) (%s)", m_handle, m_handle->firstRequest().url().string().utf8().data()); 104 105 RetainPtr<CFURLResponseRef> redirectResponse = synthesizeRedirectResponseIfNecessary(cfRequest, originalRedirectResponse); 106 ASSERT(redirectResponse); 107 108 ResourceRequest request = createResourceRequest(cfRequest, redirectResponse.get()); 109 m_handle->willSendRequest(request, redirectResponse.get()); 110 }); 111 112 dispatch_semaphore_wait(m_semaphore, DISPATCH_TIME_FOREVER); 113 114 return m_requestResult.leakRef(); 115} 116 117void ResourceHandleCFURLConnectionDelegateWithOperationQueue::didReceiveResponse(CFURLConnectionRef connection, CFURLResponseRef cfResponse) 118{ 119 RefPtr<ResourceHandleCFURLConnectionDelegateWithOperationQueue> protector(this); 120 121 dispatch_async(dispatch_get_main_queue(), ^{ 122 if (!protector->hasHandle()) { 123 continueDidReceiveResponse(); 124 return; 125 } 126 127 LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::didReceiveResponse(handle=%p) (%s)", m_handle, m_handle->firstRequest().url().string().utf8().data()); 128 129 // Avoid MIME type sniffing if the response comes back as 304 Not Modified. 130 CFHTTPMessageRef msg = wkGetCFURLResponseHTTPResponse(cfResponse); 131 int statusCode = msg ? CFHTTPMessageGetResponseStatusCode(msg) : 0; 132 133 if (statusCode != 304) 134 adjustMIMETypeIfNecessary(cfResponse); 135 136#if !PLATFORM(IOS) 137 if (_CFURLRequestCopyProtocolPropertyForKey(m_handle->firstRequest().cfURLRequest(DoNotUpdateHTTPBody), CFSTR("ForceHTMLMIMEType"))) 138 wkSetCFURLResponseMIMEType(cfResponse, CFSTR("text/html")); 139#endif // !PLATFORM(IOS) 140 141 ResourceResponse resourceResponse(cfResponse); 142#if ENABLE(WEB_TIMING) 143 ResourceHandle::getConnectionTimingData(connection, resourceResponse.resourceLoadTiming()); 144#else 145 UNUSED_PARAM(connection); 146#endif 147 148 m_handle->client()->didReceiveResponseAsync(m_handle, resourceResponse); 149 }); 150 dispatch_semaphore_wait(m_semaphore, DISPATCH_TIME_FOREVER); 151} 152 153void ResourceHandleCFURLConnectionDelegateWithOperationQueue::didReceiveData(CFDataRef data, CFIndex originalLength) 154{ 155 RefPtr<ResourceHandleCFURLConnectionDelegateWithOperationQueue> protector(this); 156 CFRetain(data); 157 158 dispatch_async(dispatch_get_main_queue(), ^{ 159 if (protector->hasHandle()) { 160 LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::didReceiveData(handle=%p) (%s)", m_handle, m_handle->firstRequest().url().string().utf8().data()); 161 162 m_handle->client()->didReceiveBuffer(m_handle, SharedBuffer::wrapCFData(data), originalLength); 163 } 164 165 CFRelease(data); 166 }); 167} 168 169void ResourceHandleCFURLConnectionDelegateWithOperationQueue::didFinishLoading() 170{ 171 RefPtr<ResourceHandleCFURLConnectionDelegateWithOperationQueue> protector(this); 172 dispatch_async(dispatch_get_main_queue(), ^{ 173 if (!protector->hasHandle()) 174 return; 175 176 LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::didFinishLoading(handle=%p) (%s)", m_handle, m_handle->firstRequest().url().string().utf8().data()); 177 178 m_handle->client()->didFinishLoading(m_handle, 0); 179 }); 180} 181 182void ResourceHandleCFURLConnectionDelegateWithOperationQueue::didFail(CFErrorRef error) 183{ 184 RefPtr<ResourceHandleCFURLConnectionDelegateWithOperationQueue> protector(this); 185 CFRetain(error); 186 dispatch_async(dispatch_get_main_queue(), ^{ 187 if (protector->hasHandle()) { 188 LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::didFail(handle=%p) (%s)", m_handle, m_handle->firstRequest().url().string().utf8().data()); 189 190 m_handle->client()->didFail(m_handle, ResourceError(error)); 191 } 192 193 CFRelease(error); 194 }); 195} 196 197CFCachedURLResponseRef ResourceHandleCFURLConnectionDelegateWithOperationQueue::willCacheResponse(CFCachedURLResponseRef cachedResponse) 198{ 199 RefPtr<ResourceHandleCFURLConnectionDelegateWithOperationQueue> protector(this); 200 201 dispatch_async(dispatch_get_main_queue(), ^{ 202 if (!protector->hasHandle()) { 203 continueWillCacheResponse(nullptr); 204 return; 205 } 206 207 LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::willCacheResponse(handle=%p) (%s)", m_handle, m_handle->firstRequest().url().string().utf8().data()); 208 209 m_handle->client()->willCacheResponseAsync(m_handle, cachedResponse); 210 }); 211 dispatch_semaphore_wait(m_semaphore, DISPATCH_TIME_FOREVER); 212 return m_cachedResponseResult.leakRef(); 213} 214 215void ResourceHandleCFURLConnectionDelegateWithOperationQueue::didReceiveChallenge(CFURLAuthChallengeRef challenge) 216{ 217 RefPtr<ResourceHandleCFURLConnectionDelegateWithOperationQueue> protector(this); 218 CFRetain(challenge); 219 dispatch_async(dispatch_get_main_queue(), ^{ 220 if (protector->hasHandle()) { 221 LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::didReceiveChallenge(handle=%p) (%s)", m_handle, m_handle->firstRequest().url().string().utf8().data()); 222 223 m_handle->didReceiveAuthenticationChallenge(AuthenticationChallenge(challenge, m_handle)); 224 } 225 226 CFRelease(challenge); 227 }); 228} 229 230void ResourceHandleCFURLConnectionDelegateWithOperationQueue::didSendBodyData(CFIndex totalBytesWritten, CFIndex totalBytesExpectedToWrite) 231{ 232 RefPtr<ResourceHandleCFURLConnectionDelegateWithOperationQueue> protector(this); 233 dispatch_async(dispatch_get_main_queue(), ^{ 234 if (!protector->hasHandle()) 235 return; 236 237 LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::didSendBodyData(handle=%p) (%s)", m_handle, m_handle->firstRequest().url().string().utf8().data()); 238 239 m_handle->client()->didSendData(m_handle, totalBytesWritten, totalBytesExpectedToWrite); 240 }); 241} 242 243Boolean ResourceHandleCFURLConnectionDelegateWithOperationQueue::shouldUseCredentialStorage() 244{ 245 return NO; 246} 247 248#if USE(PROTECTION_SPACE_AUTH_CALLBACK) 249Boolean ResourceHandleCFURLConnectionDelegateWithOperationQueue::canRespondToProtectionSpace(CFURLProtectionSpaceRef protectionSpace) 250{ 251 RefPtr<ResourceHandleCFURLConnectionDelegateWithOperationQueue> protector(this); 252 253 dispatch_async(dispatch_get_main_queue(), ^{ 254 if (!protector->hasHandle()) { 255 continueCanAuthenticateAgainstProtectionSpace(false); 256 return; 257 } 258 259 LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::canRespondToProtectionSpace(handle=%p) (%s)", m_handle, m_handle->firstRequest().url().string().utf8().data()); 260 261 ProtectionSpace coreProtectionSpace = ProtectionSpace(protectionSpace); 262#if PLATFORM(IOS) 263 if (coreProtectionSpace.authenticationScheme() == ProtectionSpaceAuthenticationSchemeUnknown) { 264 m_boolResult = false; 265 dispatch_semaphore_signal(m_semaphore); 266 return; 267 } 268#endif // PLATFORM(IOS) 269 m_handle->canAuthenticateAgainstProtectionSpace(coreProtectionSpace); 270 }); 271 dispatch_semaphore_wait(m_semaphore, DISPATCH_TIME_FOREVER); 272 return m_boolResult; 273} 274#endif // USE(PROTECTION_SPACE_AUTH_CALLBACK) 275 276#if USE(NETWORK_CFDATA_ARRAY_CALLBACK) 277void ResourceHandleCFURLConnectionDelegateWithOperationQueue::didReceiveDataArray(CFArrayRef dataArray) 278{ 279 RefPtr<ResourceHandleCFURLConnectionDelegateWithOperationQueue> protector(this); 280 CFRetain(dataArray); 281 dispatch_async(dispatch_get_main_queue(), ^{ 282 if (protector->hasHandle()) { 283 LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::didSendBodyData(handle=%p) (%s)", m_handle, m_handle->firstRequest().url().string().utf8().data()); 284 285 m_handle->client()->didReceiveBuffer(m_handle, SharedBuffer::wrapCFDataArray(dataArray), -1); 286 } 287 CFRelease(dataArray); 288 }); 289} 290#endif // USE(NETWORK_CFDATA_ARRAY_CALLBACK) 291 292void ResourceHandleCFURLConnectionDelegateWithOperationQueue::continueWillSendRequest(CFURLRequestRef request) 293{ 294 m_requestResult = request; 295 dispatch_semaphore_signal(m_semaphore); 296} 297 298void ResourceHandleCFURLConnectionDelegateWithOperationQueue::continueDidReceiveResponse() 299{ 300 dispatch_semaphore_signal(m_semaphore); 301} 302 303void ResourceHandleCFURLConnectionDelegateWithOperationQueue::continueWillCacheResponse(CFCachedURLResponseRef response) 304{ 305 m_cachedResponseResult = response; 306 dispatch_semaphore_signal(m_semaphore); 307} 308 309#if USE(PROTECTION_SPACE_AUTH_CALLBACK) 310void ResourceHandleCFURLConnectionDelegateWithOperationQueue::continueCanAuthenticateAgainstProtectionSpace(bool canAuthenticate) 311{ 312 m_boolResult = canAuthenticate; 313 dispatch_semaphore_signal(m_semaphore); 314} 315#endif // USE(PROTECTION_SPACE_AUTH_CALLBACK) 316} // namespace WebCore 317 318#endif // USE(CFNETWORK) 319