Diff
Modified: trunk/Source/WebCore/ChangeLog (222664 => 222665)
--- trunk/Source/WebCore/ChangeLog 2017-09-29 21:14:09 UTC (rev 222664)
+++ trunk/Source/WebCore/ChangeLog 2017-09-29 21:19:32 UTC (rev 222665)
@@ -1,3 +1,95 @@
+2017-09-29 Basuke Suzuki <[email protected]>
+
+ [Curl] Extract a features to manage HTTP communication from ResourceHandle
+ https://bugs.webkit.org/show_bug.cgi?id=175148
+
+ Reviewed by Alex Christensen.
+
+ * platform/Curl.cmake:
+ * platform/network/curl/CurlRequest.cpp: Added.
+ (WebCore::CurlRequest::CurlRequest):
+ (WebCore::CurlRequest::setUserPass):
+ (WebCore::CurlRequest::start):
+ (WebCore::CurlRequest::startWithJobManager):
+ (WebCore::CurlRequest::cancel):
+ (WebCore::CurlRequest::suspend):
+ (WebCore::CurlRequest::resume):
+ (WebCore::CurlRequest::callDelegate):
+ (WebCore::CurlRequest::setupTransfer):
+ (WebCore::CurlRequest::willSetupSslCtx):
+ (WebCore::CurlRequest::willSendData):
+ (WebCore::CurlRequest::didReceiveHeader):
+ (WebCore::CurlRequest::didReceiveData):
+ (WebCore::CurlRequest::didCompleteTransfer):
+ (WebCore::CurlRequest::didCancelTransfer):
+ (WebCore::CurlRequest::resolveBlobReferences):
+ (WebCore::CurlRequest::setupPUT):
+ (WebCore::CurlRequest::setupPOST):
+ (WebCore::CurlRequest::setupFormData):
+ (WebCore::CurlRequest::invokeDidReceiveResponseForFile):
+ (WebCore::CurlRequest::invokeDidReceiveResponse):
+ (WebCore::CurlRequest::setPaused):
+ (WebCore::CurlRequest::willSetupSslCtxCallback):
+ (WebCore::CurlRequest::willSendDataCallback):
+ (WebCore::CurlRequest::didReceiveHeaderCallback):
+ (WebCore::CurlRequest::didReceiveDataCallback):
+ * platform/network/curl/CurlRequest.h: Added.
+ (WebCore::CurlRequest::~CurlRequest):
+ (WebCore::CurlRequest::setDelegate):
+ (WebCore::CurlRequest::isSyncRequest):
+ (WebCore::CurlRequest::getNetworkLoadMetrics):
+ * platform/network/curl/CurlRequestDelegate.h: Added.
+ * platform/network/curl/ResourceHandleCurl.cpp:
+ (WebCore::ResourceHandle::cancel):
+ (WebCore::ResourceHandle::platformSetDefersLoading):
+ (WebCore::ResourceHandle::didReceiveAuthenticationChallenge):
+ (WebCore::ResourceHandle::receivedCredential):
+ (WebCore::ResourceHandle::receivedRequestToContinueWithoutCredential):
+ (WebCore::ResourceHandle::receivedCancellation):
+ * platform/network/curl/ResourceHandleCurlDelegate.cpp:
+ (WebCore::ResourceHandleCurlDelegate::ResourceHandleCurlDelegate):
+ (WebCore::ResourceHandleCurlDelegate::~ResourceHandleCurlDelegate):
+ (WebCore::ResourceHandleCurlDelegate::start):
+ (WebCore::ResourceHandleCurlDelegate::cancel):
+ (WebCore::ResourceHandleCurlDelegate::setDefersLoading):
+ (WebCore::ResourceHandleCurlDelegate::setAuthentication):
+ (WebCore::ResourceHandleCurlDelegate::dispatchSynchronousJob):
+ (WebCore::ResourceHandleCurlDelegate::createCurlRequest):
+ (WebCore::ResourceHandleCurlDelegate::cancelledOrClientless):
+ (WebCore::ResourceHandleCurlDelegate::curlDidReceiveResponse):
+ (WebCore::ResourceHandleCurlDelegate::curlDidReceiveBuffer):
+ (WebCore::ResourceHandleCurlDelegate::curlDidComplete):
+ (WebCore::ResourceHandleCurlDelegate::curlDidFailWithError):
+ (WebCore::ResourceHandleCurlDelegate::response):
+ (WebCore::ResourceHandleCurlDelegate::getCredential):
+ (WebCore::ResourceHandleCurlDelegate::retain): Deleted.
+ (WebCore::ResourceHandleCurlDelegate::release): Deleted.
+ (WebCore::ResourceHandleCurlDelegate::setupTransfer): Deleted.
+ (WebCore::ResourceHandleCurlDelegate::didCompleteTransfer): Deleted.
+ (WebCore::ResourceHandleCurlDelegate::didCancelTransfer): Deleted.
+ (WebCore::ResourceHandleCurlDelegate::setupAuthentication): Deleted.
+ (WebCore::ResourceHandleCurlDelegate::didReceiveAllHeaders): Deleted.
+ (WebCore::ResourceHandleCurlDelegate::didReceiveContentData): Deleted.
+ (WebCore::ResourceHandleCurlDelegate::handleLocalReceiveResponse): Deleted.
+ (WebCore::ResourceHandleCurlDelegate::prepareSendData): Deleted.
+ (WebCore::ResourceHandleCurlDelegate::didFinish): Deleted.
+ (WebCore::ResourceHandleCurlDelegate::didFail): Deleted.
+ (WebCore::ResourceHandleCurlDelegate::setupPOST): Deleted.
+ (WebCore::ResourceHandleCurlDelegate::setupPUT): Deleted.
+ (WebCore::ResourceHandleCurlDelegate::getFormElementsCount): Deleted.
+ (WebCore::ResourceHandleCurlDelegate::setupFormData): Deleted.
+ (WebCore::ResourceHandleCurlDelegate::applyAuthentication): Deleted.
+ (WebCore::ResourceHandleCurlDelegate::getNetworkLoadMetrics): Deleted.
+ (WebCore::ResourceHandleCurlDelegate::willSetupSslCtx): Deleted.
+ (WebCore::ResourceHandleCurlDelegate::didReceiveHeader): Deleted.
+ (WebCore::ResourceHandleCurlDelegate::didReceiveData): Deleted.
+ (WebCore::ResourceHandleCurlDelegate::willSendData): Deleted.
+ (WebCore::ResourceHandleCurlDelegate::willSetupSslCtxCallback): Deleted.
+ (WebCore::ResourceHandleCurlDelegate::didReceiveHeaderCallback): Deleted.
+ (WebCore::ResourceHandleCurlDelegate::didReceiveDataCallback): Deleted.
+ (WebCore::ResourceHandleCurlDelegate::willSendDataCallback): Deleted.
+ * platform/network/curl/ResourceHandleCurlDelegate.h:
+
2017-09-29 Chris Dumez <[email protected]>
Split some logic out of VisitedLinkStore and make it reusable
Modified: trunk/Source/WebCore/platform/Curl.cmake (222664 => 222665)
--- trunk/Source/WebCore/platform/Curl.cmake 2017-09-29 21:14:09 UTC (rev 222664)
+++ trunk/Source/WebCore/platform/Curl.cmake 2017-09-29 21:19:32 UTC (rev 222665)
@@ -11,6 +11,7 @@
platform/network/curl/CurlContext.cpp
platform/network/curl/CurlDownload.cpp
platform/network/curl/CurlJobManager.cpp
+ platform/network/curl/CurlRequest.cpp
platform/network/curl/CurlSSLHandle.cpp
platform/network/curl/CurlSSLVerifier.cpp
platform/network/curl/DNSCurl.cpp
Added: trunk/Source/WebCore/platform/network/curl/CurlRequest.cpp (0 => 222665)
--- trunk/Source/WebCore/platform/network/curl/CurlRequest.cpp (rev 0)
+++ trunk/Source/WebCore/platform/network/curl/CurlRequest.cpp 2017-09-29 21:19:32 UTC (rev 222665)
@@ -0,0 +1,537 @@
+/*
+ * Copyright (C) 2017 Sony Interactive Entertainment Inc.
+ *
+ * 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 "CurlRequest.h"
+
+#if USE(CURL)
+
+#include "CurlRequestDelegate.h"
+#include "MIMETypeRegistry.h"
+#include "ResourceError.h"
+#include "SharedBuffer.h"
+#include <wtf/MainThread.h>
+
+namespace WebCore {
+
+CurlRequest::CurlRequest(const ResourceRequest&request, CurlRequestDelegate* delegate, bool shouldSuspend)
+ : m_request(request.isolatedCopy())
+ , m_shouldSuspend(shouldSuspend)
+{
+ ASSERT(isMainThread());
+
+ setDelegate(delegate);
+ resolveBlobReferences(m_request);
+}
+
+void CurlRequest::setUserPass(const String& user, const String& password)
+{
+ ASSERT(isMainThread());
+
+ m_user = user.isolatedCopy();
+ m_password = user.isolatedCopy();
+}
+
+void CurlRequest::start(bool isSyncRequest)
+{
+ ASSERT(isMainThread());
+
+ m_isSyncRequest = isSyncRequest;
+
+ auto url = ""
+
+ if (!m_isSyncRequest) {
+ // For asynchronous, use CurlJobManager. Curl processes runs on sub thread.
+ if (url.isLocalFile())
+ invokeDidReceiveResponseForFile(url);
+
+ startWithJobManager();
+ } else {
+ // For synchronous, does not use CurlJobManager. Curl processes runs on main thread.
+ // curl_easy_perform blocks until the transfer is finished.
+ retain();
+ if (url.isLocalFile())
+ invokeDidReceiveResponseForFile(url);
+
+ setupTransfer();
+ CURLcode resultCode = m_curlHandle->perform();
+ didCompleteTransfer(resultCode);
+ release();
+ }
+}
+
+void CurlRequest::startWithJobManager()
+{
+ ASSERT(isMainThread());
+
+ CurlJobManager::singleton().add(this);
+}
+
+void CurlRequest::cancel()
+{
+ ASSERT(isMainThread());
+
+ if (m_cancelled)
+ return;
+
+ m_cancelled = true;
+
+ if (!m_isSyncRequest)
+ CurlJobManager::singleton().cancel(this);
+
+ setPaused(false);
+}
+
+void CurlRequest::suspend()
+{
+ ASSERT(isMainThread());
+
+ setPaused(true);
+}
+
+void CurlRequest::resume()
+{
+ ASSERT(isMainThread());
+
+ setPaused(false);
+}
+
+/* `this` is protected inside this method. */
+void CurlRequest::callDelegate(WTF::Function<void(CurlRequestDelegate*)> task)
+{
+ if (isMainThread()) {
+ if (CurlRequestDelegate* delegate = m_delegate)
+ task(delegate);
+ } else {
+ callOnMainThread([protectedThis = makeRef(*this), task = WTFMove(task)]() mutable {
+ if (CurlRequestDelegate* delegate = protectedThis->m_delegate)
+ task(delegate);
+ });
+ }
+}
+
+CURL* CurlRequest::setupTransfer()
+{
+ auto& sslHandle = CurlContext::singleton().sslHandle();
+
+ m_curlHandle = std::make_unique<CurlHandle>();
+
+ m_curlHandle->initialize();
+ m_curlHandle->setUrl(m_request.url());
+ m_curlHandle->appendRequestHeaders(m_request.httpHeaderFields());
+
+ auto method = m_request.httpMethod();
+ if (method == "GET")
+ m_curlHandle->enableHttpGetRequest();
+ else if (method == "POST")
+ setupPOST(m_request);
+ else if (method == "PUT")
+ setupPUT(m_request);
+ else if (method == "HEAD")
+ m_curlHandle->enableHttpHeadRequest();
+ else {
+ m_curlHandle->setHttpCustomRequest(method);
+ setupPUT(m_request);
+ }
+
+ if (!m_user.isEmpty() || !m_password.isEmpty()) {
+ m_curlHandle->enableHttpAuthentication(CURLAUTH_ANY);
+ m_curlHandle->setHttpAuthUserPass(m_user.latin1().data(), m_password.latin1().data());
+ }
+
+ m_curlHandle->setHeaderCallbackFunction(didReceiveHeaderCallback, this);
+ m_curlHandle->setWriteCallbackFunction(didReceiveDataCallback, this);
+
+ m_curlHandle->enableShareHandle();
+ m_curlHandle->enableAllowedProtocols();
+ m_curlHandle->enableAcceptEncoding();
+ m_curlHandle->enableTimeout();
+
+ m_curlHandle->enableAutoReferer();
+ m_curlHandle->enableFollowLocation();
+ m_curlHandle->enableProxyIfExists();
+ m_curlHandle->enableCookieJarIfExists();
+
+ m_curlHandle->setSslVerifyPeer(CurlHandle::VerifyPeer::Enable);
+ m_curlHandle->setSslVerifyHost(CurlHandle::VerifyHost::StrictNameCheck);
+
+ auto sslClientCertificate = sslHandle.getSSLClientCertificate(m_request.url().host());
+ if (sslClientCertificate) {
+ m_curlHandle->setSslCert(sslClientCertificate->first.utf8().data());
+ m_curlHandle->setSslCertType("P12");
+ m_curlHandle->setSslKeyPassword(sslClientCertificate->second.utf8().data());
+ }
+
+ if (sslHandle.shouldIgnoreSSLErrors())
+ m_curlHandle->setSslVerifyPeer(CurlHandle::VerifyPeer::Disable);
+ else
+ m_curlHandle->setSslCtxCallbackFunction(willSetupSslCtxCallback, this);
+
+ m_curlHandle->setCACertPath(sslHandle.getCACertPath());
+
+ if (m_shouldSuspend)
+ suspend();
+
+#ifndef NDEBUG
+ m_curlHandle->enableVerboseIfUsed();
+ m_curlHandle->enableStdErrIfUsed();
+#endif
+
+ return m_curlHandle->handle();
+}
+
+CURLcode CurlRequest::willSetupSslCtx(void* sslCtx)
+{
+ m_sslVerifier.setCurlHandle(m_curlHandle.get());
+ m_sslVerifier.setHostName(m_request.url().host());
+ m_sslVerifier.setSslCtx(sslCtx);
+
+ return CURLE_OK;
+}
+
+// This is called to obtain HTTP POST or PUT data.
+// Iterate through FormData elements and upload files.
+// Carefully respect the given buffer size and fill the rest of the data at the next calls.
+
+size_t CurlRequest::willSendData(char* ptr, size_t blockSize, size_t numberOfBlocks)
+{
+ if (m_cancelled)
+ return CURL_READFUNC_ABORT;
+
+ if (!blockSize || !numberOfBlocks)
+ return 0;
+
+ if (!m_formDataStream || !m_formDataStream->hasMoreElements())
+ return 0;
+
+ auto sendBytes = m_formDataStream->read(ptr, blockSize, numberOfBlocks);
+ if (!sendBytes) {
+ // Something went wrong so error the job.
+ return CURL_READFUNC_ABORT;
+ }
+
+ return sendBytes;
+}
+
+// This is being called for each HTTP header in the response. This includes '\r\n'
+// for the last line of the header.
+
+size_t CurlRequest::didReceiveHeader(String&& header)
+{
+ static const auto emptyLineCRLF = "\r\n";
+ static const auto emptyLineLF = "\n";
+
+ if (m_cancelled)
+ return 0;
+
+ auto receiveBytes = static_cast<size_t>(header.length());
+
+ // The HTTP standard requires to use \r\n but for compatibility it recommends to accept also \n.
+ if ((header != emptyLineCRLF) && (header != emptyLineLF)) {
+ m_response.headers.append(WTFMove(header));
+ return receiveBytes;
+ }
+
+ long statusCode = 0;
+ if (auto code = m_curlHandle->getResponseCode())
+ statusCode = *code;
+
+ long httpConnectCode = 0;
+ if (auto code = m_curlHandle->getHttpConnectCode())
+ httpConnectCode = *code;
+
+ if ((100 <= statusCode) && (statusCode < 200)) {
+ // Just return when receiving http info, e.g. HTTP/1.1 100 Continue.
+ // If not, the request might be cancelled, because the MIME type will be empty for this response.
+ m_response = CurlResponse { };
+ return receiveBytes;
+ }
+
+ if (!statusCode && (httpConnectCode == 200)) {
+ // Comes here when receiving 200 Connection Established. Just return.
+ m_response = CurlResponse { };
+ return receiveBytes;
+ }
+
+ // If the FOLLOWLOCATION option is enabled for the curl handle then
+ // curl will follow the redirections internally. Thus this header callback
+ // will be called more than one time with the line starting "HTTP" for one job.
+
+ m_response.url = ""
+ m_response.statusCode = statusCode;
+
+ if (auto length = m_curlHandle->getContentLength())
+ m_response.expectedContentLength = *length;
+
+ if (auto port = m_curlHandle->getPrimaryPort())
+ m_response.connectPort = *port;
+
+ if (auto auth = m_curlHandle->getHttpAuthAvail())
+ m_response.availableHttpAuth = *auth;
+
+ if (auto metrics = m_curlHandle->getNetworkLoadMetrics())
+ m_networkLoadMetrics = *metrics;
+
+ invokeDidReceiveResponse();
+
+ return receiveBytes;
+}
+
+// called with data after all headers have been processed via headerCallback
+
+size_t CurlRequest::didReceiveData(Ref<SharedBuffer>&& buffer)
+{
+ if (m_cancelled)
+ return 0;
+
+ auto receiveBytes = buffer->size();
+
+ // this shouldn't be necessary but apparently is. CURL writes the data
+ // of html page even if it is a redirect that was handled internally
+ // can be observed e.g. on gmail.com
+ auto statusCode = m_curlHandle->getResponseCode();
+ if (statusCode && (300 <= *statusCode) && (*statusCode < 400))
+ return receiveBytes;
+
+ if (receiveBytes) {
+ callDelegate([this, buffer = WTFMove(buffer)](CurlRequestDelegate* delegate) mutable {
+ if (delegate)
+ delegate->curlDidReceiveBuffer(WTFMove(buffer));
+ });
+ }
+
+ return receiveBytes;
+}
+
+void CurlRequest::didCompleteTransfer(CURLcode result)
+{
+ if (m_cancelled) {
+ m_curlHandle = nullptr;
+ return;
+ }
+
+ if (result == CURLE_OK) {
+ if (auto metrics = m_curlHandle->getNetworkLoadMetrics())
+ m_networkLoadMetrics = *metrics;
+
+ callDelegate([this](CurlRequestDelegate* delegate) {
+ if (delegate)
+ delegate->curlDidComplete();
+ });
+ } else {
+ auto resourceError = ResourceError::httpError(result, m_request.url());
+ if (m_sslVerifier.sslErrors())
+ resourceError.setSslErrors(m_sslVerifier.sslErrors());
+
+ callDelegate([this, error = resourceError.isolatedCopy()](CurlRequestDelegate* delegate) {
+ if (delegate)
+ delegate->curlDidFailWithError(error);
+ });
+ }
+
+ m_formDataStream = nullptr;
+ m_curlHandle = nullptr;
+}
+
+void CurlRequest::didCancelTransfer()
+{
+ m_formDataStream = nullptr;
+ m_curlHandle = nullptr;
+}
+
+void CurlRequest::resolveBlobReferences(ResourceRequest& request)
+{
+ ASSERT(isMainThread());
+
+ RefPtr<FormData> formData = request.httpBody();
+ if (!formData)
+ return;
+
+ // Resolve the blob elements so the formData can correctly report it's size.
+ formData = formData->resolveBlobReferences();
+ request.setHTTPBody(WTFMove(formData));
+}
+
+void CurlRequest::setupPUT(ResourceRequest& request)
+{
+ m_curlHandle->enableHttpPutRequest();
+
+ // Disable the Expect: 100 continue header
+ m_curlHandle->removeRequestHeader("Expect");
+
+ auto numElements = request.httpBody()->elements().size();
+ if (!numElements)
+ return;
+
+ setupFormData(request, false);
+}
+
+void CurlRequest::setupPOST(ResourceRequest& request)
+{
+ m_curlHandle->enableHttpPostRequest();
+
+ auto numElements = request.httpBody()->elements().size();
+ if (!numElements)
+ return;
+
+ // Do not stream for simple POST data
+ if (numElements == 1) {
+ m_postBuffer = request.httpBody()->flatten();
+ if (m_postBuffer.size())
+ m_curlHandle->setPostFields(m_postBuffer.data(), m_postBuffer.size());
+ } else
+ setupFormData(request, true);
+}
+
+void CurlRequest::setupFormData(ResourceRequest& request, bool isPostRequest)
+{
+ static auto maxCurlOffT = CurlHandle::maxCurlOffT();
+
+ // Obtain the total size of the form data
+ curl_off_t size = 0;
+ bool chunkedTransfer = false;
+ auto elements = request.httpBody()->elements();
+
+ for (auto element : elements) {
+ if (element.m_type == FormDataElement::Type::EncodedFile) {
+ long long fileSizeResult;
+ if (getFileSize(element.m_filename, fileSizeResult)) {
+ if (fileSizeResult > maxCurlOffT) {
+ // File size is too big for specifying it to cURL
+ chunkedTransfer = true;
+ break;
+ }
+ size += fileSizeResult;
+ } else {
+ chunkedTransfer = true;
+ break;
+ }
+ } else
+ size += element.m_data.size();
+ }
+
+ // cURL guesses that we want chunked encoding as long as we specify the header
+ if (chunkedTransfer)
+ m_curlHandle->appendRequestHeader("Transfer-Encoding: chunked");
+ else {
+ if (isPostRequest)
+ m_curlHandle->setPostFieldLarge(size);
+ else
+ m_curlHandle->setInFileSizeLarge(size);
+ }
+
+ m_formDataStream = std::make_unique<FormDataStream>();
+ m_formDataStream->setHTTPBody(request.httpBody());
+
+ m_curlHandle->setReadCallbackFunction(willSendDataCallback, this);
+}
+
+void CurlRequest::invokeDidReceiveResponseForFile(URL& url)
+{
+ // Since the code in didReceiveHeader() will not have run for local files
+ // the code to set the URL and fire didReceiveResponse is never run,
+ // which means the ResourceLoader's response does not contain the URL.
+ // Run the code here for local files to resolve the issue.
+
+ ASSERT(isMainThread());
+ ASSERT(url.isLocalFile());
+
+ m_response.url = ""
+ m_response.statusCode = 200;
+
+ // Determine the MIME type based on the path.
+ m_response.headers.append(String("Content-Type: " + MIMETypeRegistry::getMIMETypeForPath(m_response.url)));
+
+ if (!m_isSyncRequest) {
+ // DidReceiveResponse must not be called immediately
+ CurlJobManager::singleton().callOnJobThread([protectedThis = makeRef(*this)]() {
+ protectedThis->invokeDidReceiveResponse();
+ });
+ } else
+ invokeDidReceiveResponse();
+}
+
+void CurlRequest::invokeDidReceiveResponse()
+{
+ callDelegate([this, response = m_response.isolatedCopy()](CurlRequestDelegate* delegate) {
+ if (delegate)
+ delegate->curlDidReceiveResponse(response);
+ });
+}
+
+void CurlRequest::setPaused(bool paused)
+{
+ if (m_cancelled)
+ return;
+
+ if (paused == m_isPaused)
+ return;
+
+ m_isPaused = paused;
+
+ if (!m_curlHandle)
+ return;
+
+ if (!m_isSyncRequest && isMainThread()) {
+ CurlJobManager::singleton().callOnJobThread([protectedThis = makeRef(*this), paused = m_isPaused]() {
+ if (protectedThis->m_cancelled)
+ return;
+
+ auto error = protectedThis->m_curlHandle->pause(paused ? CURLPAUSE_ALL : CURLPAUSE_CONT);
+ if ((error != CURLE_OK) && !paused) {
+ // Restarting the handle has failed so just cancel it.
+ protectedThis->cancel();
+ }
+ });
+ } else {
+ auto error = m_curlHandle->pause(m_isPaused ? CURLPAUSE_ALL : CURLPAUSE_CONT);
+ if ((error != CURLE_OK) && !m_isPaused)
+ cancel();
+ }
+}
+
+CURLcode CurlRequest::willSetupSslCtxCallback(CURL*, void* sslCtx, void* userData)
+{
+ return static_cast<CurlRequest*>(userData)->willSetupSslCtx(sslCtx);
+}
+
+size_t CurlRequest::willSendDataCallback(char* ptr, size_t blockSize, size_t numberOfBlocks, void* userData)
+{
+ return static_cast<CurlRequest*>(userData)->willSendData(ptr, blockSize, numberOfBlocks);
+}
+
+size_t CurlRequest::didReceiveHeaderCallback(char* ptr, size_t blockSize, size_t numberOfBlocks, void* userData)
+{
+ return static_cast<CurlRequest*>(userData)->didReceiveHeader(String(ptr, blockSize * numberOfBlocks));
+}
+
+size_t CurlRequest::didReceiveDataCallback(char* ptr, size_t blockSize, size_t numberOfBlocks, void* userData)
+{
+ return static_cast<CurlRequest*>(userData)->didReceiveData(SharedBuffer::create(ptr, blockSize * numberOfBlocks));
+}
+
+}
+
+#endif
Added: trunk/Source/WebCore/platform/network/curl/CurlRequest.h (0 => 222665)
--- trunk/Source/WebCore/platform/network/curl/CurlRequest.h (rev 0)
+++ trunk/Source/WebCore/platform/network/curl/CurlRequest.h 2017-09-29 21:19:32 UTC (rev 222665)
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2017 Sony Interactive Entertainment Inc.
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include "CurlJobManager.h"
+#include "CurlResponse.h"
+#include "CurlSSLVerifier.h"
+#include "FormDataStreamCurl.h"
+#include "NetworkLoadMetrics.h"
+#include "ResourceRequest.h"
+#include <wtf/Noncopyable.h>
+
+namespace WebCore {
+
+class CurlRequestDelegate;
+class ResourceError;
+class SharedBuffer;
+
+class CurlRequest : public ThreadSafeRefCounted<CurlRequest>, public CurlJobClient {
+ WTF_MAKE_NONCOPYABLE(CurlRequest);
+
+public:
+ CurlRequest(const ResourceRequest&, CurlRequestDelegate* = nullptr, bool shouldSuspend = false);
+ virtual ~CurlRequest() { }
+
+ void setDelegate(CurlRequestDelegate* delegate) { m_delegate = delegate; }
+ void setUserPass(const String&, const String&);
+
+ void start(bool isSyncRequest = false);
+ void cancel();
+ void suspend();
+ void resume();
+
+ bool isSyncRequest() { return m_isSyncRequest; }
+
+ NetworkLoadMetrics getNetworkLoadMetrics() { return m_networkLoadMetrics.isolatedCopy(); }
+
+private:
+ void retain() override { ref(); }
+ void release() override { deref(); }
+ CURL* handle() override { return m_curlHandle ? m_curlHandle->handle() : nullptr; }
+
+ void startWithJobManager();
+
+ void callDelegate(WTF::Function<void(CurlRequestDelegate*)>);
+
+ // Transfer processing of Request body, Response header/body
+ // Called by worker thread in case of async, main thread in case of sync.
+ CURL* setupTransfer() override;
+ CURLcode willSetupSslCtx(void*);
+ size_t willSendData(char*, size_t, size_t);
+ size_t didReceiveHeader(String&&);
+ size_t didReceiveData(Ref<SharedBuffer>&&);
+ void didCompleteTransfer(CURLcode) override;
+ void didCancelTransfer() override;
+
+ // For POST and PUT method
+ void resolveBlobReferences(ResourceRequest&);
+ void setupPOST(ResourceRequest&);
+ void setupPUT(ResourceRequest&);
+ void setupFormData(ResourceRequest&, bool);
+
+ // Processing for DidResourceResponse
+ void invokeDidReceiveResponseForFile(URL&);
+ void invokeDidReceiveResponse();
+ void setPaused(bool);
+
+ // Callback functions for curl
+ static CURLcode willSetupSslCtxCallback(CURL*, void*, void*);
+ static size_t willSendDataCallback(char*, size_t, size_t, void*);
+ static size_t didReceiveHeaderCallback(char*, size_t, size_t, void*);
+ static size_t didReceiveDataCallback(char*, size_t, size_t, void*);
+
+
+ std::atomic<CurlRequestDelegate*> m_delegate { };
+ bool m_isSyncRequest { false };
+ bool m_cancelled { false };
+
+ // Used by worker thread in case of async, and main thread in case of sync.
+ ResourceRequest m_request;
+ String m_user;
+ String m_password;
+ bool m_shouldSuspend { false };
+
+ std::unique_ptr<CurlHandle> m_curlHandle;
+ std::unique_ptr<FormDataStream> m_formDataStream;
+ Vector<char> m_postBuffer;
+ CurlSSLVerifier m_sslVerifier;
+ CurlResponse m_response;
+
+ bool m_isPaused { false };
+
+ NetworkLoadMetrics m_networkLoadMetrics;
+};
+
+}
Added: trunk/Source/WebCore/platform/network/curl/CurlRequestDelegate.h (0 => 222665)
--- trunk/Source/WebCore/platform/network/curl/CurlRequestDelegate.h (rev 0)
+++ trunk/Source/WebCore/platform/network/curl/CurlRequestDelegate.h 2017-09-29 21:19:32 UTC (rev 222665)
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2017 Sony Interactive Entertainment Inc.
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include <wtf/Ref.h>
+
+namespace WebCore {
+
+class CurlResponse;
+class ResourceError;
+class SharedBuffer;
+
+class CurlRequestDelegate {
+public:
+ virtual void curlDidReceiveResponse(const CurlResponse&) = 0;
+ virtual void curlDidReceiveBuffer(Ref<SharedBuffer>&&) = 0;
+ virtual void curlDidComplete() = 0;
+ virtual void curlDidFailWithError(const ResourceError&) = 0;
+};
+
+}
Modified: trunk/Source/WebCore/platform/network/curl/ResourceHandleCurl.cpp (222664 => 222665)
--- trunk/Source/WebCore/platform/network/curl/ResourceHandleCurl.cpp 2017-09-29 21:14:09 UTC (rev 222664)
+++ trunk/Source/WebCore/platform/network/curl/ResourceHandleCurl.cpp 2017-09-29 21:19:32 UTC (rev 222665)
@@ -32,19 +32,12 @@
#if USE(CURL)
-#include "CachedResourceLoader.h"
#include "CredentialStorage.h"
-#include "CurlCacheManager.h"
#include "CurlContext.h"
-#include "CurlJobManager.h"
-#include "CurlSSLHandle.h"
#include "FileSystem.h"
#include "Logging.h"
-#include "MIMETypeRegistry.h"
-#include "NetworkingContext.h"
#include "ResourceHandleInternal.h"
#include "SynchronousLoaderClient.h"
-#include <wtf/text/Base64.h>
namespace WebCore {
@@ -78,7 +71,8 @@
void ResourceHandle::cancel()
{
- d->m_delegate->cancel();
+ if (d->m_delegate)
+ d->m_delegate->cancel();
}
#if OS(WINDOWS)
@@ -114,7 +108,8 @@
{
ASSERT(isMainThread());
- d->m_delegate->setDefersLoading(defers);
+ if (d->m_delegate)
+ d->m_delegate->setDefersLoading(defers);
}
bool ResourceHandle::shouldUseCredentialStorage()
@@ -136,7 +131,8 @@
urlToStore = challenge.failureResponse().url();
CredentialStorage::defaultCredentialStorage().set(partition, credential, challenge.protectionSpace(), urlToStore);
- d->m_delegate->setAuthentication(credential.user(), credential.password());
+ if (d->m_delegate)
+ d->m_delegate->setAuthentication(credential.user(), credential.password());
d->m_user = String();
d->m_pass = String();
@@ -161,7 +157,8 @@
CredentialStorage::defaultCredentialStorage().set(partition, credential, challenge.protectionSpace(), challenge.failureResponse().url());
}
- d->m_delegate->setAuthentication(credential.user(), credential.password());
+ if (d->m_delegate)
+ d->m_delegate->setAuthentication(credential.user(), credential.password());
return;
}
}
@@ -168,9 +165,11 @@
}
d->m_currentWebChallenge = challenge;
-
- if (client())
+
+ if (client()) {
+ auto protectedThis = makeRef(*this);
client()->didReceiveAuthenticationChallenge(this, d->m_currentWebChallenge);
+ }
}
void ResourceHandle::receivedCredential(const AuthenticationChallenge& challenge, const Credential& credential)
@@ -194,7 +193,9 @@
}
}
- d->m_delegate->setAuthentication(credential.user(), credential.password());
+ if (d->m_delegate)
+ d->m_delegate->setAuthentication(credential.user(), credential.password());
+
clearAuthentication();
}
@@ -205,7 +206,9 @@
if (challenge != d->m_currentWebChallenge)
return;
- d->m_delegate->setAuthentication("", "");
+ if (d->m_delegate)
+ d->m_delegate->setAuthentication("", "");
+
clearAuthentication();
}
@@ -216,8 +219,10 @@
if (challenge != d->m_currentWebChallenge)
return;
- if (client())
+ if (client()) {
+ auto protectedThis = makeRef(*this);
client()->receivedCancellation(this, challenge);
+ }
}
void ResourceHandle::receivedRequestToPerformDefaultHandling(const AuthenticationChallenge&)
Modified: trunk/Source/WebCore/platform/network/curl/ResourceHandleCurlDelegate.cpp (222664 => 222665)
--- trunk/Source/WebCore/platform/network/curl/ResourceHandleCurlDelegate.cpp 2017-09-29 21:14:09 UTC (rev 222664)
+++ trunk/Source/WebCore/platform/network/curl/ResourceHandleCurlDelegate.cpp 2017-09-29 21:19:32 UTC (rev 222665)
@@ -32,17 +32,15 @@
#if USE(CURL)
+#include "AuthenticationChallenge.h"
#include "CredentialStorage.h"
#include "CurlCacheManager.h"
+#include "CurlRequest.h"
#include "HTTPParsers.h"
-#include "MIMETypeRegistry.h"
#include "MultipartHandle.h"
-#include "ResourceHandle.h"
#include "ResourceHandleInternal.h"
#include "SharedBuffer.h"
#include "TextEncoding.h"
-#include "URL.h"
-#include <wtf/MainThread.h>
#include <wtf/text/Base64.h>
namespace WebCore {
@@ -50,7 +48,7 @@
ResourceHandleCurlDelegate::ResourceHandleCurlDelegate(ResourceHandle* handle)
: m_handle(handle)
, m_firstRequest(handle->firstRequest().isolatedCopy())
- , m_customHTTPHeaderFields(m_firstRequest.httpHeaderFields().isolatedCopy())
+ , m_currentRequest(handle->firstRequest().isolatedCopy())
, m_shouldUseCredentialStorage(handle->shouldUseCredentialStorage())
, m_user(handle->getInternal()->m_user.isolatedCopy())
, m_pass(handle->getInternal()->m_pass.isolatedCopy())
@@ -57,25 +55,13 @@
, m_initialCredential(handle->getInternal()->m_initialCredential)
, m_defersLoading(handle->getInternal()->m_defersLoading)
{
- const URL& url = ""
- if (m_customHTTPHeaderFields.size()) {
- auto& cache = CurlCacheManager::getInstance();
- bool hasCacheHeaders = m_customHTTPHeaderFields.contains(HTTPHeaderName::IfModifiedSince) || m_customHTTPHeaderFields.contains(HTTPHeaderName::IfNoneMatch);
- if (!hasCacheHeaders && cache.isCached(url)) {
- cache.addCacheEntryClient(url, m_handle);
- // append additional cache information
- for (auto entry : cache.requestHeaders(url))
- m_customHTTPHeaderFields.set(entry.key, entry.value);
- m_addedCacheValidationHeaders = true;
- }
- }
-
- setupAuthentication();
}
ResourceHandleCurlDelegate::~ResourceHandleCurlDelegate()
{
+ if (m_curlRequest)
+ m_curlRequest->setDelegate(nullptr);
}
bool ResourceHandleCurlDelegate::hasHandle() const
@@ -88,76 +74,66 @@
m_handle = nullptr;
}
-void ResourceHandleCurlDelegate::start(bool isSyncRequest)
+bool ResourceHandleCurlDelegate::start()
{
- m_isSyncRequest = isSyncRequest;
+ ASSERT(isMainThread());
- if (!m_isSyncRequest) {
- // For asynchronous, use CurlJobManager. Curl processes runs on sub thread.
- CurlJobManager::singleton().add(this);
- } else {
- // For synchronous, does not use CurlJobManager. Curl processes runs on main thread.
- retain();
- setupTransfer();
+ auto credential = getCredential(m_currentRequest, false);
- // curl_easy_perform blocks until the transfer is finished.
- CURLcode resultCode = m_curlHandle->perform();
- didCompleteTransfer(resultCode);
- release();
- }
+ m_curlRequest = createCurlRequest(m_currentRequest);
+ m_curlRequest->setUserPass(credential.first, credential.second);
+ m_curlRequest->start();
+
+ return true;
}
void ResourceHandleCurlDelegate::cancel()
{
+ ASSERT(isMainThread());
+
releaseHandle();
- if (!m_isSyncRequest)
- CurlJobManager::singleton().cancel(this);
+ if (!m_curlRequest)
+ m_curlRequest->cancel();
}
void ResourceHandleCurlDelegate::setDefersLoading(bool defers)
{
+ ASSERT(isMainThread());
+
if (defers == m_defersLoading)
return;
m_defersLoading = defers;
- auto action = "" = makeRef(*this)]() {
- if (!protectedThis->m_curlHandle)
- return;
+ if (!m_curlRequest)
+ return;
- if (protectedThis->m_defersLoading) {
- CURLcode error = protectedThis->m_curlHandle->pause(CURLPAUSE_ALL);
- // If we could not defer the handle, so don't do it.
- if (error != CURLE_OK)
- return;
- } else {
- CURLcode error = protectedThis->m_curlHandle->pause(CURLPAUSE_CONT);
- if (error != CURLE_OK) {
- // Restarting the handle has failed so just cancel it.
- protectedThis->m_handle->cancel();
- }
- }
- };
-
- CurlJobManager::singleton().callOnJobThread(WTFMove(action));
+ if (m_defersLoading)
+ m_curlRequest->suspend();
+ else
+ m_curlRequest->resume();
}
-void ResourceHandleCurlDelegate::setAuthentication(const String& user, const String& pass)
+void ResourceHandleCurlDelegate::setAuthentication(const String& user, const String& password)
{
- auto action = "" = makeRef(*this), user = user.isolatedCopy(), pass = pass.isolatedCopy()]() {
- protectedThis->m_user = user;
- protectedThis->m_pass = pass;
- if (protectedThis->m_curlHandle)
- protectedThis->m_curlHandle->setHttpAuthUserPass(user, pass);
- };
+ ASSERT(isMainThread());
- CurlJobManager::singleton().callOnJobThread(WTFMove(action));
+ if (!m_curlRequest)
+ return;
+
+ bool isSyncRequest = m_curlRequest->isSyncRequest();
+ m_curlRequest->cancel();
+ m_curlRequest->setDelegate(nullptr);
+
+ m_curlRequest = createCurlRequest(m_currentRequest);
+ m_curlRequest->setUserPass(user, password);
+ m_curlRequest->start(isSyncRequest);
}
void ResourceHandleCurlDelegate::dispatchSynchronousJob()
{
- if (m_firstRequest.url().protocolIsData()) {
+ if (m_currentRequest.url().protocolIsData()) {
handleDataURL();
return;
}
@@ -167,162 +143,57 @@
// and we would assert so force defersLoading to be false.
m_defersLoading = false;
- start(true);
+ m_curlRequest = createCurlRequest(m_currentRequest);
+ m_curlRequest->start(true);
}
-void ResourceHandleCurlDelegate::retain()
+RefPtr<CurlRequest> ResourceHandleCurlDelegate::createCurlRequest(ResourceRequest& request)
{
- ref();
-}
+ ASSERT(isMainThread());
-void ResourceHandleCurlDelegate::release()
-{
- deref();
-}
+ // CurlCache : append additional cache information
+ m_addedCacheValidationHeaders = false;
-CURL* ResourceHandleCurlDelegate::setupTransfer()
-{
- m_curlHandle = std::make_unique<CurlHandle>();
- m_curlHandle->initialize();
+ bool hasCacheHeaders = request.httpHeaderFields().contains(HTTPHeaderName::IfModifiedSince) || request.httpHeaderFields().contains(HTTPHeaderName::IfNoneMatch);
+ if (!hasCacheHeaders) {
+ auto& cache = CurlCacheManager::getInstance();
+ URL cacheUrl = request.url();
+ cacheUrl.removeFragmentIdentifier();
- if (m_defersLoading) {
- CURLcode error = m_curlHandle->pause(CURLPAUSE_ALL);
- // If we did not pause the handle, we would ASSERT in the
- // header callback. So just assert here.
- ASSERT_UNUSED(error, error == CURLE_OK);
- }
+ if (cache.isCached(cacheUrl)) {
+ cache.addCacheEntryClient(cacheUrl, m_handle);
-#ifndef NDEBUG
- m_curlHandle->enableVerboseIfUsed();
- m_curlHandle->enableStdErrIfUsed();
-#endif
+ for (auto entry : cache.requestHeaders(cacheUrl))
+ request.addHTTPHeaderField(entry.key, entry.value);
- auto& sslHandle = CurlContext::singleton().sslHandle();
-
- m_curlHandle->setSslVerifyPeer(CurlHandle::VerifyPeer::Enable);
- m_curlHandle->setSslVerifyHost(CurlHandle::VerifyHost::StrictNameCheck);
- m_curlHandle->setWriteCallbackFunction(didReceiveDataCallback, this);
- m_curlHandle->setHeaderCallbackFunction(didReceiveHeaderCallback, this);
- m_curlHandle->enableAutoReferer();
- m_curlHandle->enableFollowLocation();
- m_curlHandle->enableHttpAuthentication(CURLAUTH_ANY);
- m_curlHandle->enableShareHandle();
- m_curlHandle->enableTimeout();
- m_curlHandle->enableAllowedProtocols();
-
- auto sslClientCertificate = sslHandle.getSSLClientCertificate(m_firstRequest.url().host());
- if (sslClientCertificate) {
- m_curlHandle->setSslCert(sslClientCertificate->first.utf8().data());
- m_curlHandle->setSslCertType("P12");
- m_curlHandle->setSslKeyPassword(sslClientCertificate->second.utf8().data());
- }
-
- if (sslHandle.shouldIgnoreSSLErrors())
- m_curlHandle->setSslVerifyPeer(CurlHandle::VerifyPeer::Disable);
- else
- m_curlHandle->setSslCtxCallbackFunction(willSetupSslCtxCallback, this);
-
- m_curlHandle->setCACertPath(sslHandle.getCACertPath());
-
- m_curlHandle->enableAcceptEncoding();
- m_curlHandle->setUrl(m_firstRequest.url());
- m_curlHandle->enableCookieJarIfExists();
-
- if (m_customHTTPHeaderFields.size())
- m_curlHandle->appendRequestHeaders(m_customHTTPHeaderFields);
-
- String method = m_firstRequest.httpMethod();
- if ("GET" == method)
- m_curlHandle->enableHttpGetRequest();
- else if ("POST" == method)
- setupPOST();
- else if ("PUT" == method)
- setupPUT();
- else if ("HEAD" == method)
- m_curlHandle->enableHttpHeadRequest();
- else {
- m_curlHandle->setHttpCustomRequest(method);
- setupPUT();
- }
-
- applyAuthentication();
-
- m_curlHandle->enableProxyIfExists();
-
- return m_curlHandle->handle();
-}
-
-void ResourceHandleCurlDelegate::didCompleteTransfer(CURLcode result)
-{
- if (result == CURLE_OK) {
- NetworkLoadMetrics networkLoadMetrics = getNetworkLoadMetrics();
-
- if (isMainThread())
- didFinish(networkLoadMetrics);
- else {
- callOnMainThread([protectedThis = makeRef(*this), metrics = networkLoadMetrics.isolatedCopy()] {
- if (!protectedThis->m_handle)
- return;
- protectedThis->didFinish(metrics);
- });
+ m_addedCacheValidationHeaders = true;
}
- } else {
- ResourceError resourceError = ResourceError::httpError(result, m_firstRequest.url());
- if (m_sslVerifier.sslErrors())
- resourceError.setSslErrors(m_sslVerifier.sslErrors());
-
- if (isMainThread())
- didFail(resourceError);
- else {
- callOnMainThread([protectedThis = makeRef(*this), error = resourceError.isolatedCopy()] {
- if (!protectedThis->m_handle)
- return;
- protectedThis->didFail(error);
- });
- }
}
- m_formDataStream = nullptr;
- m_curlHandle = nullptr;
+ return adoptRef(new CurlRequest(request, this, m_defersLoading));
}
-void ResourceHandleCurlDelegate::didCancelTransfer()
+bool ResourceHandleCurlDelegate::cancelledOrClientless()
{
- m_formDataStream = nullptr;
- m_curlHandle = nullptr;
-}
+ if (!m_handle)
+ return true;
-ResourceResponse& ResourceHandleCurlDelegate::response()
-{
- return m_handle->getInternal()->m_response;
+ return !m_handle->client();
}
-void ResourceHandleCurlDelegate::setupAuthentication()
+void ResourceHandleCurlDelegate::curlDidReceiveResponse(const CurlResponse& receivedResponse)
{
- // m_user/m_pass are credentials given manually, for instance, by the arguments passed to XMLHttpRequest.open().
- String partition = m_firstRequest.cachePartition();
+ ASSERT(isMainThread());
+ ASSERT(!m_defersLoading);
- if (m_shouldUseCredentialStorage) {
- if (m_user.isEmpty() && m_pass.isEmpty()) {
- // <rdar://problem/7174050> - For URLs that match the paths of those previously challenged for HTTP Basic authentication,
- // try and reuse the credential preemptively, as allowed by RFC 2617.
- m_initialCredential = CredentialStorage::defaultCredentialStorage().get(partition, m_firstRequest.url());
- } else {
- // If there is already a protection space known for the URL, update stored credentials
- // before sending a request. This makes it possible to implement logout by sending an
- // XMLHttpRequest with known incorrect credentials, and aborting it immediately (so that
- // an authentication dialog doesn't pop up).
- CredentialStorage::defaultCredentialStorage().set(partition, Credential(m_user, m_pass, CredentialPersistenceNone), m_firstRequest.url());
- }
- }
-}
+ if (cancelledOrClientless())
+ return;
-void ResourceHandleCurlDelegate::didReceiveAllHeaders(const CurlResponse& receivedResponse)
-{
- ASSERT(isMainThread());
-
m_handle->getInternal()->m_response = ResourceResponse(receivedResponse);
+ if (m_curlRequest)
+ m_handle->getInternal()->m_response.setDeprecatedNetworkLoadMetrics(m_curlRequest->getNetworkLoadMetrics());
+
if (response().isMultipart()) {
String boundary;
bool parsed = MultipartHandle::extractBoundary(response().httpHeaderField(HTTPHeaderName::ContentType), boundary);
@@ -346,7 +217,9 @@
return;
}
- } else if (response().isUnauthorized()) {
+ }
+
+ if (response().isUnauthorized()) {
AuthenticationChallenge challenge(receivedResponse, m_authFailureCount, response(), m_handle);
m_handle->didReceiveAuthenticationChallenge(challenge);
m_authFailureCount++;
@@ -353,12 +226,12 @@
return;
}
- m_didNotifyResponse = true;
-
if (m_handle->client()) {
if (response().isNotModified()) {
- const String& url = ""
- if (CurlCacheManager::getInstance().getCachedResponse(url, response())) {
+ URL cacheUrl = m_currentRequest.url();
+ cacheUrl.removeFragmentIdentifier();
+
+ if (CurlCacheManager::getInstance().getCachedResponse(cacheUrl, response())) {
if (m_addedCacheValidationHeaders) {
response().setHTTPStatusCode(200);
response().setHTTPStatusText("OK");
@@ -365,17 +238,18 @@
}
}
}
+
CurlCacheManager::getInstance().didReceiveResponse(*m_handle, response());
m_handle->client()->didReceiveResponse(m_handle, ResourceResponse(response()));
}
}
-void ResourceHandleCurlDelegate::didReceiveContentData(Ref<SharedBuffer>&& buffer)
+void ResourceHandleCurlDelegate::curlDidReceiveBuffer(Ref<SharedBuffer>&& buffer)
{
ASSERT(isMainThread());
- if (!m_didNotifyResponse)
- handleLocalReceiveResponse();
+ if (cancelledOrClientless())
+ return;
if (m_multipartHandle)
m_multipartHandle->contentReceived(buffer->data(), buffer->size());
@@ -385,65 +259,16 @@
}
}
-void ResourceHandleCurlDelegate::handleLocalReceiveResponse()
+void ResourceHandleCurlDelegate::curlDidComplete()
{
ASSERT(isMainThread());
- // since the code in headerCallback will not have run for local files
- // the code to set the URL and fire didReceiveResponse is never run,
- // which means the ResourceLoader's response does not contain the URL.
- // Run the code here for local files to resolve the issue.
- // TODO: See if there is a better approach for handling this.
- URL url = ""
- ASSERT(url.isValid());
- response().setURL(url);
-
- // Determine the MIME type based on the path.
- response().setMimeType(MIMETypeRegistry::getMIMETypeForPath(url));
-
- m_didNotifyResponse = true;
-
- if (m_handle->client())
- m_handle->client()->didReceiveResponse(m_handle, ResourceResponse(response()));
-}
-
-void ResourceHandleCurlDelegate::prepareSendData(char* buffer, size_t blockSize, size_t numberOfBlocks)
-{
- ASSERT(isMainThread());
- ASSERT(!m_sendBytes);
-
- std::unique_lock<Lock> lock(m_workerThreadMutex);
-
- if (!m_formDataStream || !m_formDataStream->hasMoreElements()) {
- m_workerThreadConditionVariable.notifyOne();
+ if (cancelledOrClientless())
return;
- }
- size_t size = m_formDataStream->read(buffer, blockSize, numberOfBlocks);
- if (!size) {
- // Something went wrong so cancel the job.
- m_handle->cancel();
- m_workerThreadConditionVariable.notifyOne();
- return;
- }
+ if (m_curlRequest)
+ m_handle->getInternal()->m_response.setDeprecatedNetworkLoadMetrics(m_curlRequest->getNetworkLoadMetrics());
- m_sendBytes = size;
- m_workerThreadConditionVariable.notifyOne();
-}
-
-void ResourceHandleCurlDelegate::didFinish(NetworkLoadMetrics networkLoadMetrics)
-{
- response().setDeprecatedNetworkLoadMetrics(networkLoadMetrics);
-
- if (!m_handle)
- return;
-
- if (!m_didNotifyResponse) {
- handleLocalReceiveResponse();
- if (!m_handle)
- return;
- }
-
if (m_multipartHandle)
m_multipartHandle->contentEnded();
@@ -453,17 +278,22 @@
}
}
-void ResourceHandleCurlDelegate::didFail(const ResourceError& resourceError)
+void ResourceHandleCurlDelegate::curlDidFailWithError(const ResourceError& resourceError)
{
- if (!m_handle)
+ ASSERT(isMainThread());
+
+ if (cancelledOrClientless())
return;
- if (m_handle->client()) {
- CurlCacheManager::getInstance().didFail(*m_handle);
- m_handle->client()->didFail(m_handle, resourceError);
- }
+ CurlCacheManager::getInstance().didFail(*m_handle);
+ m_handle->client()->didFail(m_handle, resourceError);
}
+ResourceResponse& ResourceHandleCurlDelegate::response()
+{
+ return m_handle->getInternal()->m_response;
+}
+
void ResourceHandleCurlDelegate::handleDataURL()
{
ASSERT(m_firstRequest.url().protocolIsData());
@@ -526,99 +356,25 @@
m_handle->client()->didFinishLoading(m_handle);
}
-void ResourceHandleCurlDelegate::setupPOST()
+std::pair<String, String> ResourceHandleCurlDelegate::getCredential(ResourceRequest& request, bool redirect)
{
- m_curlHandle->enableHttpPostRequest();
+ // m_user/m_pass are credentials given manually, for instance, by the arguments passed to XMLHttpRequest.open().
+ String partition = request.cachePartition();
- size_t numElements = getFormElementsCount();
- if (!numElements)
- return;
-
- // Do not stream for simple POST data
- if (numElements == 1) {
- m_postBytes = m_firstRequest.httpBody()->flatten();
- if (m_postBytes.size())
- m_curlHandle->setPostFields(m_postBytes.data(), m_postBytes.size());
- return;
+ if (m_shouldUseCredentialStorage) {
+ if (m_user.isEmpty() && m_pass.isEmpty()) {
+ // <rdar://problem/7174050> - For URLs that match the paths of those previously challenged for HTTP Basic authentication,
+ // try and reuse the credential preemptively, as allowed by RFC 2617.
+ m_initialCredential = CredentialStorage::defaultCredentialStorage().get(partition, request.url());
+ } else if (!redirect) {
+ // If there is already a protection space known for the URL, update stored credentials
+ // before sending a request. This makes it possible to implement logout by sending an
+ // XMLHttpRequest with known incorrect credentials, and aborting it immediately (so that
+ // an authentication dialog doesn't pop up).
+ CredentialStorage::defaultCredentialStorage().set(partition, Credential(m_user, m_pass, CredentialPersistenceNone), request.url());
+ }
}
- setupFormData(true);
-}
-
-void ResourceHandleCurlDelegate::setupPUT()
-{
- m_curlHandle->enableHttpPutRequest();
-
- // Disable the Expect: 100 continue header
- m_curlHandle->removeRequestHeader("Expect");
-
- size_t numElements = getFormElementsCount();
- if (!numElements)
- return;
-
- setupFormData(false);
-}
-
-size_t ResourceHandleCurlDelegate::getFormElementsCount()
-{
- RefPtr<FormData> formData = m_firstRequest.httpBody();
- if (!formData)
- return 0;
-
- // Resolve the blob elements so the formData can correctly report it's size.
- formData = formData->resolveBlobReferences();
- size_t size = formData->elements().size();
- m_firstRequest.setHTTPBody(WTFMove(formData));
- return size;
-}
-
-void ResourceHandleCurlDelegate::setupFormData(bool isPostRequest)
-{
- Vector<FormDataElement> elements = m_firstRequest.httpBody()->elements();
- size_t numElements = elements.size();
-
- static const long long maxCurlOffT = m_curlHandle->maxCurlOffT();
-
- // Obtain the total size of the form data
- curl_off_t size = 0;
- bool chunkedTransfer = false;
- for (size_t i = 0; i < numElements; i++) {
- FormDataElement element = elements[i];
- if (element.m_type == FormDataElement::Type::EncodedFile) {
- long long fileSizeResult;
- if (getFileSize(element.m_filename, fileSizeResult)) {
- if (fileSizeResult > maxCurlOffT) {
- // File size is too big for specifying it to cURL
- chunkedTransfer = true;
- break;
- }
- size += fileSizeResult;
- } else {
- chunkedTransfer = true;
- break;
- }
- } else
- size += elements[i].m_data.size();
- }
-
- // cURL guesses that we want chunked encoding as long as we specify the header
- if (chunkedTransfer)
- m_curlHandle->appendRequestHeader("Transfer-Encoding: chunked");
- else {
- if (isPostRequest)
- m_curlHandle->setPostFieldLarge(size);
- else
- m_curlHandle->setInFileSizeLarge(size);
- }
-
- m_formDataStream = std::make_unique<FormDataStream>();
- m_formDataStream->setHTTPBody(m_firstRequest.httpBody());
-
- m_curlHandle->setReadCallbackFunction(willSendDataCallback, this);
-}
-
-void ResourceHandleCurlDelegate::applyAuthentication()
-{
String user = m_user;
String password = m_pass;
@@ -625,208 +381,14 @@
if (!m_initialCredential.isEmpty()) {
user = m_initialCredential.user();
password = m_initialCredential.password();
- m_curlHandle->enableHttpAuthentication(CURLAUTH_BASIC);
}
- // It seems we need to set CURLOPT_USERPWD even if username and password is empty.
- // Otherwise cURL will not automatically continue with a new request after a 401 response.
+ if (user.isEmpty() && password.isEmpty())
+ return std::pair<String, String>("", "");
- // curl CURLOPT_USERPWD expects username:password
- m_curlHandle->setHttpAuthUserPass(user, password);
+ return std::pair<String, String>(user, password);
}
-NetworkLoadMetrics ResourceHandleCurlDelegate::getNetworkLoadMetrics()
-{
- NetworkLoadMetrics networkLoadMetrics;
- if (auto metrics = m_curlHandle->getNetworkLoadMetrics())
- networkLoadMetrics = *metrics;
-
- return networkLoadMetrics;
-}
-
-CURLcode ResourceHandleCurlDelegate::willSetupSslCtx(void* sslCtx)
-{
- m_sslVerifier.setCurlHandle(m_curlHandle.get());
- m_sslVerifier.setHostName(m_firstRequest.url().host());
- m_sslVerifier.setSslCtx(sslCtx);
-
- return CURLE_OK;
-}
-
-/*
-* This is being called for each HTTP header in the response. This includes '\r\n'
-* for the last line of the header.
-*
-* We will add each HTTP Header to the ResourceResponse and on the termination
-* of the header (\r\n) we will parse Content-Type and Content-Disposition and
-* update the ResourceResponse and then send it away.
-*
-*/
-size_t ResourceHandleCurlDelegate::didReceiveHeader(String&& header)
-{
- static const auto emptyLineCRLF = "\r\n";
- static const auto emptyLineLF = "\n";
-
- if (!m_handle)
- return 0;
-
- if (m_defersLoading)
- return 0;
-
- size_t receiveBytes = header.length();
-
- // The HTTP standard requires to use \r\n but for compatibility it recommends to accept also \n.
- // We will add the current header to the CurlResponse.headers
- if ((header != emptyLineCRLF) && (header != emptyLineLF)) {
- m_response.headers.append(WTFMove(header));
- return receiveBytes;
- }
-
- // We can finish and send the ResourceResponse
- long statusCode = 0;
- if (auto code = m_curlHandle->getResponseCode())
- statusCode = *code;
-
- long httpConnectCode = 0;
- if (auto code = m_curlHandle->getHttpConnectCode())
- httpConnectCode = *code;
-
- if ((100 <= statusCode) && (statusCode < 200)) {
- // Just return when receiving http info, e.g. HTTP/1.1 100 Continue.
- // If not, the request might be cancelled, because the MIME type will be empty for this response.
- m_response = CurlResponse();
- return receiveBytes;
- }
-
- if (!statusCode && (httpConnectCode == 200)) {
- // Comes here when receiving 200 Connection Established. Just return.
- m_response = CurlResponse();
- return receiveBytes;
- }
-
- // If the FOLLOWLOCATION option is enabled for the curl handle then
- // curl will follow the redirections internally. Thus this header callback
- // will be called more than one time with the line starting "HTTP" for one job.
-
- m_response.url = ""
- m_response.statusCode = statusCode;
-
- if (auto length = m_curlHandle->getContentLength())
- m_response.expectedContentLength = *length;
-
- if (auto port = m_curlHandle->getPrimaryPort())
- m_response.connectPort = *port;
-
- if (auto auth = m_curlHandle->getHttpAuthAvail())
- m_response.availableHttpAuth = *auth;
-
- if (isMainThread())
- didReceiveAllHeaders(m_response);
- else {
- callOnMainThread([protectedThis = makeRef(*this), response = m_response.isolatedCopy()] {
- if (!protectedThis->m_handle)
- return;
- protectedThis->didReceiveAllHeaders(response);
- });
- }
-
- return receiveBytes;
-}
-
-// called with data after all headers have been processed via headerCallback
-size_t ResourceHandleCurlDelegate::didReceiveData(Ref<SharedBuffer>&& buffer)
-{
- if (!m_handle)
- return 0;
-
- if (m_defersLoading)
- return 0;
-
- size_t receiveBytes = buffer->size();
-
- // this shouldn't be necessary but apparently is. CURL writes the data
- // of html page even if it is a redirect that was handled internally
- // can be observed e.g. on gmail.com
- if (auto httpCode = m_curlHandle->getResponseCode()) {
- if (*httpCode >= 300 && *httpCode < 400)
- return receiveBytes;
- }
-
- if (receiveBytes) {
- if (isMainThread())
- didReceiveContentData(WTFMove(buffer));
- else {
- callOnMainThread([protectedThis = makeRef(*this), buf = WTFMove(buffer)]() mutable {
- if (!protectedThis->m_handle)
- return;
- protectedThis->didReceiveContentData(WTFMove(buf));
- });
- }
- }
-
- return receiveBytes;
-}
-
-/* This is called to obtain HTTP POST or PUT data.
-Iterate through FormData elements and upload files.
-Carefully respect the given buffer blockSize and fill the rest of the data at the next calls.
-*/
-size_t ResourceHandleCurlDelegate::willSendData(char* buffer, size_t blockSize, size_t numberOfBlocks)
-{
- ASSERT(!isMainThread());
-
- if (!m_handle)
- return 0;
-
- if (m_defersLoading)
- return 0;
-
- if (!blockSize || !numberOfBlocks)
- return 0;
-
- {
- std::unique_lock<Lock> lock(m_workerThreadMutex);
-
- m_sendBytes = 0;
-
- if (isMainThread())
- prepareSendData(buffer, blockSize, numberOfBlocks);
- else {
- callOnMainThread([protectedThis = makeRef(*this), buffer, blockSize, numberOfBlocks] {
- if (!protectedThis->m_handle)
- return;
- protectedThis->prepareSendData(buffer, blockSize, numberOfBlocks);
- });
-
- m_workerThreadConditionVariable.wait(lock, [this] {
- return m_sendBytes;
- });
- }
- }
-
- return m_sendBytes;
-}
-
-CURLcode ResourceHandleCurlDelegate::willSetupSslCtxCallback(CURL*, void* sslCtx, void* userData)
-{
- return static_cast<ResourceHandleCurlDelegate*>(userData)->willSetupSslCtx(sslCtx);
-}
-
-size_t ResourceHandleCurlDelegate::didReceiveHeaderCallback(char* ptr, size_t blockSize, size_t numberOfBlocks, void* data)
-{
- return static_cast<ResourceHandleCurlDelegate*>(const_cast<void*>(data))->didReceiveHeader(String(static_cast<const char*>(ptr), blockSize * numberOfBlocks));
-}
-
-size_t ResourceHandleCurlDelegate::didReceiveDataCallback(char* ptr, size_t blockSize, size_t numberOfBlocks, void* data)
-{
- return static_cast<ResourceHandleCurlDelegate*>(const_cast<void*>(data))->didReceiveData(SharedBuffer::create(ptr, blockSize * numberOfBlocks));
-}
-
-size_t ResourceHandleCurlDelegate::willSendDataCallback(char* ptr, size_t blockSize, size_t numberOfBlocks, void* data)
-{
- return static_cast<ResourceHandleCurlDelegate*>(const_cast<void*>(data))->willSendData(ptr, blockSize, numberOfBlocks);
-}
-
} // namespace WebCore
#endif
Modified: trunk/Source/WebCore/platform/network/curl/ResourceHandleCurlDelegate.h (222664 => 222665)
--- trunk/Source/WebCore/platform/network/curl/ResourceHandleCurlDelegate.h 2017-09-29 21:14:09 UTC (rev 222664)
+++ trunk/Source/WebCore/platform/network/curl/ResourceHandleCurlDelegate.h 2017-09-29 21:19:32 UTC (rev 222665)
@@ -29,24 +29,17 @@
#if USE(CURL)
#include "Credential.h"
-#include "CurlJobManager.h"
-#include "CurlResponse.h"
-#include "CurlSSLVerifier.h"
-#include "FormDataStreamCurl.h"
+#include "CurlRequestDelegate.h"
#include "ResourceRequest.h"
-#include "ResourceResponse.h"
-#include <wtf/Condition.h>
-#include <wtf/ThreadSafeRefCounted.h>
namespace WebCore {
+class CurlRequest;
class MultipartHandle;
-class ProtectionSpace;
-class ResourceError;
class ResourceHandle;
-class SharedBuffer;
+class ResourceResponse;
-class ResourceHandleCurlDelegate final : public ThreadSafeRefCounted<ResourceHandleCurlDelegate>, public CurlJobClient {
+class ResourceHandleCurlDelegate final : public ThreadSafeRefCounted<ResourceHandleCurlDelegate>, public CurlRequestDelegate {
public:
ResourceHandleCurlDelegate(ResourceHandle*);
~ResourceHandleCurlDelegate();
@@ -54,7 +47,7 @@
bool hasHandle() const;
void releaseHandle();
- bool start() { start(false); return true; }
+ bool start();
void cancel();
void setDefersLoading(bool);
@@ -63,57 +56,28 @@
void dispatchSynchronousJob();
private:
- void retain() override;
- void release() override;
-
- CURL* handle() override { return m_curlHandle ? m_curlHandle->handle() : nullptr; }
- CURL* setupTransfer() override;
- void didCompleteTransfer(CURLcode) override;
- void didCancelTransfer() override;
-
// Called from main thread.
ResourceResponse& response();
- void start(bool isSyncRequest);
+ std::pair<String, String> getCredential(ResourceRequest&, bool);
- void setupAuthentication();
+ bool cancelledOrClientless();
- void didReceiveAllHeaders(const CurlResponse&);
- void didReceiveContentData(Ref<SharedBuffer>&&);
- void handleLocalReceiveResponse();
- void prepareSendData(char*, size_t blockSize, size_t numberOfBlocks);
+ RefPtr<CurlRequest> createCurlRequest(ResourceRequest&);
+ void curlDidReceiveResponse(const CurlResponse&) override;
+ void curlDidReceiveBuffer(Ref<SharedBuffer>&&) override;
+ void curlDidComplete() override;
+ void curlDidFailWithError(const ResourceError&) override;
- void didFinish(NetworkLoadMetrics);
- void didFail(const ResourceError&);
-
void handleDataURL();
- // Called from worker thread.
- void setupPOST();
- void setupPUT();
- size_t getFormElementsCount();
- void setupFormData(bool);
- void applyAuthentication();
- NetworkLoadMetrics getNetworkLoadMetrics();
-
- CURLcode willSetupSslCtx(void*);
- size_t didReceiveHeader(String&&);
- size_t didReceiveData(Ref<SharedBuffer>&&);
- size_t willSendData(char*, size_t blockSize, size_t numberOfBlocks);
-
- static CURLcode willSetupSslCtxCallback(CURL*, void*, void*);
- static size_t didReceiveHeaderCallback(char*, size_t blockSize, size_t numberOfBlocks, void*);
- static size_t didReceiveDataCallback(char*, size_t blockSize, size_t numberOfBlocks, void*);
- static size_t willSendDataCallback(char*, size_t blockSize, size_t numberOfBlocks, void*);
-
// Used by main thread.
ResourceHandle* m_handle;
- std::unique_ptr<FormDataStream> m_formDataStream;
std::unique_ptr<MultipartHandle> m_multipartHandle;
- unsigned short m_authFailureCount { 0 };
+ unsigned m_authFailureCount { 0 };
// Used by worker thread.
ResourceRequest m_firstRequest;
- HTTPHeaderMap m_customHTTPHeaderFields;
+ ResourceRequest m_currentRequest;
bool m_shouldUseCredentialStorage;
String m_user;
String m_pass;
@@ -120,16 +84,7 @@
Credential m_initialCredential;
bool m_defersLoading;
bool m_addedCacheValidationHeaders { false };
- Vector<char> m_postBytes;
- std::unique_ptr<CurlHandle> m_curlHandle;
- CurlSSLVerifier m_sslVerifier;
- CurlResponse m_response;
- bool m_didNotifyResponse { false };
- // Used by both threads.
- bool m_isSyncRequest { false };
- Condition m_workerThreadConditionVariable;
- Lock m_workerThreadMutex;
- size_t m_sendBytes { 0 };
+ RefPtr<CurlRequest> m_curlRequest;
};
} // namespace WebCore