[WTF] Use Semaphore and BinarySemaphore instead of dispatch_semaphore_t
[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_messageQueue = messageQueue;
94
95     return self;
96 }
97
98 - (void)detachHandle
99 {
100     LockHolder lock(m_mutex);
101
102     m_handle = nullptr;
103
104     m_messageQueue = nullptr;
105     m_requestResult = nullptr;
106     m_cachedResponseResult = nullptr;
107     m_boolResult = NO;
108     m_semaphore.signal(); // OK to signal even if we are not waiting.
109 }
110
111 - (void)dealloc
112 {
113     [super dealloc];
114 }
115
116 - (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)newRequest redirectResponse:(NSURLResponse *)redirectResponse
117 {
118     ASSERT(!isMainThread());
119     UNUSED_PARAM(connection);
120
121     redirectResponse = synthesizeRedirectResponseIfNecessary([connection currentRequest], newRequest, redirectResponse);
122
123     // See <rdar://problem/5380697>. This is a workaround for a behavior change in CFNetwork where willSendRequest gets called more often.
124     if (!redirectResponse)
125         return newRequest;
126
127 #if !LOG_DISABLED
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"]);
130     else
131         LOG(Network, "Handle %p delegate connection:%p willSendRequest:%@ redirectResponse:non-HTTP", m_handle, connection, [newRequest description]); 
132 #endif
133
134     auto protectedSelf = retainPtr(self);
135     auto work = [self, protectedSelf, newRequest = retainPtr(newRequest), redirectResponse = retainPtr(redirectResponse)] () mutable {
136         if (!m_handle) {
137             m_requestResult = nullptr;
138             m_semaphore.signal();
139             return;
140         }
141
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();
145         });
146     };
147
148     [self callFunctionOnMainThread:WTFMove(work)];
149     m_semaphore.wait();
150
151     LockHolder lock(m_mutex);
152     if (!m_handle)
153         return nil;
154
155     RetainPtr<NSURLRequest> requestResult = m_requestResult;
156
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)] { }];
160
161     return requestResult.autorelease();
162 }
163
164 - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
165 {
166     ASSERT(!isMainThread());
167     UNUSED_PARAM(connection);
168
169     LOG(Network, "Handle %p delegate connection:%p didReceiveAuthenticationChallenge:%p", m_handle, connection, challenge);
170
171     auto work = [self, protectedSelf = retainPtr(self), challenge = retainPtr(challenge)] () mutable {
172         if (!m_handle) {
173             [[challenge sender] cancelAuthenticationChallenge:challenge.get()];
174             return;
175         }
176         m_handle->didReceiveAuthenticationChallenge(core(challenge.get()));
177     };
178
179     [self callFunctionOnMainThread:WTFMove(work)];
180 }
181
182 - (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
183 {
184     ASSERT(!isMainThread());
185     UNUSED_PARAM(connection);
186
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] : @"");
188
189     auto protectedSelf = retainPtr(self);
190     auto work = [self, protectedSelf, protectionSpace = retainPtr(protectionSpace)] () mutable {
191         if (!m_handle) {
192             m_boolResult = NO;
193             m_semaphore.signal();
194             return;
195         }
196         m_handle->canAuthenticateAgainstProtectionSpace(ProtectionSpace(protectionSpace.get()), [self, protectedSelf = WTFMove(protectedSelf)] (bool result) mutable {
197             m_boolResult = result;
198             m_semaphore.signal();
199         });
200     };
201
202     [self callFunctionOnMainThread:WTFMove(work)];
203     m_semaphore.wait();
204
205     LockHolder lock(m_mutex);
206     if (!m_handle)
207         return NO;
208
209     auto boolResult = m_boolResult;
210
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)] { }];
214
215     return boolResult;
216 }
217
218 - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)r
219 {
220     ASSERT(!isMainThread());
221
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]);
223
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();
229             return;
230         }
231
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);
237         }
238
239         if ([m_handle->firstRequest().nsURLRequest(HTTPBodyUpdatePolicy::DoNotUpdateHTTPBody) _propertyForKey:@"ForceHTMLMIMEType"])
240             [r _setMIMEType:@"text/html"];
241
242         ResourceResponse resourceResponse(r.get());
243         resourceResponse.setSource(ResourceResponse::Source::Network);
244         ResourceHandle::getConnectionTimingData(connection.get(), resourceResponse.deprecatedNetworkLoadMetrics());
245
246         m_handle->didReceiveResponse(WTFMove(resourceResponse), [self, protectedSelf = WTFMove(protectedSelf)] {
247             m_semaphore.signal();
248         });
249     };
250
251     [self callFunctionOnMainThread:WTFMove(work)];
252     m_semaphore.wait();
253
254     // Make sure we get destroyed on the main thread.
255     [self callFunctionOnMainThread:[protectedSelf = WTFMove(protectedSelf)] { }];
256 }
257
258 - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data lengthReceived:(long long)lengthReceived
259 {
260     ASSERT(!isMainThread());
261     UNUSED_PARAM(connection);
262     UNUSED_PARAM(lengthReceived);
263
264     LOG(Network, "Handle %p delegate connection:%p didReceiveData:%p lengthReceived:%lld", m_handle, connection, data, lengthReceived);
265
266     auto work = [self = self, protectedSelf = retainPtr(self), data = retainPtr(data)] () mutable {
267         if (!m_handle || !m_handle->client())
268             return;
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.
272
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);
277     };
278
279     [self callFunctionOnMainThread:WTFMove(work)];
280 }
281
282 - (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite
283 {
284     ASSERT(!isMainThread());
285     UNUSED_PARAM(connection);
286     UNUSED_PARAM(bytesWritten);
287
288     LOG(Network, "Handle %p delegate connection:%p didSendBodyData:%d totalBytesWritten:%d totalBytesExpectedToWrite:%d", m_handle, connection, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
289
290     auto work = [self = self, protectedSelf = retainPtr(self), totalBytesWritten = totalBytesWritten, totalBytesExpectedToWrite = totalBytesExpectedToWrite] () mutable {
291         if (!m_handle || !m_handle->client())
292             return;
293         m_handle->client()->didSendData(m_handle, totalBytesWritten, totalBytesExpectedToWrite);
294     };
295
296     [self callFunctionOnMainThread:WTFMove(work)];
297 }
298
299 - (void)connectionDidFinishLoading:(NSURLConnection *)connection
300 {
301     ASSERT(!isMainThread());
302     UNUSED_PARAM(connection);
303
304     LOG(Network, "Handle %p delegate connectionDidFinishLoading:%p", m_handle, connection);
305
306     auto work = [self = self, protectedSelf = retainPtr(self)] () mutable {
307         if (!m_handle || !m_handle->client())
308             return;
309
310         m_handle->client()->didFinishLoading(m_handle);
311         if (m_messageQueue) {
312             m_messageQueue->kill();
313             m_messageQueue = nullptr;
314         }
315     };
316
317     [self callFunctionOnMainThread:WTFMove(work)];
318 }
319
320 - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
321 {
322     ASSERT(!isMainThread());
323     UNUSED_PARAM(connection);
324
325     LOG(Network, "Handle %p delegate connection:%p didFailWithError:%@", m_handle, connection, error);
326
327     auto work = [self = self, protectedSelf = retainPtr(self), error = retainPtr(error)] () mutable {
328         if (!m_handle || !m_handle->client())
329             return;
330
331         m_handle->client()->didFail(m_handle, error.get());
332         if (m_messageQueue) {
333             m_messageQueue->kill();
334             m_messageQueue = nullptr;
335         }
336     };
337
338     [self callFunctionOnMainThread:WTFMove(work)];
339 }
340
341
342 - (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse
343 {
344     ASSERT(!isMainThread());
345     UNUSED_PARAM(connection);
346
347     LOG(Network, "Handle %p delegate connection:%p willCacheResponse:%p", m_handle, connection, cachedResponse);
348
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();
354             return;
355         }
356
357         m_handle->client()->willCacheResponseAsync(m_handle, cachedResponse.get(), [self, protectedSelf = WTFMove(protectedSelf)] (NSCachedURLResponse * response) mutable {
358             m_cachedResponseResult = response;
359             m_semaphore.signal();
360         });
361     };
362
363     [self callFunctionOnMainThread:WTFMove(work)];
364     m_semaphore.wait();
365
366     LockHolder lock(m_mutex);
367     if (!m_handle)
368         return nil;
369
370     RetainPtr<NSCachedURLResponse> cachedResponseResult = m_cachedResponseResult;
371
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)] { }];
375
376     return cachedResponseResult.autorelease();
377 }
378
379 @end
380
381 @implementation WebCoreResourceHandleWithCredentialStorageAsOperationQueueDelegate
382
383 - (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection
384 {
385     ASSERT(!isMainThread());
386     UNUSED_PARAM(connection);
387     return NO;
388 }
389
390 @end