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