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