2 * Copyright (C) 2013-2018 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "ResourceHandleCFURLConnectionDelegateWithOperationQueue.h"
29 #if USE(CFURLCONNECTION)
31 #include "AuthenticationCF.h"
32 #include "AuthenticationChallenge.h"
34 #include "MIMETypeRegistry.h"
35 #include "ResourceHandle.h"
36 #include "ResourceHandleClient.h"
37 #include "ResourceResponse.h"
38 #include "SharedBuffer.h"
40 #include "WebCoreURLResponse.h"
42 #include <pal/spi/cf/CFNetworkSPI.h>
43 #include <wtf/CompletionHandler.h>
44 #include <wtf/MainThread.h>
45 #include <wtf/Threading.h>
46 #include <wtf/text/CString.h>
47 #include <wtf/text/WTFString.h>
51 ResourceHandleCFURLConnectionDelegateWithOperationQueue::ResourceHandleCFURLConnectionDelegateWithOperationQueue(ResourceHandle* handle, MessageQueue<Function<void()>>* messageQueue)
52 : ResourceHandleCFURLConnectionDelegate(handle)
53 , m_messageQueue(messageQueue)
57 ResourceHandleCFURLConnectionDelegateWithOperationQueue::~ResourceHandleCFURLConnectionDelegateWithOperationQueue()
61 bool ResourceHandleCFURLConnectionDelegateWithOperationQueue::hasHandle() const
66 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::releaseHandle()
68 ResourceHandleCFURLConnectionDelegate::releaseHandle();
69 m_requestResult = nullptr;
70 m_cachedResponseResult = nullptr;
74 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::setupRequest(CFMutableURLRequestRef request)
76 CFURLRef requestURL = CFURLRequestGetURL(request);
79 m_originalScheme = adoptCF(CFURLCopyScheme(requestURL));
82 LRESULT CALLBACK hookToRemoveCFNetworkMessage(int code, WPARAM wParam, LPARAM lParam)
84 MSG* msg = reinterpret_cast<MSG*>(lParam);
85 // This message which CFNetwork sends to itself, will block the main thread, remove it.
86 if (msg->message == WM_USER + 0xcf)
87 msg->message = WM_NULL;
88 return CallNextHookEx(nullptr, code, wParam, lParam);
91 static void installHookToRemoveCFNetworkMessageBlockingMainThread()
93 static HHOOK hook = nullptr;
95 DWORD threadID = ::GetCurrentThreadId();
96 hook = ::SetWindowsHookExW(WH_GETMESSAGE, hookToRemoveCFNetworkMessage, 0, threadID);
100 static void emptyPerform(void*)
104 static CFRunLoopRef getRunLoop()
106 static CFRunLoopRef runLoop = nullptr;
110 Thread::create("CFNetwork Loader", [&] {
111 runLoop = CFRunLoopGetCurrent();
113 // Must add a source to the run loop to prevent CFRunLoopRun() from exiting.
114 CFRunLoopSourceContext ctxt = { 0, (void*)1 /*must be non-null*/, 0, 0, 0, 0, 0, 0, 0, emptyPerform };
115 CFRunLoopSourceRef bogusSource = CFRunLoopSourceCreate(0, 0, &ctxt);
116 CFRunLoopAddSource(runLoop, bogusSource, kCFRunLoopDefaultMode);
120 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1E30, true);
122 sem.wait(TimeWithDynamicClockType(WallTime::infinity()));
128 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::setupConnectionScheduling(CFURLConnectionRef connection)
130 installHookToRemoveCFNetworkMessageBlockingMainThread();
131 CFRunLoopRef runLoop = getRunLoop();
132 CFURLConnectionScheduleWithRunLoop(connection, runLoop, kCFRunLoopDefaultMode);
133 CFURLConnectionScheduleDownloadWithRunLoop(connection, runLoop, kCFRunLoopDefaultMode);
136 CFURLRequestRef ResourceHandleCFURLConnectionDelegateWithOperationQueue::willSendRequest(CFURLRequestRef cfRequest, CFURLResponseRef originalRedirectResponse)
138 // 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.
139 if (!originalRedirectResponse) {
140 RetainPtr<CFStringRef> newScheme = adoptCF(CFURLCopyScheme(CFURLRequestGetURL(cfRequest)));
141 if (CFStringCompare(newScheme.get(), m_originalScheme.get(), kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
147 ASSERT(!isMainThread());
149 auto protectedThis = makeRef(*this);
150 auto work = [this, protectedThis = makeRef(*this), cfRequest = RetainPtr<CFURLRequestRef>(cfRequest), originalRedirectResponse = RetainPtr<CFURLResponseRef>(originalRedirectResponse)] () mutable {
151 auto& handle = protectedThis->m_handle;
152 auto completionHandler = [this, protectedThis = WTFMove(protectedThis)] (ResourceRequest&& request) {
153 m_requestResult = request.cfURLRequest(UpdateHTTPBody);
154 m_semaphore.signal();
158 completionHandler({ });
162 LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::willSendRequest(handle=%p) (%s)", handle, handle->firstRequest().url().string().utf8().data());
164 RetainPtr<CFURLResponseRef> redirectResponse = synthesizeRedirectResponseIfNecessary(cfRequest.get(), originalRedirectResponse.get());
165 ASSERT(redirectResponse);
167 ResourceRequest request = createResourceRequest(cfRequest.get(), redirectResponse.get());
168 handle->willSendRequest(WTFMove(request), redirectResponse.get(), WTFMove(completionHandler));
172 m_messageQueue->append(std::make_unique<Function<void()>>(WTFMove(work)));
174 callOnMainThread(WTFMove(work));
175 m_semaphore.wait(TimeWithDynamicClockType(WallTime::infinity()));
177 return m_requestResult.leakRef();
180 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::didReceiveResponse(CFURLConnectionRef connection, CFURLResponseRef cfResponse)
182 auto protectedThis = makeRef(*this);
183 auto work = [this, protectedThis = makeRef(*this), cfResponse = RetainPtr<CFURLResponseRef>(cfResponse), connection = RetainPtr<CFURLConnectionRef>(connection)] () mutable {
184 if (!hasHandle() || !m_handle->client() || !m_handle->connection()) {
185 m_semaphore.signal();
189 LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::didReceiveResponse(handle=%p) (%s)", m_handle, m_handle->firstRequest().url().string().utf8().data());
191 // Avoid MIME type sniffing if the response comes back as 304 Not Modified.
192 auto msg = CFURLResponseGetHTTPResponse(cfResponse.get());
193 int statusCode = msg ? CFHTTPMessageGetResponseStatusCode(msg) : 0;
195 if (statusCode != 304) {
196 bool isMainResourceLoad = m_handle->firstRequest().requester() == ResourceRequest::Requester::Main;
199 if (_CFURLRequestCopyProtocolPropertyForKey(m_handle->firstRequest().cfURLRequest(DoNotUpdateHTTPBody), CFSTR("ForceHTMLMIMEType")))
200 CFURLResponseSetMIMEType(cfResponse.get(), CFSTR("text/html"));
202 ResourceResponse resourceResponse(cfResponse.get());
203 resourceResponse.setSource(ResourceResponse::Source::Network);
204 m_handle->didReceiveResponse(WTFMove(resourceResponse), [this, protectedThis = WTFMove(protectedThis)] {
205 m_semaphore.signal();
210 m_messageQueue->append(std::make_unique<Function<void()>>(WTFMove(work)));
212 callOnMainThread(WTFMove(work));
213 m_semaphore.wait(TimeWithDynamicClockType(WallTime::infinity()));
216 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::didReceiveData(CFDataRef data, CFIndex originalLength)
218 auto work = [protectedThis = makeRef(*this), data = RetainPtr<CFDataRef>(data), originalLength = originalLength] () mutable {
219 auto& handle = protectedThis->m_handle;
220 if (!protectedThis->hasHandle() || !handle->client() || !handle->connection())
223 LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::didReceiveData(handle=%p) (%s)", handle, handle->firstRequest().url().string().utf8().data());
225 handle->client()->didReceiveBuffer(handle, SharedBuffer::create(data.get()), originalLength);
229 m_messageQueue->append(std::make_unique<Function<void()>>(WTFMove(work)));
231 callOnMainThread(WTFMove(work));
234 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::didFinishLoading()
236 auto work = [protectedThis = makeRef(*this)] () mutable {
237 auto& handle = protectedThis->m_handle;
238 if (!protectedThis->hasHandle() || !handle->client() || !handle->connection()) {
239 protectedThis->m_handle->deref();
243 LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::didFinishLoading(handle=%p) (%s)", handle, handle->firstRequest().url().string().utf8().data());
245 handle->client()->didFinishLoading(handle);
246 if (protectedThis->m_messageQueue) {
247 protectedThis->m_messageQueue->kill();
248 protectedThis->m_messageQueue = nullptr;
250 protectedThis->m_handle->deref();
254 m_messageQueue->append(std::make_unique<Function<void()>>(WTFMove(work)));
256 callOnMainThread(WTFMove(work));
259 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::didFail(CFErrorRef error)
261 auto work = [protectedThis = makeRef(*this), error = RetainPtr<CFErrorRef>(error)] () mutable {
262 auto& handle = protectedThis->m_handle;
263 if (!protectedThis->hasHandle() || !handle->client() || !handle->connection()) {
264 protectedThis->m_handle->deref();
268 LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::didFail(handle=%p) (%s)", handle, handle->firstRequest().url().string().utf8().data());
270 handle->client()->didFail(handle, ResourceError(error.get()));
271 if (protectedThis->m_messageQueue) {
272 protectedThis->m_messageQueue->kill();
273 protectedThis->m_messageQueue = nullptr;
275 protectedThis->m_handle->deref();
279 m_messageQueue->append(std::make_unique<Function<void()>>(WTFMove(work)));
281 callOnMainThread(WTFMove(work));
284 CFCachedURLResponseRef ResourceHandleCFURLConnectionDelegateWithOperationQueue::willCacheResponse(CFCachedURLResponseRef cachedResponse)
286 // Workaround for <rdar://problem/6300990> Caching does not respect Vary HTTP header.
287 // FIXME: WebCore cache has issues with Vary, too (bug 58797, bug 71509).
288 CFURLResponseRef wrappedResponse = CFCachedURLResponseGetWrappedResponse(cachedResponse);
289 if (CFHTTPMessageRef httpResponse = CFURLResponseGetHTTPResponse(wrappedResponse)) {
290 ASSERT(CFHTTPMessageIsHeaderComplete(httpResponse));
291 RetainPtr<CFStringRef> varyValue = adoptCF(CFHTTPMessageCopyHeaderFieldValue(httpResponse, CFSTR("Vary")));
296 auto protectedThis = makeRef(*this);
297 auto work = [protectedThis = makeRef(*this), cachedResponse = RetainPtr<CFCachedURLResponseRef>(cachedResponse)] () {
298 auto& handle = protectedThis->m_handle;
300 if (!protectedThis->hasHandle() || !handle->client() || !handle->connection()) {
301 protectedThis->continueWillCacheResponse(nullptr);
305 LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::willCacheResponse(handle=%p) (%s)", handle, handle->firstRequest().url().string().utf8().data());
307 handle->client()->willCacheResponseAsync(handle, cachedResponse.get());
311 m_messageQueue->append(std::make_unique<Function<void()>>(WTFMove(work)));
313 callOnMainThread(WTFMove(work));
314 m_semaphore.wait(TimeWithDynamicClockType(WallTime::infinity()));
315 return m_cachedResponseResult.leakRef();
318 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::didReceiveChallenge(CFURLAuthChallengeRef challenge)
320 auto work = [protectedThis = makeRef(*this), challenge = RetainPtr<CFURLAuthChallengeRef>(challenge)] () mutable {
321 auto& handle = protectedThis->m_handle;
322 if (!protectedThis->hasHandle())
325 LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::didReceiveChallenge(handle=%p) (%s)", handle, handle->firstRequest().url().string().utf8().data());
327 handle->didReceiveAuthenticationChallenge(AuthenticationChallenge(challenge.get(), handle));
331 m_messageQueue->append(std::make_unique<Function<void()>>(WTFMove(work)));
333 callOnMainThread(WTFMove(work));
336 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::didSendBodyData(CFIndex totalBytesWritten, CFIndex totalBytesExpectedToWrite)
338 auto work = [protectedThis = makeRef(*this), totalBytesWritten, totalBytesExpectedToWrite] () mutable {
339 auto& handle = protectedThis->m_handle;
340 if (!protectedThis->hasHandle() || !handle->client() || !handle->connection())
343 LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::didSendBodyData(handle=%p) (%s)", handle, handle->firstRequest().url().string().utf8().data());
345 handle->client()->didSendData(handle, totalBytesWritten, totalBytesExpectedToWrite);
349 m_messageQueue->append(std::make_unique<Function<void()>>(WTFMove(work)));
351 callOnMainThread(WTFMove(work));
354 Boolean ResourceHandleCFURLConnectionDelegateWithOperationQueue::shouldUseCredentialStorage()
359 #if USE(PROTECTION_SPACE_AUTH_CALLBACK)
360 Boolean ResourceHandleCFURLConnectionDelegateWithOperationQueue::canRespondToProtectionSpace(CFURLProtectionSpaceRef protectionSpace)
362 auto protectedThis = makeRef(*this);
363 auto work = [protectedThis = makeRef(*this), protectionSpace = RetainPtr<CFURLProtectionSpaceRef>(protectionSpace)] () mutable {
364 auto& handle = protectedThis->m_handle;
366 if (!protectedThis->hasHandle()) {
367 protectedThis->continueCanAuthenticateAgainstProtectionSpace(false);
371 LOG(Network, "CFNet - ResourceHandleCFURLConnectionDelegateWithOperationQueue::canRespondToProtectionSpace(handle=%p) (%s)", handle, handle->firstRequest().url().string().utf8().data());
373 ProtectionSpace coreProtectionSpace = ProtectionSpace(protectionSpace.get());
374 handle->canAuthenticateAgainstProtectionSpace(coreProtectionSpace);
378 m_messageQueue->append(std::make_unique<Function<void()>>(WTFMove(work)));
380 callOnMainThread(WTFMove(work));
381 m_semaphore.wait(TimeWithDynamicClockType(WallTime::infinity()));
385 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::continueCanAuthenticateAgainstProtectionSpace(bool canAuthenticate)
387 m_boolResult = canAuthenticate;
388 m_semaphore.signal();
390 #endif // USE(PROTECTION_SPACE_AUTH_CALLBACK)
392 void ResourceHandleCFURLConnectionDelegateWithOperationQueue::continueWillCacheResponse(CFCachedURLResponseRef response)
394 m_cachedResponseResult = response;
395 m_semaphore.signal();
398 } // namespace WebCore
400 #endif // USE(CFURLCONNECTION)