#include "config.h"
#include "NetworkLoad.h"
#include "AuthenticationManager.h"
#include "NetworkProcess.h"
#include "SessionTracker.h"
#include "WebErrors.h"
#include <WebCore/NotImplemented.h>
#include <WebCore/ResourceHandle.h>
#include <WebCore/SessionID.h>
#include <WebCore/SharedBuffer.h>
#include <wtf/MainThread.h>
namespace WebKit {
using namespace WebCore;
#if USE(NETWORK_SESSION)
NetworkLoad::NetworkLoad(NetworkLoadClient& client, NetworkLoadParameters&& parameters, NetworkSession& networkSession)
: m_client(client)
, m_parameters(WTFMove(parameters))
, m_currentRequest(m_parameters.request)
{
if (m_parameters.request.url().protocolIsBlob()) {
m_handle = ResourceHandle::create(nullptr, m_parameters.request, this, m_parameters.defersLoading, m_parameters.contentSniffingPolicy == SniffContent);
return;
}
m_task = NetworkDataTask::create(networkSession, *this, m_parameters.request, m_parameters.allowStoredCredentials, m_parameters.contentSniffingPolicy, m_parameters.shouldClearReferrerOnHTTPSToHTTPRedirect);
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 (m_challengeCompletionHandler)
m_challengeCompletionHandler(AuthenticationChallengeDisposition::Cancel, { });
if (m_task)
m_task->clearClient();
#elif USE(PROTECTION_SPACE_AUTH_CALLBACK)
if (m_handle && m_waitingForContinueCanAuthenticateAgainstProtectionSpace)
m_handle->continueCanAuthenticateAgainstProtectionSpace(false);
#endif
if (m_handle)
m_handle->clearClient();
}
void NetworkLoad::setDefersLoading(bool defers)
{
#if USE(NETWORK_SESSION)
if (m_task) {
if (defers)
m_task->suspend();
else
m_task->resume();
}
#endif
if (m_handle)
m_handle->setDefersLoading(defers);
}
void NetworkLoad::cancel()
{
#if USE(NETWORK_SESSION)
if (m_task)
m_task->cancel();
#endif
if (m_handle)
m_handle->cancel();
}
void NetworkLoad::continueWillSendRequest(WebCore::ResourceRequest&& newRequest)
{
#if PLATFORM(COCOA)
m_currentRequest.updateFromDelegatePreservingOldProperties(newRequest.nsURLRequest(DoNotUpdateHTTPBody));
#elif USE(SOUP)
m_currentRequest.updateFromDelegatePreservingOldProperties(newRequest);
#endif
#if USE(NETWORK_SESSION)
auto redirectCompletionHandler = std::exchange(m_redirectCompletionHandler, nullptr);
ASSERT(redirectCompletionHandler);
#endif
if (m_currentRequest.isNull()) {
if (m_handle)
m_handle->cancel();
didFail(m_handle.get(), cancelledError(m_currentRequest));
#if USE(NETWORK_SESSION)
if (redirectCompletionHandler)
redirectCompletionHandler({ });
#endif
return;
} else if (m_handle) {
auto currentRequestCopy = m_currentRequest;
m_handle->continueWillSendRequest(WTFMove(currentRequestCopy));
}
#if USE(NETWORK_SESSION)
if (redirectCompletionHandler)
redirectCompletionHandler(m_currentRequest);
#endif
}
void NetworkLoad::continueDidReceiveResponse()
{
#if USE(NETWORK_SESSION)
ASSERT(m_responseCompletionHandler);
if (m_responseCompletionHandler) {
m_responseCompletionHandler(PolicyUse);
m_responseCompletionHandler = nullptr;
}
#endif
if (m_handle)
m_handle->continueDidReceiveResponse();
}
NetworkLoadClient::ShouldContinueDidReceiveResponse NetworkLoad::sharedDidReceiveResponse(ResourceResponse&& response)
{
response.setSource(ResourceResponse::Source::Network);
if (m_parameters.needsCertificateInfo)
response.includeCertificateInfo();
return m_client.didReceiveResponse(WTFMove(response));
}
void NetworkLoad::sharedWillSendRedirectedRequest(ResourceRequest&& request, ResourceResponse&& redirectResponse)
{
ASSERT(!redirectResponse.isNull());
ASSERT(RunLoop::isMain());
auto oldRequest = WTFMove(m_currentRequest);
m_currentRequest = request;
m_client.willSendRedirectedRequest(WTFMove(oldRequest), WTFMove(request), WTFMove(redirectResponse));
}
#if USE(NETWORK_SESSION)
void NetworkLoad::convertTaskToDownload(DownloadID downloadID, const ResourceRequest& updatedRequest)
{
if (!m_task)
return;
m_task->setPendingDownloadID(downloadID);
ASSERT(m_responseCompletionHandler);
if (m_responseCompletionHandler)
NetworkProcess::singleton().findPendingDownloadLocation(*m_task.get(), std::exchange(m_responseCompletionHandler, nullptr), updatedRequest);
}
void NetworkLoad::setPendingDownloadID(DownloadID downloadID)
{
if (!m_task)
return;
m_task->setPendingDownloadID(downloadID);
}
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)
{
ASSERT(m_parameters.clientCredentialPolicy != DoNotAskClientForCrossOriginCredentials);
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;
}
m_challengeCompletionHandler = WTFMove(completionHandler);
m_challenge = challenge;
m_client.canAuthenticateAgainstProtectionSpaceAsync(challenge.protectionSpace());
}
void NetworkLoad::didReceiveResponseNetworkSession(ResourceResponse&& response, ResponseCompletionHandler&& completionHandler)
{
ASSERT(isMainThread());
if (m_task && m_task->pendingDownloadID().downloadID())
NetworkProcess::singleton().findPendingDownloadLocation(*m_task.get(), WTFMove(completionHandler), m_task->currentRequest());
else if (sharedDidReceiveResponse(WTFMove(response)) == NetworkLoadClient::ShouldContinueDidReceiveResponse::Yes)
completionHandler(PolicyUse);
else
m_responseCompletionHandler = WTFMove(completionHandler);
}
void NetworkLoad::didReceiveData(Ref<SharedBuffer>&& buffer)
{
auto size = buffer->size();
m_client.didReceiveBuffer(WTFMove(buffer), size);
}
void NetworkLoad::didCompleteWithError(const ResourceError& error)
{
if (error.isNull())
m_client.didFinishLoading(WTF::monotonicallyIncreasingTime());
else
m_client.didFailLoading(error);
}
void NetworkLoad::didBecomeDownload()
{
m_client.didBecomeDownload();
}
void NetworkLoad::didSendData(uint64_t totalBytesSent, uint64_t totalBytesExpectedToSend)
{
m_client.didSendData(totalBytesSent, totalBytesExpectedToSend);
}
void NetworkLoad::wasBlocked()
{
m_client.didFailLoading(blockedError(m_currentRequest));
}
void NetworkLoad::cannotShowURL()
{
m_client.didFailLoading(cannotShowURLError(m_currentRequest));
}
#endif
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* , unsigned , int )
{
ASSERT_NOT_REACHED();
}
void NetworkLoad::didReceiveBuffer(ResourceHandle* handle, Ref<SharedBuffer>&& buffer, int reportedEncodedDataLength)
{
ASSERT_UNUSED(handle, handle == m_handle);
m_client.didReceiveBuffer(WTFMove(buffer), reportedEncodedDataLength);
}
void NetworkLoad::didFinishLoading(ResourceHandle* handle, double finishTime)
{
ASSERT_UNUSED(handle, handle == m_handle);
m_client.didFinishLoading(finishTime);
}
void NetworkLoad::didFail(ResourceHandle* handle, const ResourceError& error)
{
ASSERT_UNUSED(handle, !handle || handle == m_handle);
ASSERT(!error.isNull());
m_client.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);
if (protectionSpace.authenticationScheme() == ProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested
&& !NetworkProcess::singleton().canHandleHTTPSServerTrustEvaluation()) {
continueCanAuthenticateAgainstProtectionSpace(false);
return;
}
#if !USE(NETWORK_SESSION)
m_waitingForContinueCanAuthenticateAgainstProtectionSpace = true;
#endif
m_client.canAuthenticateAgainstProtectionSpaceAsync(protectionSpace);
}
#endif
#if USE(PROTECTION_SPACE_AUTH_CALLBACK)
void NetworkLoad::continueCanAuthenticateAgainstProtectionSpace(bool result)
{
#if USE(NETWORK_SESSION)
ASSERT_WITH_MESSAGE(!m_handle, "Blobs should never give authentication challenges");
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;
}
if (m_parameters.clientCredentialPolicy == DoNotAskClientForAnyCredentials) {
completionHandler(AuthenticationChallengeDisposition::UseCredential, { });
return;
}
if (m_task) {
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));
}
#else
m_waitingForContinueCanAuthenticateAgainstProtectionSpace = false;
#endif
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.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);
return m_parameters.allowStoredCredentials == AllowStoredCredentials;
}
void NetworkLoad::didReceiveAuthenticationChallenge(ResourceHandle* handle, const AuthenticationChallenge& challenge)
{
ASSERT_UNUSED(handle, handle == m_handle);
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 NetworkLoad::receivedCancellation(ResourceHandle* handle, const AuthenticationChallenge&)
{
ASSERT_UNUSED(handle, handle == m_handle);
m_handle->cancel();
didFail(m_handle.get(), cancelledError(m_currentRequest));
}
}