[WK2][iOS] Only adjust network responses' MIME type for QuickLook in the context...
[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             bool isMainResourceLoad = m_handle->firstRequest().requester() == ResourceRequest::Requester::Main;
149             adjustMIMETypeIfNecessary(cfResponse, isMainResourceLoad);
150         }
151
152 #if !PLATFORM(IOS)
153         if (_CFURLRequestCopyProtocolPropertyForKey(m_handle->firstRequest().cfURLRequest(DoNotUpdateHTTPBody), CFSTR("ForceHTMLMIMEType")))
154             CFURLResponseSetMIMEType(cfResponse, CFSTR("text/html"));
155 #endif // !PLATFORM(IOS)
156         
157         ResourceResponse resourceResponse(cfResponse);
158 #if ENABLE(WEB_TIMING)
159         ResourceHandle::getConnectionTimingData(connection, resourceResponse.resourceLoadTiming());
160 #else
161         UNUSED_PARAM(connection);
162 #endif
163         
164         m_handle->client()->didReceiveResponseAsync(m_handle, resourceResponse);
165     });
166     dispatch_semaphore_wait(m_semaphore, DISPATCH_TIME_FOREVER);
167 }
168
169 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::didReceiveData(CFDataRef data, CFIndex originalLength)
170 {
171     // FIXME: The block implicitly copies protector object, which is wasteful. We should just call ref(),
172     // capture "this" by pointer value, and use a C++ lambda to prevent other unintentional capturing.
173     RefPtr<ResourceHandleCFURLConnectionDelegateWithOperationQueue> protector(this);
174     CFRetain(data);
175
176     dispatch_async(dispatch_get_main_queue(), ^{
177         if (protector->hasHandle() && m_handle->client()) {
178             LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::didReceiveData(handle=%p) (%s)", m_handle, m_handle->firstRequest().url().string().utf8().data());
179
180             m_handle->client()->didReceiveBuffer(m_handle, SharedBuffer::wrapCFData(data), originalLength);
181         }
182
183         CFRelease(data);
184     });
185 }
186
187 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::didFinishLoading()
188 {
189     // FIXME: The block implicitly copies protector object, which is wasteful. We should just call ref(),
190     // capture "this" by pointer value, and use a C++ lambda to prevent other unintentional capturing.
191     RefPtr<ResourceHandleCFURLConnectionDelegateWithOperationQueue> protector(this);
192     dispatch_async(dispatch_get_main_queue(), ^{
193         if (!protector->hasHandle() || !m_handle->client())
194             return;
195
196         LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::didFinishLoading(handle=%p) (%s)", m_handle, m_handle->firstRequest().url().string().utf8().data());
197
198         m_handle->client()->didFinishLoading(m_handle, 0);
199     });
200 }
201
202 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::didFail(CFErrorRef error)
203 {
204     // FIXME: The block implicitly copies protector object, which is wasteful. We should just call ref(),
205     // capture "this" by pointer value, and use a C++ lambda to prevent other unintentional capturing.
206     RefPtr<ResourceHandleCFURLConnectionDelegateWithOperationQueue> protector(this);
207     CFRetain(error);
208     dispatch_async(dispatch_get_main_queue(), ^{
209         if (protector->hasHandle() && m_handle->client()) {
210             LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::didFail(handle=%p) (%s)", m_handle, m_handle->firstRequest().url().string().utf8().data());
211
212             m_handle->client()->didFail(m_handle, ResourceError(error));
213         }
214
215         CFRelease(error);
216     });
217 }
218
219 CFCachedURLResponseRef ResourceHandleCFURLConnectionDelegateWithOperationQueue::willCacheResponse(CFCachedURLResponseRef cachedResponse)
220 {
221     // FIXME: The block implicitly copies protector object, which is wasteful. We should just call ref(),
222     // capture "this" by pointer value, and use a C++ lambda to prevent other unintentional capturing.
223     RefPtr<ResourceHandleCFURLConnectionDelegateWithOperationQueue> protector(this);
224
225     dispatch_async(dispatch_get_main_queue(), ^{
226         if (!protector->hasHandle() || !m_handle->client()) {
227             continueWillCacheResponse(nullptr);
228             return;
229         }
230
231         LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::willCacheResponse(handle=%p) (%s)", m_handle, m_handle->firstRequest().url().string().utf8().data());
232
233         m_handle->client()->willCacheResponseAsync(m_handle, cachedResponse);
234     });
235     dispatch_semaphore_wait(m_semaphore, DISPATCH_TIME_FOREVER);
236     return m_cachedResponseResult.leakRef();
237 }
238
239 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::didReceiveChallenge(CFURLAuthChallengeRef challenge)
240 {
241     // FIXME: The block implicitly copies protector object, which is wasteful. We should just call ref(),
242     // capture "this" by pointer value, and use a C++ lambda to prevent other unintentional capturing.
243     RefPtr<ResourceHandleCFURLConnectionDelegateWithOperationQueue> protector(this);
244     CFRetain(challenge);
245     dispatch_async(dispatch_get_main_queue(), ^{
246         if (protector->hasHandle()) {
247             LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::didReceiveChallenge(handle=%p) (%s)", m_handle, m_handle->firstRequest().url().string().utf8().data());
248
249             m_handle->didReceiveAuthenticationChallenge(AuthenticationChallenge(challenge, m_handle));
250         }
251
252         CFRelease(challenge);
253     });
254 }
255
256 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::didSendBodyData(CFIndex totalBytesWritten, CFIndex totalBytesExpectedToWrite)
257 {
258     // FIXME: The block implicitly copies protector object, which is wasteful. We should just call ref(),
259     // capture "this" by pointer value, and use a C++ lambda to prevent other unintentional capturing.
260     RefPtr<ResourceHandleCFURLConnectionDelegateWithOperationQueue> protector(this);
261     dispatch_async(dispatch_get_main_queue(), ^{
262         if (!protector->hasHandle() || !m_handle->client())
263             return;
264
265         LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::didSendBodyData(handle=%p) (%s)", m_handle, m_handle->firstRequest().url().string().utf8().data());
266
267         m_handle->client()->didSendData(m_handle, totalBytesWritten, totalBytesExpectedToWrite);
268     });
269 }
270
271 Boolean ResourceHandleCFURLConnectionDelegateWithOperationQueue::shouldUseCredentialStorage()
272 {
273     return NO;
274 }
275
276 #if USE(PROTECTION_SPACE_AUTH_CALLBACK)
277 Boolean ResourceHandleCFURLConnectionDelegateWithOperationQueue::canRespondToProtectionSpace(CFURLProtectionSpaceRef protectionSpace)
278 {
279     // FIXME: The block implicitly copies protector object, which is wasteful. We should just call ref(),
280     // capture "this" by pointer value, and use a C++ lambda to prevent other unintentional capturing.
281     RefPtr<ResourceHandleCFURLConnectionDelegateWithOperationQueue> protector(this);
282
283     dispatch_async(dispatch_get_main_queue(), ^{
284         if (!protector->hasHandle()) {
285             continueCanAuthenticateAgainstProtectionSpace(false);
286             return;
287         }
288
289         LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::canRespondToProtectionSpace(handle=%p) (%s)", m_handle, m_handle->firstRequest().url().string().utf8().data());
290
291         ProtectionSpace coreProtectionSpace = ProtectionSpace(protectionSpace);
292 #if PLATFORM(IOS)
293         if (coreProtectionSpace.authenticationScheme() == ProtectionSpaceAuthenticationSchemeUnknown) {
294             m_boolResult = false;
295             dispatch_semaphore_signal(m_semaphore);
296             return;
297         }
298 #endif // PLATFORM(IOS)
299         m_handle->canAuthenticateAgainstProtectionSpace(coreProtectionSpace);
300     });
301     dispatch_semaphore_wait(m_semaphore, DISPATCH_TIME_FOREVER);
302     return m_boolResult;
303 }
304 #endif // USE(PROTECTION_SPACE_AUTH_CALLBACK)
305
306 #if USE(NETWORK_CFDATA_ARRAY_CALLBACK)
307 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::didReceiveDataArray(CFArrayRef dataArray)
308 {
309     // FIXME: The block implicitly copies protector object, which is wasteful. We should just call ref(),
310     // capture "this" by pointer value, and use a C++ lambda to prevent other unintentional capturing.
311     RefPtr<ResourceHandleCFURLConnectionDelegateWithOperationQueue> protector(this);
312     CFRetain(dataArray);
313     dispatch_async(dispatch_get_main_queue(), ^{
314         if (protector->hasHandle() && m_handle->client()) {
315             LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::didSendBodyData(handle=%p) (%s)", m_handle, m_handle->firstRequest().url().string().utf8().data());
316
317             m_handle->client()->didReceiveBuffer(m_handle, SharedBuffer::wrapCFDataArray(dataArray), -1);
318         }
319         CFRelease(dataArray);
320     });
321 }
322 #endif // USE(NETWORK_CFDATA_ARRAY_CALLBACK)
323
324 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::continueWillSendRequest(CFURLRequestRef request)
325 {
326     m_requestResult = request;
327     dispatch_semaphore_signal(m_semaphore);
328 }
329
330 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::continueDidReceiveResponse()
331 {
332     dispatch_semaphore_signal(m_semaphore);
333 }
334
335 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::continueWillCacheResponse(CFCachedURLResponseRef response)
336 {
337     m_cachedResponseResult = response;
338     dispatch_semaphore_signal(m_semaphore);
339 }
340
341 #if USE(PROTECTION_SPACE_AUTH_CALLBACK)
342 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::continueCanAuthenticateAgainstProtectionSpace(bool canAuthenticate)
343 {
344     m_boolResult = canAuthenticate;
345     dispatch_semaphore_signal(m_semaphore);
346 }
347 #endif // USE(PROTECTION_SPACE_AUTH_CALLBACK)
348 } // namespace WebCore
349
350 #endif // USE(CFNETWORK)