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