diff options
Diffstat (limited to 'Source/WebKit2/NetworkProcess/NetworkLoad.cpp')
-rw-r--r-- | Source/WebKit2/NetworkProcess/NetworkLoad.cpp | 585 |
1 files changed, 585 insertions, 0 deletions
diff --git a/Source/WebKit2/NetworkProcess/NetworkLoad.cpp b/Source/WebKit2/NetworkProcess/NetworkLoad.cpp new file mode 100644 index 000000000..1c3e2813d --- /dev/null +++ b/Source/WebKit2/NetworkProcess/NetworkLoad.cpp @@ -0,0 +1,585 @@ +/* + * Copyright (C) 2015 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 "NetworkLoad.h" + +#include "AuthenticationManager.h" +#include "DownloadProxyMessages.h" +#include "NetworkProcess.h" +#include "SessionTracker.h" +#include "WebCoreArgumentCoders.h" +#include "WebErrors.h" +#include <WebCore/NotImplemented.h> +#include <WebCore/ResourceHandle.h> +#include <WebCore/ResourceRequest.h> +#include <WebCore/SessionID.h> +#include <WebCore/SharedBuffer.h> +#include <wtf/MainThread.h> + +#if PLATFORM(COCOA) +#include "NetworkDataTaskCocoa.h" +#endif + +#if ENABLE(NETWORK_CAPTURE) +#include "NetworkCaptureManager.h" +#endif + +namespace WebKit { + +using namespace WebCore; + +#if USE(NETWORK_SESSION) + +struct NetworkLoad::Throttle { + Throttle(NetworkLoad& load, std::chrono::milliseconds delay, ResourceResponse&& response, ResponseCompletionHandler&& handler) + : timer(load, &NetworkLoad::throttleDelayCompleted) + , response(WTFMove(response)) + , responseCompletionHandler(WTFMove(handler)) + { + timer.startOneShot(delay); + } + Timer timer; + ResourceResponse response; + ResponseCompletionHandler responseCompletionHandler; +}; + +NetworkLoad::NetworkLoad(NetworkLoadClient& client, NetworkLoadParameters&& parameters, NetworkSession& networkSession) + : m_client(client) + , m_parameters(WTFMove(parameters)) + , m_currentRequest(m_parameters.request) +{ +#if ENABLE(NETWORK_CAPTURE) + switch (NetworkCapture::Manager::singleton().mode()) { + case NetworkCapture::Manager::RecordReplayMode::Record: + initializeForRecord(networkSession); + break; + case NetworkCapture::Manager::RecordReplayMode::Replay: + initializeForReplay(networkSession); + break; + case NetworkCapture::Manager::RecordReplayMode::Disabled: + initialize(networkSession); + break; + } +#else + initialize(networkSession); +#endif +} + +#if ENABLE(NETWORK_CAPTURE) +void NetworkLoad::initializeForRecord(NetworkSession& networkSession) +{ + m_recorder = std::make_unique<NetworkCapture::Recorder>(); + m_task = NetworkDataTask::create(networkSession, *this, m_parameters); + if (!m_parameters.defersLoading) { + m_task->resume(); + m_recorder->recordRequestSent(m_parameters.request); + } +} + +void NetworkLoad::initializeForReplay(NetworkSession& networkSession) +{ + m_replayer = std::make_unique<NetworkCapture::Replayer>(); + m_task = m_replayer->replayResource(networkSession, *this, m_parameters); + if (!m_parameters.defersLoading) + m_task->resume(); +} +#endif + +void NetworkLoad::initialize(NetworkSession& networkSession) +{ + m_task = NetworkDataTask::create(networkSession, *this, m_parameters); + if (!m_parameters.defersLoading) + m_task->resume(); +} + +#else + +NetworkLoad::NetworkLoad(NetworkLoadClient& client, NetworkLoadParameters&& parameters) + : m_client(client) + , m_parameters(WTFMove(parameters)) + , m_networkingContext(RemoteNetworkingContext::create(m_parameters.sessionID, m_parameters.shouldClearReferrerOnHTTPSToHTTPRedirect)) + , m_currentRequest(m_parameters.request) +{ + m_handle = ResourceHandle::create(m_networkingContext.get(), m_parameters.request, this, m_parameters.defersLoading, m_parameters.contentSniffingPolicy == SniffContent); +} + +#endif + +NetworkLoad::~NetworkLoad() +{ + ASSERT(RunLoop::isMain()); +#if USE(NETWORK_SESSION) + if (m_responseCompletionHandler) + m_responseCompletionHandler(PolicyIgnore); +#if USE(PROTECTION_SPACE_AUTH_CALLBACK) + if (m_challengeCompletionHandler) + m_challengeCompletionHandler(AuthenticationChallengeDisposition::Cancel, { }); +#endif + if (m_task) + m_task->clearClient(); +#else +#if USE(PROTECTION_SPACE_AUTH_CALLBACK) + if (m_handle && m_waitingForContinueCanAuthenticateAgainstProtectionSpace) + m_handle->continueCanAuthenticateAgainstProtectionSpace(false); +#endif + if (m_handle) + m_handle->clearClient(); +#endif +} + +void NetworkLoad::setDefersLoading(bool defers) +{ +#if USE(NETWORK_SESSION) + if (m_task) { + if (defers) + m_task->suspend(); + else { + m_task->resume(); +#if ENABLE(NETWORK_CAPTURE) + if (m_recorder) + m_recorder->recordRequestSent(m_parameters.request); +#endif + } + } +#else + if (m_handle) + m_handle->setDefersLoading(defers); +#endif +} + +void NetworkLoad::cancel() +{ +#if USE(NETWORK_SESSION) + if (m_task) + m_task->cancel(); +#else + if (m_handle) + m_handle->cancel(); +#endif +} + +void NetworkLoad::continueWillSendRequest(WebCore::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 ENABLE(NETWORK_CAPTURE) + if (m_recorder) + m_recorder->recordRedirectSent(newRequest); +#endif + +#if USE(NETWORK_SESSION) + auto redirectCompletionHandler = std::exchange(m_redirectCompletionHandler, nullptr); + ASSERT(redirectCompletionHandler); + if (m_currentRequest.isNull()) { + didCompleteWithError(cancelledError(m_currentRequest)); + if (redirectCompletionHandler) + redirectCompletionHandler({ }); + return; + } + + if (redirectCompletionHandler) + redirectCompletionHandler(m_currentRequest); +#else + if (m_currentRequest.isNull()) { + if (m_handle) + m_handle->cancel(); + didFail(m_handle.get(), cancelledError(m_currentRequest)); + } else if (m_handle) { + auto currentRequestCopy = m_currentRequest; + m_handle->continueWillSendRequest(WTFMove(currentRequestCopy)); + } +#endif +} + +void NetworkLoad::continueDidReceiveResponse() +{ +#if USE(NETWORK_SESSION) + if (m_responseCompletionHandler) { + auto responseCompletionHandler = std::exchange(m_responseCompletionHandler, nullptr); + responseCompletionHandler(PolicyUse); + } +#else + if (m_handle) + m_handle->continueDidReceiveResponse(); +#endif +} + +NetworkLoadClient::ShouldContinueDidReceiveResponse NetworkLoad::sharedDidReceiveResponse(ResourceResponse&& response) +{ + response.setSource(ResourceResponse::Source::Network); + if (m_parameters.needsCertificateInfo) + response.includeCertificateInfo(); + + return m_client.get().didReceiveResponse(WTFMove(response)); +} + +void NetworkLoad::sharedWillSendRedirectedRequest(ResourceRequest&& request, ResourceResponse&& redirectResponse) +{ + // We only expect to get the willSendRequest callback from ResourceHandle as the result of a redirect. + ASSERT(!redirectResponse.isNull()); + ASSERT(RunLoop::isMain()); + +#if ENABLE(NETWORK_CAPTURE) + if (m_recorder) + m_recorder->recordRedirectReceived(request, redirectResponse); +#endif + + auto oldRequest = WTFMove(m_currentRequest); + m_currentRequest = request; + m_client.get().willSendRedirectedRequest(WTFMove(oldRequest), WTFMove(request), WTFMove(redirectResponse)); +} + +#if USE(NETWORK_SESSION) + +void NetworkLoad::convertTaskToDownload(PendingDownload& pendingDownload, const ResourceRequest& updatedRequest, const ResourceResponse& response) +{ + if (!m_task) + return; + + m_client = pendingDownload; + m_currentRequest = updatedRequest; + m_task->setPendingDownload(pendingDownload); + + if (m_responseCompletionHandler) + NetworkProcess::singleton().findPendingDownloadLocation(*m_task.get(), std::exchange(m_responseCompletionHandler, nullptr), response); +} + +void NetworkLoad::setPendingDownloadID(DownloadID downloadID) +{ + if (!m_task) + return; + + m_task->setPendingDownloadID(downloadID); +} + +void NetworkLoad::setSuggestedFilename(const String& suggestedName) +{ + if (!m_task) + return; + + m_task->setSuggestedFilename(suggestedName); +} + +void NetworkLoad::setPendingDownload(PendingDownload& pendingDownload) +{ + if (!m_task) + return; + + m_task->setPendingDownload(pendingDownload); +} + +void NetworkLoad::willPerformHTTPRedirection(ResourceResponse&& response, ResourceRequest&& request, RedirectCompletionHandler&& completionHandler) +{ + ASSERT(!m_redirectCompletionHandler); + m_redirectCompletionHandler = WTFMove(completionHandler); + sharedWillSendRedirectedRequest(WTFMove(request), WTFMove(response)); +} + +void NetworkLoad::didReceiveChallenge(const AuthenticationChallenge& challenge, ChallengeCompletionHandler&& completionHandler) +{ + // Handle server trust evaluation at platform-level if requested, for performance reasons. +#if PLATFORM(COCOA) + if (challenge.protectionSpace().authenticationScheme() == ProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested + && !NetworkProcess::singleton().canHandleHTTPSServerTrustEvaluation()) { + if (m_task && m_task->allowsSpecificHTTPSCertificateForHost(challenge)) + completionHandler(AuthenticationChallengeDisposition::UseCredential, serverTrustCredential(challenge)); + else + completionHandler(AuthenticationChallengeDisposition::RejectProtectionSpace, { }); + return; + } +#endif + + m_challenge = challenge; +#if USE(PROTECTION_SPACE_AUTH_CALLBACK) + m_challengeCompletionHandler = WTFMove(completionHandler); + m_client.get().canAuthenticateAgainstProtectionSpaceAsync(challenge.protectionSpace()); +#else + completeAuthenticationChallenge(WTFMove(completionHandler)); +#endif +} + +void NetworkLoad::completeAuthenticationChallenge(ChallengeCompletionHandler&& completionHandler) +{ + if (m_parameters.clientCredentialPolicy == ClientCredentialPolicy::CannotAskClientForCredentials) { + completionHandler(AuthenticationChallengeDisposition::UseCredential, { }); + return; + } + + if (!m_task) + return; + + if (auto* pendingDownload = m_task->pendingDownload()) + NetworkProcess::singleton().authenticationManager().didReceiveAuthenticationChallenge(*pendingDownload, *m_challenge, WTFMove(completionHandler)); + else + NetworkProcess::singleton().authenticationManager().didReceiveAuthenticationChallenge(m_parameters.webPageID, m_parameters.webFrameID, *m_challenge, WTFMove(completionHandler)); +} + +#if USE(PROTECTION_SPACE_AUTH_CALLBACK) +void NetworkLoad::continueCanAuthenticateAgainstProtectionSpace(bool result) +{ + ASSERT(m_challengeCompletionHandler); + auto completionHandler = std::exchange(m_challengeCompletionHandler, nullptr); + if (!result) { + if (m_task && m_task->allowsSpecificHTTPSCertificateForHost(*m_challenge)) + completionHandler(AuthenticationChallengeDisposition::UseCredential, serverTrustCredential(*m_challenge)); + else + completionHandler(AuthenticationChallengeDisposition::RejectProtectionSpace, { }); + return; + } + + completeAuthenticationChallenge(WTFMove(completionHandler)); +} +#endif + +void NetworkLoad::didReceiveResponseNetworkSession(ResourceResponse&& response, ResponseCompletionHandler&& completionHandler) +{ + ASSERT(isMainThread()); + ASSERT(!m_throttle); + + if (m_task && m_task->isDownload()) { + NetworkProcess::singleton().findPendingDownloadLocation(*m_task.get(), WTFMove(completionHandler), response); + return; + } + + auto delay = NetworkProcess::singleton().loadThrottleLatency(); + if (delay > 0ms) { + m_throttle = std::make_unique<Throttle>(*this, delay, WTFMove(response), WTFMove(completionHandler)); + return; + } + + notifyDidReceiveResponse(WTFMove(response), WTFMove(completionHandler)); +} + +void NetworkLoad::notifyDidReceiveResponse(ResourceResponse&& response, ResponseCompletionHandler&& completionHandler) +{ + ASSERT(isMainThread()); + +#if ENABLE(NETWORK_CAPTURE) + if (m_recorder) + m_recorder->recordResponseReceived(response); +#endif + + if (sharedDidReceiveResponse(WTFMove(response)) == NetworkLoadClient::ShouldContinueDidReceiveResponse::No) { + m_responseCompletionHandler = WTFMove(completionHandler); + return; + } + completionHandler(PolicyUse); +} + +void NetworkLoad::didReceiveData(Ref<SharedBuffer>&& buffer) +{ + ASSERT(!m_throttle); + +#if ENABLE(NETWORK_CAPTURE) + if (m_recorder) + m_recorder->recordDataReceived(buffer.get()); +#endif + + // FIXME: This should be the encoded data length, not the decoded data length. + auto size = buffer->size(); + m_client.get().didReceiveBuffer(WTFMove(buffer), size); +} + +void NetworkLoad::didCompleteWithError(const ResourceError& error) +{ + ASSERT(!m_throttle); + +#if ENABLE(NETWORK_CAPTURE) + if (m_recorder) + m_recorder->recordFinish(error); +#endif + + if (error.isNull()) + m_client.get().didFinishLoading(WTF::monotonicallyIncreasingTime()); + else + m_client.get().didFailLoading(error); +} + +void NetworkLoad::throttleDelayCompleted() +{ + ASSERT(m_throttle); + + auto throttle = WTFMove(m_throttle); + + notifyDidReceiveResponse(WTFMove(throttle->response), WTFMove(throttle->responseCompletionHandler)); +} + +void NetworkLoad::didSendData(uint64_t totalBytesSent, uint64_t totalBytesExpectedToSend) +{ + m_client.get().didSendData(totalBytesSent, totalBytesExpectedToSend); +} + +void NetworkLoad::wasBlocked() +{ + m_client.get().didFailLoading(blockedError(m_currentRequest)); +} + +void NetworkLoad::cannotShowURL() +{ + m_client.get().didFailLoading(cannotShowURLError(m_currentRequest)); +} + +#else + +void NetworkLoad::didReceiveResponseAsync(ResourceHandle* handle, ResourceResponse&& receivedResponse) +{ + ASSERT_UNUSED(handle, handle == m_handle); + if (sharedDidReceiveResponse(WTFMove(receivedResponse)) == NetworkLoadClient::ShouldContinueDidReceiveResponse::Yes) + m_handle->continueDidReceiveResponse(); +} + +void NetworkLoad::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 NetworkLoad::didReceiveBuffer(ResourceHandle* handle, Ref<SharedBuffer>&& buffer, int reportedEncodedDataLength) +{ + ASSERT_UNUSED(handle, handle == m_handle); + m_client.get().didReceiveBuffer(WTFMove(buffer), reportedEncodedDataLength); +} + +void NetworkLoad::didFinishLoading(ResourceHandle* handle, double finishTime) +{ + ASSERT_UNUSED(handle, handle == m_handle); + m_client.get().didFinishLoading(finishTime); +} + +void NetworkLoad::didFail(ResourceHandle* handle, const ResourceError& error) +{ + ASSERT_UNUSED(handle, !handle || handle == m_handle); + ASSERT(!error.isNull()); + + m_client.get().didFailLoading(error); +} + +void NetworkLoad::willSendRequestAsync(ResourceHandle* handle, ResourceRequest&& request, ResourceResponse&& redirectResponse) +{ + ASSERT_UNUSED(handle, handle == m_handle); + sharedWillSendRedirectedRequest(WTFMove(request), WTFMove(redirectResponse)); +} + +#if USE(PROTECTION_SPACE_AUTH_CALLBACK) +void NetworkLoad::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; + } + + m_waitingForContinueCanAuthenticateAgainstProtectionSpace = true; + m_client.get().canAuthenticateAgainstProtectionSpaceAsync(protectionSpace); +} + +void NetworkLoad::continueCanAuthenticateAgainstProtectionSpace(bool result) +{ + m_waitingForContinueCanAuthenticateAgainstProtectionSpace = false; + if (m_handle) + m_handle->continueCanAuthenticateAgainstProtectionSpace(result); +} +#endif + +#if USE(NETWORK_CFDATA_ARRAY_CALLBACK) +bool NetworkLoad::supportsDataArray() +{ + notImplemented(); + return false; +} + +void NetworkLoad::didReceiveDataArray(ResourceHandle*, CFArrayRef) +{ + ASSERT_NOT_REACHED(); + notImplemented(); +} +#endif + +void NetworkLoad::didSendData(ResourceHandle* handle, unsigned long long bytesSent, unsigned long long totalBytesToBeSent) +{ + ASSERT_UNUSED(handle, handle == m_handle); + + m_client.get().didSendData(bytesSent, totalBytesToBeSent); +} + +void NetworkLoad::wasBlocked(ResourceHandle* handle) +{ + ASSERT_UNUSED(handle, handle == m_handle); + + didFail(handle, WebKit::blockedError(m_currentRequest)); +} + +void NetworkLoad::cannotShowURL(ResourceHandle* handle) +{ + ASSERT_UNUSED(handle, handle == m_handle); + + didFail(handle, WebKit::cannotShowURLError(m_currentRequest)); +} + +bool NetworkLoad::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 NetworkLoad::didReceiveAuthenticationChallenge(ResourceHandle* handle, const AuthenticationChallenge& challenge) +{ + ASSERT_UNUSED(handle, handle == m_handle); + + if (m_parameters.clientCredentialPolicy == ClientCredentialPolicy::CannotAskClientForCredentials) { + challenge.authenticationClient()->receivedRequestToContinueWithoutCredential(challenge); + return; + } + + NetworkProcess::singleton().authenticationManager().didReceiveAuthenticationChallenge(m_parameters.webPageID, m_parameters.webFrameID, challenge); +} + +void NetworkLoad::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) + +} // namespace WebKit |