Diff
Modified: trunk/Source/WebCore/ChangeLog (220938 => 220939)
--- trunk/Source/WebCore/ChangeLog 2017-08-18 22:46:17 UTC (rev 220938)
+++ trunk/Source/WebCore/ChangeLog 2017-08-18 23:02:51 UTC (rev 220939)
@@ -1,3 +1,137 @@
+2017-08-18 Daewoong Jang <[email protected]>
+
+ [Curl] Improve multi-threaded networking
+ https://bugs.webkit.org/show_bug.cgi?id=175713
+
+ Reviewed by Alex Christensen.
+
+ * platform/Curl.cmake:
+ * platform/network/ResourceHandleInternal.h:
+ (WebCore::ResourceHandleInternal::ResourceHandleInternal):
+ (WebCore::ResourceHandleInternal::m_failureTimer): Deleted.
+ * platform/network/curl/CurlCacheManager.cpp:
+ (WebCore::CurlCacheManager::didReceiveResponse):
+ * platform/network/curl/CurlDownload.cpp:
+ (WebCore::CurlDownload::init):
+ (WebCore::CurlDownload::start):
+ (WebCore::CurlDownload::cancel):
+ (WebCore::CurlDownload::retain):
+ (WebCore::CurlDownload::release):
+ (WebCore::CurlDownload::setupRequest):
+ (WebCore::CurlDownload::notifyFinish):
+ (WebCore::CurlDownload::notifyFail):
+ * platform/network/curl/CurlDownload.h:
+ * platform/network/curl/CurlJobManager.cpp:
+ (WebCore::CurlJobList::isEmpty const):
+ (WebCore::CurlJobList::startJobs):
+ (WebCore::CurlJobList::finishJobs):
+ (WebCore::CurlJobList::notifyResult):
+ (WebCore::CurlJobManager::add):
+ (WebCore::CurlJobManager::cancel):
+ (WebCore::CurlJobManager::callOnJobThread):
+ (WebCore::CurlJobManager::startThreadIfNeeded):
+ (WebCore::CurlJobManager::updateJobList):
+ (WebCore::CurlJobManager::workerThread):
+ (WebCore::CurlJobList::append): Deleted.
+ (WebCore::CurlJobList::cancel): Deleted.
+ (WebCore::CurlJobList::complete): Deleted.
+ (WebCore::CurlJobList::withJob): Deleted.
+ (WebCore::CurlJobList::withCurlHandle): Deleted.
+ (WebCore::CurlJob::invoke): Deleted.
+ (WebCore::CurlJobManager::updateJobs): Deleted.
+ * platform/network/curl/CurlJobManager.h:
+ (WebCore::CurlJob::CurlJob): Deleted.
+ (WebCore::CurlJob::~CurlJob): Deleted.
+ (WebCore::CurlJob::operator=): Deleted.
+ (WebCore::CurlJob::curlHandle const): Deleted.
+ (WebCore::CurlJob::ticket const): Deleted.
+ (WebCore::CurlJob::finished): Deleted.
+ (WebCore::CurlJob::error): Deleted.
+ (WebCore::CurlJob::cancel): Deleted.
+ (WebCore::CurlJobManager::isActiveJob const): Deleted.
+ * platform/network/curl/MultipartHandle.cpp:
+ (WebCore::MultipartHandle::didReceiveData):
+ * platform/network/curl/ResourceHandleCurl.cpp:
+ (WebCore::ResourceHandleInternal::~ResourceHandleInternal):
+ (WebCore::ResourceHandle::start):
+ (WebCore::ResourceHandle::cancel):
+ (WebCore::ResourceHandle::platformSetDefersLoading):
+ (WebCore::ResourceHandle::didReceiveAuthenticationChallenge):
+ (WebCore::ResourceHandle::receivedCredential):
+ (WebCore::ResourceHandle::receivedRequestToContinueWithoutCredential):
+ (WebCore::ResourceHandle::platformLoadResourceSynchronously):
+ (WebCore::ResourceHandleInternal::initialize): Deleted.
+ (WebCore::ResourceHandleInternal::applyAuthentication): Deleted.
+ (WebCore::getFormElementsCount): Deleted.
+ (WebCore::ResourceHandleInternal::setupPUT): Deleted.
+ (WebCore::ResourceHandleInternal::setupPOST): Deleted.
+ (WebCore::ResourceHandleInternal::setupFormData): Deleted.
+ (WebCore::ResourceHandleInternal::didFinish): Deleted.
+ (WebCore::ResourceHandleInternal::didFail): Deleted.
+ (WebCore::ResourceHandleInternal::calculateWebTimingInformations): Deleted.
+ (WebCore::ResourceHandleInternal::handleLocalReceiveResponse): Deleted.
+ (WebCore::isHttpInfo): Deleted.
+ (WebCore::isHttpRedirect): Deleted.
+ (WebCore::isHttpAuthentication): Deleted.
+ (WebCore::isHttpNotModified): Deleted.
+ (WebCore::isAppendableHeader): Deleted.
+ (WebCore::removeLeadingAndTrailingQuotes): Deleted.
+ (WebCore::getProtectionSpace): Deleted.
+ (WebCore::ResourceHandleInternal::willPrepareSendData): Deleted.
+ (WebCore::ResourceHandleInternal::didReceiveHeaderLine): Deleted.
+ (WebCore::ResourceHandleInternal::didReceiveAllHeaders): Deleted.
+ (WebCore::ResourceHandleInternal::didReceiveContentData): Deleted.
+ (WebCore::ResourceHandleInternal::readCallback): Deleted.
+ (WebCore::ResourceHandleInternal::headerCallback): Deleted.
+ (WebCore::ResourceHandleInternal::writeCallback): Deleted.
+ (WebCore::ResourceHandleInternal::dispatchSynchronousJob): Deleted.
+ (WebCore::ResourceHandleInternal::handleDataURL): Deleted.
+ * platform/network/curl/ResourceHandleCurlDelegate.cpp: Added.
+ (WebCore::ResourceHandleCurlDelegate::ResourceHandleCurlDelegate):
+ (WebCore::ResourceHandleCurlDelegate::~ResourceHandleCurlDelegate):
+ (WebCore::ResourceHandleCurlDelegate::hasHandle const):
+ (WebCore::ResourceHandleCurlDelegate::releaseHandle):
+ (WebCore::ResourceHandleCurlDelegate::start):
+ (WebCore::ResourceHandleCurlDelegate::cancel):
+ (WebCore::ResourceHandleCurlDelegate::setDefersLoading):
+ (WebCore::ResourceHandleCurlDelegate::setAuthentication):
+ (WebCore::ResourceHandleCurlDelegate::dispatchSynchronousJob):
+ (WebCore::ResourceHandleCurlDelegate::retain):
+ (WebCore::ResourceHandleCurlDelegate::release):
+ (WebCore::ResourceHandleCurlDelegate::setupRequest):
+ (WebCore::ResourceHandleCurlDelegate::notifyFinish):
+ (WebCore::ResourceHandleCurlDelegate::notifyFail):
+ (WebCore::ResourceHandleCurlDelegate::response):
+ (WebCore::ResourceHandleCurlDelegate::setupAuthentication):
+ (WebCore::removeLeadingAndTrailingQuotes):
+ (WebCore::ResourceHandleCurlDelegate::getProtectionSpace):
+ (WebCore::isHttpInfo):
+ (WebCore::isHttpRedirect):
+ (WebCore::isHttpAuthentication):
+ (WebCore::isHttpNotModified):
+ (WebCore::isAppendableHeader):
+ (WebCore::ResourceHandleCurlDelegate::didReceiveHeaderLine):
+ (WebCore::ResourceHandleCurlDelegate::didReceiveAllHeaders):
+ (WebCore::ResourceHandleCurlDelegate::didReceiveContentData):
+ (WebCore::ResourceHandleCurlDelegate::handleLocalReceiveResponse):
+ (WebCore::ResourceHandleCurlDelegate::prepareSendData):
+ (WebCore::ResourceHandleCurlDelegate::didFinish):
+ (WebCore::ResourceHandleCurlDelegate::didFail):
+ (WebCore::ResourceHandleCurlDelegate::handleDataURL):
+ (WebCore::ResourceHandleCurlDelegate::setupPOST):
+ (WebCore::ResourceHandleCurlDelegate::setupPUT):
+ (WebCore::ResourceHandleCurlDelegate::getFormElementsCount):
+ (WebCore::ResourceHandleCurlDelegate::setupFormData):
+ (WebCore::ResourceHandleCurlDelegate::applyAuthentication):
+ (WebCore::ResourceHandleCurlDelegate::setWebTimings):
+ (WebCore::ResourceHandleCurlDelegate::didReceiveHeader):
+ (WebCore::ResourceHandleCurlDelegate::didReceiveData):
+ (WebCore::ResourceHandleCurlDelegate::willSendData):
+ (WebCore::ResourceHandleCurlDelegate::didReceiveHeaderCallback):
+ (WebCore::ResourceHandleCurlDelegate::didReceiveDataCallback):
+ (WebCore::ResourceHandleCurlDelegate::willSendDataCallback):
+ * platform/network/curl/ResourceHandleCurlDelegate.h: Added.
+
2017-08-18 Ryosuke Niwa <[email protected]>
iOS 10 debug build fix.
Modified: trunk/Source/WebCore/platform/Curl.cmake (220938 => 220939)
--- trunk/Source/WebCore/platform/Curl.cmake 2017-08-18 22:46:17 UTC (rev 220938)
+++ trunk/Source/WebCore/platform/Curl.cmake 2017-08-18 23:02:51 UTC (rev 220939)
@@ -15,6 +15,7 @@
platform/network/curl/MultipartHandle.cpp
platform/network/curl/ProxyServerCurl.cpp
platform/network/curl/ResourceHandleCurl.cpp
+ platform/network/curl/ResourceHandleCurlDelegate.cpp
platform/network/curl/SSLHandle.cpp
platform/network/curl/SocketStreamHandleImplCurl.cpp
platform/network/curl/SynchronousLoaderClientCurl.cpp
Modified: trunk/Source/WebCore/platform/network/ResourceHandleInternal.h (220938 => 220939)
--- trunk/Source/WebCore/platform/network/ResourceHandleInternal.h 2017-08-18 22:46:17 UTC (rev 220938)
+++ trunk/Source/WebCore/platform/network/ResourceHandleInternal.h 2017-08-18 23:02:51 UTC (rev 220939)
@@ -43,11 +43,7 @@
#endif
#if USE(CURL)
-#include "CurlContext.h"
-#include "CurlJobManager.h"
-#include "FormDataStreamCurl.h"
-#include "MultipartHandle.h"
-#include <wtf/Lock.h>
+#include "ResourceHandleCurlDelegate.h"
#endif
#if USE(SOUP)
@@ -88,10 +84,6 @@
#if USE(CFURLCONNECTION)
, m_currentRequest(request)
#endif
-#if USE(CURL)
- , m_handle { loader }
- , m_formDataStream { loader }
-#endif
#if USE(SOUP)
, m_timeoutSource(RunLoop::main(), loader, &ResourceHandle::timeoutFired)
#endif
@@ -140,49 +132,8 @@
RetainPtr<CFURLStorageSessionRef> m_storageSession;
#endif
#if USE(CURL)
- ResourceHandle* m_handle;
- CurlHandle m_curlHandle;
-
+ RefPtr<ResourceHandleCurlDelegate> m_delegate;
ResourceResponse m_response;
- bool m_cancelled { false };
- unsigned short m_authFailureCount { 0 };
-
- FormDataStream m_formDataStream;
- unsigned m_sslErrors { 0 };
- Vector<char> m_postBytes;
-
- std::unique_ptr<MultipartHandle> m_multipartHandle;
- bool m_addedCacheValidationHeaders { false };
- CurlJobTicket m_job { nullptr };
-
- Vector<char> m_receivedBuffer;
- Lock m_receivedBufferMutex;
-
- void initialize();
- void applyAuthentication();
- void setupPOST();
- void setupPUT();
- void setupFormData(bool isPostRequest);
-
- void didFinish();
- void didFail();
-
- size_t willPrepareSendData(char* ptr, size_t blockSize, size_t numberOfBlocks);
- void didReceiveHeaderLine(const String& header);
- void didReceiveAllHeaders(long httpCode, long long contentLength);
- void didReceiveContentData();
-
- void handleLocalReceiveResponse();
-
- static size_t readCallback(char* ptr, size_t blockSize, size_t numberOfBlocks, void* data);
- static size_t headerCallback(char* ptr, size_t blockSize, size_t numberOfBlocks, void* data);
- static size_t writeCallback(char* ptr, size_t blockSize, size_t numberOfBlocks, void* data);
-
- void dispatchSynchronousJob();
- void handleDataURL();
-
- void calculateWebTimingInformations();
-
#endif
#if USE(SOUP)
Modified: trunk/Source/WebCore/platform/network/curl/CurlCacheManager.cpp (220938 => 220939)
--- trunk/Source/WebCore/platform/network/curl/CurlCacheManager.cpp 2017-08-18 22:46:17 UTC (rev 220938)
+++ trunk/Source/WebCore/platform/network/curl/CurlCacheManager.cpp 2017-08-18 23:02:51 UTC (rev 220939)
@@ -199,7 +199,7 @@
return;
ResourceHandleInternal* d = job.getInternal();
- if (d->m_cancelled)
+ if (!d->m_delegate->hasHandle())
return;
const String& url = ""
Modified: trunk/Source/WebCore/platform/network/curl/CurlDownload.cpp (220938 => 220939)
--- trunk/Source/WebCore/platform/network/curl/CurlDownload.cpp 2017-08-18 22:46:17 UTC (rev 220938)
+++ trunk/Source/WebCore/platform/network/curl/CurlDownload.cpp 2017-08-18 23:02:51 UTC (rev 220939)
@@ -58,8 +58,6 @@
LockHolder locker(m_mutex);
- setupRequest();
-
m_listener = listener;
m_url = url;
}
@@ -78,26 +76,15 @@
bool CurlDownload::start()
{
- m_job = CurlJobManager::singleton().add(m_curlHandle, [this, protectedThis = makeRef(*this)](CurlJobResult result) mutable {
- switch (result) {
- case CurlJobResult::Done:
- didFinish();
- break;
-
- case CurlJobResult::Error:
- didFail();
- break;
-
- case CurlJobResult::Cancelled:
- break;
- }
- });
- return true;
+ m_job = CurlJobManager::singleton().add(m_curlHandle, *this);
+ return !!m_job;
}
bool CurlDownload::cancel()
{
- CurlJobManager::singleton().cancel(m_job);
+ CurlJobTicket job = m_job;
+ m_job = nullptr;
+ CurlJobManager::singleton().cancel(job);
return true;
}
@@ -109,6 +96,16 @@
return response;
}
+void CurlDownload::retain()
+{
+ ref();
+}
+
+void CurlDownload::release()
+{
+ deref();
+}
+
void CurlDownload::setupRequest()
{
LockHolder locker(m_mutex);
@@ -125,6 +122,24 @@
m_curlHandle.enableCAInfoIfExists();
}
+void CurlDownload::notifyFinish()
+{
+ callOnMainThread([protectedThis = makeRef(*this)] {
+ if (!protectedThis->m_job)
+ return;
+ protectedThis->didFinish();
+ });
+}
+
+void CurlDownload::notifyFail()
+{
+ callOnMainThread([protectedThis = makeRef(*this)] {
+ if (!protectedThis->m_job)
+ return;
+ protectedThis->didFail();
+ });
+}
+
void CurlDownload::closeFile()
{
LockHolder locker(m_mutex);
Modified: trunk/Source/WebCore/platform/network/curl/CurlDownload.h (220938 => 220939)
--- trunk/Source/WebCore/platform/network/curl/CurlDownload.h 2017-08-18 22:46:17 UTC (rev 220938)
+++ trunk/Source/WebCore/platform/network/curl/CurlDownload.h 2017-08-18 23:02:51 UTC (rev 220939)
@@ -44,7 +44,7 @@
virtual void didFail() { }
};
-class CurlDownload : public ThreadSafeRefCounted<CurlDownload> {
+class CurlDownload : public ThreadSafeRefCounted<CurlDownload>, public CurlJobClient {
public:
CurlDownload();
~CurlDownload();
@@ -65,8 +65,13 @@
void setDestination(const String& destination) { m_destination = destination; }
private:
- void setupRequest();
+ void retain() override;
+ void release() override;
+ void setupRequest() override;
+ void notifyFinish() override;
+ void notifyFail() override;
+
void closeFile();
void moveFileToDestination();
void writeDataToFile(const char* data, int size);
Modified: trunk/Source/WebCore/platform/network/curl/CurlJobManager.cpp (220938 => 220939)
--- trunk/Source/WebCore/platform/network/curl/CurlJobManager.cpp 2017-08-18 22:46:17 UTC (rev 220938)
+++ trunk/Source/WebCore/platform/network/curl/CurlJobManager.cpp 2017-08-18 23:02:51 UTC (rev 220939)
@@ -1,6 +1,7 @@
/*
* 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
@@ -25,10 +26,11 @@
*/
#include "config.h"
+#include "CurlJobManager.h"
#if USE(CURL)
-#include "CurlJobManager.h"
+#include "ResourceHandleCurlDelegate.h"
#include <iterator>
#include <wtf/MainThread.h>
#include <wtf/text/CString.h>
@@ -37,88 +39,69 @@
namespace WebCore {
+enum class CurlJobResult { Done, Error, Cancelled };
+
/*
* CurlJobList is used only in background so that no need to manage mutex
*/
class CurlJobList : public CurlMultiHandle {
- using Predicate = WTF::Function<bool(const CurlJob&)>;
- using Action = ""
- using JobMap = HashMap<CurlJobTicket, CurlJob>;
-
public:
- void append(CurlJob& job)
- {
- CurlHandle* curl = job.curlHandle();
+ bool isEmpty() const { return m_activeJobs.isEmpty(); }
- CURLMcode retval = addHandle(curl->handle());
- // @FIXME error logging
- if (retval == CURLM_OK)
- m_jobs.set(job.ticket(), WTFMove(job));
- }
-
- void cancel(CurlJobTicket ticket)
+ void startJobs(HashMap<CurlJobTicket, CurlJobClient*>&& jobs)
{
- complete(ticket, [](auto&& job) { job.cancel(); });
+ auto localJobs = WTFMove(jobs);
+ for (auto& job : localJobs) {
+ job.value->setupRequest();
+ m_activeJobs.add(job.key, job.value);
+ addHandle(job.key);
+ }
}
- void complete(CurlJobTicket ticket, Action action)
+ void finishJobs(HashSet<CurlJobTicket>&& tickets, CurlJobResult result)
{
- auto found = m_jobs.find(ticket);
- if (found != m_jobs.end()) {
- auto job = WTFMove(found->value);
-
- removeHandle(job.curlHandle()->handle());
- action(job);
-
- m_jobs.remove(found);
+ auto localTickets = WTFMove(tickets);
+ for (auto& ticket : localTickets) {
+ if (!m_activeJobs.contains(ticket))
+ continue;
+ removeHandle(ticket);
+ notifyResult(m_activeJobs.fastGet(ticket), result);
+ m_activeJobs.remove(ticket);
}
}
- bool isEmpty() const { return m_jobs.isEmpty(); }
-
- bool withJob(CurlJobTicket ticket, WTF::Function<void(JobMap::iterator)> callback)
+private:
+ void notifyResult(CurlJobClient* client, CurlJobResult result)
{
- auto found = m_jobs.find(ticket);
- if (found == m_jobs.end())
- return false;
-
- callback(found);
- return true;
- }
+ switch (result) {
+ case CurlJobResult::Done:
+ client->notifyFinish();
+ break;
+ case CurlJobResult::Error:
+ client->notifyFail();
+ break;
+ case CurlJobResult::Cancelled:
+ break;
+ }
- bool withCurlHandle(CurlJobTicket ticket, WTF::Function<void(CurlHandle&)> callback)
- {
- return withJob(ticket, [&callback](JobMap::iterator it) {
- callback(*it->value.curlHandle());
- });
+ client->release();
}
-private:
- JobMap m_jobs;
+ HashMap<CurlJobTicket, CurlJobClient*> m_activeJobs;
};
-void CurlJob::invoke(CurlJobResult result)
+CurlJobTicket CurlJobManager::add(CurlHandle& curl, CurlJobClient& client)
{
- callOnMainThread([job = WTFMove(m_job), result] {
- job(result);
- });
-}
-
-CurlJobTicket CurlJobManager::add(CurlHandle& curl, Callback callback)
-{
ASSERT(isMainThread());
+ client.retain();
+
CurlJobTicket ticket = static_cast<CurlJobTicket>(curl.handle());
{
LockHolder locker(m_mutex);
-
- if (isActiveJob(ticket))
- return ticket;
-
-
- m_pendingJobs.append(CurlJob { &curl, WTFMove(callback) });
- m_activeJobs.add(ticket);
+ m_cancelledTickets.remove(ticket);
+ m_pendingJobs.add(ticket, &client);
}
startThreadIfNeeded();
@@ -126,28 +109,18 @@
return ticket;
}
-bool CurlJobManager::cancel(CurlJobTicket job)
+void CurlJobManager::cancel(CurlJobTicket job)
{
ASSERT(isMainThread());
- if (m_runThread) {
- LockHolder locker(m_mutex);
-
- if (!isActiveJob(job))
- return false;
-
- m_cancelledTickets.append(job);
- m_activeJobs.remove(job);
- }
-
- return true;
+ LockHolder locker(m_mutex);
+ m_cancelledTickets.add(job);
}
-void CurlJobManager::callOnJobThread(WTF::Function<void()>&& callback)
+void CurlJobManager::callOnJobThread(WTF::Function<void()>&& task)
{
- LockHolder locker { m_mutex };
-
- m_taskQueue.append(std::make_unique<WTF::Function<void()>>(WTFMove(callback)));
+ LockHolder locker(m_mutex);
+ m_taskQueue.append(WTFMove(task));
}
void CurlJobManager::startThreadIfNeeded()
@@ -162,6 +135,7 @@
m_runThread = true;
m_thread = Thread::create("curlThread", [this] {
workerThread();
+ m_runThread = false;
});
}
}
@@ -186,32 +160,29 @@
}
}
-bool CurlJobManager::updateJobs(CurlJobList& jobs)
+void CurlJobManager::updateJobList(CurlJobList& jobs)
{
ASSERT(!isMainThread());
- Vector<CurlJob> pendingJobs;
- Vector<CurlJobTicket> cancelledTickets;
+ HashMap<CurlJobTicket, CurlJobClient*> pendingJobs;
+ HashSet<CurlJobTicket> cancelledTickets;
+ Vector<WTF::Function<void()>> taskQueue;
+
{
LockHolder locker(m_mutex);
- if (!m_runThread)
- return false;
pendingJobs = WTFMove(m_pendingJobs);
cancelledTickets = WTFMove(m_cancelledTickets);
+ taskQueue = WTFMove(m_taskQueue);
}
- for (auto& job : pendingJobs)
- jobs.append(job);
+ jobs.startJobs(WTFMove(pendingJobs));
+ jobs.finishJobs(WTFMove(cancelledTickets), CurlJobResult::Cancelled);
+ jobs.finishJobs(WTFMove(m_finishedTickets), CurlJobResult::Done);
+ jobs.finishJobs(WTFMove(m_failedTickets), CurlJobResult::Error);
- for (auto& ticket : cancelledTickets)
- jobs.cancel(ticket);
-
- for (auto& callback : m_taskQueue.takeAllMessages()) {
- (*callback)();
- }
-
- return true;
+ for (auto& task : taskQueue)
+ task();
}
void CurlJobManager::workerThread()
@@ -220,7 +191,9 @@
CurlJobList jobs;
- while (updateJobs(jobs)) {
+ while (m_runThread) {
+ updateJobList(jobs);
+
// Retry 'select' if it was interrupted by a process signal.
int rc = 0;
do {
@@ -255,38 +228,16 @@
if (!msg)
break;
- if (msg->msg == CURLMSG_DONE) {
- auto ticket = static_cast<CurlJobTicket>(msg->easy_handle);
- CURLcode result = msg->data.result;
-
- {
- LockHolder locker(m_mutex);
- m_activeJobs.remove(ticket);
- }
-
- jobs.complete(ticket, [result](auto& job) {
- job.curlHandle()->setErrorCode(result);
-
- bool done = result == CURLE_OK;
- if (done)
- job.finished();
- else {
- URL url = ""
-#ifndef NDEBUG
- fprintf(stderr, "Curl ERROR for url='', error: '%s'\n", url.string().utf8().data(), job.curlHandle()->errorDescription().utf8().data());
-#endif
- job.error();
- }
- });
- } else
- ASSERT_NOT_REACHED();
+ ASSERT(msg->msg == CURLMSG_DONE);
+ auto ticket = static_cast<CurlJobTicket>(msg->easy_handle);
+ if (msg->data.result == CURLE_OK)
+ m_finishedTickets.add(ticket);
+ else
+ m_failedTickets.add(ticket);
}
- if (jobs.isEmpty()) {
+ if (jobs.isEmpty())
stopThreadIfNoMoreJobRunning();
- if (!m_runThread)
- break;
- }
}
}
Modified: trunk/Source/WebCore/platform/network/curl/CurlJobManager.h (220938 => 220939)
--- trunk/Source/WebCore/platform/network/curl/CurlJobManager.h 2017-08-18 22:46:17 UTC (rev 220938)
+++ trunk/Source/WebCore/platform/network/curl/CurlJobManager.h 2017-08-18 23:02:51 UTC (rev 220939)
@@ -1,6 +1,7 @@
/*
* 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
@@ -27,74 +28,30 @@
#pragma once
#include "CurlContext.h"
-
-#include <wtf/Function.h>
#include <wtf/HashMap.h>
#include <wtf/HashSet.h>
#include <wtf/Lock.h>
-#include <wtf/MessageQueue.h>
#include <wtf/Noncopyable.h>
#include <wtf/Threading.h>
-#include <wtf/Vector.h>
-#if OS(WINDOWS)
-#include <windows.h>
-#include <winsock2.h>
-#endif
-
-
namespace WebCore {
-enum class CurlJobResult { Done, Error, Cancelled };
+class CurlJobList;
using CurlJobTicket = void*;
-using CurlJobCallback = WTF::Function<void(CurlJobResult)>;
-using CurlJobTask = WTF::Function<void(CurlHandle&)>;
-class CurlJobList;
-
-class CurlJob {
- WTF_MAKE_NONCOPYABLE(CurlJob);
+class CurlJobClient {
public:
- CurlJob() { }
- CurlJob(CurlHandle* curl, CurlJobCallback job)
- : m_curl { curl }, m_job { WTFMove(job) } { }
- CurlJob(CurlJob&& other)
- {
- m_curl = other.m_curl;
- other.m_curl = nullptr;
- m_job = WTFMove(other.m_job);
- }
- ~CurlJob() { }
+ virtual void retain() = 0;
+ virtual void release() = 0;
- CurlJob& operator=(CurlJob&& other)
- {
- m_curl = other.m_curl;
- other.m_curl = nullptr;
- m_job = WTFMove(other.m_job);
- return *this;
- }
-
- CurlHandle* curlHandle() const { return m_curl; }
- CurlJobTicket ticket() const { return static_cast<CurlJobTicket>(m_curl->handle()); }
-
- void finished() { invoke(CurlJobResult::Done); }
- void error() { invoke(CurlJobResult::Error); }
- void cancel() { invoke(CurlJobResult::Cancelled); }
-
-private:
- CurlHandle* m_curl;
- CurlJobCallback m_job;
-
- void invoke(CurlJobResult);
+ virtual void setupRequest() = 0;
+ virtual void notifyFinish() = 0;
+ virtual void notifyFail() = 0;
};
-
class CurlJobManager {
WTF_MAKE_NONCOPYABLE(CurlJobManager);
- using Callback = CurlJobCallback;
-
public:
-
static CurlJobManager& singleton()
{
static CurlJobManager shared;
@@ -104,8 +61,9 @@
CurlJobManager() = default;
~CurlJobManager() { stopThread(); }
- CurlJobTicket add(CurlHandle&, Callback);
- bool cancel(CurlJobTicket);
+ CurlJobTicket add(CurlHandle&, CurlJobClient&);
+ void cancel(CurlJobTicket);
+
void callOnJobThread(WTF::Function<void()>&&);
private:
@@ -113,18 +71,18 @@
void stopThreadIfNoMoreJobRunning();
void stopThread();
- bool updateJobs(CurlJobList& jobs);
- bool isActiveJob(CurlJobTicket job) const { return m_activeJobs.contains(job); }
+ void updateJobList(CurlJobList&);
void workerThread();
RefPtr<Thread> m_thread;
- Vector<CurlJob> m_pendingJobs;
- HashSet<CurlJobTicket> m_activeJobs;
- Vector<CurlJobTicket> m_cancelledTickets;
- MessageQueue<WTF::Function<void()>> m_taskQueue;
+ HashMap<CurlJobTicket, CurlJobClient*> m_pendingJobs;
+ HashSet<CurlJobTicket> m_cancelledTickets;
+ HashSet<CurlJobTicket> m_finishedTickets;
+ HashSet<CurlJobTicket> m_failedTickets;
+ Vector<WTF::Function<void()>> m_taskQueue;
mutable Lock m_mutex;
- bool m_runThread { };
+ bool m_runThread { false };
};
}
Modified: trunk/Source/WebCore/platform/network/curl/MultipartHandle.cpp (220938 => 220939)
--- trunk/Source/WebCore/platform/network/curl/MultipartHandle.cpp 2017-08-18 22:46:17 UTC (rev 220938)
+++ trunk/Source/WebCore/platform/network/curl/MultipartHandle.cpp 2017-08-18 23:02:51 UTC (rev 220939)
@@ -321,7 +321,7 @@
{
ResourceHandleInternal* d = m_resourceHandle->getInternal();
- if (d->m_cancelled) {
+ if (!d->m_delegate->hasHandle()) {
// Request has been canceled, so we'll go to the end state.
m_state = End;
return;
Modified: trunk/Source/WebCore/platform/network/curl/ResourceHandleCurl.cpp (220938 => 220939)
--- trunk/Source/WebCore/platform/network/curl/ResourceHandleCurl.cpp 2017-08-18 22:46:17 UTC (rev 220938)
+++ trunk/Source/WebCore/platform/network/curl/ResourceHandleCurl.cpp 2017-08-18 23:02:51 UTC (rev 220939)
@@ -3,6 +3,7 @@
* Copyright (C) 2005, 2006 Michael Emmel [email protected]
* Copyright (C) 2017 Sony Interactive Entertainment Inc.
* All rights reserved.
+ * Copyright (C) 2017 NAVER Corp.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -49,6 +50,8 @@
ResourceHandleInternal::~ResourceHandleInternal()
{
+ if (m_delegate)
+ m_delegate->releaseHandle();
}
ResourceHandle::~ResourceHandle()
@@ -59,6 +62,8 @@
{
ASSERT(isMainThread());
+ CurlContext::singleton();
+
// The frame could be null if the ResourceHandle is not associated to any
// Frame, e.g. if we are downloading a file.
// If the frame is not null but the page is null this must be an attempted
@@ -67,263 +72,15 @@
if (d->m_context && !d->m_context->isValid())
return false;
- d->initialize();
-
- d->m_job = CurlJobManager::singleton().add(d->m_curlHandle, [this, protectedThis = makeRef(*this)](CurlJobResult result) {
- ASSERT(isMainThread());
-
- switch (result) {
- case CurlJobResult::Done:
- d->didFinish();
- break;
-
- case CurlJobResult::Error:
- d->didFail();
- break;
-
- case CurlJobResult::Cancelled:
- break;
- }
- });
- ASSERT(d->m_job);
-
- return true;
+ d->m_delegate = adoptRef(new ResourceHandleCurlDelegate(this));
+ return d->m_delegate->start();
}
void ResourceHandle::cancel()
{
- d->m_cancelled = true;
- CurlJobManager::singleton().cancel(d->m_job);
+ d->m_delegate->cancel();
}
-void ResourceHandleInternal::initialize()
-{
- CurlContext& context = CurlContext::singleton();
-
- URL url = ""
-
- // Remove any fragment part, otherwise curl will send it as part of the request.
- url.removeFragmentIdentifier();
-
- String urlString = url.string();
-
- if (url.isLocalFile()) {
- // Remove any query part sent to a local file.
- if (!url.query().isEmpty()) {
- // By setting the query to a null string it'll be removed.
- url.setQuery(String());
- urlString = url.string();
- }
- // Determine the MIME type based on the path.
- m_response.setMimeType(MIMETypeRegistry::getMIMETypeForPath(url));
- }
-
- 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);
- }
-
-#ifndef NDEBUG
- m_curlHandle.enableVerboseIfUsed();
- m_curlHandle.enableStdErrIfUsed();
-#endif
-
- m_curlHandle.initialize();
- m_curlHandle.setSslVerifyPeer(CurlHandle::VerifyPeerEnable);
- m_curlHandle.setSslVerifyHost(CurlHandle::VerifyHostStrictNameCheck);
- m_curlHandle.setPrivateData(this);
- m_curlHandle.setWriteCallbackFunction(writeCallback, this);
- m_curlHandle.setHeaderCallbackFunction(headerCallback, this);
- m_curlHandle.enableAutoReferer();
- m_curlHandle.enableFollowLocation();
- m_curlHandle.enableHttpAuthentication(CURLAUTH_ANY);
- m_curlHandle.enableShareHandle();
- m_curlHandle.enableTimeout();
- m_curlHandle.enableAllowedProtocols();
- auto certificate = getSSLClientCertificate(m_firstRequest.url().host());
- if (certificate) {
- m_curlHandle.setSslCert((*certificate).first.utf8().data());
- m_curlHandle.setSslCertType("P12");
- m_curlHandle.setSslKeyPassword((*certificate).second.utf8().data());
- }
-
- if (CurlContext::singleton().shouldIgnoreSSLErrors())
- m_curlHandle.setSslVerifyPeer(CurlHandle::VerifyPeerDisable);
- else
- setSSLVerifyOptions(m_curlHandle);
-
- m_curlHandle.enableCAInfoIfExists();
-
- m_curlHandle.enableAcceptEncoding();
- m_curlHandle.setUrl(urlString);
- m_curlHandle.enableCookieJarIfExists();
-
- if (m_firstRequest.httpHeaderFields().size()) {
- auto customHeaders = m_firstRequest.httpHeaderFields();
- auto& cache = CurlCacheManager::getInstance();
-
- bool hasCacheHeaders = customHeaders.contains(HTTPHeaderName::IfModifiedSince) || customHeaders.contains(HTTPHeaderName::IfNoneMatch);
- if (!hasCacheHeaders && cache.isCached(url)) {
- cache.addCacheEntryClient(url, m_handle);
-
- // append additional cache information
- for (auto entry : cache.requestHeaders(url))
- customHeaders.set(entry.key, entry.value);
-
- m_addedCacheValidationHeaders = true;
- }
-
- m_curlHandle.appendRequestHeaders(customHeaders);
- }
-
- 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();
- }
-
- m_curlHandle.enableRequestHeaders();
-
- applyAuthentication();
-
- m_curlHandle.enableProxyIfExists();
-}
-
-void ResourceHandleInternal::applyAuthentication()
-{
- // m_user/m_pass are credentials given manually, for instance, by the arguments passed to XMLHttpRequest.open().
- String partition = m_firstRequest.cachePartition();
-
- if (m_handle->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());
- }
- }
-
- String user = m_user;
- String password = m_pass;
-
- 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.
-
- // curl CURLOPT_USERPWD expects username:password
- m_curlHandle.setHttpAuthUserPass(user, password);
-}
-
-static inline size_t getFormElementsCount(ResourceHandle* job)
-{
- RefPtr<FormData> formData = job->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();
- job->firstRequest().setHTTPBody(WTFMove(formData));
-
- return size;
-}
-
-void ResourceHandleInternal::setupPUT()
-{
- m_curlHandle.enableHttpPutRequest();
-
- // Disable the Expect: 100 continue header
- m_curlHandle.appendRequestHeader("Expect:");
-
- size_t numElements = getFormElementsCount(m_handle);
- if (!numElements)
- return;
-
- setupFormData(false);
-}
-
-void ResourceHandleInternal::setupPOST()
-{
- m_curlHandle.enableHttpPostRequest();
-
- size_t numElements = getFormElementsCount(m_handle);
- if (!numElements)
- return;
-
- // Do not stream for simple POST data
- if (numElements == 1) {
- m_firstRequest.httpBody()->flatten(m_postBytes);
- if (m_postBytes.size())
- m_curlHandle.setPostFields(m_postBytes.data(), m_postBytes.size());
- return;
- }
-
- setupFormData(true);
-}
-
-void ResourceHandleInternal::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_curlHandle.setReadCallbackFunction(readCallback, this);
-}
-
#if OS(WINDOWS)
void ResourceHandle::setHostAllowsAnyHTTPSCertificate(const String& host)
@@ -357,61 +114,9 @@
{
ASSERT(isMainThread());
- auto action = "" this, protectedThis = makeRef(*this)]() {
- if (defers) {
- CURLcode error = d->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 = d->m_curlHandle.pause(CURLPAUSE_CONT);
- if (error != CURLE_OK) {
- // Restarting the handle has failed so just cancel it.
- cancel();
- }
- }
- };
-
- if (d->m_job) {
- CurlJobManager::singleton().callOnJobThread(WTFMove(action));
- } else {
- action();
- }
+ d->m_delegate->setDefersLoading(defers);
}
-void ResourceHandleInternal::didFinish()
-{
- calculateWebTimingInformations();
-
- if (m_cancelled)
- return;
-
- if (!m_response.responseFired()) {
- handleLocalReceiveResponse();
- if (m_cancelled)
- return;
- }
-
- if (m_multipartHandle)
- m_multipartHandle->contentEnded();
-
- if (client()) {
- client()->didFinishLoading(m_handle);
- CurlCacheManager::getInstance().didFinishLoading(*m_handle);
- }
-}
-
-void ResourceHandleInternal::didFail()
-{
- if (m_cancelled)
- return;
- URL url = ""
- if (client()) {
- client()->didFail(m_handle, ResourceError(CurlContext::errorDomain, m_curlHandle.errorCode(), m_curlHandle.getEffectiveURL(), m_curlHandle.errorDescription(), m_curlHandle.getSslErrors()));
- CurlCacheManager::getInstance().didFail(*m_handle);
- }
-}
-
bool ResourceHandle::shouldUseCredentialStorage()
{
return (!client() || client()->shouldUseCredentialStorage(this)) && firstRequest().url().protocolIsInHTTPFamily();
@@ -431,7 +136,7 @@
urlToStore = challenge.failureResponse().url();
CredentialStorage::defaultCredentialStorage().set(partition, credential, challenge.protectionSpace(), urlToStore);
- d->m_curlHandle.setHttpAuthUserPass(credential.user(), credential.password());
+ d->m_delegate->setAuthentication(credential.user(), credential.password());
d->m_user = String();
d->m_pass = String();
@@ -456,7 +161,7 @@
CredentialStorage::defaultCredentialStorage().set(partition, credential, challenge.protectionSpace(), challenge.failureResponse().url());
}
- d->m_curlHandle.setHttpAuthUserPass(credential.user(), credential.password());
+ d->m_delegate->setAuthentication(credential.user(), credential.password());
return;
}
}
@@ -489,7 +194,7 @@
}
}
- d->m_curlHandle.setHttpAuthUserPass(credential.user(), credential.password());
+ d->m_delegate->setAuthentication(credential.user(), credential.password());
clearAuthentication();
}
@@ -500,7 +205,7 @@
if (challenge != d->m_currentWebChallenge)
return;
- d->m_curlHandle.setHttpAuthUserPass("", "");
+ d->m_delegate->setAuthentication("", "");
clearAuthentication();
}
@@ -525,438 +230,6 @@
ASSERT_NOT_REACHED();
}
-void ResourceHandleInternal::calculateWebTimingInformations()
-{
- double preTransferTime = 0;
- double dnslookupTime = 0;
- double connectTime = 0;
- double appConnectTime = 0;
-
- m_curlHandle.getTimes(preTransferTime, dnslookupTime, connectTime, appConnectTime);
-
- m_response.deprecatedNetworkLoadMetrics().domainLookupStart = Seconds(0);
- m_response.deprecatedNetworkLoadMetrics().domainLookupEnd = Seconds(dnslookupTime);
-
- m_response.deprecatedNetworkLoadMetrics().connectStart = Seconds(dnslookupTime);
- m_response.deprecatedNetworkLoadMetrics().connectEnd = Seconds(connectTime);
-
- m_response.deprecatedNetworkLoadMetrics().requestStart = Seconds(connectTime);
- m_response.deprecatedNetworkLoadMetrics().responseStart = Seconds(preTransferTime);
-
- if (appConnectTime)
- m_response.deprecatedNetworkLoadMetrics().secureConnectionStart = Seconds(connectTime);
-}
-
-void ResourceHandleInternal::handleLocalReceiveResponse()
-{
- 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());
- m_response.setURL(url);
- if (client())
- client()->didReceiveResponse(m_handle, ResourceResponse(m_response));
- m_response.setResponseFired(true);
-}
-
-inline static bool isHttpInfo(int statusCode)
-{
- return 100 <= statusCode && statusCode < 200;
-}
-
-inline static bool isHttpRedirect(int statusCode)
-{
- return 300 <= statusCode && statusCode < 400 && statusCode != 304;
-}
-
-inline static bool isHttpAuthentication(int statusCode)
-{
- return statusCode == 401;
-}
-
-inline static bool isHttpNotModified(int statusCode)
-{
- return statusCode == 304;
-}
-
-static bool isAppendableHeader(const String &key)
-{
- static const char* appendableHeaders[] = {
- "access-control-allow-headers",
- "access-control-allow-methods",
- "access-control-allow-origin",
- "access-control-expose-headers",
- "allow",
- "cache-control",
- "connection",
- "content-encoding",
- "content-language",
- "if-match",
- "if-none-match",
- "keep-alive",
- "pragma",
- "proxy-authenticate",
- "public",
- "server",
- "set-cookie",
- "te",
- "trailer",
- "transfer-encoding",
- "upgrade",
- "user-agent",
- "vary",
- "via",
- "warning",
- "www-authenticate"
- };
-
- // Custom headers start with 'X-', and need no further checking.
- if (key.startsWith("x-", /* caseSensitive */ false))
- return true;
-
- for (auto& header : appendableHeaders) {
- if (equalIgnoringASCIICase(key, header))
- return true;
- }
-
- return false;
-}
-
-static void removeLeadingAndTrailingQuotes(String& value)
-{
- unsigned length = value.length();
- if (value.startsWith('"') && value.endsWith('"') && length > 1)
- value = value.substring(1, length - 2);
-}
-
-static bool getProtectionSpace(ResourceHandle* job, const ResourceResponse& response, ProtectionSpace& protectionSpace)
-{
- ResourceHandleInternal* d = job->getInternal();
-
- CURLcode err;
-
- long port = 0;
- err = d->m_curlHandle.getPrimaryPort(port);
- if (err != CURLE_OK)
- return false;
-
- long availableAuth = CURLAUTH_NONE;
- err = d->m_curlHandle.getHttpAuthAvail(availableAuth);
- if (err != CURLE_OK)
- return false;
-
- URL url = ""
- if (!url.isValid())
- return false;
-
- String host = url.host();
- StringView protocol = url.protocol();
-
- String realm;
-
- const String authHeader = response.httpHeaderField(HTTPHeaderName::Authorization);
- const String realmString = "realm=";
- int realmPos = authHeader.find(realmString);
- if (realmPos > 0) {
- realm = authHeader.substring(realmPos + realmString.length());
- realm = realm.left(realm.find(','));
- removeLeadingAndTrailingQuotes(realm);
- }
-
- ProtectionSpaceServerType serverType = ProtectionSpaceServerHTTP;
- if (protocol == "https")
- serverType = ProtectionSpaceServerHTTPS;
-
- ProtectionSpaceAuthenticationScheme authScheme = ProtectionSpaceAuthenticationSchemeUnknown;
-
- if (availableAuth & CURLAUTH_BASIC)
- authScheme = ProtectionSpaceAuthenticationSchemeHTTPBasic;
- if (availableAuth & CURLAUTH_DIGEST)
- authScheme = ProtectionSpaceAuthenticationSchemeHTTPDigest;
- if (availableAuth & CURLAUTH_GSSNEGOTIATE)
- authScheme = ProtectionSpaceAuthenticationSchemeNegotiate;
- if (availableAuth & CURLAUTH_NTLM)
- authScheme = ProtectionSpaceAuthenticationSchemeNTLM;
-
- protectionSpace = ProtectionSpace(host, port, serverType, realm, authScheme);
-
- return true;
-}
-
-size_t ResourceHandleInternal::willPrepareSendData(char* ptr, size_t blockSize, size_t numberOfBlocks)
-{
- if (!m_formDataStream.hasMoreElements())
- return 0;
-
- size_t size = m_formDataStream.read(ptr, blockSize, numberOfBlocks);
-
- // Something went wrong so cancel the job.
- if (!size) {
- m_handle->cancel();
- return 0;
- }
-
- return size;
-
-}
-
-void ResourceHandleInternal::didReceiveHeaderLine(const String& header)
-{
- ASSERT(isMainThread());
-
- auto splitPosition = header.find(":");
- if (splitPosition != notFound) {
- String key = header.left(splitPosition).stripWhiteSpace();
- String value = header.substring(splitPosition + 1).stripWhiteSpace();
-
- if (isAppendableHeader(key))
- m_response.addHTTPHeaderField(key, value);
- else
- m_response.setHTTPHeaderField(key, value);
- } else if (header.startsWith("HTTP", false)) {
- // This is the first line of the response.
- // Extract the http status text from this.
- //
- // 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.
- long httpCode = 0;
- m_curlHandle.getResponseCode(httpCode);
-
- String httpCodeString = String::number(httpCode);
- int statusCodePos = header.find(httpCodeString);
-
- if (statusCodePos != notFound) {
- // The status text is after the status code.
- String status = header.substring(statusCodePos + httpCodeString.length());
- m_response.setHTTPStatusText(status.stripWhiteSpace());
- }
- }
-}
-
-void ResourceHandleInternal::didReceiveAllHeaders(long httpCode, long long contentLength)
-{
- ASSERT(isMainThread());
-
- m_response.setExpectedContentLength(contentLength);
-
- m_response.setURL(m_curlHandle.getEffectiveURL());
-
- m_response.setHTTPStatusCode(httpCode);
- m_response.setMimeType(extractMIMETypeFromMediaType(m_response.httpHeaderField(HTTPHeaderName::ContentType)).convertToASCIILowercase());
- m_response.setTextEncodingName(extractCharsetFromMediaType(m_response.httpHeaderField(HTTPHeaderName::ContentType)));
-
- if (m_response.isMultipart()) {
- String boundary;
- bool parsed = MultipartHandle::extractBoundary(m_response.httpHeaderField(HTTPHeaderName::ContentType), boundary);
- if (parsed)
- m_multipartHandle = std::make_unique<MultipartHandle>(m_handle, boundary);
- }
-
- // HTTP redirection
- if (isHttpRedirect(httpCode)) {
- String location = m_response.httpHeaderField(HTTPHeaderName::Location);
- if (!location.isEmpty()) {
- URL newURL = URL(m_firstRequest.url(), location);
-
- ResourceRequest redirectedRequest = m_firstRequest;
- redirectedRequest.setURL(newURL);
- ResourceResponse response = m_response;
- if (client())
- client()->willSendRequest(m_handle, WTFMove(redirectedRequest), WTFMove(response));
-
- m_firstRequest.setURL(newURL);
-
- return;
- }
- } else if (isHttpAuthentication(httpCode)) {
- ProtectionSpace protectionSpace;
- if (getProtectionSpace(m_handle, m_response, protectionSpace)) {
- Credential credential;
- AuthenticationChallenge challenge(protectionSpace, credential, m_authFailureCount, m_response, ResourceError());
- challenge.setAuthenticationClient(m_handle);
- m_handle->didReceiveAuthenticationChallenge(challenge);
- m_authFailureCount++;
- return;
- }
- }
-
- if (client()) {
- if (isHttpNotModified(httpCode)) {
- const String& url = ""
- if (CurlCacheManager::getInstance().getCachedResponse(url, m_response)) {
- if (m_addedCacheValidationHeaders) {
- m_response.setHTTPStatusCode(200);
- m_response.setHTTPStatusText("OK");
- }
- }
- }
- client()->didReceiveResponse(m_handle, ResourceResponse(m_response));
- CurlCacheManager::getInstance().didReceiveResponse(*m_handle, m_response);
- }
-
- m_response.setResponseFired(true);
-}
-
-void ResourceHandleInternal::didReceiveContentData()
-{
- ASSERT(isMainThread());
-
- if (!m_response.responseFired())
- handleLocalReceiveResponse();
-
- Vector<char> buffer;
- {
- LockHolder locker { m_receivedBufferMutex };
- buffer = WTFMove(m_receivedBuffer);
- }
-
- char* ptr = buffer.begin();
- size_t size = buffer.size();
-
- if (m_multipartHandle)
- m_multipartHandle->contentReceived(static_cast<const char*>(ptr), size);
- else if (client()) {
- client()->didReceiveData(m_handle, ptr, size, 0);
- CurlCacheManager::getInstance().didReceiveData(*m_handle, ptr, size);
- }
-}
-
-/* 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 ResourceHandleInternal::readCallback(char* ptr, size_t size, size_t nmemb, void* data)
-{
- ASSERT(!isMainThread());
-
- ResourceHandleInternal* d = static_cast<ResourceHandleInternal*>(data);
-
- if (d->m_cancelled)
- return 0;
-
- // We should never be called when deferred loading is activated.
- ASSERT(!d->m_defersLoading);
-
- if (!size || !nmemb)
- return 0;
-
- return d->willPrepareSendData(ptr, size, nmemb);
-}
-
-/*
-* 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 ResourceHandleInternal::headerCallback(char* ptr, size_t size, size_t nmemb, void* data)
-{
- ASSERT(!isMainThread());
-
- ResourceHandleInternal* d = static_cast<ResourceHandleInternal*>(data);
- ResourceHandle* job = d->m_handle;
-
- if (d->m_cancelled)
- return 0;
-
- // We should never be called when deferred loading is activated.
- ASSERT(!d->m_defersLoading);
-
- size_t totalSize = size * nmemb;
-
- String header(static_cast<const char*>(ptr), totalSize);
-
- /*
- * a) We can finish and send the ResourceResponse
- * b) We will add the current header to the HTTPHeaderMap of the ResourceResponse
- *
- * The HTTP standard requires to use \r\n but for compatibility it recommends to
- * accept also \n.
- */
- if (header == AtomicString("\r\n") || header == AtomicString("\n")) {
- long httpCode = 0;
- d->m_curlHandle.getResponseCode(httpCode);
-
- if (!httpCode) {
- // Comes here when receiving 200 Connection Established. Just return.
- return totalSize;
- }
- if (isHttpInfo(httpCode)) {
- // 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.
- return totalSize;
- }
-
- long long contentLength = 0;
- d->m_curlHandle.getContentLenghtDownload(contentLength);
-
- callOnMainThread([job = RefPtr<ResourceHandle>(job), d, httpCode, contentLength] {
- if (!d->m_cancelled)
- d->didReceiveAllHeaders(httpCode, contentLength);
- });
- } else {
- callOnMainThread([job = RefPtr<ResourceHandle>(job), d, header] {
- if (!d->m_cancelled)
- d->didReceiveHeaderLine(header);
- });
- }
-
- return totalSize;
-}
-
-// called with data after all headers have been processed via headerCallback
-size_t ResourceHandleInternal::writeCallback(char* ptr, size_t size, size_t nmemb, void* data)
-{
- ASSERT(!isMainThread());
-
- ResourceHandleInternal* d = static_cast<ResourceHandleInternal*>(data);
- ResourceHandle* job = d->m_handle;
-
- if (d->m_cancelled)
- return 0;
-
- // We should never be called when deferred loading is activated.
- ASSERT(!d->m_defersLoading);
-
- size_t totalSize = size * nmemb;
-
- // 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
- long httpCode = 0;
- CURLcode errCd = d->m_curlHandle.getResponseCode(httpCode);
- if (CURLE_OK == errCd && httpCode >= 300 && httpCode < 400)
- return totalSize;
-
- bool shouldCall { false };
- {
- LockHolder locker(d->m_receivedBufferMutex);
-
- if (d->m_receivedBuffer.isEmpty())
- shouldCall = true;
-
- d->m_receivedBuffer.append(ptr, totalSize);
- }
-
- if (shouldCall) {
- callOnMainThread([job = RefPtr<ResourceHandle>(job), d] {
- if (!d->m_cancelled)
- d->didReceiveContentData();
- });
- }
-
- return totalSize;
-}
-
// sync loader
void ResourceHandle::platformLoadResourceSynchronously(NetworkingContext* context, const ResourceRequest& request, StoredCredentials, ResourceError& error, ResourceResponse& response, Vector<char>& data)
@@ -966,7 +239,7 @@
SynchronousLoaderClient client;
RefPtr<ResourceHandle> handle = adoptRef(new ResourceHandle(context, request, &client, false, false));
- handle->d->dispatchSynchronousJob();
+ handle->d->m_delegate->dispatchSynchronousJob();
error = client.error();
data.swap(client.mutableData());
@@ -973,96 +246,6 @@
response = client.response();
}
-void ResourceHandleInternal::dispatchSynchronousJob()
-{
- URL kurl = m_firstRequest.url();
-
- if (kurl.protocolIsData()) {
- handleDataURL();
- return;
- }
-
- // If defersLoading is true and we call curl_easy_perform
- // on a paused handle, libcURL would do the transfert anyway
- // and we would assert so force defersLoading to be false.
- m_defersLoading = false;
-
- initialize();
-
- // curl_easy_perform blocks until the transfert is finished.
- CURLcode ret = m_curlHandle.perform();
-
- calculateWebTimingInformations();
-
- if (client()) {
- if (ret != CURLE_OK)
- client()->didFail(m_handle, ResourceError(CurlContext::errorDomain, m_curlHandle.errorCode(), m_curlHandle.getEffectiveURL(), m_curlHandle.errorDescription(), m_curlHandle.getSslErrors()));
- else
- client()->didReceiveResponse(m_handle, ResourceResponse(m_response));
- }
-}
-
-void ResourceHandleInternal::handleDataURL()
-{
- ASSERT(m_firstRequest.url().protocolIsData());
- String url = ""
-
- ASSERT(client());
-
- int index = url.find(',');
- if (index == -1) {
- client()->cannotShowURL(m_handle);
- return;
- }
-
- String mediaType = url.substring(5, index - 5);
- String data = "" + 1);
-
- bool base64 = mediaType.endsWith(";base64", false);
- if (base64)
- mediaType = mediaType.left(mediaType.length() - 7);
-
- if (mediaType.isEmpty())
- mediaType = "text/plain";
-
- String mimeType = extractMIMETypeFromMediaType(mediaType);
- String charset = extractCharsetFromMediaType(mediaType);
-
- if (charset.isEmpty())
- charset = "US-ASCII";
-
- ResourceResponse response;
- response.setMimeType(mimeType);
- response.setTextEncodingName(charset);
- response.setURL(m_firstRequest.url());
-
- if (base64) {
- data = ""
- client()->didReceiveResponse(m_handle, WTFMove(response));
-
- // didReceiveResponse might cause the client to be deleted.
- if (client()) {
- Vector<char> out;
- if (base64Decode(data, out, Base64IgnoreSpacesAndNewLines) && out.size() > 0)
- client()->didReceiveData(m_handle, out.data(), out.size(), 0);
- }
- } else {
- TextEncoding encoding(charset);
- data = "" encoding);
- client()->didReceiveResponse(m_handle, WTFMove(response));
-
- // didReceiveResponse might cause the client to be deleted.
- if (client()) {
- CString encodedData = encoding.encode(data, URLEncodedEntitiesForUnencodables);
- if (encodedData.length())
- client()->didReceiveData(m_handle, encodedData.data(), encodedData.length(), 0);
- }
- }
-
- if (client())
- client()->didFinishLoading(m_handle);
-}
-
} // namespace WebCore
#endif
Copied: trunk/Source/WebCore/platform/network/curl/ResourceHandleCurlDelegate.cpp (from rev 220938, trunk/Source/WebCore/platform/network/curl/ResourceHandleCurl.cpp) (0 => 220939)
--- trunk/Source/WebCore/platform/network/curl/ResourceHandleCurlDelegate.cpp (rev 0)
+++ trunk/Source/WebCore/platform/network/curl/ResourceHandleCurlDelegate.cpp 2017-08-18 23:02:51 UTC (rev 220939)
@@ -0,0 +1,969 @@
+/*
+ * Copyright (C) 2004, 2006 Apple Inc. All rights reserved.
+ * Copyright (C) 2005, 2006 Michael Emmel [email protected]
+ * Copyright (C) 2017 Sony Interactive Entertainment Inc.
+ * All rights reserved.
+ * Copyright (C) 2017 NAVER Corp. 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. ``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 "ResourceHandleCurlDelegate.h"
+
+#if USE(CURL)
+
+#include "CredentialStorage.h"
+#include "CurlCacheManager.h"
+#include "MIMETypeRegistry.h"
+#include "MultipartHandle.h"
+#include "ResourceHandle.h"
+#include "ResourceHandleInternal.h"
+#include "SSLHandle.h"
+#include "TextEncoding.h"
+#include "ThreadSafeDataBuffer.h"
+#include "URL.h"
+#include <wtf/MainThread.h>
+#include <wtf/text/Base64.h>
+
+namespace WebCore {
+
+ResourceHandleCurlDelegate::ResourceHandleCurlDelegate(ResourceHandle* handle)
+ : m_handle(handle)
+ , m_formDataStream(handle)
+ , m_firstRequest(handle->firstRequest().isolatedCopy())
+ , m_customHTTPHeaderFields(m_firstRequest.httpHeaderFields().isolatedCopy())
+ , m_shouldUseCredentialStorage(handle->shouldUseCredentialStorage())
+ , m_user(handle->getInternal()->m_user.isolatedCopy())
+ , m_pass(handle->getInternal()->m_pass.isolatedCopy())
+ , m_initialCredential(handle->getInternal()->m_initialCredential)
+ , m_defersLoading(handle->getInternal()->m_defersLoading)
+{
+ const URL& url = ""
+
+ if (url.isLocalFile()) {
+ // Determine the MIME type based on the path.
+ response().setMimeType(MIMETypeRegistry::getMIMETypeForPath(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;
+ }
+ }
+
+ m_sslClientCertificate = getSSLClientCertificate(url.host());
+
+ setupAuthentication();
+}
+
+ResourceHandleCurlDelegate::~ResourceHandleCurlDelegate()
+{
+}
+
+bool ResourceHandleCurlDelegate::hasHandle() const
+{
+ return !!m_handle;
+}
+
+void ResourceHandleCurlDelegate::releaseHandle()
+{
+ m_handle = nullptr;
+}
+
+bool ResourceHandleCurlDelegate::start()
+{
+ m_job = CurlJobManager::singleton().add(m_curlHandle, *this);
+ return !!m_job;
+}
+
+void ResourceHandleCurlDelegate::cancel()
+{
+ releaseHandle();
+ CurlJobManager::singleton().cancel(m_job);
+}
+
+void ResourceHandleCurlDelegate::setDefersLoading(bool defers)
+{
+ if (defers == m_defersLoading)
+ return;
+
+ m_defersLoading = defers;
+
+ auto action = "" = makeRef(*this)]() {
+ 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));
+}
+
+void ResourceHandleCurlDelegate::setAuthentication(const String& user, const String& pass)
+{
+ auto action = "" = makeRef(*this), user = user.isolatedCopy(), pass = pass.isolatedCopy()]() {
+ protectedThis->m_user = user;
+ protectedThis->m_pass = pass;
+ protectedThis->m_curlHandle.setHttpAuthUserPass(user, pass);
+ };
+
+ CurlJobManager::singleton().callOnJobThread(WTFMove(action));
+}
+
+void ResourceHandleCurlDelegate::dispatchSynchronousJob()
+{
+ URL kurl = m_firstRequest.url();
+
+ if (kurl.protocolIsData()) {
+ handleDataURL();
+ return;
+ }
+
+ // If defersLoading is true and we call curl_easy_perform
+ // on a paused handle, libcURL would do the transfert anyway
+ // and we would assert so force defersLoading to be false.
+ m_defersLoading = false;
+
+ setupRequest();
+
+ // curl_easy_perform blocks until the transfer is finished.
+ CURLcode ret = m_curlHandle.perform();
+
+ double pretransferTime = 0;
+ double dnsLookupTime = 0;
+ double connectTime = 0;
+ double appConnectTime = 0;
+
+ m_curlHandle.getTimes(pretransferTime, dnsLookupTime, connectTime, appConnectTime);
+ setWebTimings(pretransferTime, dnsLookupTime, connectTime, appConnectTime);
+
+ if (m_handle->client()) {
+ if (ret != CURLE_OK) {
+ String domain = CurlContext::errorDomain;
+ int errorCode = m_curlHandle.errorCode();
+ URL failingURL = m_curlHandle.getEffectiveURL();
+ String errorDescription = m_curlHandle.errorDescription();
+ unsigned sslErrors = m_curlHandle.getSslErrors();
+
+ m_handle->client()->didFail(m_handle, ResourceError(domain, errorCode, failingURL, errorDescription, sslErrors));
+ } else
+ m_handle->client()->didReceiveResponse(m_handle, ResourceResponse(response()));
+ }
+}
+
+void ResourceHandleCurlDelegate::retain()
+{
+ ref();
+}
+
+void ResourceHandleCurlDelegate::release()
+{
+ deref();
+}
+
+void ResourceHandleCurlDelegate::setupRequest()
+{
+ CurlContext& context = CurlContext::singleton();
+
+ URL url = ""
+
+ // Remove any fragment part, otherwise curl will send it as part of the request.
+ url.removeFragmentIdentifier();
+
+ String urlString = url.string();
+
+ m_curlHandle.initialize();
+
+ if (url.isLocalFile()) {
+ // Remove any query part sent to a local file.
+ if (!url.query().isEmpty()) {
+ // By setting the query to a null string it'll be removed.
+ url.setQuery(String());
+ urlString = url.string();
+ }
+ }
+
+ 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);
+ }
+
+#ifndef NDEBUG
+ m_curlHandle.enableVerboseIfUsed();
+ m_curlHandle.enableStdErrIfUsed();
+#endif
+
+ m_curlHandle.setSslVerifyPeer(CurlHandle::VerifyPeerEnable);
+ m_curlHandle.setSslVerifyHost(CurlHandle::VerifyHostStrictNameCheck);
+ m_curlHandle.setPrivateData(this);
+ 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();
+
+ if (m_sslClientCertificate) {
+ m_curlHandle.setSslCert((*m_sslClientCertificate).first.utf8().data());
+ m_curlHandle.setSslCertType("P12");
+ m_curlHandle.setSslKeyPassword((*m_sslClientCertificate).second.utf8().data());
+ }
+
+ if (CurlContext::singleton().shouldIgnoreSSLErrors())
+ m_curlHandle.setSslVerifyPeer(CurlHandle::VerifyPeerDisable);
+ else
+ setSSLVerifyOptions(m_curlHandle);
+
+ m_curlHandle.enableCAInfoIfExists();
+
+ m_curlHandle.enableAcceptEncoding();
+ m_curlHandle.setUrl(urlString);
+ 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();
+ }
+
+ m_curlHandle.enableRequestHeaders();
+
+ applyAuthentication();
+
+ m_curlHandle.enableProxyIfExists();
+}
+
+void ResourceHandleCurlDelegate::notifyFinish()
+{
+ double pretransferTime = 0;
+ double dnsLookupTime = 0;
+ double connectTime = 0;
+ double appConnectTime = 0;
+
+ m_curlHandle.getTimes(pretransferTime, dnsLookupTime, connectTime, appConnectTime);
+
+ callOnMainThread([protectedThis = makeRef(*this), pretransferTime, dnsLookupTime, connectTime, appConnectTime] {
+ if (!protectedThis->m_handle)
+ return;
+ protectedThis->didFinish(pretransferTime, dnsLookupTime, connectTime, appConnectTime);
+ });
+}
+
+void ResourceHandleCurlDelegate::notifyFail()
+{
+ String domain = CurlContext::errorDomain;
+ int errorCode = m_curlHandle.errorCode();
+ URL failingURL = m_curlHandle.getEffectiveURL();
+ String errorDescription = m_curlHandle.errorDescription();
+ unsigned sslErrors = m_curlHandle.getSslErrors();
+
+ callOnMainThread([protectedThis = makeRef(*this), domain = domain.isolatedCopy(), errorCode, failingURL = failingURL.isolatedCopy(), errorDescription = errorDescription.isolatedCopy(), sslErrors] {
+ if (!protectedThis->m_handle)
+ return;
+ protectedThis->didFail(domain, errorCode, failingURL, errorDescription, sslErrors);
+ });
+}
+
+ResourceResponse& ResourceHandleCurlDelegate::response()
+{
+ return m_handle->getInternal()->m_response;
+}
+
+void ResourceHandleCurlDelegate::setupAuthentication()
+{
+ // m_user/m_pass are credentials given manually, for instance, by the arguments passed to XMLHttpRequest.open().
+ String partition = m_firstRequest.cachePartition();
+
+ 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());
+ }
+ }
+}
+
+static void removeLeadingAndTrailingQuotes(String& value)
+{
+ unsigned length = value.length();
+ if (value.startsWith('"') && value.endsWith('"') && length > 1)
+ value = value.substring(1, length - 2);
+}
+
+bool ResourceHandleCurlDelegate::getProtectionSpace(const ResourceResponse& response, ProtectionSpace& protectionSpace)
+{
+ CURLcode err;
+
+ long port = 0;
+ err = m_curlHandle.getPrimaryPort(port);
+ if (err != CURLE_OK)
+ return false;
+
+ long availableAuth = CURLAUTH_NONE;
+ err = m_curlHandle.getHttpAuthAvail(availableAuth);
+ if (err != CURLE_OK)
+ return false;
+
+ URL url = ""
+ if (!url.isValid())
+ return false;
+
+ String host = url.host();
+ StringView protocol = url.protocol();
+
+ String realm;
+
+ const String authHeader = response.httpHeaderField(HTTPHeaderName::Authorization);
+ const String realmString = "realm=";
+ int realmPos = authHeader.find(realmString);
+ if (realmPos > 0) {
+ realm = authHeader.substring(realmPos + realmString.length());
+ realm = realm.left(realm.find(','));
+ removeLeadingAndTrailingQuotes(realm);
+ }
+
+ ProtectionSpaceServerType serverType = ProtectionSpaceServerHTTP;
+ if (protocol == "https")
+ serverType = ProtectionSpaceServerHTTPS;
+
+ ProtectionSpaceAuthenticationScheme authScheme = ProtectionSpaceAuthenticationSchemeUnknown;
+
+ if (availableAuth & CURLAUTH_BASIC)
+ authScheme = ProtectionSpaceAuthenticationSchemeHTTPBasic;
+ if (availableAuth & CURLAUTH_DIGEST)
+ authScheme = ProtectionSpaceAuthenticationSchemeHTTPDigest;
+ if (availableAuth & CURLAUTH_GSSNEGOTIATE)
+ authScheme = ProtectionSpaceAuthenticationSchemeNegotiate;
+ if (availableAuth & CURLAUTH_NTLM)
+ authScheme = ProtectionSpaceAuthenticationSchemeNTLM;
+
+ protectionSpace = ProtectionSpace(host, port, serverType, realm, authScheme);
+
+ return true;
+}
+
+inline static bool isHttpInfo(int statusCode)
+{
+ return 100 <= statusCode && statusCode < 200;
+}
+
+inline static bool isHttpRedirect(int statusCode)
+{
+ return 300 <= statusCode && statusCode < 400 && statusCode != 304;
+}
+
+inline static bool isHttpAuthentication(int statusCode)
+{
+ return statusCode == 401;
+}
+
+inline static bool isHttpNotModified(int statusCode)
+{
+ return statusCode == 304;
+}
+
+static bool isAppendableHeader(const String &key)
+{
+ static const char* appendableHeaders[] = {
+ "access-control-allow-headers",
+ "access-control-allow-methods",
+ "access-control-allow-origin",
+ "access-control-expose-headers",
+ "allow",
+ "cache-control",
+ "connection",
+ "content-encoding",
+ "content-language",
+ "if-match",
+ "if-none-match",
+ "keep-alive",
+ "pragma",
+ "proxy-authenticate",
+ "public",
+ "server",
+ "set-cookie",
+ "te",
+ "trailer",
+ "transfer-encoding",
+ "upgrade",
+ "user-agent",
+ "vary",
+ "via",
+ "warning",
+ "www-authenticate"
+ };
+
+ // Custom headers start with 'X-', and need no further checking.
+ if (key.startsWith("x-", /* caseSensitive */ false))
+ return true;
+
+ for (auto& header : appendableHeaders) {
+ if (equalIgnoringASCIICase(key, header))
+ return true;
+ }
+
+ return false;
+}
+
+void ResourceHandleCurlDelegate::didReceiveHeaderLine(const String& header)
+{
+ ASSERT(isMainThread());
+
+ auto splitPosition = header.find(":");
+ if (splitPosition != notFound) {
+ String key = header.left(splitPosition).stripWhiteSpace();
+ String value = header.substring(splitPosition + 1).stripWhiteSpace();
+
+ if (isAppendableHeader(key))
+ response().addHTTPHeaderField(key, value);
+ else
+ response().setHTTPHeaderField(key, value);
+ } else if (header.startsWith("HTTP", false)) {
+ // This is the first line of the response.
+ // Extract the http status text from this.
+ //
+ // 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.
+ long httpCode = 0;
+ m_curlHandle.getResponseCode(httpCode);
+
+ String httpCodeString = String::number(httpCode);
+ int statusCodePos = header.find(httpCodeString);
+
+ if (statusCodePos != notFound) {
+ // The status text is after the status code.
+ String status = header.substring(statusCodePos + httpCodeString.length());
+ response().setHTTPStatusText(status.stripWhiteSpace());
+ }
+ }
+}
+
+void ResourceHandleCurlDelegate::didReceiveAllHeaders(long httpCode, long long contentLength)
+{
+ ASSERT(isMainThread());
+
+ response().setExpectedContentLength(contentLength);
+ response().setURL(m_curlHandle.getEffectiveURL());
+ response().setHTTPStatusCode(httpCode);
+ response().setMimeType(extractMIMETypeFromMediaType(response().httpHeaderField(HTTPHeaderName::ContentType)).convertToASCIILowercase());
+ response().setTextEncodingName(extractCharsetFromMediaType(response().httpHeaderField(HTTPHeaderName::ContentType)));
+
+ if (response().isMultipart()) {
+ String boundary;
+ bool parsed = MultipartHandle::extractBoundary(response().httpHeaderField(HTTPHeaderName::ContentType), boundary);
+ if (parsed)
+ m_multipartHandle = std::make_unique<MultipartHandle>(m_handle, boundary);
+ }
+
+ // HTTP redirection
+ if (isHttpRedirect(httpCode)) {
+ String location = response().httpHeaderField(HTTPHeaderName::Location);
+ if (!location.isEmpty()) {
+ URL newURL = URL(m_firstRequest.url(), location);
+
+ ResourceRequest redirectedRequest = m_firstRequest;
+ redirectedRequest.setURL(newURL);
+ ResourceResponse localResponse = response();
+ if (m_handle->client())
+ m_handle->client()->willSendRequest(m_handle, WTFMove(redirectedRequest), WTFMove(localResponse));
+
+ m_firstRequest.setURL(newURL);
+
+ return;
+ }
+ } else if (isHttpAuthentication(httpCode)) {
+ ProtectionSpace protectionSpace;
+ if (getProtectionSpace(response(), protectionSpace)) {
+ Credential credential;
+ AuthenticationChallenge challenge(protectionSpace, credential, m_authFailureCount, response(), ResourceError());
+ challenge.setAuthenticationClient(m_handle);
+ m_handle->didReceiveAuthenticationChallenge(challenge);
+ m_authFailureCount++;
+ return;
+ }
+ }
+
+ response().setResponseFired(true);
+
+ if (m_handle->client()) {
+ if (isHttpNotModified(httpCode)) {
+ const String& url = ""
+ if (CurlCacheManager::getInstance().getCachedResponse(url, response())) {
+ if (m_addedCacheValidationHeaders) {
+ response().setHTTPStatusCode(200);
+ response().setHTTPStatusText("OK");
+ }
+ }
+ }
+ CurlCacheManager::getInstance().didReceiveResponse(*m_handle, response());
+ m_handle->client()->didReceiveResponse(m_handle, ResourceResponse(response()));
+ }
+}
+
+void ResourceHandleCurlDelegate::didReceiveContentData(ThreadSafeDataBuffer buffer)
+{
+ ASSERT(isMainThread());
+
+ if (!response().responseFired())
+ handleLocalReceiveResponse();
+
+ const char* ptr = reinterpret_cast<const char*>(buffer.data()->begin());
+ size_t size = buffer.size();
+
+ if (m_multipartHandle)
+ m_multipartHandle->contentReceived(ptr, size);
+ else if (m_handle->client()) {
+ CurlCacheManager::getInstance().didReceiveData(*m_handle, ptr, size);
+ m_handle->client()->didReceiveData(m_handle, ptr, size, 0);
+ }
+}
+
+void ResourceHandleCurlDelegate::handleLocalReceiveResponse()
+{
+ 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);
+ response().setResponseFired(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.hasMoreElements())
+ return;
+
+ size_t size = m_formDataStream.read(buffer, blockSize, numberOfBlocks);
+ if (!size) {
+ // Something went wrong so cancel the job.
+ m_handle->cancel();
+ return;
+ }
+
+ m_sendBytes = size;
+ m_workerThreadConditionVariable.notifyOne();
+}
+
+void ResourceHandleCurlDelegate::didFinish(double pretransferTime, double dnsLookupTime, double connectTime, double appConnectTime)
+{
+ setWebTimings(pretransferTime, dnsLookupTime, connectTime, appConnectTime);
+
+ if (!m_handle)
+ return;
+
+ if (!response().responseFired()) {
+ handleLocalReceiveResponse();
+ if (!m_handle)
+ return;
+ }
+
+ if (m_multipartHandle)
+ m_multipartHandle->contentEnded();
+
+ if (m_handle->client()) {
+ CurlCacheManager::getInstance().didFinishLoading(*m_handle);
+ m_handle->client()->didFinishLoading(m_handle);
+ }
+}
+
+void ResourceHandleCurlDelegate::didFail(const String& domain, int errorCode, const URL& failingURL, const String& localizedDescription, unsigned sslErrors)
+{
+ if (!m_handle)
+ return;
+
+ if (m_handle->client()) {
+ CurlCacheManager::getInstance().didFail(*m_handle);
+ m_handle->client()->didFail(m_handle, ResourceError(domain, errorCode, failingURL, localizedDescription, sslErrors));
+ }
+}
+
+void ResourceHandleCurlDelegate::handleDataURL()
+{
+ ASSERT(m_firstRequest.url().protocolIsData());
+ String url = ""
+
+ ASSERT(m_handle->client());
+
+ int index = url.find(',');
+ if (index == -1) {
+ m_handle->client()->cannotShowURL(m_handle);
+ return;
+ }
+
+ String mediaType = url.substring(5, index - 5);
+ String data = "" + 1);
+
+ bool base64 = mediaType.endsWith(";base64", false);
+ if (base64)
+ mediaType = mediaType.left(mediaType.length() - 7);
+
+ if (mediaType.isEmpty())
+ mediaType = "text/plain";
+
+ String mimeType = extractMIMETypeFromMediaType(mediaType);
+ String charset = extractCharsetFromMediaType(mediaType);
+
+ if (charset.isEmpty())
+ charset = "US-ASCII";
+
+ ResourceResponse response;
+ response.setMimeType(mimeType);
+ response.setTextEncodingName(charset);
+ response.setURL(m_firstRequest.url());
+
+ if (base64) {
+ data = ""
+ m_handle->client()->didReceiveResponse(m_handle, WTFMove(response));
+
+ // didReceiveResponse might cause the client to be deleted.
+ if (m_handle->client()) {
+ Vector<char> out;
+ if (base64Decode(data, out, Base64IgnoreSpacesAndNewLines) && out.size() > 0)
+ m_handle->client()->didReceiveData(m_handle, out.data(), out.size(), 0);
+ }
+ } else {
+ TextEncoding encoding(charset);
+ data = "" encoding);
+ m_handle->client()->didReceiveResponse(m_handle, WTFMove(response));
+
+ // didReceiveResponse might cause the client to be deleted.
+ if (m_handle->client()) {
+ CString encodedData = encoding.encode(data, URLEncodedEntitiesForUnencodables);
+ if (encodedData.length())
+ m_handle->client()->didReceiveData(m_handle, encodedData.data(), encodedData.length(), 0);
+ }
+ }
+
+ if (m_handle->client())
+ m_handle->client()->didFinishLoading(m_handle);
+}
+
+void ResourceHandleCurlDelegate::setupPOST()
+{
+ m_curlHandle.enableHttpPostRequest();
+
+ size_t numElements = getFormElementsCount();
+ if (!numElements)
+ return;
+
+ // Do not stream for simple POST data
+ if (numElements == 1) {
+ m_firstRequest.httpBody()->flatten(m_postBytes);
+ if (m_postBytes.size())
+ m_curlHandle.setPostFields(m_postBytes.data(), m_postBytes.size());
+ return;
+ }
+
+ setupFormData(true);
+}
+
+void ResourceHandleCurlDelegate::setupPUT()
+{
+ m_curlHandle.enableHttpPutRequest();
+
+ // Disable the Expect: 100 continue header
+ m_curlHandle.appendRequestHeader("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_curlHandle.setReadCallbackFunction(willSendDataCallback, this);
+}
+
+void ResourceHandleCurlDelegate::applyAuthentication()
+{
+ String user = m_user;
+ String password = m_pass;
+
+ 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.
+
+ // curl CURLOPT_USERPWD expects username:password
+ m_curlHandle.setHttpAuthUserPass(user, password);
+}
+
+void ResourceHandleCurlDelegate::setWebTimings(double pretransferTime, double dnsLookupTime, double connectTime, double appConnectTime)
+{
+ response().deprecatedNetworkLoadMetrics().domainLookupStart = Seconds(0);
+ response().deprecatedNetworkLoadMetrics().domainLookupEnd = Seconds(dnsLookupTime);
+
+ response().deprecatedNetworkLoadMetrics().connectStart = Seconds(dnsLookupTime);
+ response().deprecatedNetworkLoadMetrics().connectEnd = Seconds(connectTime);
+
+ response().deprecatedNetworkLoadMetrics().requestStart = Seconds(connectTime);
+ response().deprecatedNetworkLoadMetrics().responseStart = Seconds(pretransferTime);
+
+ if (appConnectTime)
+ response().deprecatedNetworkLoadMetrics().secureConnectionStart = Seconds(connectTime);
+}
+
+/*
+* 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)
+{
+ if (!m_handle)
+ return 0;
+
+ if (m_defersLoading)
+ return 0;
+
+ /*
+ * a) We can finish and send the ResourceResponse
+ * b) We will add the current header to the HTTPHeaderMap of the ResourceResponse
+ *
+ * The HTTP standard requires to use \r\n but for compatibility it recommends to
+ * accept also \n.
+ */
+ if (header == AtomicString("\r\n") || header == AtomicString("\n")) {
+ long httpCode = 0;
+ m_curlHandle.getResponseCode(httpCode);
+
+ if (!httpCode) {
+ // Comes here when receiving 200 Connection Established. Just return.
+ return header.length();
+ }
+ if (isHttpInfo(httpCode)) {
+ // 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.
+ return header.length();
+ }
+
+ long long contentLength = 0;
+ m_curlHandle.getContentLenghtDownload(contentLength);
+
+ callOnMainThread([protectedThis = makeRef(*this), httpCode, contentLength] {
+ if (!protectedThis->m_handle)
+ return;
+ protectedThis->didReceiveAllHeaders(httpCode, contentLength);
+ });
+ } else {
+ callOnMainThread([protectedThis = makeRef(*this), header = header.isolatedCopy() ] {
+ if (!protectedThis->m_handle)
+ return;
+ protectedThis->didReceiveHeaderLine(header);
+ });
+ }
+
+ return header.length();
+}
+
+// called with data after all headers have been processed via headerCallback
+size_t ResourceHandleCurlDelegate::didReceiveData(ThreadSafeDataBuffer data)
+{
+ if (!m_handle)
+ return 0;
+
+ if (m_defersLoading)
+ return 0;
+
+ // 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
+ long httpCode = 0;
+ CURLcode errCd = m_curlHandle.getResponseCode(httpCode);
+ if (CURLE_OK == errCd && httpCode >= 300 && httpCode < 400)
+ return data.size();
+
+ if (!data.size())
+ return 0;
+
+ callOnMainThread([protectedThis = makeRef(*this), data] {
+ if (!protectedThis->m_handle)
+ return;
+ protectedThis->didReceiveContentData(data);
+ });
+
+ return data.size();
+}
+
+/* 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;
+
+ 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;
+}
+
+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(ThreadSafeDataBuffer::copyData(static_cast<const char*>(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
Added: trunk/Source/WebCore/platform/network/curl/ResourceHandleCurlDelegate.h (0 => 220939)
--- trunk/Source/WebCore/platform/network/curl/ResourceHandleCurlDelegate.h (rev 0)
+++ trunk/Source/WebCore/platform/network/curl/ResourceHandleCurlDelegate.h 2017-08-18 23:02:51 UTC (rev 220939)
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2017 NAVER Corp. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 THE COPYRIGHT HOLDERS ``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 THE COPYRIGHT OWNER 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.
+ */
+
+#pragma once
+
+#if USE(CURL)
+
+#include "Credential.h"
+#include "CurlContext.h"
+#include "CurlJobManager.h"
+#include "FormDataStreamCurl.h"
+#include "ResourceRequest.h"
+#include "ResourceResponse.h"
+#include "SSLHandle.h"
+#include <wtf/Condition.h>
+#include <wtf/ThreadSafeRefCounted.h>
+
+namespace WebCore {
+
+class MultipartHandle;
+class ProtectionSpace;
+class ResourceHandle;
+class ThreadSafeDataBuffer;
+
+class ResourceHandleCurlDelegate final : public ThreadSafeRefCounted<ResourceHandleCurlDelegate>, public CurlJobClient {
+public:
+ ResourceHandleCurlDelegate(ResourceHandle*);
+ ~ResourceHandleCurlDelegate();
+
+ bool hasHandle() const;
+ void releaseHandle();
+
+ bool start();
+ void cancel();
+
+ void setDefersLoading(bool);
+ void setAuthentication(const String&, const String&);
+
+ void dispatchSynchronousJob();
+
+private:
+ void retain() override;
+ void release() override;
+
+ void setupRequest() override;
+ void notifyFinish() override;
+ void notifyFail() override;
+
+ // Called from main thread.
+ ResourceResponse& response();
+
+ void setupAuthentication();
+ bool getProtectionSpace(const ResourceResponse&, ProtectionSpace&);
+
+ void didReceiveHeaderLine(const String&);
+ void didReceiveAllHeaders(long httpCode, long long contentLength);
+ void didReceiveContentData(ThreadSafeDataBuffer);
+ void handleLocalReceiveResponse();
+ void prepareSendData(char*, size_t blockSize, size_t numberOfBlocks);
+
+ void didFinish(double, double, double, double);
+ void didFail(const String& domain, int errorCode, const URL& failingURL, const String& localizedDescription, unsigned sslErrors);
+
+ void handleDataURL();
+
+ // Called from worker thread.
+ void setupPOST();
+ void setupPUT();
+ size_t getFormElementsCount();
+ void setupFormData(bool);
+ void applyAuthentication();
+ void setWebTimings(double, double, double, double);
+
+ size_t didReceiveHeader(String&&);
+ size_t didReceiveData(ThreadSafeDataBuffer);
+ size_t willSendData(char*, size_t blockSize, size_t numberOfBlocks);
+
+ 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;
+ FormDataStream m_formDataStream;
+ std::unique_ptr<MultipartHandle> m_multipartHandle;
+ unsigned short m_authFailureCount { 0 };
+ CurlJobTicket m_job { nullptr };
+ // Used by worker thread.
+ ResourceRequest m_firstRequest;
+ HTTPHeaderMap m_customHTTPHeaderFields;
+ bool m_shouldUseCredentialStorage;
+ String m_user;
+ String m_pass;
+ Credential m_initialCredential;
+ std::optional<ClientCertificate> m_sslClientCertificate;
+ bool m_defersLoading;
+ bool m_addedCacheValidationHeaders { false };
+ Vector<char> m_postBytes;
+ CurlHandle m_curlHandle;
+ // Used by both threads.
+ Condition m_workerThreadConditionVariable;
+ Lock m_workerThreadMutex;
+ size_t m_sendBytes { 0 };
+};
+
+} // namespace WebCore
+
+#endif