| /* |
| * 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 |