abc19b10a55de2f73807654888e4c98aa1f0945f
[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 "MIMETypeRegistry.h"
35 #include "ResourceHandle.h"
36 #include "ResourceHandleClient.h"
37 #include "ResourceResponse.h"
38 #include "SharedBuffer.h"
39 #if !PLATFORM(WIN)
40 #include "WebCoreURLResponse.h"
41 #endif
42 #include <pal/spi/cf/CFNetworkSPI.h>
43 #include <wtf/MainThread.h>
44 #include <wtf/Threading.h>
45 #include <wtf/text/CString.h>
46 #include <wtf/text/WTFString.h>
47
48 namespace WebCore {
49
50 ResourceHandleCFURLConnectionDelegateWithOperationQueue::ResourceHandleCFURLConnectionDelegateWithOperationQueue(ResourceHandle* handle, MessageQueue<Function<void()>>* messageQueue)
51     : ResourceHandleCFURLConnectionDelegate(handle)
52     , m_messageQueue(messageQueue)
53 {
54 }
55
56 ResourceHandleCFURLConnectionDelegateWithOperationQueue::~ResourceHandleCFURLConnectionDelegateWithOperationQueue()
57 {
58 }
59
60 bool ResourceHandleCFURLConnectionDelegateWithOperationQueue::hasHandle() const
61 {
62     return !!m_handle;
63 }
64
65 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::releaseHandle()
66 {
67     ResourceHandleCFURLConnectionDelegate::releaseHandle();
68     m_requestResult = nullptr;
69     m_cachedResponseResult = nullptr;
70     m_semaphore.signal();
71 }
72
73 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::setupRequest(CFMutableURLRequestRef request)
74 {
75 #if PLATFORM(IOS)
76     CFURLRequestSetShouldStartSynchronously(request, 1);
77 #endif
78     CFURLRef requestURL = CFURLRequestGetURL(request);
79     if (!requestURL)
80         return;
81     m_originalScheme = adoptCF(CFURLCopyScheme(requestURL));
82 }
83
84 #if PLATFORM(WIN)
85 LRESULT CALLBACK hookToRemoveCFNetworkMessage(int code, WPARAM wParam, LPARAM lParam)
86 {
87     MSG* msg = reinterpret_cast<MSG*>(lParam);
88     // This message which CFNetwork sends to itself, will block the main thread, remove it.
89     if (msg->message == WM_USER + 0xcf)
90         msg->message = WM_NULL;
91     return CallNextHookEx(nullptr, code, wParam, lParam);
92 }
93
94 static void installHookToRemoveCFNetworkMessageBlockingMainThread()
95 {
96     static HHOOK hook = nullptr;
97     if (!hook) {
98         DWORD threadID = ::GetCurrentThreadId();
99         hook = ::SetWindowsHookExW(WH_GETMESSAGE, hookToRemoveCFNetworkMessage, 0, threadID);
100     }
101 }
102 #endif
103
104 static void emptyPerform(void*)
105 {
106 }
107
108 static CFRunLoopRef getRunLoop()
109 {
110     static CFRunLoopRef runLoop = nullptr;
111
112     if (!runLoop) {
113         BinarySemaphore sem;
114         Thread::create("CFNetwork Loader", [&] {
115             runLoop = CFRunLoopGetCurrent();
116
117             // Must add a source to the run loop to prevent CFRunLoopRun() from exiting.
118             CFRunLoopSourceContext ctxt = { 0, (void*)1 /*must be non-null*/, 0, 0, 0, 0, 0, 0, 0, emptyPerform };
119             CFRunLoopSourceRef bogusSource = CFRunLoopSourceCreate(0, 0, &ctxt);
120             CFRunLoopAddSource(runLoop, bogusSource, kCFRunLoopDefaultMode);
121             sem.signal();
122
123             while (true)
124                 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1E30, true);
125         });
126         sem.wait(TimeWithDynamicClockType(WallTime::infinity()));
127     }
128
129     return runLoop;
130 }
131
132 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::setupConnectionScheduling(CFURLConnectionRef connection)
133 {
134 #if PLATFORM(WIN)
135     installHookToRemoveCFNetworkMessageBlockingMainThread();
136 #endif
137     CFRunLoopRef runLoop = getRunLoop();
138     CFURLConnectionScheduleWithRunLoop(connection, runLoop, kCFRunLoopDefaultMode);
139     CFURLConnectionScheduleDownloadWithRunLoop(connection, runLoop, kCFRunLoopDefaultMode);
140 }
141
142 CFURLRequestRef ResourceHandleCFURLConnectionDelegateWithOperationQueue::willSendRequest(CFURLRequestRef cfRequest, CFURLResponseRef originalRedirectResponse)
143 {
144     // 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.
145     if (!originalRedirectResponse) {
146         RetainPtr<CFStringRef> newScheme = adoptCF(CFURLCopyScheme(CFURLRequestGetURL(cfRequest)));
147         if (CFStringCompare(newScheme.get(), m_originalScheme.get(), kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
148             CFRetain(cfRequest);
149             return cfRequest;
150         }
151     }
152
153     ASSERT(!isMainThread());
154     
155     auto work = [protectedThis = makeRef(*this), cfRequest = RetainPtr<CFURLRequestRef>(cfRequest), originalRedirectResponse = RetainPtr<CFURLResponseRef>(originalRedirectResponse)] () {
156         auto& handle = protectedThis->m_handle;
157         
158         if (!protectedThis->hasHandle()) {
159             protectedThis->continueWillSendRequest(nullptr);
160             return;
161         }
162
163         LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::willSendRequest(handle=%p) (%s)", handle, handle->firstRequest().url().string().utf8().data());
164
165         RetainPtr<CFURLResponseRef> redirectResponse = protectedThis->synthesizeRedirectResponseIfNecessary(cfRequest.get(), originalRedirectResponse.get());
166         ASSERT(redirectResponse);
167
168         ResourceRequest request = protectedThis->createResourceRequest(cfRequest.get(), redirectResponse.get());
169         handle->willSendRequest(WTFMove(request), redirectResponse.get());
170     };
171     
172     if (m_messageQueue)
173         m_messageQueue->append(std::make_unique<Function<void()>>(WTFMove(work)));
174     else
175         callOnMainThread(WTFMove(work));
176     m_semaphore.wait(TimeWithDynamicClockType(WallTime::infinity()));
177
178     return m_requestResult.leakRef();
179 }
180
181 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::didReceiveResponse(CFURLConnectionRef connection, CFURLResponseRef cfResponse)
182 {
183     auto work = [protectedThis = makeRef(*this), cfResponse = RetainPtr<CFURLResponseRef>(cfResponse), connection = RetainPtr<CFURLConnectionRef>(connection)] () {
184         auto& handle = protectedThis->m_handle;
185         
186         if (!protectedThis->hasHandle() || !handle->client() || !handle->connection()) {
187             protectedThis->continueDidReceiveResponse();
188             return;
189         }
190
191         LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::didReceiveResponse(handle=%p) (%s)", handle, handle->firstRequest().url().string().utf8().data());
192
193         // Avoid MIME type sniffing if the response comes back as 304 Not Modified.
194         auto msg = CFURLResponseGetHTTPResponse(cfResponse.get());
195         int statusCode = msg ? CFHTTPMessageGetResponseStatusCode(msg) : 0;
196
197         if (statusCode != 304) {
198             bool isMainResourceLoad = handle->firstRequest().requester() == ResourceRequest::Requester::Main;
199 #if !PLATFORM(WIN)
200             adjustMIMETypeIfNecessary(cfResponse.get(), isMainResourceLoad);
201 #endif
202         }
203
204 #if !PLATFORM(IOS)
205         if (_CFURLRequestCopyProtocolPropertyForKey(handle->firstRequest().cfURLRequest(DoNotUpdateHTTPBody), CFSTR("ForceHTMLMIMEType")))
206             CFURLResponseSetMIMEType(cfResponse.get(), CFSTR("text/html"));
207 #endif // !PLATFORM(IOS)
208
209         ResourceResponse resourceResponse(cfResponse.get());
210         resourceResponse.setSource(ResourceResponse::Source::Network);
211 #if !PLATFORM(WIN)
212         ResourceHandle::getConnectionTimingData(connection.get(), resourceResponse.deprecatedNetworkLoadMetrics());
213 #endif
214
215         handle->didReceiveResponse(WTFMove(resourceResponse));
216     };
217
218     if (m_messageQueue)
219         m_messageQueue->append(std::make_unique<Function<void()>>(WTFMove(work)));
220     else
221         callOnMainThread(WTFMove(work));
222     m_semaphore.wait(TimeWithDynamicClockType(WallTime::infinity()));
223 }
224
225 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::didReceiveData(CFDataRef data, CFIndex originalLength)
226 {
227     auto work = [protectedThis = makeRef(*this), data = RetainPtr<CFDataRef>(data), originalLength = originalLength] () mutable {
228         auto& handle = protectedThis->m_handle;
229         if (!protectedThis->hasHandle() || !handle->client() || !handle->connection())
230             return;
231         
232         LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::didReceiveData(handle=%p) (%s)", handle, handle->firstRequest().url().string().utf8().data());
233
234         handle->client()->didReceiveBuffer(handle, SharedBuffer::create(data.get()), originalLength);
235     };
236     
237     if (m_messageQueue)
238         m_messageQueue->append(std::make_unique<Function<void()>>(WTFMove(work)));
239     else
240         callOnMainThread(WTFMove(work));
241 }
242
243 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::didFinishLoading()
244 {
245     auto work = [protectedThis = makeRef(*this)] () mutable {
246         auto& handle = protectedThis->m_handle;
247         if (!protectedThis->hasHandle() || !handle->client() || !handle->connection()) {
248             protectedThis->m_handle->deref();
249             return;
250         }
251
252         LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::didFinishLoading(handle=%p) (%s)", handle, handle->firstRequest().url().string().utf8().data());
253
254         handle->client()->didFinishLoading(handle);
255         if (protectedThis->m_messageQueue) {
256             protectedThis->m_messageQueue->kill();
257             protectedThis->m_messageQueue = nullptr;
258         }
259         protectedThis->m_handle->deref();
260     };
261     
262     if (m_messageQueue)
263         m_messageQueue->append(std::make_unique<Function<void()>>(WTFMove(work)));
264     else
265         callOnMainThread(WTFMove(work));
266 }
267
268 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::didFail(CFErrorRef error)
269 {
270     auto work = [protectedThis = makeRef(*this), error = RetainPtr<CFErrorRef>(error)] () mutable {
271         auto& handle = protectedThis->m_handle;
272         if (!protectedThis->hasHandle() || !handle->client() || !handle->connection()) {
273             protectedThis->m_handle->deref();
274             return;
275         }
276         
277         LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::didFail(handle=%p) (%s)", handle, handle->firstRequest().url().string().utf8().data());
278
279         handle->client()->didFail(handle, ResourceError(error.get()));
280         if (protectedThis->m_messageQueue) {
281             protectedThis->m_messageQueue->kill();
282             protectedThis->m_messageQueue = nullptr;
283         }
284         protectedThis->m_handle->deref();
285     };
286
287     if (m_messageQueue)
288         m_messageQueue->append(std::make_unique<Function<void()>>(WTFMove(work)));
289     else
290         callOnMainThread(WTFMove(work));
291 }
292
293 CFCachedURLResponseRef ResourceHandleCFURLConnectionDelegateWithOperationQueue::willCacheResponse(CFCachedURLResponseRef cachedResponse)
294 {
295 #if PLATFORM(WIN)
296     // Workaround for <rdar://problem/6300990> Caching does not respect Vary HTTP header.
297     // FIXME: WebCore cache has issues with Vary, too (bug 58797, bug 71509).
298     CFURLResponseRef wrappedResponse = CFCachedURLResponseGetWrappedResponse(cachedResponse);
299     if (CFHTTPMessageRef httpResponse = CFURLResponseGetHTTPResponse(wrappedResponse)) {
300         ASSERT(CFHTTPMessageIsHeaderComplete(httpResponse));
301         RetainPtr<CFStringRef> varyValue = adoptCF(CFHTTPMessageCopyHeaderFieldValue(httpResponse, CFSTR("Vary")));
302         if (varyValue)
303             return nullptr;
304     }
305 #endif // PLATFORM(WIN)
306
307     auto work = [protectedThis = makeRef(*this), cachedResponse = RetainPtr<CFCachedURLResponseRef>(cachedResponse)] () {
308         auto& handle = protectedThis->m_handle;
309         
310         if (!protectedThis->hasHandle() || !handle->client() || !handle->connection()) {
311             protectedThis->continueWillCacheResponse(nullptr);
312             return;
313         }
314
315         LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::willCacheResponse(handle=%p) (%s)", handle, handle->firstRequest().url().string().utf8().data());
316
317         handle->client()->willCacheResponseAsync(handle, cachedResponse.get());
318     };
319     
320     if (m_messageQueue)
321         m_messageQueue->append(std::make_unique<Function<void()>>(WTFMove(work)));
322     else
323         callOnMainThread(WTFMove(work));
324     m_semaphore.wait(TimeWithDynamicClockType(WallTime::infinity()));
325     return m_cachedResponseResult.leakRef();
326 }
327
328 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::didReceiveChallenge(CFURLAuthChallengeRef challenge)
329 {
330     auto work = [protectedThis = makeRef(*this), challenge = RetainPtr<CFURLAuthChallengeRef>(challenge)] () mutable {
331         auto& handle = protectedThis->m_handle;
332         if (!protectedThis->hasHandle())
333             return;
334         
335         LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::didReceiveChallenge(handle=%p) (%s)", handle, handle->firstRequest().url().string().utf8().data());
336
337         handle->didReceiveAuthenticationChallenge(AuthenticationChallenge(challenge.get(), handle));
338     };
339
340     if (m_messageQueue)
341         m_messageQueue->append(std::make_unique<Function<void()>>(WTFMove(work)));
342     else
343         callOnMainThread(WTFMove(work));
344 }
345
346 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::didSendBodyData(CFIndex totalBytesWritten, CFIndex totalBytesExpectedToWrite)
347 {
348     auto work = [protectedThis = makeRef(*this), totalBytesWritten, totalBytesExpectedToWrite] () mutable {
349         auto& handle = protectedThis->m_handle;
350         if (!protectedThis->hasHandle() || !handle->client())
351             return;
352
353         LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::didSendBodyData(handle=%p) (%s)", handle, handle->firstRequest().url().string().utf8().data());
354
355         handle->client()->didSendData(handle, totalBytesWritten, totalBytesExpectedToWrite);
356     };
357
358     if (m_messageQueue)
359         m_messageQueue->append(std::make_unique<Function<void()>>(WTFMove(work)));
360     else
361         callOnMainThread(WTFMove(work));
362 }
363
364 Boolean ResourceHandleCFURLConnectionDelegateWithOperationQueue::shouldUseCredentialStorage()
365 {
366     return false;
367 }
368
369 #if USE(PROTECTION_SPACE_AUTH_CALLBACK)
370 Boolean ResourceHandleCFURLConnectionDelegateWithOperationQueue::canRespondToProtectionSpace(CFURLProtectionSpaceRef protectionSpace)
371 {
372     auto work = [protectedThis = makeRef(*this), protectionSpace = RetainPtr<CFURLProtectionSpaceRef>(protectionSpace)] () mutable {
373         auto& handle = protectedThis->m_handle;
374         
375         if (!protectedThis->hasHandle()) {
376             protectedThis->continueCanAuthenticateAgainstProtectionSpace(false);
377             return;
378         }
379
380         LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::canRespondToProtectionSpace(handle=%p) (%s)", handle, handle->firstRequest().url().string().utf8().data());
381
382         ProtectionSpace coreProtectionSpace = ProtectionSpace(protectionSpace.get());
383 #if PLATFORM(IOS)
384         if (coreProtectionSpace.authenticationScheme() == ProtectionSpaceAuthenticationSchemeUnknown) {
385             m_boolResult = false;
386             dispatch_semaphore_signal(m_semaphore);
387             return;
388         }
389 #endif // PLATFORM(IOS)
390         handle->canAuthenticateAgainstProtectionSpace(coreProtectionSpace);
391     };
392     
393     if (m_messageQueue)
394         m_messageQueue->append(std::make_unique<Function<void()>>(WTFMove(work)));
395     else
396         callOnMainThread(WTFMove(work));
397     m_semaphore.wait(TimeWithDynamicClockType(WallTime::infinity()));
398     return m_boolResult;
399 }
400
401 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::continueCanAuthenticateAgainstProtectionSpace(bool canAuthenticate)
402 {
403     m_boolResult = canAuthenticate;
404     m_semaphore.signal();
405 }
406 #endif // USE(PROTECTION_SPACE_AUTH_CALLBACK)
407
408 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::continueWillSendRequest(CFURLRequestRef request)
409 {
410     m_requestResult = request;
411     m_semaphore.signal();
412 }
413
414 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::continueDidReceiveResponse()
415 {
416     m_semaphore.signal();
417 }
418
419 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::continueWillCacheResponse(CFCachedURLResponseRef response)
420 {
421     m_cachedResponseResult = response;
422     m_semaphore.signal();
423 }
424
425 } // namespace WebCore
426
427 #endif // USE(CFURLCONNECTION)