Collect web timing data on iOS.
[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(CFURLConnectionRef connection, 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         ResourceResponse resourceResponse(cfResponse);
136 #if ENABLE(WEB_TIMING)
137         ResourceHandle::getConnectionTimingData(connection, resourceResponse.resourceLoadTiming());
138 #else
139         UNUSED_PARAM(connection);
140 #endif
141         
142         m_handle->client()->didReceiveResponseAsync(m_handle, resourceResponse);
143     });
144     dispatch_semaphore_wait(m_semaphore, DISPATCH_TIME_FOREVER);
145 }
146
147 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::didReceiveData(CFDataRef data, CFIndex originalLength)
148 {
149     RefPtr<ResourceHandleCFURLConnectionDelegateWithOperationQueue> protector(this);
150     CFRetain(data);
151
152     dispatch_async(dispatch_get_main_queue(), ^{
153         if (protector->hasHandle()) {
154             LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::didReceiveData(handle=%p) (%s)", m_handle, m_handle->firstRequest().url().string().utf8().data());
155
156             m_handle->client()->didReceiveBuffer(m_handle, SharedBuffer::wrapCFData(data), originalLength);
157         }
158
159         CFRelease(data);
160     });
161 }
162
163 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::didFinishLoading()
164 {
165     RefPtr<ResourceHandleCFURLConnectionDelegateWithOperationQueue> protector(this);
166     dispatch_async(dispatch_get_main_queue(), ^{
167         if (!protector->hasHandle())
168             return;
169
170         LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::didFinishLoading(handle=%p) (%s)", m_handle, m_handle->firstRequest().url().string().utf8().data());
171
172         m_handle->client()->didFinishLoading(m_handle, 0);
173     });
174 }
175
176 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::didFail(CFErrorRef error)
177 {
178     RefPtr<ResourceHandleCFURLConnectionDelegateWithOperationQueue> protector(this);
179     CFRetain(error);
180     dispatch_async(dispatch_get_main_queue(), ^{
181         if (protector->hasHandle()) {
182             LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::didFail(handle=%p) (%s)", m_handle, m_handle->firstRequest().url().string().utf8().data());
183
184             m_handle->client()->didFail(m_handle, ResourceError(error));
185         }
186
187         CFRelease(error);
188     });
189 }
190
191 CFCachedURLResponseRef ResourceHandleCFURLConnectionDelegateWithOperationQueue::willCacheResponse(CFCachedURLResponseRef cachedResponse)
192 {
193     RefPtr<ResourceHandleCFURLConnectionDelegateWithOperationQueue> protector(this);
194
195     dispatch_async(dispatch_get_main_queue(), ^{
196         if (!protector->hasHandle()) {
197             continueWillCacheResponse(nullptr);
198             return;
199         }
200
201         LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::willCacheResponse(handle=%p) (%s)", m_handle, m_handle->firstRequest().url().string().utf8().data());
202
203         m_handle->client()->willCacheResponseAsync(m_handle, cachedResponse);
204     });
205     dispatch_semaphore_wait(m_semaphore, DISPATCH_TIME_FOREVER);
206     return m_cachedResponseResult.leakRef();
207 }
208
209 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::didReceiveChallenge(CFURLAuthChallengeRef challenge)
210 {
211     RefPtr<ResourceHandleCFURLConnectionDelegateWithOperationQueue> protector(this);
212     CFRetain(challenge);
213     dispatch_async(dispatch_get_main_queue(), ^{
214         if (protector->hasHandle()) {
215             LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::didReceiveChallenge(handle=%p) (%s)", m_handle, m_handle->firstRequest().url().string().utf8().data());
216
217             m_handle->didReceiveAuthenticationChallenge(AuthenticationChallenge(challenge, m_handle));
218         }
219
220         CFRelease(challenge);
221     });
222 }
223
224 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::didSendBodyData(CFIndex totalBytesWritten, CFIndex totalBytesExpectedToWrite)
225 {
226     RefPtr<ResourceHandleCFURLConnectionDelegateWithOperationQueue> protector(this);
227     dispatch_async(dispatch_get_main_queue(), ^{
228         if (!protector->hasHandle())
229             return;
230
231         LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::didSendBodyData(handle=%p) (%s)", m_handle, m_handle->firstRequest().url().string().utf8().data());
232
233         m_handle->client()->didSendData(m_handle, totalBytesWritten, totalBytesExpectedToWrite);
234     });
235 }
236
237 Boolean ResourceHandleCFURLConnectionDelegateWithOperationQueue::shouldUseCredentialStorage()
238 {
239     return NO;
240 }
241
242 #if USE(PROTECTION_SPACE_AUTH_CALLBACK)
243 Boolean ResourceHandleCFURLConnectionDelegateWithOperationQueue::canRespondToProtectionSpace(CFURLProtectionSpaceRef protectionSpace)
244 {
245     RefPtr<ResourceHandleCFURLConnectionDelegateWithOperationQueue> protector(this);
246
247     dispatch_async(dispatch_get_main_queue(), ^{
248         if (!protector->hasHandle()) {
249             continueCanAuthenticateAgainstProtectionSpace(false);
250             return;
251         }
252
253         LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::canRespondToProtectionSpace(handle=%p) (%s)", m_handle, m_handle->firstRequest().url().string().utf8().data());
254
255         ProtectionSpace coreProtectionSpace = core(protectionSpace);
256 #if PLATFORM(IOS)
257         if (coreProtectionSpace.authenticationScheme() == ProtectionSpaceAuthenticationSchemeUnknown) {
258             m_boolResult = false;
259             dispatch_semaphore_signal(m_semaphore);
260             return;
261         }
262 #endif // PLATFORM(IOS)
263         m_handle->canAuthenticateAgainstProtectionSpace(coreProtectionSpace);
264     });
265     dispatch_semaphore_wait(m_semaphore, DISPATCH_TIME_FOREVER);
266     return m_boolResult;
267 }
268 #endif // USE(PROTECTION_SPACE_AUTH_CALLBACK)
269
270 #if USE(NETWORK_CFDATA_ARRAY_CALLBACK)
271 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::didReceiveDataArray(CFArrayRef dataArray)
272 {
273     RefPtr<ResourceHandleCFURLConnectionDelegateWithOperationQueue> protector(this);
274     CFRetain(dataArray);
275     dispatch_async(dispatch_get_main_queue(), ^{
276         if (protector->hasHandle()) {
277             LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::didSendBodyData(handle=%p) (%s)", m_handle, m_handle->firstRequest().url().string().utf8().data());
278
279             m_handle->handleDataArray(dataArray);
280         }
281         CFRelease(dataArray);
282     });
283 }
284 #endif // USE(NETWORK_CFDATA_ARRAY_CALLBACK)
285
286 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::continueWillSendRequest(CFURLRequestRef request)
287 {
288     m_requestResult = request;
289     dispatch_semaphore_signal(m_semaphore);
290 }
291
292 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::continueDidReceiveResponse()
293 {
294     dispatch_semaphore_signal(m_semaphore);
295 }
296
297 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::continueWillCacheResponse(CFCachedURLResponseRef response)
298 {
299     m_cachedResponseResult = response;
300     dispatch_semaphore_signal(m_semaphore);
301 }
302
303 #if USE(PROTECTION_SPACE_AUTH_CALLBACK)
304 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::continueCanAuthenticateAgainstProtectionSpace(bool canAuthenticate)
305 {
306     m_boolResult = canAuthenticate;
307     dispatch_semaphore_signal(m_semaphore);
308 }
309 #endif // USE(PROTECTION_SPACE_AUTH_CALLBACK)
310 } // namespace WebCore
311
312 #endif // USE(CFNETWORK)