1/*
2 * Copyright (C) 2015 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#include "config.h"
27#include "NetworkLoad.h"
28
29#include "AuthenticationChallengeDisposition.h"
30#include "AuthenticationManager.h"
31#include "NetworkDataTaskBlob.h"
32#include "NetworkProcess.h"
33#include "NetworkSession.h"
34#include "WebErrors.h"
35#include <WebCore/ResourceRequest.h>
36#include <WebCore/SharedBuffer.h>
37#include <wtf/Seconds.h>
38
39namespace WebKit {
40
41using namespace WebCore;
42
43struct NetworkLoad::Throttle {
44 WTF_MAKE_STRUCT_FAST_ALLOCATED;
45
46 Throttle(NetworkLoad& load, Seconds delay, ResourceResponse&& response, ResponseCompletionHandler&& handler)
47 : timer(load, &NetworkLoad::throttleDelayCompleted)
48 , response(WTFMove(response))
49 , responseCompletionHandler(WTFMove(handler))
50 {
51 timer.startOneShot(delay);
52 }
53 Timer timer;
54 ResourceResponse response;
55 ResponseCompletionHandler responseCompletionHandler;
56};
57
58NetworkLoad::NetworkLoad(NetworkLoadClient& client, BlobRegistryImpl* blobRegistry, NetworkLoadParameters&& parameters, NetworkSession& networkSession)
59 : m_client(client)
60 , m_networkProcess(networkSession.networkProcess())
61 , m_parameters(WTFMove(parameters))
62 , m_loadThrottleLatency(networkSession.loadThrottleLatency())
63 , m_currentRequest(m_parameters.request)
64{
65 initialize(networkSession, blobRegistry);
66}
67
68void NetworkLoad::initialize(NetworkSession& networkSession, WebCore::BlobRegistryImpl* blobRegistry)
69{
70 if (blobRegistry && m_parameters.request.url().protocolIsBlob())
71 m_task = NetworkDataTaskBlob::create(networkSession, *blobRegistry, *this, m_parameters.request, m_parameters.contentSniffingPolicy, m_parameters.blobFileReferences);
72 else
73 m_task = NetworkDataTask::create(networkSession, *this, m_parameters);
74
75 m_task->resume();
76}
77
78NetworkLoad::~NetworkLoad()
79{
80 ASSERT(RunLoop::isMain());
81 if (m_redirectCompletionHandler)
82 m_redirectCompletionHandler({ });
83 if (m_task)
84 m_task->clearClient();
85}
86
87void NetworkLoad::cancel()
88{
89 if (m_task)
90 m_task->cancel();
91}
92
93static inline void updateRequest(ResourceRequest& currentRequest, const ResourceRequest& newRequest)
94{
95#if PLATFORM(COCOA)
96 currentRequest.updateFromDelegatePreservingOldProperties(newRequest.nsURLRequest(HTTPBodyUpdatePolicy::DoNotUpdateHTTPBody));
97#else
98 // FIXME: Implement ResourceRequest::updateFromDelegatePreservingOldProperties. See https://bugs.webkit.org/show_bug.cgi?id=126127.
99 currentRequest.updateFromDelegatePreservingOldProperties(newRequest);
100#endif
101}
102
103void NetworkLoad::updateRequestAfterRedirection(WebCore::ResourceRequest& newRequest) const
104{
105 ResourceRequest updatedRequest = m_currentRequest;
106 updateRequest(updatedRequest, newRequest);
107 newRequest = WTFMove(updatedRequest);
108}
109
110void NetworkLoad::continueWillSendRequest(WebCore::ResourceRequest&& newRequest)
111{
112 updateRequest(m_currentRequest, newRequest);
113
114 auto redirectCompletionHandler = std::exchange(m_redirectCompletionHandler, nullptr);
115 ASSERT(redirectCompletionHandler);
116 if (m_currentRequest.isNull()) {
117 NetworkLoadMetrics emptyMetrics;
118 didCompleteWithError(cancelledError(m_currentRequest), emptyMetrics);
119 if (redirectCompletionHandler)
120 redirectCompletionHandler({ });
121 return;
122 }
123
124 if (redirectCompletionHandler)
125 redirectCompletionHandler(ResourceRequest(m_currentRequest));
126}
127
128bool NetworkLoad::shouldCaptureExtraNetworkLoadMetrics() const
129{
130 return m_client.get().shouldCaptureExtraNetworkLoadMetrics();
131}
132
133bool NetworkLoad::isAllowedToAskUserForCredentials() const
134{
135 return m_client.get().isAllowedToAskUserForCredentials();
136}
137
138void NetworkLoad::convertTaskToDownload(PendingDownload& pendingDownload, const ResourceRequest& updatedRequest, const ResourceResponse& response, ResponseCompletionHandler&& completionHandler)
139{
140 if (!m_task)
141 return completionHandler(PolicyAction::Ignore);
142
143 m_client = pendingDownload;
144 m_currentRequest = updatedRequest;
145 m_task->setPendingDownload(pendingDownload);
146
147 m_networkProcess->findPendingDownloadLocation(*m_task.get(), WTFMove(completionHandler), response);
148}
149
150void NetworkLoad::setPendingDownloadID(DownloadID downloadID)
151{
152 if (!m_task)
153 return;
154
155 m_task->setPendingDownloadID(downloadID);
156}
157
158void NetworkLoad::setSuggestedFilename(const String& suggestedName)
159{
160 if (!m_task)
161 return;
162
163 m_task->setSuggestedFilename(suggestedName);
164}
165
166void NetworkLoad::setPendingDownload(PendingDownload& pendingDownload)
167{
168 if (!m_task)
169 return;
170
171 m_task->setPendingDownload(pendingDownload);
172}
173
174void NetworkLoad::willPerformHTTPRedirection(ResourceResponse&& redirectResponse, ResourceRequest&& request, RedirectCompletionHandler&& completionHandler)
175{
176 ASSERT(!redirectResponse.isNull());
177 ASSERT(RunLoop::isMain());
178 ASSERT(!m_redirectCompletionHandler);
179
180 redirectResponse.setSource(ResourceResponse::Source::Network);
181 m_redirectCompletionHandler = WTFMove(completionHandler);
182
183 auto oldRequest = WTFMove(m_currentRequest);
184 request.setRequester(oldRequest.requester());
185
186 m_currentRequest = request;
187 m_client.get().willSendRedirectedRequest(WTFMove(oldRequest), WTFMove(request), WTFMove(redirectResponse));
188}
189
190void NetworkLoad::didReceiveChallenge(AuthenticationChallenge&& challenge, ChallengeCompletionHandler&& completionHandler)
191{
192 auto scheme = challenge.protectionSpace().authenticationScheme();
193 bool isTLSHandshake = scheme == ProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested
194 || scheme == ProtectionSpaceAuthenticationSchemeClientCertificateRequested;
195 if (!isAllowedToAskUserForCredentials() && !isTLSHandshake) {
196 m_client.get().didBlockAuthenticationChallenge();
197 completionHandler(AuthenticationChallengeDisposition::UseCredential, { });
198 return;
199 }
200
201 if (auto* pendingDownload = m_task->pendingDownload())
202 m_networkProcess->authenticationManager().didReceiveAuthenticationChallenge(*pendingDownload, challenge, WTFMove(completionHandler));
203 else
204 m_networkProcess->authenticationManager().didReceiveAuthenticationChallenge(m_parameters.webPageID, m_parameters.webFrameID, challenge, WTFMove(completionHandler));
205}
206
207void NetworkLoad::didReceiveResponse(ResourceResponse&& response, ResponseCompletionHandler&& completionHandler)
208{
209 ASSERT(RunLoop::isMain());
210 ASSERT(!m_throttle);
211
212 if (m_task && m_task->isDownload()) {
213 m_networkProcess->findPendingDownloadLocation(*m_task.get(), WTFMove(completionHandler), response);
214 return;
215 }
216
217 if (m_loadThrottleLatency > 0_s) {
218 m_throttle = std::make_unique<Throttle>(*this, m_loadThrottleLatency, WTFMove(response), WTFMove(completionHandler));
219 return;
220 }
221
222 notifyDidReceiveResponse(WTFMove(response), WTFMove(completionHandler));
223}
224
225void NetworkLoad::notifyDidReceiveResponse(ResourceResponse&& response, ResponseCompletionHandler&& completionHandler)
226{
227 ASSERT(RunLoop::isMain());
228
229 response.setSource(ResourceResponse::Source::Network);
230 if (m_parameters.needsCertificateInfo)
231 response.includeCertificateInfo();
232
233 m_client.get().didReceiveResponse(WTFMove(response), WTFMove(completionHandler));
234}
235
236void NetworkLoad::didReceiveData(Ref<SharedBuffer>&& buffer)
237{
238 ASSERT(!m_throttle);
239
240 // FIXME: This should be the encoded data length, not the decoded data length.
241 auto size = buffer->size();
242 m_client.get().didReceiveBuffer(WTFMove(buffer), size);
243}
244
245void NetworkLoad::didCompleteWithError(const ResourceError& error, const WebCore::NetworkLoadMetrics& networkLoadMetrics)
246{
247 ASSERT(!m_throttle);
248
249 if (error.isNull())
250 m_client.get().didFinishLoading(networkLoadMetrics);
251 else
252 m_client.get().didFailLoading(error);
253}
254
255void NetworkLoad::throttleDelayCompleted()
256{
257 ASSERT(m_throttle);
258
259 auto throttle = WTFMove(m_throttle);
260
261 notifyDidReceiveResponse(WTFMove(throttle->response), WTFMove(throttle->responseCompletionHandler));
262}
263
264void NetworkLoad::didSendData(uint64_t totalBytesSent, uint64_t totalBytesExpectedToSend)
265{
266 m_client.get().didSendData(totalBytesSent, totalBytesExpectedToSend);
267}
268
269void NetworkLoad::wasBlocked()
270{
271 m_client.get().didFailLoading(blockedError(m_currentRequest));
272}
273
274void NetworkLoad::cannotShowURL()
275{
276 m_client.get().didFailLoading(cannotShowURLError(m_currentRequest));
277}
278
279void NetworkLoad::wasBlockedByRestrictions()
280{
281 m_client.get().didFailLoading(wasBlockedByRestrictionsError(m_currentRequest));
282}
283
284String NetworkLoad::description() const
285{
286 if (m_task.get())
287 return m_task->description();
288 return emptyString();
289}
290
291} // namespace WebKit
292