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