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