2 * Copyright (C) 2012-2015 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "NetworkResourceLoader.h"
29 #include "DataReference.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>
52 using namespace WebCore;
54 #define RELEASE_LOG_IF_ALLOWED(fmt, ...) RELEASE_LOG_IF(isAlwaysOnLoggingAllowed(), Network, "%p - NetworkResourceLoader::" fmt, this, ##__VA_ARGS__)
55 #define RELEASE_LOG_ERROR_IF_ALLOWED(fmt, ...) RELEASE_LOG_ERROR_IF(isAlwaysOnLoggingAllowed(), Network, "%p - NetworkResourceLoader::" fmt, this, ##__VA_ARGS__)
59 struct NetworkResourceLoader::SynchronousLoadData {
60 SynchronousLoadData(RefPtr<Messages::NetworkConnectionToWebProcess::PerformSynchronousLoad::DelayedReply>&& reply)
61 : delayedReply(WTFMove(reply))
65 ResourceRequest currentRequest;
66 RefPtr<Messages::NetworkConnectionToWebProcess::PerformSynchronousLoad::DelayedReply> delayedReply;
67 ResourceResponse response;
71 static void sendReplyToSynchronousRequest(NetworkResourceLoader::SynchronousLoadData& data, const SharedBuffer* buffer)
73 ASSERT(data.delayedReply);
74 ASSERT(!data.response.isNull() || !data.error.isNull());
76 Vector<char> responseBuffer;
77 if (buffer && buffer->size())
78 responseBuffer.append(buffer->data(), buffer->size());
80 data.delayedReply->send(data.error, data.response, responseBuffer);
81 data.delayedReply = nullptr;
84 NetworkResourceLoader::NetworkResourceLoader(const NetworkResourceLoadParameters& parameters, NetworkConnectionToWebProcess& connection, RefPtr<Messages::NetworkConnectionToWebProcess::PerformSynchronousLoad::DelayedReply>&& synchronousReply)
85 : m_parameters(parameters)
86 , m_connection(connection)
87 , m_defersLoading(parameters.defersLoading)
88 , m_bufferingTimer(*this, &NetworkResourceLoader::bufferingTimerFired)
89 , m_cache(sessionID().isEphemeral() ? nullptr : NetworkProcess::singleton().cache())
91 ASSERT(RunLoop::isMain());
92 // FIXME: This is necessary because of the existence of EmptyFrameLoaderClient in WebCore.
93 // Once bug 116233 is resolved, this ASSERT can just be "m_webPageID && m_webFrameID"
94 ASSERT((m_parameters.webPageID && m_parameters.webFrameID) || m_parameters.clientCredentialPolicy == ClientCredentialPolicy::CannotAskClientForCredentials);
96 if (originalRequest().httpBody()) {
97 for (const auto& element : originalRequest().httpBody()->elements()) {
98 if (element.m_type == FormDataElement::Type::EncodedBlob)
99 m_fileReferences.appendVector(NetworkBlobRegistry::singleton().filesInBlob(connection, element.m_url));
103 #if !USE(NETWORK_SESSION)
104 if (originalRequest().url().protocolIsBlob()) {
105 ASSERT(!m_parameters.resourceSandboxExtension);
106 m_fileReferences.appendVector(NetworkBlobRegistry::singleton().filesInBlob(connection, originalRequest().url()));
111 if (synchronousReply)
112 m_synchronousLoadData = std::make_unique<SynchronousLoadData>(WTFMove(synchronousReply));
115 NetworkResourceLoader::~NetworkResourceLoader()
117 ASSERT(RunLoop::isMain());
118 ASSERT(!m_networkLoad);
119 ASSERT(!isSynchronous() || !m_synchronousLoadData->delayedReply);
122 #if ENABLE(NETWORK_CACHE)
123 bool NetworkResourceLoader::canUseCache(const ResourceRequest& request) const
127 ASSERT(!sessionID().isEphemeral());
129 if (!request.url().protocolIsInHTTPFamily())
131 if (originalRequest().cachePolicy() == WebCore::DoNotUseAnyCache)
137 bool NetworkResourceLoader::canUseCachedRedirect(const ResourceRequest& request) const
139 if (!canUseCache(request))
141 // Limit cached redirects to avoid cycles and other trouble.
142 // Networking layer follows over 30 redirects but caching that many seems unnecessary.
143 static const unsigned maximumCachedRedirectCount { 5 };
144 if (m_redirectCount > maximumCachedRedirectCount)
151 bool NetworkResourceLoader::isSynchronous() const
153 return !!m_synchronousLoadData;
156 void NetworkResourceLoader::start()
158 ASSERT(RunLoop::isMain());
160 if (m_defersLoading) {
161 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());
165 ASSERT(!m_wasStarted);
168 #if ENABLE(NETWORK_CACHE)
169 if (canUseCache(originalRequest())) {
170 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());
171 retrieveCacheEntry(originalRequest());
176 startNetworkLoad(originalRequest());
179 #if ENABLE(NETWORK_CACHE)
180 void NetworkResourceLoader::retrieveCacheEntry(const ResourceRequest& request)
182 ASSERT(canUseCache(request));
184 RefPtr<NetworkResourceLoader> loader(this);
185 m_cache->retrieve(request, { m_parameters.webPageID, m_parameters.webFrameID }, [this, loader = WTFMove(loader), request](auto entry) {
186 if (loader->hasOneRef()) {
187 // The loader has been aborted and is only held alive by this lambda.
191 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());
192 loader->startNetworkLoad(request);
195 if (entry->redirectRequest()) {
196 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());
197 loader->dispatchWillSendRequestForCacheEntry(WTFMove(entry));
200 if (loader->m_parameters.needsCertificateInfo && !entry->response().certificateInfo()) {
201 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());
202 loader->startNetworkLoad(request);
205 if (entry->needsValidation() || request.cachePolicy() == WebCore::RefreshAnyCacheData) {
206 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());
207 loader->validateCacheEntry(WTFMove(entry));
210 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());
211 loader->didRetrieveCacheEntry(WTFMove(entry));
216 void NetworkResourceLoader::startNetworkLoad(const ResourceRequest& request)
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());
220 consumeSandboxExtensions();
222 if (isSynchronous() || m_parameters.maximumBufferingTime > 0_s)
223 m_bufferedData = SharedBuffer::create();
225 #if ENABLE(NETWORK_CACHE)
226 if (canUseCache(request))
227 m_bufferedDataForCache = SharedBuffer::create();
230 NetworkLoadParameters parameters = m_parameters;
231 parameters.defersLoading = m_defersLoading;
232 parameters.request = request;
234 #if USE(NETWORK_SESSION)
235 if (request.url().protocolIsBlob())
236 parameters.blobFileReferences = NetworkBlobRegistry::singleton().filesInBlob(m_connection, originalRequest().url());
238 auto* networkSession = SessionTracker::networkSession(parameters.sessionID);
239 if (!networkSession) {
240 WTFLogAlways("Attempted to create a NetworkLoad with a session (id=%" PRIu64 ") that does not exist.", parameters.sessionID.sessionID());
241 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());
242 NetworkProcess::singleton().logDiagnosticMessage(m_parameters.webPageID, WebCore::DiagnosticLoggingKeys::internalErrorKey(), WebCore::DiagnosticLoggingKeys::invalidSessionIDKey(), WebCore::ShouldSample::No);
243 didFailLoading(internalError(request.url()));
246 m_networkLoad = std::make_unique<NetworkLoad>(*this, WTFMove(parameters), *networkSession);
248 m_networkLoad = std::make_unique<NetworkLoad>(*this, WTFMove(parameters));
251 if (m_defersLoading) {
252 RELEASE_LOG_IF_ALLOWED("startNetworkLoad: Created, but deferred (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")",
253 m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier);
257 void NetworkResourceLoader::setDefersLoading(bool defers)
259 if (m_defersLoading == defers)
261 m_defersLoading = defers;
264 RELEASE_LOG_IF_ALLOWED("setDefersLoading: Deferring resource load (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier);
266 RELEASE_LOG_IF_ALLOWED("setDefersLoading: Resuming deferred resource load (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier);
269 m_networkLoad->setDefersLoading(defers);
273 if (!m_defersLoading && !m_wasStarted)
276 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);
279 void NetworkResourceLoader::cleanup()
281 ASSERT(RunLoop::isMain());
283 m_bufferingTimer.stop();
285 invalidateSandboxExtensions();
287 m_networkLoad = nullptr;
289 // This will cause NetworkResourceLoader to be destroyed and therefore we do it last.
290 m_connection->didCleanupResourceLoader(*this);
293 void NetworkResourceLoader::convertToDownload(DownloadID downloadID, const ResourceRequest& request, const ResourceResponse& response)
295 ASSERT(m_networkLoad);
296 NetworkProcess::singleton().downloadManager().convertNetworkLoadToDownload(downloadID, std::exchange(m_networkLoad, nullptr), WTFMove(m_fileReferences), request, response);
299 void NetworkResourceLoader::abort()
301 ASSERT(RunLoop::isMain());
303 RELEASE_LOG_IF_ALLOWED("abort: Canceling resource load (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")",
304 m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier);
307 #if ENABLE(NETWORK_CACHE)
308 if (canUseCache(m_networkLoad->currentRequest())) {
309 // We might already have used data from this incomplete load. Ensure older versions don't remain in the cache after cancel.
310 if (!m_response.isNull())
311 m_cache->remove(m_networkLoad->currentRequest());
314 m_networkLoad->cancel();
320 auto NetworkResourceLoader::didReceiveResponse(ResourceResponse&& receivedResponse) -> ShouldContinueDidReceiveResponse
322 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());
324 m_response = WTFMove(receivedResponse);
326 // For multipart/x-mixed-replace didReceiveResponseAsync gets called multiple times and buffering would require special handling.
327 if (!isSynchronous() && m_response.isMultipart())
328 m_bufferedData = nullptr;
330 bool shouldSendDidReceiveResponse = true;
331 #if ENABLE(NETWORK_CACHE)
332 if (m_response.isMultipart())
333 m_bufferedDataForCache = nullptr;
335 if (m_cacheEntryForValidation) {
336 bool validationSucceeded = m_response.httpStatusCode() == 304; // 304 Not Modified
337 if (validationSucceeded) {
338 m_cacheEntryForValidation = m_cache->update(originalRequest(), { m_parameters.webPageID, m_parameters.webFrameID }, *m_cacheEntryForValidation, m_response);
339 // If the request was conditional then this revalidation was not triggered by the network cache and we pass the 304 response to WebCore.
340 if (originalRequest().isConditional())
341 m_cacheEntryForValidation = nullptr;
343 m_cacheEntryForValidation = nullptr;
345 shouldSendDidReceiveResponse = !m_cacheEntryForValidation;
348 bool shouldWaitContinueDidReceiveResponse = isMainResource();
349 if (shouldSendDidReceiveResponse) {
351 m_synchronousLoadData->response = m_response;
353 send(Messages::WebResourceLoader::DidReceiveResponse(m_response, shouldWaitContinueDidReceiveResponse));
356 // For main resources, the web process is responsible for sending back a NetworkResourceLoader::ContinueDidReceiveResponse message.
357 bool shouldContinueDidReceiveResponse = !shouldWaitContinueDidReceiveResponse;
358 #if ENABLE(NETWORK_CACHE)
359 shouldContinueDidReceiveResponse = shouldContinueDidReceiveResponse || m_cacheEntryForValidation;
362 if (shouldContinueDidReceiveResponse) {
363 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);
364 return ShouldContinueDidReceiveResponse::Yes;
367 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);
368 return ShouldContinueDidReceiveResponse::No;
371 void NetworkResourceLoader::didReceiveBuffer(Ref<SharedBuffer>&& buffer, int reportedEncodedDataLength)
373 if (!m_numBytesReceived) {
374 RELEASE_LOG_IF_ALLOWED("didReceiveBuffer: Started receiving data (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier);
376 m_numBytesReceived += buffer->size();
378 #if ENABLE(NETWORK_CACHE)
379 ASSERT(!m_cacheEntryForValidation);
381 if (m_bufferedDataForCache) {
382 // Prevent memory growth in case of streaming data.
383 const size_t maximumCacheBufferSize = 10 * 1024 * 1024;
384 if (m_bufferedDataForCache->size() + buffer->size() <= maximumCacheBufferSize)
385 m_bufferedDataForCache->append(buffer.get());
387 m_bufferedDataForCache = nullptr;
390 // FIXME: At least on OS X Yosemite we always get -1 from the resource handle.
391 unsigned encodedDataLength = reportedEncodedDataLength >= 0 ? reportedEncodedDataLength : buffer->size();
393 m_bytesReceived += buffer->size();
394 if (m_bufferedData) {
395 m_bufferedData->append(buffer.get());
396 m_bufferedDataEncodedDataLength += encodedDataLength;
397 startBufferingTimerIfNeeded();
400 sendBuffer(buffer, encodedDataLength);
403 void NetworkResourceLoader::didFinishLoading(const NetworkLoadMetrics& networkLoadMetrics)
405 RELEASE_LOG_IF_ALLOWED("didFinishLoading: (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", length = %zd)", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier, m_numBytesReceived);
407 #if ENABLE(NETWORK_CACHE)
408 if (m_cacheEntryForValidation) {
410 ASSERT(m_response.httpStatusCode() == 304);
411 LOG(NetworkCache, "(NetworkProcess) revalidated");
412 didRetrieveCacheEntry(WTFMove(m_cacheEntryForValidation));
418 sendReplyToSynchronousRequest(*m_synchronousLoadData, m_bufferedData.get());
420 if (m_bufferedData && !m_bufferedData->isEmpty()) {
421 // FIXME: Pass a real value or remove the encoded data size feature.
422 sendBuffer(*m_bufferedData, -1);
424 send(Messages::WebResourceLoader::DidFinishResourceLoad(networkLoadMetrics));
427 #if ENABLE(NETWORK_CACHE)
428 tryStoreAsCacheEntry();
434 void NetworkResourceLoader::didFailLoading(const ResourceError& error)
436 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());
438 ASSERT(!error.isNull());
440 #if ENABLE(NETWORK_CACHE)
441 m_cacheEntryForValidation = nullptr;
444 if (isSynchronous()) {
445 m_synchronousLoadData->error = error;
446 sendReplyToSynchronousRequest(*m_synchronousLoadData, nullptr);
447 } else if (auto* connection = messageSenderConnection())
448 connection->send(Messages::WebResourceLoader::DidFailResourceLoad(error), messageSenderDestinationID());
453 void NetworkResourceLoader::willSendRedirectedRequest(ResourceRequest&& request, WebCore::ResourceRequest&& redirectRequest, ResourceResponse&& redirectResponse)
457 if (isSynchronous()) {
458 ResourceRequest overridenRequest = redirectRequest;
459 // FIXME: This needs to be fixed to follow the redirect correctly even for cross-domain requests.
460 // This includes at least updating host records, and comparing the current request instead of the original request here.
461 if (!protocolHostAndPortAreEqual(originalRequest().url(), redirectRequest.url())) {
462 ASSERT(m_synchronousLoadData->error.isNull());
463 m_synchronousLoadData->error = SynchronousLoaderClient::platformBadResponseError();
464 m_networkLoad->clearCurrentRequest();
465 overridenRequest = ResourceRequest();
467 continueWillSendRequest(WTFMove(overridenRequest));
470 send(Messages::WebResourceLoader::WillSendRequest(redirectRequest, redirectResponse));
472 #if ENABLE(NETWORK_CACHE)
473 if (canUseCachedRedirect(request))
474 m_cache->storeRedirect(request, redirectResponse, redirectRequest);
476 UNUSED_PARAM(request);
480 void NetworkResourceLoader::continueWillSendRequest(ResourceRequest&& newRequest)
482 RELEASE_LOG_IF_ALLOWED("continueWillSendRequest: (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier);
484 // If there is a match in the network cache, we need to reuse the original cache policy and partition.
485 newRequest.setCachePolicy(originalRequest().cachePolicy());
486 newRequest.setCachePartition(originalRequest().cachePartition());
488 #if ENABLE(NETWORK_CACHE)
489 if (m_isWaitingContinueWillSendRequestForCachedRedirect) {
490 m_isWaitingContinueWillSendRequestForCachedRedirect = false;
492 LOG(NetworkCache, "(NetworkProcess) Retrieving cached redirect");
494 if (canUseCachedRedirect(newRequest))
495 retrieveCacheEntry(newRequest);
497 startNetworkLoad(newRequest);
504 m_networkLoad->continueWillSendRequest(WTFMove(newRequest));
507 void NetworkResourceLoader::continueDidReceiveResponse()
509 // FIXME: Remove this check once BlobResourceHandle implements didReceiveResponseAsync correctly.
510 // Currently, it does not wait for response, so the load is likely to finish before continueDidReceiveResponse.
512 m_networkLoad->continueDidReceiveResponse();
515 void NetworkResourceLoader::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
517 if (!isSynchronous())
518 send(Messages::WebResourceLoader::DidSendData(bytesSent, totalBytesToBeSent));
521 void NetworkResourceLoader::startBufferingTimerIfNeeded()
525 if (m_bufferingTimer.isActive())
527 m_bufferingTimer.startOneShot(m_parameters.maximumBufferingTime);
530 void NetworkResourceLoader::bufferingTimerFired()
532 ASSERT(m_bufferedData);
533 ASSERT(m_networkLoad);
535 if (m_bufferedData->isEmpty())
538 IPC::SharedBufferDataReference dataReference(m_bufferedData.get());
539 size_t encodedLength = m_bufferedDataEncodedDataLength;
541 m_bufferedData = SharedBuffer::create();
542 m_bufferedDataEncodedDataLength = 0;
544 send(Messages::WebResourceLoader::DidReceiveData(dataReference, encodedLength));
547 void NetworkResourceLoader::sendBuffer(SharedBuffer& buffer, size_t encodedDataLength)
549 ASSERT(!isSynchronous());
551 IPC::SharedBufferDataReference dataReference(&buffer);
552 send(Messages::WebResourceLoader::DidReceiveData(dataReference, encodedDataLength));
555 #if ENABLE(NETWORK_CACHE)
556 void NetworkResourceLoader::tryStoreAsCacheEntry()
558 if (!canUseCache(m_networkLoad->currentRequest()))
560 if (!m_bufferedDataForCache)
563 m_cache->store(m_networkLoad->currentRequest(), m_response, WTFMove(m_bufferedDataForCache), [loader = makeRef(*this)](auto& mappedBody) mutable {
564 #if ENABLE(SHAREABLE_RESOURCE)
565 if (mappedBody.shareableResourceHandle.isNull())
567 LOG(NetworkCache, "(NetworkProcess) sending DidCacheResource");
568 loader->send(Messages::NetworkProcessConnection::DidCacheResource(loader->originalRequest(), mappedBody.shareableResourceHandle, loader->sessionID()));
573 void NetworkResourceLoader::didRetrieveCacheEntry(std::unique_ptr<NetworkCache::Entry> entry)
575 if (isSynchronous()) {
576 m_synchronousLoadData->response = entry->response();
577 sendReplyToSynchronousRequest(*m_synchronousLoadData, entry->buffer());
582 bool needsContinueDidReceiveResponseMessage = isMainResource();
583 send(Messages::WebResourceLoader::DidReceiveResponse(entry->response(), needsContinueDidReceiveResponseMessage));
585 if (entry->sourceStorageRecord().bodyHash && !m_parameters.derivedCachedDataTypesToRetrieve.isEmpty()) {
586 auto bodyHash = *entry->sourceStorageRecord().bodyHash;
587 auto* entryPtr = entry.release();
588 auto retrieveCount = m_parameters.derivedCachedDataTypesToRetrieve.size();
590 for (auto& type : m_parameters.derivedCachedDataTypesToRetrieve) {
591 NetworkCache::DataKey key { originalRequest().cachePartition(), type, bodyHash };
592 m_cache->retrieveData(key, [loader = makeRef(*this), entryPtr, type, retrieveCount] (const uint8_t* data, size_t size) mutable {
593 loader->m_retrievedDerivedDataCount++;
594 bool retrievedAll = loader->m_retrievedDerivedDataCount == retrieveCount;
595 std::unique_ptr<NetworkCache::Entry> entry(retrievedAll ? entryPtr : nullptr);
596 if (loader->hasOneRef())
599 IPC::DataReference dataReference(data, size);
600 loader->send(Messages::WebResourceLoader::DidRetrieveDerivedData(type, dataReference));
603 loader->sendResultForCacheEntry(WTFMove(entry));
611 sendResultForCacheEntry(WTFMove(entry));
616 void NetworkResourceLoader::sendResultForCacheEntry(std::unique_ptr<NetworkCache::Entry> entry)
618 #if ENABLE(SHAREABLE_RESOURCE)
619 if (!entry->shareableResourceHandle().isNull()) {
620 send(Messages::WebResourceLoader::DidReceiveResource(entry->shareableResourceHandle()));
625 WebCore::NetworkLoadMetrics networkLoadMetrics;
626 networkLoadMetrics.markComplete();
627 networkLoadMetrics.requestHeaderBytesSent = 0;
628 networkLoadMetrics.requestBodyBytesSent = 0;
629 networkLoadMetrics.responseHeaderBytesReceived = 0;
630 networkLoadMetrics.responseBodyBytesReceived = 0;
631 networkLoadMetrics.responseBodyDecodedSize = 0;
633 sendBuffer(*entry->buffer(), entry->buffer()->size());
634 send(Messages::WebResourceLoader::DidFinishResourceLoad(networkLoadMetrics));
637 void NetworkResourceLoader::validateCacheEntry(std::unique_ptr<NetworkCache::Entry> entry)
639 ASSERT(!m_networkLoad);
641 // If the request is already conditional then the revalidation was not triggered by the disk cache
642 // and we should not overwrite the existing conditional headers.
643 ResourceRequest revalidationRequest = originalRequest();
644 if (!revalidationRequest.isConditional()) {
645 String eTag = entry->response().httpHeaderField(HTTPHeaderName::ETag);
646 String lastModified = entry->response().httpHeaderField(HTTPHeaderName::LastModified);
648 revalidationRequest.setHTTPHeaderField(HTTPHeaderName::IfNoneMatch, eTag);
649 if (!lastModified.isEmpty())
650 revalidationRequest.setHTTPHeaderField(HTTPHeaderName::IfModifiedSince, lastModified);
653 m_cacheEntryForValidation = WTFMove(entry);
655 startNetworkLoad(revalidationRequest);
658 void NetworkResourceLoader::dispatchWillSendRequestForCacheEntry(std::unique_ptr<NetworkCache::Entry> entry)
660 ASSERT(entry->redirectRequest());
661 ASSERT(!m_isWaitingContinueWillSendRequestForCachedRedirect);
663 LOG(NetworkCache, "(NetworkProcess) Executing cached redirect");
666 send(Messages::WebResourceLoader::WillSendRequest(*entry->redirectRequest(), entry->response()));
667 m_isWaitingContinueWillSendRequestForCachedRedirect = true;
671 IPC::Connection* NetworkResourceLoader::messageSenderConnection()
673 return &connectionToWebProcess().connection();
676 void NetworkResourceLoader::consumeSandboxExtensions()
678 ASSERT(!m_didConsumeSandboxExtensions);
680 for (auto& extension : m_parameters.requestBodySandboxExtensions)
681 extension->consume();
683 if (auto& extension = m_parameters.resourceSandboxExtension)
684 extension->consume();
686 for (auto& fileReference : m_fileReferences)
687 fileReference->prepareForFileAccess();
689 m_didConsumeSandboxExtensions = true;
692 void NetworkResourceLoader::invalidateSandboxExtensions()
694 if (m_didConsumeSandboxExtensions) {
695 for (auto& extension : m_parameters.requestBodySandboxExtensions)
697 if (auto& extension = m_parameters.resourceSandboxExtension)
699 for (auto& fileReference : m_fileReferences)
700 fileReference->revokeFileAccess();
702 m_didConsumeSandboxExtensions = false;
705 m_fileReferences.clear();
708 #if USE(PROTECTION_SPACE_AUTH_CALLBACK)
709 void NetworkResourceLoader::canAuthenticateAgainstProtectionSpaceAsync(const ProtectionSpace& protectionSpace)
711 NetworkProcess::singleton().canAuthenticateAgainstProtectionSpace(*this, protectionSpace);
714 void NetworkResourceLoader::continueCanAuthenticateAgainstProtectionSpace(bool result)
717 m_networkLoad->continueCanAuthenticateAgainstProtectionSpace(result);
721 bool NetworkResourceLoader::isAlwaysOnLoggingAllowed() const
723 return sessionID().isAlwaysOnLoggingAllowed();
726 bool NetworkResourceLoader::shouldCaptureExtraNetworkLoadMetrics() const
728 return m_connection->captureExtraNetworkLoadMetricsEnabled();
731 } // namespace WebKit