Remove WebCoreSystemInterface
[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(CFURLCONNECTION)
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 "WebCoreURLResponse.h"
39 #include <pal/spi/cf/CFNetworkSPI.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     struct ProtectedParameters {
105         Ref<ResourceHandleCFURLConnectionDelegateWithOperationQueue> protectedThis;
106         RetainPtr<CFURLRequestRef> cfRequest;
107         RetainPtr<CFURLResponseRef> originalRedirectResponse;
108     };
109     
110     auto work = [] (void* context) {
111         auto& parameters = *reinterpret_cast<ProtectedParameters*>(context);
112         auto& protectedThis = parameters.protectedThis;
113         auto& handle = protectedThis->m_handle;
114         auto& cfRequest = parameters.cfRequest;
115         
116         if (!protectedThis->hasHandle()) {
117             protectedThis->continueWillSendRequest(nullptr);
118             return;
119         }
120
121         LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::willSendRequest(handle=%p) (%s)", handle, handle->firstRequest().url().string().utf8().data());
122
123         RetainPtr<CFURLResponseRef> redirectResponse = protectedThis->synthesizeRedirectResponseIfNecessary(cfRequest.get(), parameters.originalRedirectResponse.get());
124         ASSERT(redirectResponse);
125
126         ResourceRequest request = protectedThis->createResourceRequest(cfRequest.get(), redirectResponse.get());
127         handle->willSendRequest(WTFMove(request), redirectResponse.get());
128     };
129     
130     ProtectedParameters parameters { makeRef(*this), cfRequest, originalRedirectResponse };
131     dispatch_async_f(dispatch_get_main_queue(), &parameters, work);
132     dispatch_semaphore_wait(m_semaphore, DISPATCH_TIME_FOREVER);
133
134     return m_requestResult.leakRef();
135 }
136
137 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::didReceiveResponse(CFURLConnectionRef connection, CFURLResponseRef cfResponse)
138 {
139     struct ProtectedParameters {
140         Ref<ResourceHandleCFURLConnectionDelegateWithOperationQueue> protectedThis;
141         RetainPtr<CFURLConnectionRef> connection;
142         RetainPtr<CFURLResponseRef> cfResponse;
143     };
144     
145     auto work = [] (void* context) {
146         auto& parameters = *reinterpret_cast<ProtectedParameters*>(context);
147         auto& protectedThis = parameters.protectedThis;
148         auto& handle = protectedThis->m_handle;
149         auto& cfResponse = parameters.cfResponse;
150         
151         if (!protectedThis->hasHandle() || !handle->client()) {
152             protectedThis->continueDidReceiveResponse();
153             return;
154         }
155
156         LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::didReceiveResponse(handle=%p) (%s)", handle, handle->firstRequest().url().string().utf8().data());
157
158         // Avoid MIME type sniffing if the response comes back as 304 Not Modified.
159         auto msg = CFURLResponseGetHTTPResponse(cfResponse.get());
160         int statusCode = msg ? CFHTTPMessageGetResponseStatusCode(msg) : 0;
161
162         if (statusCode != 304) {
163             bool isMainResourceLoad = handle->firstRequest().requester() == ResourceRequest::Requester::Main;
164             adjustMIMETypeIfNecessary(cfResponse.get(), isMainResourceLoad);
165         }
166
167 #if !PLATFORM(IOS)
168         if (_CFURLRequestCopyProtocolPropertyForKey(handle->firstRequest().cfURLRequest(DoNotUpdateHTTPBody), CFSTR("ForceHTMLMIMEType")))
169             CFURLResponseSetMIMEType(cfResponse.get(), CFSTR("text/html"));
170 #endif // !PLATFORM(IOS)
171
172         ResourceResponse resourceResponse(cfResponse.get());
173         ResourceHandle::getConnectionTimingData(parameters.connection.get(), resourceResponse.deprecatedNetworkLoadMetrics());
174
175         handle->didReceiveResponse(WTFMove(resourceResponse));
176     };
177
178     ProtectedParameters parameters { makeRef(*this), connection, cfResponse };
179     dispatch_async_f(dispatch_get_main_queue(), &parameters, work);
180     dispatch_semaphore_wait(m_semaphore, DISPATCH_TIME_FOREVER);
181 }
182
183 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::didReceiveData(CFDataRef data, CFIndex originalLength)
184 {
185     callOnMainThread([protectedThis = makeRef(*this), data = RetainPtr<CFDataRef>(data), originalLength = originalLength] () mutable {
186         auto& handle = protectedThis->m_handle;
187         if (!protectedThis->hasHandle() || !handle->client())
188             return;
189         
190         LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::didReceiveData(handle=%p) (%s)", handle, handle->firstRequest().url().string().utf8().data());
191
192         handle->client()->didReceiveBuffer(handle, SharedBuffer::create(data.get()), originalLength);
193     });
194 }
195
196 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::didFinishLoading()
197 {
198     callOnMainThread([protectedThis = makeRef(*this)] () mutable {
199         auto& handle = protectedThis->m_handle;
200         if (!protectedThis->hasHandle() || !handle->client())
201             return;
202
203         LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::didFinishLoading(handle=%p) (%s)", handle, handle->firstRequest().url().string().utf8().data());
204
205         handle->client()->didFinishLoading(handle);
206     });
207 }
208
209 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::didFail(CFErrorRef error)
210 {
211     callOnMainThread([protectedThis = makeRef(*this), error = RetainPtr<CFErrorRef>(error)] () mutable {
212         auto& handle = protectedThis->m_handle;
213         if (!protectedThis->hasHandle() || !handle->client())
214             return;
215         
216         LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::didFail(handle=%p) (%s)", handle, handle->firstRequest().url().string().utf8().data());
217
218         handle->client()->didFail(handle, ResourceError(error.get()));
219     });
220 }
221
222 CFCachedURLResponseRef ResourceHandleCFURLConnectionDelegateWithOperationQueue::willCacheResponse(CFCachedURLResponseRef cachedResponse)
223 {
224     struct ProtectedParameters {
225         Ref<ResourceHandleCFURLConnectionDelegateWithOperationQueue> protectedThis;
226         RetainPtr<CFCachedURLResponseRef> cachedResponse;
227     };
228     
229     auto work = [] (void* context) {
230         auto& parameters = *reinterpret_cast<ProtectedParameters*>(context);
231         auto& protectedThis = parameters.protectedThis;
232         auto& handle = protectedThis->m_handle;
233         
234         if (!protectedThis->hasHandle() || !handle->client()) {
235             protectedThis->continueWillCacheResponse(nullptr);
236             return;
237         }
238
239         LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::willCacheResponse(handle=%p) (%s)", handle, handle->firstRequest().url().string().utf8().data());
240
241         handle->client()->willCacheResponseAsync(handle, parameters.cachedResponse.get());
242     };
243     
244     ProtectedParameters parameters { makeRef(*this), cachedResponse };
245     dispatch_async_f(dispatch_get_main_queue(), &parameters, work);
246     dispatch_semaphore_wait(m_semaphore, DISPATCH_TIME_FOREVER);
247     return m_cachedResponseResult.leakRef();
248 }
249
250 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::didReceiveChallenge(CFURLAuthChallengeRef challenge)
251 {
252     callOnMainThread([protectedThis = makeRef(*this), challenge = RetainPtr<CFURLAuthChallengeRef>(challenge)] () mutable {
253         auto& handle = protectedThis->m_handle;
254         if (!protectedThis->hasHandle())
255             return;
256         
257         LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::didReceiveChallenge(handle=%p) (%s)", handle, handle->firstRequest().url().string().utf8().data());
258
259         handle->didReceiveAuthenticationChallenge(AuthenticationChallenge(challenge.get(), handle));
260     });
261 }
262
263 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::didSendBodyData(CFIndex totalBytesWritten, CFIndex totalBytesExpectedToWrite)
264 {
265     callOnMainThread([protectedThis = makeRef(*this), totalBytesWritten, totalBytesExpectedToWrite] () mutable {
266         auto& handle = protectedThis->m_handle;
267         if (!protectedThis->hasHandle() || !handle->client())
268             return;
269
270         LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::didSendBodyData(handle=%p) (%s)", handle, handle->firstRequest().url().string().utf8().data());
271
272         handle->client()->didSendData(handle, totalBytesWritten, totalBytesExpectedToWrite);
273     });
274 }
275
276 Boolean ResourceHandleCFURLConnectionDelegateWithOperationQueue::shouldUseCredentialStorage()
277 {
278     return NO;
279 }
280
281 #if USE(PROTECTION_SPACE_AUTH_CALLBACK)
282 Boolean ResourceHandleCFURLConnectionDelegateWithOperationQueue::canRespondToProtectionSpace(CFURLProtectionSpaceRef protectionSpace)
283 {
284     struct ProtectedParameters {
285         Ref<ResourceHandleCFURLConnectionDelegateWithOperationQueue> protectedThis;
286         RetainPtr<CFURLProtectionSpaceRef> protectionSpace;
287     };
288     
289     auto work = [] (void* context) {
290         auto& parameters = *reinterpret_cast<ProtectedParameters*>(context);
291         auto& protectedThis = parameters.protectedThis;
292         auto& handle = protectedThis->m_handle;
293         
294         if (!protectedThis->hasHandle()) {
295             protectedThis->continueCanAuthenticateAgainstProtectionSpace(false);
296             return;
297         }
298
299         LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::canRespondToProtectionSpace(handle=%p) (%s)", handle, handle->firstRequest().url().string().utf8().data());
300
301         ProtectionSpace coreProtectionSpace = ProtectionSpace(parameters.protectionSpace.get());
302 #if PLATFORM(IOS)
303         if (coreProtectionSpace.authenticationScheme() == ProtectionSpaceAuthenticationSchemeUnknown) {
304             m_boolResult = false;
305             dispatch_semaphore_signal(m_semaphore);
306             return;
307         }
308 #endif // PLATFORM(IOS)
309         handle->canAuthenticateAgainstProtectionSpace(coreProtectionSpace);
310     };
311     
312     ProtectedParameters parameters { makeRef(*this), protectionSpace };
313     dispatch_async_f(dispatch_get_main_queue(), &parameters, work);
314     dispatch_semaphore_wait(m_semaphore, DISPATCH_TIME_FOREVER);
315     return m_boolResult;
316 }
317 #endif // USE(PROTECTION_SPACE_AUTH_CALLBACK)
318
319 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::continueWillSendRequest(CFURLRequestRef request)
320 {
321     m_requestResult = request;
322     dispatch_semaphore_signal(m_semaphore);
323 }
324
325 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::continueDidReceiveResponse()
326 {
327     dispatch_semaphore_signal(m_semaphore);
328 }
329
330 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::continueWillCacheResponse(CFCachedURLResponseRef response)
331 {
332     m_cachedResponseResult = response;
333     dispatch_semaphore_signal(m_semaphore);
334 }
335
336 #if USE(PROTECTION_SPACE_AUTH_CALLBACK)
337 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::continueCanAuthenticateAgainstProtectionSpace(bool canAuthenticate)
338 {
339     m_boolResult = canAuthenticate;
340     dispatch_semaphore_signal(m_semaphore);
341 }
342 #endif // USE(PROTECTION_SPACE_AUTH_CALLBACK)
343 } // namespace WebCore
344
345 #endif // USE(CFURLCONNECTION)