blob: c1b912e4851eb84cc2598291ca6ee47d66aa0102 [file] [log] [blame]
/*
* Copyright (C) 2013 Apple Inc. All rights reserved.
* Copyright (C) 2017 Sony Interactive Entertainment Inc.
* Copyright (C) 2017 NAVER Corp.
*
* 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. ``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
* 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 "CurlDownload.h"
#if USE(CURL)
#include "HTTPParsers.h"
#include "ResourceRequest.h"
#include "ResourceResponse.h"
#include "SharedBuffer.h"
namespace WebCore {
CurlDownload::~CurlDownload()
{
if (m_curlRequest)
m_curlRequest->invalidateClient();
}
void CurlDownload::init(CurlDownloadListener& listener, const URL& url)
{
m_listener = &listener;
m_request.setURL(url);
}
void CurlDownload::init(CurlDownloadListener& listener, ResourceHandle*, const ResourceRequest& request, const ResourceResponse&)
{
m_listener = &listener;
m_request = request.isolatedCopy();
}
void CurlDownload::start()
{
ASSERT(isMainThread());
m_curlRequest = createCurlRequest(m_request);
m_curlRequest->enableDownloadToFile();
m_curlRequest->start();
}
bool CurlDownload::cancel()
{
m_isCancelled = true;
if (!m_curlRequest)
m_curlRequest->cancel();
return true;
}
Ref<CurlRequest> CurlDownload::createCurlRequest(ResourceRequest& request)
{
// FIXME: Use a correct sessionID.
auto curlRequest = CurlRequest::create(request, *this);
return curlRequest;
}
void CurlDownload::curlDidReceiveResponse(CurlRequest& request, CurlResponse&& response)
{
ASSERT(isMainThread());
if (m_isCancelled)
return;
m_response = ResourceResponse(response);
if (m_response.shouldRedirect()) {
willSendRequest();
return;
}
if (m_listener)
m_listener->didReceiveResponse(m_response);
request.completeDidReceiveResponse();
}
void CurlDownload::curlDidReceiveBuffer(CurlRequest&, Ref<SharedBuffer>&& buffer)
{
ASSERT(isMainThread());
if (m_isCancelled)
return;
if (m_listener)
m_listener->didReceiveDataOfLength(buffer->size());
}
void CurlDownload::curlDidComplete(CurlRequest& request, NetworkLoadMetrics&&)
{
ASSERT(isMainThread());
if (m_isCancelled)
return;
if (!m_destination.isEmpty()) {
if (!request.getDownloadedFilePath().isEmpty())
FileSystem::moveFile(request.getDownloadedFilePath(), m_destination);
}
if (m_listener)
m_listener->didFinish();
}
void CurlDownload::curlDidFailWithError(CurlRequest& request, ResourceError&&, CertificateInfo&&)
{
ASSERT(isMainThread());
if (m_isCancelled)
return;
if (m_deletesFileUponFailure && !request.getDownloadedFilePath().isEmpty())
FileSystem::deleteFile(request.getDownloadedFilePath());
if (m_listener)
m_listener->didFail();
}
bool CurlDownload::shouldRedirectAsGET(const ResourceRequest& request, bool crossOrigin)
{
if ((request.httpMethod() == "GET") || (request.httpMethod() == "HEAD"))
return false;
if (!request.url().protocolIsInHTTPFamily())
return true;
if (m_response.isSeeOther())
return true;
if ((m_response.isMovedPermanently() || m_response.isFound()) && (request.httpMethod() == "POST"))
return true;
if (crossOrigin && (request.httpMethod() == "DELETE"))
return true;
return false;
}
void CurlDownload::willSendRequest()
{
ASSERT(isMainThread());
static const int maxRedirects = 20;
if (m_redirectCount++ > maxRedirects) {
if (m_listener)
m_listener->didFail();
return;
}
String location = m_response.httpHeaderField(HTTPHeaderName::Location);
URL newURL = URL(m_response.url(), location);
bool crossOrigin = !protocolHostAndPortAreEqual(m_request.url(), newURL);
ResourceRequest newRequest = m_request;
newRequest.setURL(newURL);
if (shouldRedirectAsGET(newRequest, crossOrigin)) {
newRequest.setHTTPMethod("GET");
newRequest.setHTTPBody(nullptr);
newRequest.clearHTTPContentType();
}
if (crossOrigin) {
// If the network layer carries over authentication headers from the original request
// in a cross-origin redirect, we want to clear those headers here.
newRequest.clearHTTPAuthorization();
newRequest.clearHTTPOrigin();
}
m_curlRequest->cancel();
m_curlRequest = createCurlRequest(newRequest);
m_curlRequest->start();
}
}
#endif