2 * Copyright (C) 2004-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. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
27 #import "WebCoreResourceHandleAsOperationQueueDelegate.h"
29 #import "AuthenticationChallenge.h"
30 #import "AuthenticationMac.h"
32 #import "NetworkingContext.h"
33 #import "ResourceHandle.h"
34 #import "ResourceHandleClient.h"
35 #import "ResourceRequest.h"
36 #import "ResourceResponse.h"
37 #import "SharedBuffer.h"
38 #import "SynchronousLoaderClient.h"
39 #import "WebCoreURLResponse.h"
40 #import <pal/spi/cf/CFNetworkSPI.h>
41 #import <wtf/BlockPtr.h>
42 #import <wtf/MainThread.h>
44 using namespace WebCore;
46 static bool scheduledWithCustomRunLoopMode(const std::optional<SchedulePairHashSet>& pairs)
50 for (auto& pair : *pairs) {
51 auto mode = pair->mode();
52 if (mode != kCFRunLoopCommonModes && mode != kCFRunLoopDefaultMode)
58 @implementation WebCoreResourceHandleAsOperationQueueDelegate
60 - (void)callFunctionOnMainThread:(Function<void()>&&)function
62 // Sync xhr uses the message queue.
64 return m_messageQueue->append(std::make_unique<Function<void()>>(WTFMove(function)));
66 // This is the common case.
67 if (!scheduledWithCustomRunLoopMode(m_scheduledPairs))
68 return callOnMainThread(WTFMove(function));
70 // If we have been scheduled in a custom run loop mode, schedule a block in that mode.
71 auto block = BlockPtr<void()>::fromCallable([alreadyCalled = false, function = WTFMove(function)] () mutable {
78 for (auto& pair : *m_scheduledPairs)
79 CFRunLoopPerformBlock(pair->runLoop(), pair->mode(), block.get());
82 - (id)initWithHandle:(ResourceHandle*)handle messageQueue:(MessageQueue<Function<void()>>*)messageQueue
89 if (m_handle && m_handle->context()) {
90 if (auto* pairs = m_handle->context()->scheduledRunLoopPairs())
91 m_scheduledPairs = *pairs;
93 m_messageQueue = messageQueue;
100 LockHolder lock(m_mutex);
104 m_messageQueue = nullptr;
105 m_requestResult = nullptr;
106 m_cachedResponseResult = nullptr;
108 m_semaphore.signal(); // OK to signal even if we are not waiting.
116 - (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)newRequest redirectResponse:(NSURLResponse *)redirectResponse
118 ASSERT(!isMainThread());
119 UNUSED_PARAM(connection);
121 redirectResponse = synthesizeRedirectResponseIfNecessary([connection currentRequest], newRequest, redirectResponse);
123 // See <rdar://problem/5380697>. This is a workaround for a behavior change in CFNetwork where willSendRequest gets called more often.
124 if (!redirectResponse)
128 if ([redirectResponse isKindOfClass:[NSHTTPURLResponse class]])
129 LOG(Network, "Handle %p delegate connection:%p willSendRequest:%@ redirectResponse:%d, Location:<%@>", m_handle, connection, [newRequest description], static_cast<int>([(id)redirectResponse statusCode]), [[(id)redirectResponse allHeaderFields] objectForKey:@"Location"]);
131 LOG(Network, "Handle %p delegate connection:%p willSendRequest:%@ redirectResponse:non-HTTP", m_handle, connection, [newRequest description]);
134 auto protectedSelf = retainPtr(self);
135 auto work = [self, protectedSelf, newRequest = retainPtr(newRequest), redirectResponse = retainPtr(redirectResponse)] () mutable {
137 m_requestResult = nullptr;
138 m_semaphore.signal();
142 m_handle->willSendRequest(newRequest.get(), redirectResponse.get(), [self, protectedSelf = WTFMove(protectedSelf)](ResourceRequest&& request) {
143 m_requestResult = request.nsURLRequest(HTTPBodyUpdatePolicy::UpdateHTTPBody);
144 m_semaphore.signal();
148 [self callFunctionOnMainThread:WTFMove(work)];
151 LockHolder lock(m_mutex);
155 RetainPtr<NSURLRequest> requestResult = m_requestResult;
157 // Make sure protectedSelf gets destroyed on the main thread in case this is the last strong reference to self
158 // as we do not want to get destroyed on a non-main thread.
159 [self callFunctionOnMainThread:[protectedSelf = WTFMove(protectedSelf)] { }];
161 return requestResult.autorelease();
164 - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
166 ASSERT(!isMainThread());
167 UNUSED_PARAM(connection);
169 LOG(Network, "Handle %p delegate connection:%p didReceiveAuthenticationChallenge:%p", m_handle, connection, challenge);
171 auto work = [self, protectedSelf = retainPtr(self), challenge = retainPtr(challenge)] () mutable {
173 [[challenge sender] cancelAuthenticationChallenge:challenge.get()];
176 m_handle->didReceiveAuthenticationChallenge(core(challenge.get()));
179 [self callFunctionOnMainThread:WTFMove(work)];
182 - (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
184 ASSERT(!isMainThread());
185 UNUSED_PARAM(connection);
187 LOG(Network, "Handle %p delegate connection:%p canAuthenticateAgainstProtectionSpace:%@://%@:%u realm:%@ method:%@ %@%@", m_handle, connection, [protectionSpace protocol], [protectionSpace host], [protectionSpace port], [protectionSpace realm], [protectionSpace authenticationMethod], [protectionSpace isProxy] ? @"proxy:" : @"", [protectionSpace isProxy] ? [protectionSpace proxyType] : @"");
189 auto protectedSelf = retainPtr(self);
190 auto work = [self, protectedSelf, protectionSpace = retainPtr(protectionSpace)] () mutable {
193 m_semaphore.signal();
196 m_handle->canAuthenticateAgainstProtectionSpace(ProtectionSpace(protectionSpace.get()), [self, protectedSelf = WTFMove(protectedSelf)] (bool result) mutable {
197 m_boolResult = result;
198 m_semaphore.signal();
202 [self callFunctionOnMainThread:WTFMove(work)];
205 LockHolder lock(m_mutex);
209 auto boolResult = m_boolResult;
211 // Make sure protectedSelf gets destroyed on the main thread in case this is the last strong reference to self
212 // as we do not want to get destroyed on a non-main thread.
213 [self callFunctionOnMainThread:[protectedSelf = WTFMove(protectedSelf)] { }];
218 - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)r
220 ASSERT(!isMainThread());
222 LOG(Network, "Handle %p delegate connection:%p didReceiveResponse:%p (HTTP status %d, reported MIMEType '%s')", m_handle, connection, r, [r respondsToSelector:@selector(statusCode)] ? [(id)r statusCode] : 0, [[r MIMEType] UTF8String]);
224 auto protectedSelf = retainPtr(self);
225 auto work = [self, protectedSelf, r = retainPtr(r), connection = retainPtr(connection)] () mutable {
226 RefPtr<ResourceHandle> protectedHandle(m_handle);
227 if (!m_handle || !m_handle->client()) {
228 m_semaphore.signal();
232 // Avoid MIME type sniffing if the response comes back as 304 Not Modified.
233 int statusCode = [r respondsToSelector:@selector(statusCode)] ? [(id)r statusCode] : 0;
234 if (statusCode != 304) {
235 bool isMainResourceLoad = m_handle->firstRequest().requester() == ResourceRequest::Requester::Main;
236 adjustMIMETypeIfNecessary([r _CFURLResponse], isMainResourceLoad);
239 if ([m_handle->firstRequest().nsURLRequest(HTTPBodyUpdatePolicy::DoNotUpdateHTTPBody) _propertyForKey:@"ForceHTMLMIMEType"])
240 [r _setMIMEType:@"text/html"];
242 ResourceResponse resourceResponse(r.get());
243 resourceResponse.setSource(ResourceResponse::Source::Network);
244 ResourceHandle::getConnectionTimingData(connection.get(), resourceResponse.deprecatedNetworkLoadMetrics());
246 m_handle->didReceiveResponse(WTFMove(resourceResponse), [self, protectedSelf = WTFMove(protectedSelf)] {
247 m_semaphore.signal();
251 [self callFunctionOnMainThread:WTFMove(work)];
254 // Make sure we get destroyed on the main thread.
255 [self callFunctionOnMainThread:[protectedSelf = WTFMove(protectedSelf)] { }];
258 - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data lengthReceived:(long long)lengthReceived
260 ASSERT(!isMainThread());
261 UNUSED_PARAM(connection);
262 UNUSED_PARAM(lengthReceived);
264 LOG(Network, "Handle %p delegate connection:%p didReceiveData:%p lengthReceived:%lld", m_handle, connection, data, lengthReceived);
266 auto work = [self = self, protectedSelf = retainPtr(self), data = retainPtr(data)] () mutable {
267 if (!m_handle || !m_handle->client())
269 // FIXME: If we get more than 2B bytes in a single chunk, this code won't do the right thing.
270 // However, with today's computers and networking speeds, this won't happen in practice.
271 // Could be an issue with a giant local file.
273 // FIXME: https://bugs.webkit.org/show_bug.cgi?id=19793
274 // -1 means we do not provide any data about transfer size to inspector so it would use
275 // Content-Length headers or content size to show transfer size.
276 m_handle->client()->didReceiveBuffer(m_handle, SharedBuffer::create(data.get()), -1);
279 [self callFunctionOnMainThread:WTFMove(work)];
282 - (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite
284 ASSERT(!isMainThread());
285 UNUSED_PARAM(connection);
286 UNUSED_PARAM(bytesWritten);
288 LOG(Network, "Handle %p delegate connection:%p didSendBodyData:%d totalBytesWritten:%d totalBytesExpectedToWrite:%d", m_handle, connection, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
290 auto work = [self = self, protectedSelf = retainPtr(self), totalBytesWritten = totalBytesWritten, totalBytesExpectedToWrite = totalBytesExpectedToWrite] () mutable {
291 if (!m_handle || !m_handle->client())
293 m_handle->client()->didSendData(m_handle, totalBytesWritten, totalBytesExpectedToWrite);
296 [self callFunctionOnMainThread:WTFMove(work)];
299 - (void)connectionDidFinishLoading:(NSURLConnection *)connection
301 ASSERT(!isMainThread());
302 UNUSED_PARAM(connection);
304 LOG(Network, "Handle %p delegate connectionDidFinishLoading:%p", m_handle, connection);
306 auto work = [self = self, protectedSelf = retainPtr(self)] () mutable {
307 if (!m_handle || !m_handle->client())
310 m_handle->client()->didFinishLoading(m_handle);
311 if (m_messageQueue) {
312 m_messageQueue->kill();
313 m_messageQueue = nullptr;
317 [self callFunctionOnMainThread:WTFMove(work)];
320 - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
322 ASSERT(!isMainThread());
323 UNUSED_PARAM(connection);
325 LOG(Network, "Handle %p delegate connection:%p didFailWithError:%@", m_handle, connection, error);
327 auto work = [self = self, protectedSelf = retainPtr(self), error = retainPtr(error)] () mutable {
328 if (!m_handle || !m_handle->client())
331 m_handle->client()->didFail(m_handle, error.get());
332 if (m_messageQueue) {
333 m_messageQueue->kill();
334 m_messageQueue = nullptr;
338 [self callFunctionOnMainThread:WTFMove(work)];
342 - (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse
344 ASSERT(!isMainThread());
345 UNUSED_PARAM(connection);
347 LOG(Network, "Handle %p delegate connection:%p willCacheResponse:%p", m_handle, connection, cachedResponse);
349 auto protectedSelf = retainPtr(self);
350 auto work = [self, protectedSelf, cachedResponse = retainPtr(cachedResponse)] () mutable {
351 if (!m_handle || !m_handle->client()) {
352 m_cachedResponseResult = nullptr;
353 m_semaphore.signal();
357 m_handle->client()->willCacheResponseAsync(m_handle, cachedResponse.get(), [self, protectedSelf = WTFMove(protectedSelf)] (NSCachedURLResponse * response) mutable {
358 m_cachedResponseResult = response;
359 m_semaphore.signal();
363 [self callFunctionOnMainThread:WTFMove(work)];
366 LockHolder lock(m_mutex);
370 RetainPtr<NSCachedURLResponse> cachedResponseResult = m_cachedResponseResult;
372 // Make sure protectedSelf gets destroyed on the main thread in case this is the last strong reference to self
373 // as we do not want to get destroyed on a non-main thread.
374 [self callFunctionOnMainThread:[protectedSelf = WTFMove(protectedSelf)] { }];
376 return cachedResponseResult.autorelease();
381 @implementation WebCoreResourceHandleWithCredentialStorageAsOperationQueueDelegate
383 - (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection
385 ASSERT(!isMainThread());
386 UNUSED_PARAM(connection);