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