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