b3e907ffdde285f8c803d671a27ff74a0921c26c
[WebKit-https.git] / Source / WebCore / platform / network / mac / WebCoreResourceHandleAsOperationQueueDelegate.mm
1 /*
2  * Copyright (C) 2004-2018 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. 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.
24  */
25
26 #import "config.h"
27 #import "WebCoreResourceHandleAsOperationQueueDelegate.h"
28
29 #import "AuthenticationChallenge.h"
30 #import "AuthenticationMac.h"
31 #import "Logging.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>
43
44 using namespace WebCore;
45
46 static bool scheduledWithCustomRunLoopMode(const std::optional<SchedulePairHashSet>& pairs)
47 {
48     if (!pairs)
49         return false;
50     for (auto& pair : *pairs) {
51         auto mode = pair->mode();
52         if (mode != kCFRunLoopCommonModes && mode != kCFRunLoopDefaultMode)
53             return true;
54     }
55     return false;
56 }
57
58 @implementation WebCoreResourceHandleAsOperationQueueDelegate
59
60 - (void)callFunctionOnMainThread:(Function<void()>&&)function
61 {
62     // Sync xhr uses the message queue.
63     if (m_messageQueue)
64         return m_messageQueue->append(std::make_unique<Function<void()>>(WTFMove(function)));
65
66     // This is the common case.
67     if (!scheduledWithCustomRunLoopMode(m_scheduledPairs))
68         return callOnMainThread(WTFMove(function));
69
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 {
72         if (alreadyCalled)
73             return;
74         alreadyCalled = true;
75         function();
76         function = nullptr;
77     });
78     for (auto& pair : *m_scheduledPairs)
79         CFRunLoopPerformBlock(pair->runLoop(), pair->mode(), block.get());
80 }
81
82 - (id)initWithHandle:(ResourceHandle*)handle messageQueue:(MessageQueue<Function<void()>>*)messageQueue
83 {
84     self = [self init];
85     if (!self)
86         return nil;
87
88     m_handle = handle;
89     if (m_handle && m_handle->context()) {
90         if (auto* pairs = m_handle->context()->scheduledRunLoopPairs())
91             m_scheduledPairs = *pairs;
92     }
93     m_semaphore = dispatch_semaphore_create(0);
94     m_messageQueue = messageQueue;
95
96     return self;
97 }
98
99 - (void)detachHandle
100 {
101     LockHolder lock(m_mutex);
102
103     m_handle = nullptr;
104
105     m_messageQueue = nullptr;
106     m_requestResult = nullptr;
107     m_cachedResponseResult = nullptr;
108     m_boolResult = NO;
109     dispatch_semaphore_signal(m_semaphore); // OK to signal even if we are not waiting.
110 }
111
112 - (void)dealloc
113 {
114     dispatch_release(m_semaphore);
115     [super dealloc];
116 }
117
118 - (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)newRequest redirectResponse:(NSURLResponse *)redirectResponse
119 {
120     ASSERT(!isMainThread());
121     UNUSED_PARAM(connection);
122
123     redirectResponse = synthesizeRedirectResponseIfNecessary([connection currentRequest], newRequest, redirectResponse);
124
125     // See <rdar://problem/5380697>. This is a workaround for a behavior change in CFNetwork where willSendRequest gets called more often.
126     if (!redirectResponse)
127         return newRequest;
128
129 #if !LOG_DISABLED
130     if ([redirectResponse isKindOfClass:[NSHTTPURLResponse class]])
131         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"]);
132     else
133         LOG(Network, "Handle %p delegate connection:%p willSendRequest:%@ redirectResponse:non-HTTP", m_handle, connection, [newRequest description]); 
134 #endif
135
136     auto protectedSelf = retainPtr(self);
137     auto work = [self, protectedSelf, newRequest = retainPtr(newRequest), redirectResponse = retainPtr(redirectResponse)] () mutable {
138         if (!m_handle) {
139             m_requestResult = nullptr;
140             dispatch_semaphore_signal(m_semaphore);
141             return;
142         }
143
144         m_handle->willSendRequest(newRequest.get(), redirectResponse.get(), [self, protectedSelf = WTFMove(protectedSelf)](ResourceRequest&& request) {
145             m_requestResult = request.nsURLRequest(HTTPBodyUpdatePolicy::UpdateHTTPBody);
146             dispatch_semaphore_signal(m_semaphore);
147         });
148     };
149
150     [self callFunctionOnMainThread:WTFMove(work)];
151     dispatch_semaphore_wait(m_semaphore, DISPATCH_TIME_FOREVER);
152
153     LockHolder lock(m_mutex);
154     if (!m_handle)
155         return nil;
156
157     RetainPtr<NSURLRequest> requestResult = m_requestResult;
158
159     // Make sure protectedSelf gets destroyed on the main thread in case this is the last strong reference to self
160     // as we do not want to get destroyed on a non-main thread.
161     [self callFunctionOnMainThread:[protectedSelf = WTFMove(protectedSelf)] { }];
162
163     return requestResult.autorelease();
164 }
165
166 - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
167 {
168     ASSERT(!isMainThread());
169     UNUSED_PARAM(connection);
170
171     LOG(Network, "Handle %p delegate connection:%p didReceiveAuthenticationChallenge:%p", m_handle, connection, challenge);
172
173     auto work = [self, protectedSelf = retainPtr(self), challenge = retainPtr(challenge)] () mutable {
174         if (!m_handle) {
175             [[challenge sender] cancelAuthenticationChallenge:challenge.get()];
176             return;
177         }
178         m_handle->didReceiveAuthenticationChallenge(core(challenge.get()));
179     };
180
181     [self callFunctionOnMainThread:WTFMove(work)];
182 }
183
184 - (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
185 {
186     ASSERT(!isMainThread());
187     UNUSED_PARAM(connection);
188
189     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] : @"");
190
191     auto protectedSelf = retainPtr(self);
192     auto work = [self, protectedSelf, protectionSpace = retainPtr(protectionSpace)] () mutable {
193         if (!m_handle) {
194             m_boolResult = NO;
195             dispatch_semaphore_signal(m_semaphore);
196             return;
197         }
198         m_handle->canAuthenticateAgainstProtectionSpace(ProtectionSpace(protectionSpace.get()), [self, protectedSelf = WTFMove(protectedSelf)] (bool result) mutable {
199             m_boolResult = result;
200             dispatch_semaphore_signal(m_semaphore);
201         });
202     };
203
204     [self callFunctionOnMainThread:WTFMove(work)];
205     dispatch_semaphore_wait(m_semaphore, DISPATCH_TIME_FOREVER);
206
207     LockHolder lock(m_mutex);
208     if (!m_handle)
209         return NO;
210
211     auto boolResult = m_boolResult;
212
213     // Make sure protectedSelf gets destroyed on the main thread in case this is the last strong reference to self
214     // as we do not want to get destroyed on a non-main thread.
215     [self callFunctionOnMainThread:[protectedSelf = WTFMove(protectedSelf)] { }];
216
217     return boolResult;
218 }
219
220 - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)r
221 {
222     ASSERT(!isMainThread());
223
224     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]);
225
226     auto protectedSelf = retainPtr(self);
227     auto work = [self, protectedSelf, r = retainPtr(r), connection = retainPtr(connection)] () mutable {
228         RefPtr<ResourceHandle> protectedHandle(m_handle);
229         if (!m_handle || !m_handle->client()) {
230             dispatch_semaphore_signal(m_semaphore);
231             return;
232         }
233
234         // Avoid MIME type sniffing if the response comes back as 304 Not Modified.
235         int statusCode = [r respondsToSelector:@selector(statusCode)] ? [(id)r statusCode] : 0;
236         if (statusCode != 304) {
237             bool isMainResourceLoad = m_handle->firstRequest().requester() == ResourceRequest::Requester::Main;
238             adjustMIMETypeIfNecessary([r _CFURLResponse], isMainResourceLoad);
239         }
240
241         if ([m_handle->firstRequest().nsURLRequest(HTTPBodyUpdatePolicy::DoNotUpdateHTTPBody) _propertyForKey:@"ForceHTMLMIMEType"])
242             [r _setMIMEType:@"text/html"];
243
244         ResourceResponse resourceResponse(r.get());
245         resourceResponse.setSource(ResourceResponse::Source::Network);
246         ResourceHandle::getConnectionTimingData(connection.get(), resourceResponse.deprecatedNetworkLoadMetrics());
247
248         m_handle->didReceiveResponse(WTFMove(resourceResponse), [self, protectedSelf = WTFMove(protectedSelf)] {
249             dispatch_semaphore_signal(m_semaphore);
250         });
251     };
252
253     [self callFunctionOnMainThread:WTFMove(work)];
254     dispatch_semaphore_wait(m_semaphore, DISPATCH_TIME_FOREVER);
255
256     // Make sure we get destroyed on the main thread.
257     [self callFunctionOnMainThread:[protectedSelf = WTFMove(protectedSelf)] { }];
258 }
259
260 - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data lengthReceived:(long long)lengthReceived
261 {
262     ASSERT(!isMainThread());
263     UNUSED_PARAM(connection);
264     UNUSED_PARAM(lengthReceived);
265
266     LOG(Network, "Handle %p delegate connection:%p didReceiveData:%p lengthReceived:%lld", m_handle, connection, data, lengthReceived);
267
268     auto work = [self = self, protectedSelf = retainPtr(self), data = retainPtr(data)] () mutable {
269         if (!m_handle || !m_handle->client())
270             return;
271         // FIXME: If we get more than 2B bytes in a single chunk, this code won't do the right thing.
272         // However, with today's computers and networking speeds, this won't happen in practice.
273         // Could be an issue with a giant local file.
274
275         // FIXME: https://bugs.webkit.org/show_bug.cgi?id=19793
276         // -1 means we do not provide any data about transfer size to inspector so it would use
277         // Content-Length headers or content size to show transfer size.
278         m_handle->client()->didReceiveBuffer(m_handle, SharedBuffer::create(data.get()), -1);
279     };
280
281     [self callFunctionOnMainThread:WTFMove(work)];
282 }
283
284 - (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite
285 {
286     ASSERT(!isMainThread());
287     UNUSED_PARAM(connection);
288     UNUSED_PARAM(bytesWritten);
289
290     LOG(Network, "Handle %p delegate connection:%p didSendBodyData:%d totalBytesWritten:%d totalBytesExpectedToWrite:%d", m_handle, connection, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
291
292     auto work = [self = self, protectedSelf = retainPtr(self), totalBytesWritten = totalBytesWritten, totalBytesExpectedToWrite = totalBytesExpectedToWrite] () mutable {
293         if (!m_handle || !m_handle->client())
294             return;
295         m_handle->client()->didSendData(m_handle, totalBytesWritten, totalBytesExpectedToWrite);
296     };
297
298     [self callFunctionOnMainThread:WTFMove(work)];
299 }
300
301 - (void)connectionDidFinishLoading:(NSURLConnection *)connection
302 {
303     ASSERT(!isMainThread());
304     UNUSED_PARAM(connection);
305
306     LOG(Network, "Handle %p delegate connectionDidFinishLoading:%p", m_handle, connection);
307
308     auto work = [self = self, protectedSelf = retainPtr(self)] () mutable {
309         if (!m_handle || !m_handle->client())
310             return;
311
312         m_handle->client()->didFinishLoading(m_handle);
313         if (m_messageQueue) {
314             m_messageQueue->kill();
315             m_messageQueue = nullptr;
316         }
317     };
318
319     [self callFunctionOnMainThread:WTFMove(work)];
320 }
321
322 - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
323 {
324     ASSERT(!isMainThread());
325     UNUSED_PARAM(connection);
326
327     LOG(Network, "Handle %p delegate connection:%p didFailWithError:%@", m_handle, connection, error);
328
329     auto work = [self = self, protectedSelf = retainPtr(self), error = retainPtr(error)] () mutable {
330         if (!m_handle || !m_handle->client())
331             return;
332
333         m_handle->client()->didFail(m_handle, error.get());
334         if (m_messageQueue) {
335             m_messageQueue->kill();
336             m_messageQueue = nullptr;
337         }
338     };
339
340     [self callFunctionOnMainThread:WTFMove(work)];
341 }
342
343
344 - (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse
345 {
346     ASSERT(!isMainThread());
347     UNUSED_PARAM(connection);
348
349     LOG(Network, "Handle %p delegate connection:%p willCacheResponse:%p", m_handle, connection, cachedResponse);
350
351     auto protectedSelf = retainPtr(self);
352     auto work = [self, protectedSelf, cachedResponse = retainPtr(cachedResponse)] () mutable {
353         if (!m_handle || !m_handle->client()) {
354             m_cachedResponseResult = nullptr;
355             dispatch_semaphore_signal(m_semaphore);
356             return;
357         }
358
359         m_handle->client()->willCacheResponseAsync(m_handle, cachedResponse.get(), [self, protectedSelf = WTFMove(protectedSelf)] (NSCachedURLResponse * response) mutable {
360             m_cachedResponseResult = response;
361             dispatch_semaphore_signal(m_semaphore);
362         });
363     };
364
365     [self callFunctionOnMainThread:WTFMove(work)];
366     dispatch_semaphore_wait(m_semaphore, DISPATCH_TIME_FOREVER);
367
368     LockHolder lock(m_mutex);
369     if (!m_handle)
370         return nil;
371
372     RetainPtr<NSCachedURLResponse> cachedResponseResult = m_cachedResponseResult;
373
374     // Make sure protectedSelf gets destroyed on the main thread in case this is the last strong reference to self
375     // as we do not want to get destroyed on a non-main thread.
376     [self callFunctionOnMainThread:[protectedSelf = WTFMove(protectedSelf)] { }];
377
378     return cachedResponseResult.autorelease();
379 }
380
381 @end
382
383 @implementation WebCoreResourceHandleWithCredentialStorageAsOperationQueueDelegate
384
385 - (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection
386 {
387     ASSERT(!isMainThread());
388     UNUSED_PARAM(connection);
389     return NO;
390 }
391
392 @end