9889b7f3caf9ca11cb888e4f954259fc433ae181
[WebKit-https.git] / Source / WebKit / NetworkProcess / NetworkResourceLoader.cpp
1 /*
2  * Copyright (C) 2012-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 #include "config.h"
27 #include "NetworkResourceLoader.h"
28
29 #include "DataReference.h"
30 #include "Logging.h"
31 #include "NetworkBlobRegistry.h"
32 #include "NetworkCache.h"
33 #include "NetworkConnectionToWebProcess.h"
34 #include "NetworkLoad.h"
35 #include "NetworkProcess.h"
36 #include "NetworkProcessConnectionMessages.h"
37 #include "SessionTracker.h"
38 #include "WebCoreArgumentCoders.h"
39 #include "WebErrors.h"
40 #include "WebResourceLoaderMessages.h"
41 #include "WebsiteDataStoreParameters.h"
42 #include <WebCore/BlobDataFileReference.h>
43 #include <WebCore/CertificateInfo.h>
44 #include <WebCore/DiagnosticLoggingKeys.h>
45 #include <WebCore/HTTPHeaderNames.h>
46 #include <WebCore/NetworkLoadMetrics.h>
47 #include <WebCore/ProtectionSpace.h>
48 #include <WebCore/SharedBuffer.h>
49 #include <WebCore/SynchronousLoaderClient.h>
50 #include <wtf/CurrentTime.h>
51 #include <wtf/RunLoop.h>
52
53 #if HAVE(CFNETWORK_STORAGE_PARTITIONING) && !RELEASE_LOG_DISABLED
54 #include <WebCore/NetworkStorageSession.h>
55 #include <WebCore/PlatformCookieJar.h>
56 #endif
57
58 using namespace WebCore;
59
60 #define RELEASE_LOG_IF_ALLOWED(fmt, ...) RELEASE_LOG_IF(isAlwaysOnLoggingAllowed(), Network, "%p - NetworkResourceLoader::" fmt, this, ##__VA_ARGS__)
61 #define RELEASE_LOG_ERROR_IF_ALLOWED(fmt, ...) RELEASE_LOG_ERROR_IF(isAlwaysOnLoggingAllowed(), Network, "%p - NetworkResourceLoader::" fmt, this, ##__VA_ARGS__)
62
63 namespace WebKit {
64
65 struct NetworkResourceLoader::SynchronousLoadData {
66     SynchronousLoadData(RefPtr<Messages::NetworkConnectionToWebProcess::PerformSynchronousLoad::DelayedReply>&& reply)
67         : delayedReply(WTFMove(reply))
68     {
69         ASSERT(delayedReply);
70     }
71     ResourceRequest currentRequest;
72     RefPtr<Messages::NetworkConnectionToWebProcess::PerformSynchronousLoad::DelayedReply> delayedReply;
73     ResourceResponse response;
74     ResourceError error;
75 };
76
77 static void sendReplyToSynchronousRequest(NetworkResourceLoader::SynchronousLoadData& data, const SharedBuffer* buffer)
78 {
79     ASSERT(data.delayedReply);
80     ASSERT(!data.response.isNull() || !data.error.isNull());
81
82     Vector<char> responseBuffer;
83     if (buffer && buffer->size())
84         responseBuffer.append(buffer->data(), buffer->size());
85
86     data.delayedReply->send(data.error, data.response, responseBuffer);
87     data.delayedReply = nullptr;
88 }
89
90 NetworkResourceLoader::NetworkResourceLoader(const NetworkResourceLoadParameters& parameters, NetworkConnectionToWebProcess& connection, RefPtr<Messages::NetworkConnectionToWebProcess::PerformSynchronousLoad::DelayedReply>&& synchronousReply)
91     : m_parameters { parameters }
92     , m_connection { connection }
93     , m_defersLoading { parameters.defersLoading }
94     , m_isAllowedToAskUserForCredentials { parameters.clientCredentialPolicy == ClientCredentialPolicy::MayAskClientForCredentials }
95     , m_bufferingTimer { *this, &NetworkResourceLoader::bufferingTimerFired }
96     , m_cache { sessionID().isEphemeral() ? nullptr : NetworkProcess::singleton().cache() }
97 {
98     ASSERT(RunLoop::isMain());
99     // FIXME: This is necessary because of the existence of EmptyFrameLoaderClient in WebCore.
100     //        Once bug 116233 is resolved, this ASSERT can just be "m_webPageID && m_webFrameID"
101     ASSERT((m_parameters.webPageID && m_parameters.webFrameID) || m_parameters.clientCredentialPolicy == ClientCredentialPolicy::CannotAskClientForCredentials);
102
103     if (originalRequest().httpBody()) {
104         for (const auto& element : originalRequest().httpBody()->elements()) {
105             if (element.m_type == FormDataElement::Type::EncodedBlob)
106                 m_fileReferences.appendVector(NetworkBlobRegistry::singleton().filesInBlob(connection, element.m_url));
107         }
108     }
109
110     if (synchronousReply)
111         m_synchronousLoadData = std::make_unique<SynchronousLoadData>(WTFMove(synchronousReply));
112 }
113
114 NetworkResourceLoader::~NetworkResourceLoader()
115 {
116     ASSERT(RunLoop::isMain());
117     ASSERT(!m_networkLoad);
118     ASSERT(!isSynchronous() || !m_synchronousLoadData->delayedReply);
119 }
120
121 bool NetworkResourceLoader::canUseCache(const ResourceRequest& request) const
122 {
123     if (!m_cache)
124         return false;
125     ASSERT(!sessionID().isEphemeral());
126
127     if (!request.url().protocolIsInHTTPFamily())
128         return false;
129     if (originalRequest().cachePolicy() == WebCore::DoNotUseAnyCache)
130         return false;
131
132     return true;
133 }
134
135 bool NetworkResourceLoader::canUseCachedRedirect(const ResourceRequest& request) const
136 {
137     if (!canUseCache(request))
138         return false;
139     // Limit cached redirects to avoid cycles and other trouble.
140     // Networking layer follows over 30 redirects but caching that many seems unnecessary.
141     static const unsigned maximumCachedRedirectCount { 5 };
142     if (m_redirectCount > maximumCachedRedirectCount)
143         return false;
144
145     return true;
146 }
147
148 bool NetworkResourceLoader::isSynchronous() const
149 {
150     return !!m_synchronousLoadData;
151 }
152
153 void NetworkResourceLoader::start()
154 {
155     ASSERT(RunLoop::isMain());
156
157     if (m_defersLoading) {
158         RELEASE_LOG_IF_ALLOWED("start: Loading is deferred (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", isMainResource = %d, isSynchronous = %d)", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier, isMainResource(), isSynchronous());
159         return;
160     }
161
162     ASSERT(!m_wasStarted);
163     m_wasStarted = true;
164
165     if (canUseCache(originalRequest())) {
166         RELEASE_LOG_IF_ALLOWED("start: Checking cache for resource (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", isMainResource = %d, isSynchronous = %d)", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier, isMainResource(), isSynchronous());
167         retrieveCacheEntry(originalRequest());
168         return;
169     }
170
171     startNetworkLoad(originalRequest());
172 }
173
174 void NetworkResourceLoader::retrieveCacheEntry(const ResourceRequest& request)
175 {
176     ASSERT(canUseCache(request));
177
178     RefPtr<NetworkResourceLoader> loader(this);
179     m_cache->retrieve(request, { m_parameters.webPageID, m_parameters.webFrameID }, [this, loader = WTFMove(loader), request](auto entry) {
180         if (loader->hasOneRef()) {
181             // The loader has been aborted and is only held alive by this lambda.
182             return;
183         }
184         if (!entry) {
185             RELEASE_LOG_IF_ALLOWED("retrieveCacheEntry: Resource not in cache (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", isMainResource = %d, isSynchronous = %d)", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier, isMainResource(), isSynchronous());
186             loader->startNetworkLoad(request);
187             return;
188         }
189         if (entry->redirectRequest()) {
190             RELEASE_LOG_IF_ALLOWED("retrieveCacheEntry: Handling redirect (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", isMainResource = %d, isSynchronous = %d)", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier, isMainResource(), isSynchronous());
191             loader->dispatchWillSendRequestForCacheEntry(WTFMove(entry));
192             return;
193         }
194         if (loader->m_parameters.needsCertificateInfo && !entry->response().certificateInfo()) {
195             RELEASE_LOG_IF_ALLOWED("retrieveCacheEntry: Resource does not have required certificate (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", isMainResource = %d, isSynchronous = %d)", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier, isMainResource(), isSynchronous());
196             loader->startNetworkLoad(request);
197             return;
198         }
199         if (entry->needsValidation() || request.cachePolicy() == WebCore::RefreshAnyCacheData) {
200             RELEASE_LOG_IF_ALLOWED("retrieveCacheEntry: Validating cache entry (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", isMainResource = %d, isSynchronous = %d)", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier, isMainResource(), isSynchronous());
201             loader->validateCacheEntry(WTFMove(entry));
202             return;
203         }
204         RELEASE_LOG_IF_ALLOWED("retrieveCacheEntry: Retrieved resource from cache (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", isMainResource = %d, isSynchronous = %d)", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier, isMainResource(), isSynchronous());
205         loader->didRetrieveCacheEntry(WTFMove(entry));
206     });
207 }
208
209 void NetworkResourceLoader::startNetworkLoad(const ResourceRequest& request)
210 {
211     RELEASE_LOG_IF_ALLOWED("startNetworkLoad: (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", isMainResource = %d, isSynchronous = %d)", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier, isMainResource(), isSynchronous());
212
213     consumeSandboxExtensions();
214
215     if (isSynchronous() || m_parameters.maximumBufferingTime > 0_s)
216         m_bufferedData = SharedBuffer::create();
217
218     if (canUseCache(request))
219         m_bufferedDataForCache = SharedBuffer::create();
220
221     NetworkLoadParameters parameters = m_parameters;
222     parameters.defersLoading = m_defersLoading;
223     parameters.request = request;
224
225     if (request.url().protocolIsBlob())
226         parameters.blobFileReferences = NetworkBlobRegistry::singleton().filesInBlob(m_connection, originalRequest().url());
227
228     auto* networkSession = SessionTracker::networkSession(parameters.sessionID);
229     if (!networkSession && parameters.sessionID.isEphemeral()) {
230         NetworkProcess::singleton().addWebsiteDataStore(WebsiteDataStoreParameters::privateSessionParameters(parameters.sessionID));
231         networkSession = SessionTracker::networkSession(parameters.sessionID);
232     }
233     if (!networkSession) {
234         WTFLogAlways("Attempted to create a NetworkLoad with a session (id=%" PRIu64 ") that does not exist.", parameters.sessionID.sessionID());
235         RELEASE_LOG_ERROR_IF_ALLOWED("startNetworkLoad: Attempted to create a NetworkLoad with a session that does not exist (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", sessionID=%" PRIu64 ")", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier, parameters.sessionID.sessionID());
236         NetworkProcess::singleton().logDiagnosticMessage(m_parameters.webPageID, WebCore::DiagnosticLoggingKeys::internalErrorKey(), WebCore::DiagnosticLoggingKeys::invalidSessionIDKey(), WebCore::ShouldSample::No);
237         didFailLoading(internalError(request.url()));
238         return;
239     }
240     m_networkLoad = std::make_unique<NetworkLoad>(*this, WTFMove(parameters), *networkSession);
241
242     if (m_defersLoading) {
243         RELEASE_LOG_IF_ALLOWED("startNetworkLoad: Created, but deferred (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")",
244             m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier);
245     }
246 }
247
248 void NetworkResourceLoader::setDefersLoading(bool defers)
249 {
250     if (m_defersLoading == defers)
251         return;
252     m_defersLoading = defers;
253
254     if (defers)
255         RELEASE_LOG_IF_ALLOWED("setDefersLoading: Deferring resource load (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier);
256     else
257         RELEASE_LOG_IF_ALLOWED("setDefersLoading: Resuming deferred resource load (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier);
258
259     if (m_networkLoad) {
260         m_networkLoad->setDefersLoading(defers);
261         return;
262     }
263
264     if (!m_defersLoading && !m_wasStarted)
265         start();
266     else
267         RELEASE_LOG_IF_ALLOWED("setDefersLoading: defers = %d, but nothing to do (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")", m_defersLoading, m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier);
268 }
269
270 void NetworkResourceLoader::cleanup()
271 {
272     ASSERT(RunLoop::isMain());
273
274     m_bufferingTimer.stop();
275
276     invalidateSandboxExtensions();
277
278     m_networkLoad = nullptr;
279
280     // This will cause NetworkResourceLoader to be destroyed and therefore we do it last.
281     m_connection->didCleanupResourceLoader(*this);
282 }
283
284 void NetworkResourceLoader::convertToDownload(DownloadID downloadID, const ResourceRequest& request, const ResourceResponse& response)
285 {
286     ASSERT(m_networkLoad);
287     NetworkProcess::singleton().downloadManager().convertNetworkLoadToDownload(downloadID, std::exchange(m_networkLoad, nullptr), WTFMove(m_fileReferences), request, response);
288 }
289
290 void NetworkResourceLoader::abort()
291 {
292     ASSERT(RunLoop::isMain());
293
294     RELEASE_LOG_IF_ALLOWED("abort: Canceling resource load (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")",
295         m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier);
296
297     if (m_networkLoad) {
298         if (canUseCache(m_networkLoad->currentRequest())) {
299             // We might already have used data from this incomplete load. Ensure older versions don't remain in the cache after cancel.
300             if (!m_response.isNull())
301                 m_cache->remove(m_networkLoad->currentRequest());
302         }
303         m_networkLoad->cancel();
304     }
305
306     cleanup();
307 }
308
309 auto NetworkResourceLoader::didReceiveResponse(ResourceResponse&& receivedResponse) -> ShouldContinueDidReceiveResponse
310 {
311     RELEASE_LOG_IF_ALLOWED("didReceiveResponse: (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", httpStatusCode = %d, length = %" PRId64 ")", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier, receivedResponse.httpStatusCode(), receivedResponse.expectedContentLength());
312
313     m_response = WTFMove(receivedResponse);
314
315     // For multipart/x-mixed-replace didReceiveResponseAsync gets called multiple times and buffering would require special handling.
316     if (!isSynchronous() && m_response.isMultipart())
317         m_bufferedData = nullptr;
318
319     if (m_response.isMultipart())
320         m_bufferedDataForCache = nullptr;
321
322     if (m_cacheEntryForValidation) {
323         bool validationSucceeded = m_response.httpStatusCode() == 304; // 304 Not Modified
324         if (validationSucceeded) {
325             m_cacheEntryForValidation = m_cache->update(originalRequest(), { m_parameters.webPageID, m_parameters.webFrameID }, *m_cacheEntryForValidation, m_response);
326             // If the request was conditional then this revalidation was not triggered by the network cache and we pass the 304 response to WebCore.
327             if (originalRequest().isConditional())
328                 m_cacheEntryForValidation = nullptr;
329         } else
330             m_cacheEntryForValidation = nullptr;
331     }
332     bool shouldSendDidReceiveResponse = !m_cacheEntryForValidation;
333
334     bool shouldWaitContinueDidReceiveResponse = isMainResource();
335     if (shouldSendDidReceiveResponse) {
336         if (isSynchronous())
337             m_synchronousLoadData->response = m_response;
338         else
339             send(Messages::WebResourceLoader::DidReceiveResponse(m_response, shouldWaitContinueDidReceiveResponse));
340     }
341
342     // For main resources, the web process is responsible for sending back a NetworkResourceLoader::ContinueDidReceiveResponse message.
343     bool shouldContinueDidReceiveResponse = !shouldWaitContinueDidReceiveResponse || m_cacheEntryForValidation;
344
345     if (shouldContinueDidReceiveResponse) {
346         RELEASE_LOG_IF_ALLOWED("didReceiveResponse: Should not wait for message from WebContent process before continuing resource load (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier);
347         return ShouldContinueDidReceiveResponse::Yes;
348     }
349
350     RELEASE_LOG_IF_ALLOWED("didReceiveResponse: Should wait for message from WebContent process before continuing resource load (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier);
351     return ShouldContinueDidReceiveResponse::No;
352 }
353
354 void NetworkResourceLoader::didReceiveBuffer(Ref<SharedBuffer>&& buffer, int reportedEncodedDataLength)
355 {
356     if (!m_numBytesReceived) {
357         RELEASE_LOG_IF_ALLOWED("didReceiveBuffer: Started receiving data (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier);
358     }
359     m_numBytesReceived += buffer->size();
360
361     ASSERT(!m_cacheEntryForValidation);
362
363     if (m_bufferedDataForCache) {
364         // Prevent memory growth in case of streaming data.
365         const size_t maximumCacheBufferSize = 10 * 1024 * 1024;
366         if (m_bufferedDataForCache->size() + buffer->size() <= maximumCacheBufferSize)
367             m_bufferedDataForCache->append(buffer.get());
368         else
369             m_bufferedDataForCache = nullptr;
370     }
371     // FIXME: At least on OS X Yosemite we always get -1 from the resource handle.
372     unsigned encodedDataLength = reportedEncodedDataLength >= 0 ? reportedEncodedDataLength : buffer->size();
373
374     m_bytesReceived += buffer->size();
375     if (m_bufferedData) {
376         m_bufferedData->append(buffer.get());
377         m_bufferedDataEncodedDataLength += encodedDataLength;
378         startBufferingTimerIfNeeded();
379         return;
380     }
381     sendBuffer(buffer, encodedDataLength);
382 }
383
384 void NetworkResourceLoader::didFinishLoading(const NetworkLoadMetrics& networkLoadMetrics)
385 {
386     RELEASE_LOG_IF_ALLOWED("didFinishLoading: (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", length = %zd)", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier, m_numBytesReceived);
387
388     if (m_cacheEntryForValidation) {
389         // 304 Not Modified
390         ASSERT(m_response.httpStatusCode() == 304);
391         LOG(NetworkCache, "(NetworkProcess) revalidated");
392         didRetrieveCacheEntry(WTFMove(m_cacheEntryForValidation));
393         return;
394     }
395
396 #if HAVE(CFNETWORK_STORAGE_PARTITIONING) && !RELEASE_LOG_DISABLED
397     if (shouldLogCookieInformation())
398         logCookieInformation();
399 #endif
400
401     if (isSynchronous())
402         sendReplyToSynchronousRequest(*m_synchronousLoadData, m_bufferedData.get());
403     else {
404         if (m_bufferedData && !m_bufferedData->isEmpty()) {
405             // FIXME: Pass a real value or remove the encoded data size feature.
406             sendBuffer(*m_bufferedData, -1);
407         }
408         send(Messages::WebResourceLoader::DidFinishResourceLoad(networkLoadMetrics));
409     }
410
411     tryStoreAsCacheEntry();
412
413     cleanup();
414 }
415
416 void NetworkResourceLoader::didFailLoading(const ResourceError& error)
417 {
418     RELEASE_LOG_IF_ALLOWED("didFailLoading: (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", isTimeout = %d, isCancellation = %d, errCode = %d)", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier, error.isTimeout(), error.isCancellation(), error.errorCode());
419
420     ASSERT(!error.isNull());
421
422     m_cacheEntryForValidation = nullptr;
423
424     if (isSynchronous()) {
425         m_synchronousLoadData->error = error;
426         sendReplyToSynchronousRequest(*m_synchronousLoadData, nullptr);
427     } else if (auto* connection = messageSenderConnection())
428         connection->send(Messages::WebResourceLoader::DidFailResourceLoad(error), messageSenderDestinationID());
429
430     cleanup();
431 }
432
433 void NetworkResourceLoader::didBlockAuthenticationChallenge()
434 {
435     send(Messages::WebResourceLoader::DidBlockAuthenticationChallenge());
436 }
437
438 void NetworkResourceLoader::willSendRedirectedRequest(ResourceRequest&& request, WebCore::ResourceRequest&& redirectRequest, ResourceResponse&& redirectResponse)
439 {
440     ++m_redirectCount;
441
442     if (isSynchronous()) {
443         ResourceRequest overridenRequest = redirectRequest;
444         // FIXME: This needs to be fixed to follow the redirect correctly even for cross-domain requests.
445         // This includes at least updating host records, and comparing the current request instead of the original request here.
446         if (!protocolHostAndPortAreEqual(originalRequest().url(), redirectRequest.url())) {
447             ASSERT(m_synchronousLoadData->error.isNull());
448             m_synchronousLoadData->error = SynchronousLoaderClient::platformBadResponseError();
449             m_networkLoad->clearCurrentRequest();
450             overridenRequest = ResourceRequest();
451         }
452         // We do not support prompting for credentials for synchronous loads. If we ever change this policy then
453         // we need to take care to prompt if and only if request and redirectRequest are not mixed content.
454         continueWillSendRequest(WTFMove(overridenRequest), false);
455         return;
456     }
457     send(Messages::WebResourceLoader::WillSendRequest(redirectRequest, redirectResponse));
458
459     if (canUseCachedRedirect(request))
460         m_cache->storeRedirect(request, redirectResponse, redirectRequest);
461 }
462
463 void NetworkResourceLoader::continueWillSendRequest(ResourceRequest&& newRequest, bool isAllowedToAskUserForCredentials)
464 {
465     RELEASE_LOG_IF_ALLOWED("continueWillSendRequest: (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier);
466
467     m_isAllowedToAskUserForCredentials = isAllowedToAskUserForCredentials;
468
469     // If there is a match in the network cache, we need to reuse the original cache policy and partition.
470     newRequest.setCachePolicy(originalRequest().cachePolicy());
471     newRequest.setCachePartition(originalRequest().cachePartition());
472
473     if (m_isWaitingContinueWillSendRequestForCachedRedirect) {
474         m_isWaitingContinueWillSendRequestForCachedRedirect = false;
475
476         LOG(NetworkCache, "(NetworkProcess) Retrieving cached redirect");
477
478         if (canUseCachedRedirect(newRequest))
479             retrieveCacheEntry(newRequest);
480         else
481             startNetworkLoad(newRequest);
482
483         return;
484     }
485
486     if (m_networkLoad)
487         m_networkLoad->continueWillSendRequest(WTFMove(newRequest));
488 }
489
490 void NetworkResourceLoader::continueDidReceiveResponse()
491 {
492     // FIXME: Remove this check once BlobResourceHandle implements didReceiveResponseAsync correctly.
493     // Currently, it does not wait for response, so the load is likely to finish before continueDidReceiveResponse.
494     if (m_networkLoad)
495         m_networkLoad->continueDidReceiveResponse();
496 }
497
498 void NetworkResourceLoader::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
499 {
500     if (!isSynchronous())
501         send(Messages::WebResourceLoader::DidSendData(bytesSent, totalBytesToBeSent));
502 }
503
504 void NetworkResourceLoader::startBufferingTimerIfNeeded()
505 {
506     if (isSynchronous())
507         return;
508     if (m_bufferingTimer.isActive())
509         return;
510     m_bufferingTimer.startOneShot(m_parameters.maximumBufferingTime);
511 }
512
513 void NetworkResourceLoader::bufferingTimerFired()
514 {
515     ASSERT(m_bufferedData);
516     ASSERT(m_networkLoad);
517
518     if (m_bufferedData->isEmpty())
519         return;
520
521     IPC::SharedBufferDataReference dataReference(m_bufferedData.get());
522     size_t encodedLength = m_bufferedDataEncodedDataLength;
523
524     m_bufferedData = SharedBuffer::create();
525     m_bufferedDataEncodedDataLength = 0;
526
527     send(Messages::WebResourceLoader::DidReceiveData(dataReference, encodedLength));
528 }
529
530 void NetworkResourceLoader::sendBuffer(SharedBuffer& buffer, size_t encodedDataLength)
531 {
532     ASSERT(!isSynchronous());
533
534     IPC::SharedBufferDataReference dataReference(&buffer);
535     send(Messages::WebResourceLoader::DidReceiveData(dataReference, encodedDataLength));
536 }
537
538 void NetworkResourceLoader::tryStoreAsCacheEntry()
539 {
540     if (!canUseCache(m_networkLoad->currentRequest()))
541         return;
542     if (!m_bufferedDataForCache)
543         return;
544
545     m_cache->store(m_networkLoad->currentRequest(), m_response, WTFMove(m_bufferedDataForCache), [loader = makeRef(*this)](auto& mappedBody) mutable {
546 #if ENABLE(SHAREABLE_RESOURCE)
547         if (mappedBody.shareableResourceHandle.isNull())
548             return;
549         LOG(NetworkCache, "(NetworkProcess) sending DidCacheResource");
550         loader->send(Messages::NetworkProcessConnection::DidCacheResource(loader->originalRequest(), mappedBody.shareableResourceHandle, loader->sessionID()));
551 #endif
552     });
553 }
554
555 void NetworkResourceLoader::didRetrieveCacheEntry(std::unique_ptr<NetworkCache::Entry> entry)
556 {
557     if (isSynchronous()) {
558         m_synchronousLoadData->response = entry->response();
559         sendReplyToSynchronousRequest(*m_synchronousLoadData, entry->buffer());
560         cleanup();
561         return;
562     }
563
564     bool needsContinueDidReceiveResponseMessage = isMainResource();
565     send(Messages::WebResourceLoader::DidReceiveResponse(entry->response(), needsContinueDidReceiveResponseMessage));
566
567     if (entry->sourceStorageRecord().bodyHash && !m_parameters.derivedCachedDataTypesToRetrieve.isEmpty()) {
568         auto bodyHash = *entry->sourceStorageRecord().bodyHash;
569         auto* entryPtr = entry.release();
570         auto retrieveCount = m_parameters.derivedCachedDataTypesToRetrieve.size();
571
572         for (auto& type : m_parameters.derivedCachedDataTypesToRetrieve) {
573             NetworkCache::DataKey key { originalRequest().cachePartition(), type, bodyHash };
574             m_cache->retrieveData(key, [loader = makeRef(*this), entryPtr, type, retrieveCount] (const uint8_t* data, size_t size) mutable {
575                 loader->m_retrievedDerivedDataCount++;
576                 bool retrievedAll = loader->m_retrievedDerivedDataCount == retrieveCount;
577                 std::unique_ptr<NetworkCache::Entry> entry(retrievedAll ? entryPtr : nullptr);
578                 if (loader->hasOneRef())
579                     return;
580                 if (data) {
581                     IPC::DataReference dataReference(data, size);
582                     loader->send(Messages::WebResourceLoader::DidRetrieveDerivedData(type, dataReference));
583                 }
584                 if (retrievedAll) {
585                     loader->sendResultForCacheEntry(WTFMove(entry));
586                     loader->cleanup();
587                 }
588             });
589         }
590         return;
591     }
592
593     sendResultForCacheEntry(WTFMove(entry));
594
595     cleanup();
596 }
597
598 void NetworkResourceLoader::sendResultForCacheEntry(std::unique_ptr<NetworkCache::Entry> entry)
599 {
600 #if ENABLE(SHAREABLE_RESOURCE)
601     if (!entry->shareableResourceHandle().isNull()) {
602         send(Messages::WebResourceLoader::DidReceiveResource(entry->shareableResourceHandle()));
603         return;
604     }
605 #endif
606
607 #if HAVE(CFNETWORK_STORAGE_PARTITIONING) && !RELEASE_LOG_DISABLED
608     if (shouldLogCookieInformation())
609         logCookieInformation();
610 #endif
611
612     WebCore::NetworkLoadMetrics networkLoadMetrics;
613     networkLoadMetrics.markComplete();
614     networkLoadMetrics.requestHeaderBytesSent = 0;
615     networkLoadMetrics.requestBodyBytesSent = 0;
616     networkLoadMetrics.responseHeaderBytesReceived = 0;
617     networkLoadMetrics.responseBodyBytesReceived = 0;
618     networkLoadMetrics.responseBodyDecodedSize = 0;
619
620     sendBuffer(*entry->buffer(), entry->buffer()->size());
621     send(Messages::WebResourceLoader::DidFinishResourceLoad(networkLoadMetrics));
622 }
623
624 void NetworkResourceLoader::validateCacheEntry(std::unique_ptr<NetworkCache::Entry> entry)
625 {
626     ASSERT(!m_networkLoad);
627
628     // If the request is already conditional then the revalidation was not triggered by the disk cache
629     // and we should not overwrite the existing conditional headers.
630     ResourceRequest revalidationRequest = originalRequest();
631     if (!revalidationRequest.isConditional()) {
632         String eTag = entry->response().httpHeaderField(HTTPHeaderName::ETag);
633         String lastModified = entry->response().httpHeaderField(HTTPHeaderName::LastModified);
634         if (!eTag.isEmpty())
635             revalidationRequest.setHTTPHeaderField(HTTPHeaderName::IfNoneMatch, eTag);
636         if (!lastModified.isEmpty())
637             revalidationRequest.setHTTPHeaderField(HTTPHeaderName::IfModifiedSince, lastModified);
638     }
639
640     m_cacheEntryForValidation = WTFMove(entry);
641
642     startNetworkLoad(revalidationRequest);
643 }
644
645 void NetworkResourceLoader::dispatchWillSendRequestForCacheEntry(std::unique_ptr<NetworkCache::Entry> entry)
646 {
647     ASSERT(entry->redirectRequest());
648     ASSERT(!m_isWaitingContinueWillSendRequestForCachedRedirect);
649
650     LOG(NetworkCache, "(NetworkProcess) Executing cached redirect");
651
652     ++m_redirectCount;
653     send(Messages::WebResourceLoader::WillSendRequest(*entry->redirectRequest(), entry->response()));
654     m_isWaitingContinueWillSendRequestForCachedRedirect = true;
655 }
656
657 IPC::Connection* NetworkResourceLoader::messageSenderConnection()
658 {
659     return &connectionToWebProcess().connection();
660 }
661
662 void NetworkResourceLoader::consumeSandboxExtensions()
663 {
664     ASSERT(!m_didConsumeSandboxExtensions);
665
666     for (auto& extension : m_parameters.requestBodySandboxExtensions)
667         extension->consume();
668
669     if (auto& extension = m_parameters.resourceSandboxExtension)
670         extension->consume();
671
672     for (auto& fileReference : m_fileReferences)
673         fileReference->prepareForFileAccess();
674
675     m_didConsumeSandboxExtensions = true;
676 }
677
678 void NetworkResourceLoader::invalidateSandboxExtensions()
679 {
680     if (m_didConsumeSandboxExtensions) {
681         for (auto& extension : m_parameters.requestBodySandboxExtensions)
682             extension->revoke();
683         if (auto& extension = m_parameters.resourceSandboxExtension)
684             extension->revoke();
685         for (auto& fileReference : m_fileReferences)
686             fileReference->revokeFileAccess();
687
688         m_didConsumeSandboxExtensions = false;
689     }
690
691     m_fileReferences.clear();
692 }
693
694 #if USE(PROTECTION_SPACE_AUTH_CALLBACK)
695 void NetworkResourceLoader::canAuthenticateAgainstProtectionSpaceAsync(const ProtectionSpace& protectionSpace)
696 {
697     NetworkProcess::singleton().canAuthenticateAgainstProtectionSpace(*this, protectionSpace);
698 }
699
700 void NetworkResourceLoader::continueCanAuthenticateAgainstProtectionSpace(bool result)
701 {
702     if (m_networkLoad)
703         m_networkLoad->continueCanAuthenticateAgainstProtectionSpace(result);
704 }
705 #endif
706
707 bool NetworkResourceLoader::isAlwaysOnLoggingAllowed() const
708 {
709     if (NetworkProcess::singleton().sessionIsControlledByAutomation(sessionID()))
710         return true;
711
712     return sessionID().isAlwaysOnLoggingAllowed();
713 }
714
715 bool NetworkResourceLoader::shouldCaptureExtraNetworkLoadMetrics() const
716 {
717     return m_connection->captureExtraNetworkLoadMetricsEnabled();
718 }
719
720 #if HAVE(CFNETWORK_STORAGE_PARTITIONING) && !RELEASE_LOG_DISABLED
721 bool NetworkResourceLoader::shouldLogCookieInformation()
722 {
723     return NetworkProcess::singleton().shouldLogCookieInformation();
724 }
725
726 static String escapeForJSON(String s)
727 {
728     return s.replace('\\', "\\\\").replace('"', "\\\"");
729 }
730
731 void NetworkResourceLoader::logCookieInformation() const
732 {
733     ASSERT(shouldLogCookieInformation());
734
735     auto networkStorageSession = WebCore::NetworkStorageSession::storageSession(sessionID());
736     ASSERT(networkStorageSession);
737
738 #define LOCAL_LOG(str, ...) \
739     RELEASE_LOG_IF_ALLOWED("logCookieInformation: pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ": " str, pageID(), frameID(), identifier(), ##__VA_ARGS__)
740
741     auto url = originalRequest().url();
742     if (networkStorageSession->shouldBlockCookies(originalRequest())) {
743         auto escapedURL = escapeForJSON(url.string());
744         auto escapedReferrer = escapeForJSON(originalRequest().httpReferrer());
745
746         LOCAL_LOG(R"({ "url": "%{public}s",)", escapedURL.utf8().data());
747         LOCAL_LOG(R"(  "partition": "%{public}s",)", "BLOCKED");
748         LOCAL_LOG(R"(  "hasStorageAccess": %{public}s,)", "false");
749         LOCAL_LOG(R"(  "referer": "%{public}s",)", escapedReferrer.utf8().data());
750         LOCAL_LOG(R"(  "cookies": []})");
751         return;
752     }
753 #undef LOCAL_LOG
754
755     auto partition = WebCore::URL(ParsedURLString, networkStorageSession->cookieStoragePartition(originalRequest(), frameID(), pageID()));
756     NetworkResourceLoader::logCookieInformation("NetworkResourceLoader", reinterpret_cast<const void*>(this), *networkStorageSession, partition, url, originalRequest().httpReferrer(), frameID(), pageID(), identifier());
757 }
758
759 void NetworkResourceLoader::logCookieInformation(const String& label, const void* loggedObject, const WebCore::NetworkStorageSession& networkStorageSession, const WebCore::URL& partition, const WebCore::URL& url, const String& referrer, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, std::optional<uint64_t> identifier)
760 {
761     ASSERT(shouldLogCookieInformation());
762
763     Vector<WebCore::Cookie> cookies;
764     if (!WebCore::getRawCookies(networkStorageSession, partition, url, frameID, pageID, cookies))
765         return;
766
767     auto escapeIDForJSON = [](std::optional<uint64_t> value) {
768         return value ? String::number(value.value()) : String("None");
769     };
770
771     auto escapedURL = escapeForJSON(url.string());
772     auto escapedPartition = escapeForJSON(partition.string());
773     auto escapedReferrer = escapeForJSON(referrer);
774     auto escapedFrameID = escapeIDForJSON(frameID);
775     auto escapedPageID = escapeIDForJSON(pageID);
776     auto escapedIdentifier = escapeIDForJSON(identifier);
777     bool hasStorageAccessForFrame = (frameID && pageID) ? networkStorageSession.hasStorageAccessForFrame(url.string(), partition.string(), frameID.value(), pageID.value()) : false;
778
779 #define LOCAL_LOG_IF_ALLOWED(fmt, ...) RELEASE_LOG_IF(networkStorageSession.sessionID().isAlwaysOnLoggingAllowed(), Network, "%p - %s::" fmt, loggedObject, label.utf8().data(), ##__VA_ARGS__)
780 #define LOCAL_LOG(str, ...) \
781     LOCAL_LOG_IF_ALLOWED("logCookieInformation: pageID = %s, frameID = %s, resourceID = %s: " str, escapedPageID.utf8().data(), escapedFrameID.utf8().data(), escapedIdentifier.utf8().data(), ##__VA_ARGS__)
782
783     LOCAL_LOG(R"({ "url": "%{public}s",)", escapedURL.utf8().data());
784     LOCAL_LOG(R"(  "partition": "%{public}s",)", escapedPartition.utf8().data());
785     LOCAL_LOG(R"(  "hasStorageAccess": %{public}s,)", hasStorageAccessForFrame ? "true" : "false");
786     LOCAL_LOG(R"(  "referer": "%{public}s",)", escapedReferrer.utf8().data());
787     LOCAL_LOG(R"(  "cookies": [)");
788
789     auto size = cookies.size();
790     decltype(size) count = 0;
791     for (const auto& cookie : cookies) {
792         const char* trailingComma = ",";
793         if (++count == size)
794             trailingComma = "";
795
796         auto escapedName = escapeForJSON(cookie.name);
797         auto escapedValue = escapeForJSON(cookie.value);
798         auto escapedDomain = escapeForJSON(cookie.domain);
799         auto escapedPath = escapeForJSON(cookie.path);
800         auto escapedComment = escapeForJSON(cookie.comment);
801         auto escapedCommentURL = escapeForJSON(cookie.commentURL.string());
802
803         LOCAL_LOG(R"(  { "name": "%{public}s",)", escapedName.utf8().data());
804         LOCAL_LOG(R"(    "value": "%{public}s",)", escapedValue.utf8().data());
805         LOCAL_LOG(R"(    "domain": "%{public}s",)", escapedDomain.utf8().data());
806         LOCAL_LOG(R"(    "path": "%{public}s",)", escapedPath.utf8().data());
807         LOCAL_LOG(R"(    "created": %f,)", cookie.created);
808         LOCAL_LOG(R"(    "expires": %f,)", cookie.expires);
809         LOCAL_LOG(R"(    "httpOnly": %{public}s,)", cookie.httpOnly ? "true" : "false");
810         LOCAL_LOG(R"(    "secure": %{public}s,)", cookie.secure ? "true" : "false");
811         LOCAL_LOG(R"(    "session": %{public}s,)", cookie.session ? "true" : "false");
812         LOCAL_LOG(R"(    "comment": "%{public}s",)", escapedComment.utf8().data());
813         LOCAL_LOG(R"(    "commentURL": "%{public}s")", escapedCommentURL.utf8().data());
814         LOCAL_LOG(R"(  }%{public}s)", trailingComma);
815     }
816     LOCAL_LOG(R"(]})");
817 #undef LOCAL_LOG
818 #undef LOCAL_LOG_IF_ALLOWED
819 }
820 #endif
821
822 } // namespace WebKit