| /* |
| * Copyright (C) 2012-2014 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| * THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| #include "NetworkResourceLoader.h" |
| |
| #if ENABLE(NETWORK_PROCESS) |
| |
| #include "AuthenticationManager.h" |
| #include "DataReference.h" |
| #include "Logging.h" |
| #include "NetworkBlobRegistry.h" |
| #include "NetworkCache.h" |
| #include "NetworkConnectionToWebProcess.h" |
| #include "NetworkProcess.h" |
| #include "NetworkProcessConnectionMessages.h" |
| #include "NetworkResourceLoadParameters.h" |
| #include "RemoteNetworkingContext.h" |
| #include "ShareableResource.h" |
| #include "SharedMemory.h" |
| #include "WebCoreArgumentCoders.h" |
| #include "WebErrors.h" |
| #include "WebResourceLoaderMessages.h" |
| #include <WebCore/BlobDataFileReference.h> |
| #include <WebCore/CertificateInfo.h> |
| #include <WebCore/HTTPHeaderNames.h> |
| #include <WebCore/NotImplemented.h> |
| #include <WebCore/SharedBuffer.h> |
| #include <WebCore/SynchronousLoaderClient.h> |
| #include <wtf/CurrentTime.h> |
| #include <wtf/MainThread.h> |
| |
| #if !USE(NETWORK_SESSION) |
| #include <WebCore/ResourceHandle.h> |
| #endif |
| |
| using namespace WebCore; |
| |
| namespace WebKit { |
| |
| struct NetworkResourceLoader::SynchronousLoadData { |
| SynchronousLoadData(PassRefPtr<Messages::NetworkConnectionToWebProcess::PerformSynchronousLoad::DelayedReply> reply) |
| : delayedReply(reply) |
| { |
| ASSERT(delayedReply); |
| } |
| WebCore::ResourceRequest currentRequest; |
| RefPtr<Messages::NetworkConnectionToWebProcess::PerformSynchronousLoad::DelayedReply> delayedReply; |
| WebCore::ResourceResponse response; |
| WebCore::ResourceError error; |
| }; |
| |
| static void sendReplyToSynchronousRequest(NetworkResourceLoader::SynchronousLoadData& data, const WebCore::SharedBuffer* buffer) |
| { |
| ASSERT(data.delayedReply); |
| ASSERT(!data.response.isNull() || !data.error.isNull()); |
| |
| Vector<char> responseBuffer; |
| if (buffer && buffer->size()) |
| responseBuffer.append(buffer->data(), buffer->size()); |
| |
| data.delayedReply->send(data.error, data.response, responseBuffer); |
| data.delayedReply = nullptr; |
| } |
| |
| NetworkResourceLoader::NetworkResourceLoader(const NetworkResourceLoadParameters& parameters, NetworkConnectionToWebProcess* connection, PassRefPtr<Messages::NetworkConnectionToWebProcess::PerformSynchronousLoad::DelayedReply> synchronousReply) |
| : m_parameters(parameters) |
| , m_connection(connection) |
| , m_bytesReceived(0) |
| , m_bufferedDataEncodedDataLength(0) |
| , m_didConvertHandleToDownload(false) |
| , m_didConsumeSandboxExtensions(false) |
| , m_defersLoading(parameters.defersLoading) |
| , m_bufferingTimer(*this, &NetworkResourceLoader::bufferingTimerFired) |
| { |
| ASSERT(RunLoop::isMain()); |
| // FIXME: This is necessary because of the existence of EmptyFrameLoaderClient in WebCore. |
| // Once bug 116233 is resolved, this ASSERT can just be "m_webPageID && m_webFrameID" |
| ASSERT((m_parameters.webPageID && m_parameters.webFrameID) || m_parameters.clientCredentialPolicy == DoNotAskClientForAnyCredentials); |
| |
| if (originalRequest().httpBody()) { |
| for (const FormDataElement& element : originalRequest().httpBody()->elements()) { |
| if (element.m_type == FormDataElement::Type::EncodedBlob) |
| m_fileReferences.appendVector(NetworkBlobRegistry::singleton().filesInBlob(connection, element.m_url)); |
| } |
| } |
| |
| if (originalRequest().url().protocolIs("blob")) { |
| ASSERT(!m_parameters.resourceSandboxExtension); |
| m_fileReferences.appendVector(NetworkBlobRegistry::singleton().filesInBlob(connection, originalRequest().url())); |
| } |
| |
| if (synchronousReply) |
| m_synchronousLoadData = std::make_unique<SynchronousLoadData>(synchronousReply); |
| } |
| |
| NetworkResourceLoader::~NetworkResourceLoader() |
| { |
| ASSERT(RunLoop::isMain()); |
| #if USE(NETWORK_SESSION) |
| ASSERT(!m_task); |
| #else |
| ASSERT(!m_handle); |
| #endif |
| ASSERT(!isSynchronous() || !m_synchronousLoadData->delayedReply); |
| } |
| |
| bool NetworkResourceLoader::isSynchronous() const |
| { |
| return !!m_synchronousLoadData; |
| } |
| |
| void NetworkResourceLoader::start() |
| { |
| ASSERT(RunLoop::isMain()); |
| |
| if (m_defersLoading) |
| return; |
| |
| m_currentRequest = originalRequest(); |
| |
| #if ENABLE(NETWORK_CACHE) |
| if (!NetworkCache::singleton().isEnabled() || sessionID().isEphemeral() || !originalRequest().url().protocolIsInHTTPFamily()) { |
| startNetworkLoad(); |
| return; |
| } |
| |
| RefPtr<NetworkResourceLoader> loader(this); |
| NetworkCache::singleton().retrieve(originalRequest(), m_parameters.webPageID, [loader](std::unique_ptr<NetworkCache::Entry> entry) { |
| if (loader->hasOneRef()) { |
| // The loader has been aborted and is only held alive by this lambda. |
| return; |
| } |
| if (!entry) { |
| loader->startNetworkLoad(); |
| return; |
| } |
| if (loader->m_parameters.needsCertificateInfo && !entry->response().containsCertificateInfo()) { |
| loader->startNetworkLoad(); |
| return; |
| } |
| if (entry->needsValidation()) { |
| loader->validateCacheEntry(WTF::move(entry)); |
| return; |
| } |
| loader->didRetrieveCacheEntry(WTF::move(entry)); |
| }); |
| #else |
| startNetworkLoad(); |
| #endif |
| } |
| |
| void NetworkResourceLoader::startNetworkLoad() |
| { |
| m_networkingContext = RemoteNetworkingContext::create(sessionID(), m_parameters.shouldClearReferrerOnHTTPSToHTTPRedirect); |
| |
| consumeSandboxExtensions(); |
| |
| if (isSynchronous() || m_parameters.maximumBufferingTime > 0_ms) |
| m_bufferedData = WebCore::SharedBuffer::create(); |
| |
| #if ENABLE(NETWORK_CACHE) |
| if (NetworkCache::singleton().isEnabled()) |
| m_bufferedDataForCache = WebCore::SharedBuffer::create(); |
| #endif |
| |
| bool shouldSniff = m_parameters.contentSniffingPolicy == SniffContent; |
| #if USE(NETWORK_SESSION) |
| UNUSED_PARAM(shouldSniff); // FIXME: Use this. |
| m_task = NetworkSession::singleton()->createDataTaskWithRequest(m_currentRequest, *this); |
| m_task->resume(); |
| #else |
| m_handle = ResourceHandle::create(m_networkingContext.get(), m_currentRequest, this, m_defersLoading, shouldSniff); |
| #endif |
| } |
| |
| void NetworkResourceLoader::setDefersLoading(bool defers) |
| { |
| if (m_defersLoading == defers) |
| return; |
| m_defersLoading = defers; |
| #if USE(NETWORK_SESSION) |
| // FIXME: Do something here. |
| notImplemented(); |
| #else |
| if (m_handle) { |
| m_handle->setDefersLoading(defers); |
| return; |
| } |
| #endif |
| if (!m_defersLoading) |
| start(); |
| } |
| |
| void NetworkResourceLoader::cleanup() |
| { |
| ASSERT(RunLoop::isMain()); |
| |
| m_bufferingTimer.stop(); |
| |
| invalidateSandboxExtensions(); |
| |
| #if USE(NETWORK_SESSION) |
| if (m_task) { |
| m_task->clearClient(); |
| m_task = nullptr; |
| } |
| #else |
| if (m_handle) { |
| m_handle->clearClient(); |
| m_handle = nullptr; |
| } |
| #endif |
| |
| // This will cause NetworkResourceLoader to be destroyed and therefore we do it last. |
| m_connection->didCleanupResourceLoader(*this); |
| } |
| |
| #if !USE(NETWORK_SESSION) |
| void NetworkResourceLoader::didConvertHandleToDownload() |
| { |
| ASSERT(m_handle); |
| m_didConvertHandleToDownload = true; |
| } |
| #endif |
| |
| void NetworkResourceLoader::abort() |
| { |
| ASSERT(RunLoop::isMain()); |
| |
| #if USE(NETWORK_SESSION) |
| if (m_task) |
| m_task->suspend(); |
| // FIXME: Do something with the network cache here. |
| notImplemented(); |
| #else |
| if (m_handle && !m_didConvertHandleToDownload) { |
| m_handle->cancel(); |
| |
| #if ENABLE(NETWORK_CACHE) |
| if (NetworkCache::singleton().isEnabled()) { |
| // We might already have used data from this incomplete load. Ensure older versions don't remain in the cache after cancel. |
| if (!m_response.isNull()) |
| NetworkCache::singleton().remove(originalRequest()); |
| } |
| #endif |
| } |
| #endif |
| cleanup(); |
| } |
| |
| #if USE(NETWORK_SESSION) |
| |
| void NetworkResourceLoader::willPerformHTTPRedirection(const WebCore::ResourceResponse&, const WebCore::ResourceRequest& request, std::function<void(const WebCore::ResourceRequest&)> completionHandler) |
| { |
| notImplemented(); |
| completionHandler(request); |
| } |
| |
| void NetworkResourceLoader::didReceiveChallenge(const WebCore::AuthenticationChallenge& challenge, std::function<void(AuthenticationChallengeDisposition, const WebCore::Credential&)> completionHandler) |
| { |
| notImplemented(); |
| completionHandler(AuthenticationChallengeDisposition::PerformDefaultHandling, WebCore::Credential()); |
| } |
| |
| void NetworkResourceLoader::didReceiveResponse(WebCore::ResourceResponse& response, std::function<void(ResponseDisposition)> completionHandler) |
| { |
| ASSERT(isMainThread()); |
| m_response = response; |
| m_response.setSource(ResourceResponse::Source::Network); |
| if (m_parameters.needsCertificateInfo) |
| m_response.includeCertificateInfo(); |
| |
| bool shouldWaitContinueDidReceiveResponse = originalRequest().requester() == ResourceRequest::Requester::Main; |
| if (!sendAbortingOnFailure(Messages::WebResourceLoader::DidReceiveResponse(m_response, shouldWaitContinueDidReceiveResponse))) |
| return; |
| completionHandler(ResponseDisposition::Allow); |
| } |
| |
| void NetworkResourceLoader::didReceiveData(RefPtr<WebCore::SharedBuffer> buffer) |
| { |
| ASSERT(buffer); |
| sendBufferMaybeAborting(*buffer, buffer->size()); |
| } |
| |
| void NetworkResourceLoader::didCompleteWithError(const WebCore::ResourceError&) |
| { |
| send(Messages::WebResourceLoader::DidFinishResourceLoad(WTF::monotonicallyIncreasingTime())); |
| } |
| |
| #else |
| |
| void NetworkResourceLoader::didReceiveResponseAsync(ResourceHandle* handle, const ResourceResponse& receivedResponse) |
| { |
| ASSERT_UNUSED(handle, handle == m_handle); |
| |
| m_response = receivedResponse; |
| |
| m_response.setSource(ResourceResponse::Source::Network); |
| if (m_parameters.needsCertificateInfo) |
| m_response.includeCertificateInfo(); |
| // For multipart/x-mixed-replace didReceiveResponseAsync gets called multiple times and buffering would require special handling. |
| if (!isSynchronous() && m_response.isMultipart()) |
| m_bufferedData = nullptr; |
| |
| bool shouldSendDidReceiveResponse = true; |
| #if ENABLE(NETWORK_CACHE) |
| if (m_response.isMultipart()) |
| m_bufferedDataForCache = nullptr; |
| |
| if (m_cacheEntryForValidation) { |
| bool validationSucceeded = m_response.httpStatusCode() == 304; // 304 Not Modified |
| if (validationSucceeded) { |
| NetworkCache::singleton().update(originalRequest(), m_parameters.webPageID, *m_cacheEntryForValidation, m_response); |
| // If the request was conditional then this revalidation was not triggered by the network cache and we pass the |
| // 304 response to WebCore. |
| if (originalRequest().isConditional()) |
| m_cacheEntryForValidation = nullptr; |
| } else |
| m_cacheEntryForValidation = nullptr; |
| } |
| shouldSendDidReceiveResponse = !m_cacheEntryForValidation; |
| #endif |
| |
| bool shouldWaitContinueDidReceiveResponse = originalRequest().requester() == ResourceRequest::Requester::Main; |
| if (shouldSendDidReceiveResponse) { |
| if (isSynchronous()) |
| m_synchronousLoadData->response = m_response; |
| else { |
| if (!sendAbortingOnFailure(Messages::WebResourceLoader::DidReceiveResponse(m_response, shouldWaitContinueDidReceiveResponse))) |
| return; |
| } |
| } |
| |
| // For main resources, the web process is responsible for sending back a NetworkResourceLoader::ContinueDidReceiveResponse message. |
| bool shouldContinueDidReceiveResponse = !shouldWaitContinueDidReceiveResponse; |
| #if ENABLE(NETWORK_CACHE) |
| shouldContinueDidReceiveResponse = shouldContinueDidReceiveResponse || m_cacheEntryForValidation; |
| #endif |
| if (!shouldContinueDidReceiveResponse) |
| return; |
| |
| m_handle->continueDidReceiveResponse(); |
| } |
| |
| void NetworkResourceLoader::didReceiveData(ResourceHandle*, const char* /* data */, unsigned /* length */, int /* encodedDataLength */) |
| { |
| // The NetworkProcess should never get a didReceiveData callback. |
| // We should always be using didReceiveBuffer. |
| ASSERT_NOT_REACHED(); |
| } |
| |
| void NetworkResourceLoader::didReceiveBuffer(ResourceHandle* handle, PassRefPtr<SharedBuffer> buffer, int reportedEncodedDataLength) |
| { |
| ASSERT_UNUSED(handle, handle == m_handle); |
| #if ENABLE(NETWORK_CACHE) |
| ASSERT(!m_cacheEntryForValidation); |
| |
| if (m_bufferedDataForCache) { |
| // Prevent memory growth in case of streaming data. |
| const size_t maximumCacheBufferSize = 10 * 1024 * 1024; |
| if (m_bufferedDataForCache->size() + buffer->size() <= maximumCacheBufferSize) |
| m_bufferedDataForCache->append(buffer.get()); |
| else |
| m_bufferedDataForCache = nullptr; |
| } |
| #endif |
| // FIXME: At least on OS X Yosemite we always get -1 from the resource handle. |
| unsigned encodedDataLength = reportedEncodedDataLength >= 0 ? reportedEncodedDataLength : buffer->size(); |
| |
| m_bytesReceived += buffer->size(); |
| if (m_bufferedData) { |
| m_bufferedData->append(buffer.get()); |
| m_bufferedDataEncodedDataLength += encodedDataLength; |
| startBufferingTimerIfNeeded(); |
| return; |
| } |
| sendBufferMaybeAborting(*buffer, encodedDataLength); |
| } |
| |
| void NetworkResourceLoader::didFinishLoading(ResourceHandle* handle, double finishTime) |
| { |
| ASSERT_UNUSED(handle, handle == m_handle); |
| |
| #if ENABLE(NETWORK_CACHE) |
| if (NetworkCache::singleton().isEnabled()) { |
| if (m_cacheEntryForValidation) { |
| // 304 Not Modified |
| ASSERT(m_response.httpStatusCode() == 304); |
| LOG(NetworkCache, "(NetworkProcess) revalidated"); |
| didRetrieveCacheEntry(WTF::move(m_cacheEntryForValidation)); |
| return; |
| } |
| bool allowStale = originalRequest().cachePolicy() >= ReturnCacheDataElseLoad; |
| bool hasCacheableRedirect = m_response.isHTTP() && WebCore::redirectChainAllowsReuse(m_redirectChainCacheStatus, allowStale ? WebCore::ReuseExpiredRedirection : WebCore::DoNotReuseExpiredRedirection); |
| if (hasCacheableRedirect && m_redirectChainCacheStatus.status == RedirectChainCacheStatus::CachedRedirection) { |
| // Maybe we should cache the actual redirects instead of the end result? |
| auto now = std::chrono::system_clock::now(); |
| auto responseEndOfValidity = now + WebCore::computeFreshnessLifetimeForHTTPFamily(m_response, now) - WebCore::computeCurrentAge(m_response, now); |
| hasCacheableRedirect = responseEndOfValidity <= m_redirectChainCacheStatus.endOfValidity; |
| } |
| |
| bool isPrivate = sessionID().isEphemeral(); |
| if (m_bufferedDataForCache && hasCacheableRedirect && !isPrivate) { |
| // Keep the connection alive. |
| RefPtr<NetworkConnectionToWebProcess> connection(connectionToWebProcess()); |
| RefPtr<NetworkResourceLoader> loader(this); |
| NetworkCache::singleton().store(originalRequest(), m_response, WTF::move(m_bufferedDataForCache), [loader, connection](NetworkCache::MappedBody& mappedBody) { |
| #if ENABLE(SHAREABLE_RESOURCE) |
| if (mappedBody.shareableResourceHandle.isNull()) |
| return; |
| LOG(NetworkCache, "(NetworkProcess) sending DidCacheResource"); |
| loader->send(Messages::NetworkProcessConnection::DidCacheResource(loader->originalRequest(), mappedBody.shareableResourceHandle, loader->sessionID())); |
| #endif |
| }); |
| } else if (!hasCacheableRedirect) { |
| // Make sure we don't keep a stale entry in the cache. |
| NetworkCache::singleton().remove(originalRequest()); |
| } |
| } |
| #endif |
| |
| if (isSynchronous()) |
| sendReplyToSynchronousRequest(*m_synchronousLoadData, m_bufferedData.get()); |
| else { |
| if (m_bufferedData && m_bufferedData->size()) { |
| // FIXME: Pass a real value or remove the encoded data size feature. |
| bool shouldContinue = sendBufferMaybeAborting(*m_bufferedData, -1); |
| if (!shouldContinue) |
| return; |
| } |
| send(Messages::WebResourceLoader::DidFinishResourceLoad(finishTime)); |
| } |
| |
| cleanup(); |
| } |
| |
| void NetworkResourceLoader::didFail(ResourceHandle* handle, const ResourceError& error) |
| { |
| ASSERT_UNUSED(handle, !handle || handle == m_handle); |
| |
| #if ENABLE(NETWORK_CACHE) |
| m_cacheEntryForValidation = nullptr; |
| #endif |
| |
| if (isSynchronous()) { |
| m_synchronousLoadData->error = error; |
| sendReplyToSynchronousRequest(*m_synchronousLoadData, nullptr); |
| } else |
| send(Messages::WebResourceLoader::DidFailResourceLoad(error)); |
| |
| cleanup(); |
| } |
| |
| void NetworkResourceLoader::willSendRequestAsync(ResourceHandle* handle, const ResourceRequest& request, const ResourceResponse& redirectResponse) |
| { |
| ASSERT_UNUSED(handle, handle == m_handle); |
| |
| // We only expect to get the willSendRequest callback from ResourceHandle as the result of a redirect. |
| ASSERT(!redirectResponse.isNull()); |
| ASSERT(RunLoop::isMain()); |
| |
| m_currentRequest = request; |
| |
| #if ENABLE(NETWORK_CACHE) |
| WebCore::updateRedirectChainStatus(m_redirectChainCacheStatus, redirectResponse); |
| #endif |
| |
| if (isSynchronous()) { |
| // FIXME: This needs to be fixed to follow the redirect correctly even for cross-domain requests. |
| // This includes at least updating host records, and comparing the current request instead of the original request here. |
| if (!protocolHostAndPortAreEqual(originalRequest().url(), m_currentRequest.url())) { |
| ASSERT(m_synchronousLoadData->error.isNull()); |
| m_synchronousLoadData->error = SynchronousLoaderClient::platformBadResponseError(); |
| m_currentRequest = ResourceRequest(); |
| } |
| continueWillSendRequest(m_currentRequest); |
| return; |
| } |
| sendAbortingOnFailure(Messages::WebResourceLoader::WillSendRequest(m_currentRequest, redirectResponse)); |
| } |
| |
| #endif // USE(NETWORK_SESSION) |
| |
| void NetworkResourceLoader::continueWillSendRequest(const ResourceRequest& newRequest) |
| { |
| #if PLATFORM(COCOA) |
| m_currentRequest.updateFromDelegatePreservingOldProperties(newRequest.nsURLRequest(DoNotUpdateHTTPBody)); |
| #elif USE(SOUP) |
| // FIXME: Implement ResourceRequest::updateFromDelegatePreservingOldProperties. See https://bugs.webkit.org/show_bug.cgi?id=126127. |
| m_currentRequest.updateFromDelegatePreservingOldProperties(newRequest); |
| #endif |
| |
| if (m_currentRequest.isNull()) { |
| #if USE(NETWORK_SESSION) |
| // FIXME: Do something here. |
| notImplemented(); |
| #else |
| m_handle->cancel(); |
| didFail(m_handle.get(), cancelledError(m_currentRequest)); |
| #endif |
| return; |
| } |
| |
| #if USE(NETWORK_SESSION) |
| // FIXME: Do something here. |
| notImplemented(); |
| #else |
| m_handle->continueWillSendRequest(m_currentRequest); |
| #endif |
| } |
| |
| void NetworkResourceLoader::continueDidReceiveResponse() |
| { |
| // FIXME: Remove this check once BlobResourceHandle implements didReceiveResponseAsync correctly. |
| // Currently, it does not wait for response, so the load is likely to finish before continueDidReceiveResponse. |
| #if USE(NETWORK_SESSION) |
| // FIXME: Do something here. |
| notImplemented(); |
| #else |
| if (!m_handle) |
| return; |
| |
| m_handle->continueDidReceiveResponse(); |
| #endif |
| } |
| |
| #if !USE(NETWORK_SESSION) |
| |
| void NetworkResourceLoader::didSendData(ResourceHandle* handle, unsigned long long bytesSent, unsigned long long totalBytesToBeSent) |
| { |
| ASSERT_UNUSED(handle, handle == m_handle); |
| |
| if (!isSynchronous()) |
| send(Messages::WebResourceLoader::DidSendData(bytesSent, totalBytesToBeSent)); |
| } |
| |
| void NetworkResourceLoader::wasBlocked(ResourceHandle* handle) |
| { |
| ASSERT_UNUSED(handle, handle == m_handle); |
| |
| didFail(handle, WebKit::blockedError(m_currentRequest)); |
| } |
| |
| void NetworkResourceLoader::cannotShowURL(ResourceHandle* handle) |
| { |
| ASSERT_UNUSED(handle, handle == m_handle); |
| |
| didFail(handle, WebKit::cannotShowURLError(m_currentRequest)); |
| } |
| |
| bool NetworkResourceLoader::shouldUseCredentialStorage(ResourceHandle* handle) |
| { |
| ASSERT_UNUSED(handle, handle == m_handle || !m_handle); // m_handle will be 0 if called from ResourceHandle::start(). |
| |
| // When the WebProcess is handling loading a client is consulted each time this shouldUseCredentialStorage question is asked. |
| // In NetworkProcess mode we ask the WebProcess client up front once and then reuse the cached answer. |
| |
| // We still need this sync version, because ResourceHandle itself uses it internally, even when the delegate uses an async one. |
| |
| return m_parameters.allowStoredCredentials == AllowStoredCredentials; |
| } |
| |
| void NetworkResourceLoader::didReceiveAuthenticationChallenge(ResourceHandle* handle, const AuthenticationChallenge& challenge) |
| { |
| ASSERT_UNUSED(handle, handle == m_handle); |
| // NetworkResourceLoader does not know whether the request is cross origin, so Web process computes an applicable credential policy for it. |
| ASSERT(m_parameters.clientCredentialPolicy != DoNotAskClientForCrossOriginCredentials); |
| |
| if (m_parameters.clientCredentialPolicy == DoNotAskClientForAnyCredentials) { |
| challenge.authenticationClient()->receivedRequestToContinueWithoutCredential(challenge); |
| return; |
| } |
| |
| NetworkProcess::singleton().authenticationManager().didReceiveAuthenticationChallenge(m_parameters.webPageID, m_parameters.webFrameID, challenge); |
| } |
| |
| void NetworkResourceLoader::didCancelAuthenticationChallenge(ResourceHandle* handle, const AuthenticationChallenge&) |
| { |
| ASSERT_UNUSED(handle, handle == m_handle); |
| |
| // This function is probably not needed (see <rdar://problem/8960124>). |
| notImplemented(); |
| } |
| |
| void NetworkResourceLoader::receivedCancellation(ResourceHandle* handle, const AuthenticationChallenge&) |
| { |
| ASSERT_UNUSED(handle, handle == m_handle); |
| |
| m_handle->cancel(); |
| didFail(m_handle.get(), cancelledError(m_currentRequest)); |
| } |
| |
| #endif // !USE(NETWORK_SESSION) |
| |
| void NetworkResourceLoader::startBufferingTimerIfNeeded() |
| { |
| if (isSynchronous()) |
| return; |
| if (m_bufferingTimer.isActive()) |
| return; |
| m_bufferingTimer.startOneShot(m_parameters.maximumBufferingTime); |
| } |
| |
| void NetworkResourceLoader::bufferingTimerFired() |
| { |
| ASSERT(m_bufferedData); |
| #if !USE(NETWORK_SESSION) |
| ASSERT(m_handle); |
| #endif |
| if (!m_bufferedData->size()) |
| return; |
| |
| IPC::SharedBufferDataReference dataReference(m_bufferedData.get()); |
| size_t encodedLength = m_bufferedDataEncodedDataLength; |
| |
| m_bufferedData = WebCore::SharedBuffer::create(); |
| m_bufferedDataEncodedDataLength = 0; |
| |
| sendAbortingOnFailure(Messages::WebResourceLoader::DidReceiveData(dataReference, encodedLength)); |
| } |
| |
| bool NetworkResourceLoader::sendBufferMaybeAborting(const WebCore::SharedBuffer& buffer, size_t encodedDataLength) |
| { |
| ASSERT(!isSynchronous()); |
| |
| #if PLATFORM(COCOA) |
| ShareableResource::Handle shareableResourceHandle; |
| NetworkResourceLoader::tryGetShareableHandleFromSharedBuffer(shareableResourceHandle, const_cast<WebCore::SharedBuffer&>(buffer)); |
| if (!shareableResourceHandle.isNull()) { |
| send(Messages::WebResourceLoader::DidReceiveResource(shareableResourceHandle, currentTime())); |
| abort(); |
| return false; |
| } |
| #endif |
| |
| IPC::SharedBufferDataReference dataReference(&const_cast<WebCore::SharedBuffer&>(buffer)); |
| return sendAbortingOnFailure(Messages::WebResourceLoader::DidReceiveData(dataReference, encodedDataLength)); |
| } |
| |
| #if ENABLE(NETWORK_CACHE) |
| void NetworkResourceLoader::didRetrieveCacheEntry(std::unique_ptr<NetworkCache::Entry> entry) |
| { |
| if (isSynchronous()) { |
| m_synchronousLoadData->response = entry->response(); |
| sendReplyToSynchronousRequest(*m_synchronousLoadData, entry->buffer()); |
| } else { |
| if (entry->response().url() != originalRequest().url()) { |
| // This is a cached redirect. Synthesize a minimal redirect so we get things like referer header right. |
| // FIXME: We should cache the actual redirects. |
| ResourceRequest syntheticRedirectRequest(entry->response().url()); |
| ResourceResponse syntheticRedirectResponse(originalRequest().url(), { }, 0, { }); |
| sendAbortingOnFailure(Messages::WebResourceLoader::WillSendRequest(syntheticRedirectRequest, syntheticRedirectResponse)); |
| } |
| |
| bool needsContinueDidReceiveResponseMessage = originalRequest().requester() == ResourceRequest::Requester::Main; |
| sendAbortingOnFailure(Messages::WebResourceLoader::DidReceiveResponse(entry->response(), needsContinueDidReceiveResponseMessage)); |
| |
| #if ENABLE(SHAREABLE_RESOURCE) |
| if (!entry->shareableResourceHandle().isNull()) |
| send(Messages::WebResourceLoader::DidReceiveResource(entry->shareableResourceHandle(), currentTime())); |
| else { |
| #endif |
| bool shouldContinue = sendBufferMaybeAborting(*entry->buffer(), entry->buffer()->size()); |
| if (!shouldContinue) |
| return; |
| send(Messages::WebResourceLoader::DidFinishResourceLoad(currentTime())); |
| #if ENABLE(SHAREABLE_RESOURCE) |
| } |
| #endif |
| } |
| |
| cleanup(); |
| } |
| |
| void NetworkResourceLoader::validateCacheEntry(std::unique_ptr<NetworkCache::Entry> entry) |
| { |
| #if !USE(NETWORK_SESSION) |
| ASSERT(!m_handle); |
| #endif |
| |
| // If the request is already conditional then the revalidation was not triggered by the disk cache |
| // and we should not overwrite the existing conditional headers. |
| if (!m_currentRequest.isConditional()) { |
| String eTag = entry->response().httpHeaderField(WebCore::HTTPHeaderName::ETag); |
| String lastModified = entry->response().httpHeaderField(WebCore::HTTPHeaderName::LastModified); |
| if (!eTag.isEmpty()) |
| m_currentRequest.setHTTPHeaderField(WebCore::HTTPHeaderName::IfNoneMatch, eTag); |
| if (!lastModified.isEmpty()) |
| m_currentRequest.setHTTPHeaderField(WebCore::HTTPHeaderName::IfModifiedSince, lastModified); |
| } |
| |
| m_cacheEntryForValidation = WTF::move(entry); |
| |
| startNetworkLoad(); |
| } |
| #endif |
| |
| IPC::Connection* NetworkResourceLoader::messageSenderConnection() |
| { |
| return connectionToWebProcess()->connection(); |
| } |
| |
| void NetworkResourceLoader::consumeSandboxExtensions() |
| { |
| for (auto& extension : m_parameters.requestBodySandboxExtensions) |
| extension->consume(); |
| |
| if (auto& extension = m_parameters.resourceSandboxExtension) |
| extension->consume(); |
| |
| for (auto& fileReference : m_fileReferences) |
| fileReference->prepareForFileAccess(); |
| |
| m_didConsumeSandboxExtensions = true; |
| } |
| |
| void NetworkResourceLoader::invalidateSandboxExtensions() |
| { |
| if (m_didConsumeSandboxExtensions) { |
| for (auto& extension : m_parameters.requestBodySandboxExtensions) |
| extension->revoke(); |
| if (auto& extension = m_parameters.resourceSandboxExtension) |
| extension->revoke(); |
| for (auto& fileReference : m_fileReferences) |
| fileReference->revokeFileAccess(); |
| } |
| |
| m_fileReferences.clear(); |
| |
| m_didConsumeSandboxExtensions = false; |
| } |
| |
| template<typename T> |
| bool NetworkResourceLoader::sendAbortingOnFailure(T&& message, unsigned messageSendFlags) |
| { |
| bool result = messageSenderConnection()->send(std::forward<T>(message), messageSenderDestinationID(), messageSendFlags); |
| if (!result) |
| abort(); |
| return result; |
| } |
| |
| #if USE(PROTECTION_SPACE_AUTH_CALLBACK) && !USE(NETWORK_SESSION) |
| void NetworkResourceLoader::canAuthenticateAgainstProtectionSpaceAsync(ResourceHandle* handle, const ProtectionSpace& protectionSpace) |
| { |
| ASSERT(RunLoop::isMain()); |
| ASSERT_UNUSED(handle, handle == m_handle); |
| |
| // Handle server trust evaluation at platform-level if requested, for performance reasons. |
| if (protectionSpace.authenticationScheme() == ProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested |
| && !NetworkProcess::singleton().canHandleHTTPSServerTrustEvaluation()) { |
| continueCanAuthenticateAgainstProtectionSpace(false); |
| return; |
| } |
| |
| if (isSynchronous()) { |
| // FIXME: We should ask the WebProcess like the asynchronous case below does. |
| // This is currently impossible as the WebProcess is blocked waiting on this synchronous load. |
| // It's possible that we can jump straight to the UI process to resolve this. |
| continueCanAuthenticateAgainstProtectionSpace(true); |
| return; |
| } |
| sendAbortingOnFailure(Messages::WebResourceLoader::CanAuthenticateAgainstProtectionSpace(protectionSpace)); |
| } |
| #endif |
| |
| #if USE(PROTECTION_SPACE_AUTH_CALLBACK) |
| void NetworkResourceLoader::continueCanAuthenticateAgainstProtectionSpace(bool result) |
| { |
| #if USE(NETWORK_SESSION) |
| // FIXME: Do something here. |
| notImplemented(); |
| #else |
| m_handle->continueCanAuthenticateAgainstProtectionSpace(result); |
| #endif |
| } |
| #endif |
| |
| #if USE(NETWORK_CFDATA_ARRAY_CALLBACK) && !USE(NETWORK_SESSION) |
| bool NetworkResourceLoader::supportsDataArray() |
| { |
| notImplemented(); |
| return false; |
| } |
| |
| void NetworkResourceLoader::didReceiveDataArray(ResourceHandle*, CFArrayRef) |
| { |
| ASSERT_NOT_REACHED(); |
| notImplemented(); |
| } |
| #endif |
| |
| } // namespace WebKit |
| |
| #endif // ENABLE(NETWORK_PROCESS) |