Diff
Modified: trunk/Source/WebCore/ChangeLog (219605 => 219606)
--- trunk/Source/WebCore/ChangeLog 2017-07-18 07:20:33 UTC (rev 219605)
+++ trunk/Source/WebCore/ChangeLog 2017-07-18 07:20:34 UTC (rev 219606)
@@ -1,3 +1,138 @@
+2017-07-18 Basuke Suzuki <[email protected]>
+
+ [Curl] Unify ResourceHandleManager into CurlJobManager.
+ https://bugs.webkit.org/show_bug.cgi?id=173991
+
+ Use CurlJobManager to make ResourceHandle run in background.
+ CurlJobManager::start() now accept callback to manage life cycle
+ of the resource easily in a same context.
+ Actual management tasks of CurlJobManager is now private class
+ in CurlJobManager which is better thread safeness.
+ ResourceHandleManager is deleted at all.
+
+ Reviewed by Alex Christensen.
+
+ * platform/Curl.cmake:
+ * platform/network/ResourceHandle.h:
+ * platform/network/curl/CookieJarCurl.cpp:
+ (WebCore::cookiesForSession):
+ * platform/network/curl/CurlContext.cpp:
+ (WebCore::CurlHandle::CurlHandle):
+ (WebCore::CurlHandle::~CurlHandle):
+ (WebCore::CurlHandle::errorDescription):
+ (WebCore::CurlHandle::perform):
+ (WebCore::CurlHandle::pause):
+ (WebCore::CurlHandle::appendRequestHeaders):
+ (WebCore::CurlHandle::appendRequestHeader):
+ (WebCore::CurlHandle::enableRequestHeaders):
+ (WebCore::CurlHandle::fetchCookieList):
+ (WebCore::CurlHandle::getEffectiveURL):
+ (WebCore::CurlHandle::setPrivateData): Deleted.
+ (WebCore::CurlHandle::clearRequestHeaders): Deleted.
+ (WebCore::CurlHandle::getCookieList): Deleted.
+ (WebCore::CurlHandle::clearCookieList): Deleted.
+ * platform/network/curl/CurlContext.h:
+ (WebCore::CurlSList::CurlSList):
+ (WebCore::CurlSList::~CurlSList):
+ (WebCore::CurlSList::operator struct curl_slist** ):
+ (WebCore::CurlSList::head):
+ (WebCore::CurlSList::isEmpty):
+ (WebCore::CurlSList::clear):
+ (WebCore::CurlSList::append):
+ (WebCore::CurlHandle::errorCode):
+ (WebCore::CurlHandle::setErrorCode):
+ (WebCore::CurlHandle::privateData):
+ (WebCore::CurlHandle::setPrivateData):
+ * platform/network/curl/CurlDownload.cpp:
+ (WebCore::CurlDownload::start):
+ (WebCore::CurlDownload::cancel):
+ (WebCore::CurlDownload::addHeaders):
+ (WebCore::CurlDownload::didReceiveData):
+ (WebCore::CurlDownload::didFail):
+ (WebCore::CurlDownload::handleCurlMsg): Deleted.
+ * platform/network/curl/CurlDownload.h:
+ * platform/network/curl/CurlJobManager.cpp:
+ (WebCore::CurlJobList::append):
+ (WebCore::CurlJobList::cancel):
+ (WebCore::CurlJobList::complete):
+ (WebCore::CurlJobList::isEmpty):
+ (WebCore::CurlJobList::withJob):
+ (WebCore::CurlJobList::withCurlHandle):
+ (WebCore::CurlJob::invoke):
+ (WebCore::CurlJobManager::add):
+ (WebCore::CurlJobManager::cancel):
+ (WebCore::CurlJobManager::callOnJobThread):
+ (WebCore::CurlJobManager::startThreadIfNeeded):
+ (WebCore::CurlJobManager::stopThreadIfNoMoreJobRunning):
+ (WebCore::CurlJobManager::stopThread):
+ (WebCore::CurlJobManager::updateJobs):
+ (WebCore::CurlJobManager::workerThread):
+ (WebCore::CurlJobManager::CurlJobManager): Deleted.
+ (WebCore::CurlJobManager::~CurlJobManager): Deleted.
+ (WebCore::CurlJobManager::remove): Deleted.
+ (WebCore::CurlJobManager::getActiveCount): Deleted.
+ (WebCore::CurlJobManager::getPendingCount): Deleted.
+ (WebCore::CurlJobManager::stopThreadIfIdle): Deleted.
+ (WebCore::CurlJobManager::updateHandleList): Deleted.
+ (WebCore::CurlJobManager::addToCurl): Deleted.
+ (WebCore::CurlJobManager::removeFromCurl): Deleted.
+ * platform/network/curl/CurlJobManager.h:
+ (WebCore::CurlJob::CurlJob):
+ (WebCore::CurlJob::~CurlJob):
+ (WebCore::CurlJob::operator=):
+ (WebCore::CurlJob::curlHandle):
+ (WebCore::CurlJob::ticket):
+ (WebCore::CurlJob::finished):
+ (WebCore::CurlJob::error):
+ (WebCore::CurlJob::cancel):
+ (WebCore::CurlJobManager::singleton):
+ (WebCore::CurlJobManager::~CurlJobManager):
+ (WebCore::CurlJobManager::isActiveJob):
+ (WebCore::CurlJobManager::runThread): Deleted.
+ (WebCore::CurlJobManager::setRunThread): Deleted.
+ * platform/network/curl/ResourceError.h:
+ (WebCore::ResourceError::ResourceError):
+ * platform/network/curl/ResourceHandleCurl.cpp:
+ (WebCore::ResourceHandle::~ResourceHandle):
+ (WebCore::ResourceHandle::start):
+ (WebCore::ResourceHandle::cancel):
+ (WebCore::ResourceHandle::initialize):
+ (WebCore::ResourceHandle::applyAuthentication):
+ (WebCore::getFormElementsCount):
+ (WebCore::ResourceHandle::setupPUT):
+ (WebCore::ResourceHandle::setupPOST):
+ (WebCore::ResourceHandle::setupFormData):
+ (WebCore::ResourceHandle::setHostAllowsAnyHTTPSCertificate):
+ (WebCore::ResourceHandle::setClientCertificateInfo):
+ (WebCore::ResourceHandle::platformSetDefersLoading):
+ (WebCore::ResourceHandle::didFinish):
+ (WebCore::ResourceHandle::didFail):
+ (WebCore::ResourceHandle::shouldUseCredentialStorage):
+ (WebCore::ResourceHandle::didReceiveAuthenticationChallenge):
+ (WebCore::ResourceHandle::receivedCredential):
+ (WebCore::ResourceHandle::receivedRequestToContinueWithoutCredential):
+ (WebCore::ResourceHandle::receivedCancellation):
+ (WebCore::ResourceHandle::calculateWebTimingInformations):
+ (WebCore::ResourceHandle::handleLocalReceiveResponse):
+ (WebCore::ResourceHandle::willPrepareSendData):
+ (WebCore::ResourceHandle::didReceiveHeaderLine):
+ (WebCore::ResourceHandle::didReceiveAllHeaders):
+ (WebCore::ResourceHandle::didReceiveContentData):
+ (WebCore::ResourceHandle::readCallback):
+ (WebCore::ResourceHandle::headerCallback):
+ (WebCore::ResourceHandle::writeCallback):
+ (WebCore::ResourceHandle::platformLoadResourceSynchronously):
+ (WebCore::ResourceHandle::dispatchSynchronousJob):
+ (WebCore::calculateWebTimingInformations): Deleted.
+ (WebCore::handleLocalReceiveResponse): Deleted.
+ (WebCore::writeCallback): Deleted.
+ (WebCore::headerCallback): Deleted.
+ (WebCore::readCallback): Deleted.
+ (WebCore::setupFormData): Deleted.
+ (WebCore::ResourceHandle::handleCurlMsg): Deleted.
+ * platform/network/curl/ResourceHandleManager.cpp: Removed.
+ * platform/network/curl/ResourceHandleManager.h: Removed.
+
2017-07-17 Konstantin Tokarev <[email protected]>
Unreviewed fix to Mac CMake build after r219474.
Modified: trunk/Source/WebCore/platform/Curl.cmake (219605 => 219606)
--- trunk/Source/WebCore/platform/Curl.cmake 2017-07-18 07:20:33 UTC (rev 219605)
+++ trunk/Source/WebCore/platform/Curl.cmake 2017-07-18 07:20:34 UTC (rev 219606)
@@ -15,7 +15,6 @@
platform/network/curl/MultipartHandle.cpp
platform/network/curl/ProxyServerCurl.cpp
platform/network/curl/ResourceHandleCurl.cpp
- platform/network/curl/ResourceHandleManager.cpp
platform/network/curl/SSLHandle.cpp
platform/network/curl/SocketStreamHandleImplCurl.cpp
platform/network/curl/SynchronousLoaderClientCurl.cpp
Modified: trunk/Source/WebCore/platform/network/ResourceHandle.h (219605 => 219606)
--- trunk/Source/WebCore/platform/network/ResourceHandle.h 2017-07-18 07:20:33 UTC (rev 219605)
+++ trunk/Source/WebCore/platform/network/ResourceHandle.h 2017-07-18 07:20:34 UTC (rev 219606)
@@ -70,6 +70,7 @@
#if USE(CURL)
#include "CurlJobManager.h"
+#include <wtf/Lock.h>
#endif
namespace WTF {
@@ -185,12 +186,6 @@
MonotonicTime m_requestTime;
#endif
-#if USE(CURL)
- void initialize();
- void handleDataURL();
- void handleCurlMsg(CURLMsg*);
-#endif
-
bool hasAuthenticationChallenge() const;
void clearAuthentication();
WEBCORE_EXPORT virtual void cancel();
@@ -293,14 +288,40 @@
#endif
#if USE(CURL)
- void dispatchSynchronousJob();
+ CurlJobTicket m_job;
+ Vector<char> m_receivedBuffer;
+ Lock m_receivedBufferMutex;
+
+ void initialize();
+ void applyAuthentication();
void setupPOST();
void setupPUT();
+ void setupFormData(bool isPostRequest);
- void applyAuthentication();
+ 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();
+
+#if ENABLE(WEB_TIMING)
+ void calculateWebTimingInformations();
#endif
+#endif
+
friend class ResourceHandleInternal;
std::unique_ptr<ResourceHandleInternal> d;
};
Modified: trunk/Source/WebCore/platform/network/curl/CookieJarCurl.cpp (219605 => 219606)
--- trunk/Source/WebCore/platform/network/curl/CookieJarCurl.cpp 2017-07-18 07:20:33 UTC (rev 219605)
+++ trunk/Source/WebCore/platform/network/curl/CookieJarCurl.cpp 2017-07-18 07:20:34 UTC (rev 219606)
@@ -268,17 +268,18 @@
CurlHandle curlHandle;
curlHandle.enableShareHandle();
- struct curl_slist* list = curlHandle.getCookieList();
+ CurlSList cookieList;
+ curlHandle.fetchCookieList(cookieList);
+ const struct curl_slist* list = cookieList.head();
if (list) {
String domain = url.host();
String path = url.path();
StringBuilder cookiesBuilder;
- struct curl_slist* item = list;
- while (item) {
- const char* cookie = item->data;
+ while (list) {
+ const char* cookie = list->data;
addMatchingCurlCookie(cookie, domain, path, cookiesBuilder, httponly);
- item = item->next;
+ list = list->next;
}
cookies = cookiesBuilder.toString();
Modified: trunk/Source/WebCore/platform/network/curl/CurlContext.cpp (219605 => 219606)
--- trunk/Source/WebCore/platform/network/curl/CurlContext.cpp 2017-07-18 07:20:33 UTC (rev 219605)
+++ trunk/Source/WebCore/platform/network/curl/CurlContext.cpp 2017-07-18 07:20:34 UTC (rev 219606)
@@ -28,7 +28,7 @@
#if USE(CURL)
#include "CurlContext.h"
-
+#include "HTTPHeaderMap.h"
#include <wtf/MainThread.h>
#include <wtf/text/CString.h>
@@ -101,6 +101,8 @@
// CurlContext -------------------------------------------------------------------
+const char* const CurlContext::errorDomain = "CurlErrorDomain";
+
CurlContext::CurlContext()
: m_cookieJarFileName { cookieJarPath() }
, m_certificatePath { certificatePath() }
@@ -285,28 +287,31 @@
m_handle = curl_easy_init();
curl_easy_setopt(m_handle, CURLOPT_ERRORBUFFER, m_errorBuffer);
+ curl_easy_setopt(m_handle, CURLOPT_PRIVATE, this);
}
CurlHandle::~CurlHandle()
{
- clearCookieList();
clearUrl();
- clearRequestHeaders();
- if (m_handle) {
- curl_easy_cleanup(m_handle);
- m_handle = nullptr;
- }
+ curl_easy_cleanup(m_handle);
}
+const String CurlHandle::errorDescription() const
+{
+ return String(curl_easy_strerror(m_errorCode));
+}
+
CURLcode CurlHandle::perform()
{
- return curl_easy_perform(m_handle);
+ m_errorCode = curl_easy_perform(m_handle);
+ return m_errorCode;
}
CURLcode CurlHandle::pause(int bitmask)
{
- return curl_easy_pause(m_handle, CURLPAUSE_ALL);
+ m_errorCode = curl_easy_pause(m_handle, CURLPAUSE_ALL);
+ return m_errorCode;
}
void CurlHandle::enableShareHandle()
@@ -314,11 +319,6 @@
curl_easy_setopt(m_handle, CURLOPT_SHARE, CurlContext::singleton().shareHandle().handle());
}
-void CurlHandle::setPrivateData(void* userData)
-{
- curl_easy_setopt(m_handle, CURLOPT_PRIVATE, userData);
-}
-
void CurlHandle::setUrl(const String& url)
{
clearUrl();
@@ -336,11 +336,15 @@
}
}
-void CurlHandle::clearRequestHeaders()
+void CurlHandle::appendRequestHeaders(const HTTPHeaderMap& headers)
{
- if (m_requestHeaders) {
- curl_slist_free_all(m_requestHeaders);
- m_requestHeaders = nullptr;
+ if (headers.size()) {
+ for (auto& entry : headers) {
+ auto& value = entry.value;
+ appendRequestHeader(entry.key, entry.value);
+ }
+
+ enableRequestHeaders();
}
}
@@ -361,13 +365,15 @@
void CurlHandle::appendRequestHeader(const String& header)
{
- m_requestHeaders = curl_slist_append(m_requestHeaders, header.latin1().data());
+ m_requestHeaders.append(header);
}
void CurlHandle::enableRequestHeaders()
{
- if (m_requestHeaders)
- curl_easy_setopt(m_handle, CURLOPT_HTTPHEADER, m_requestHeaders);
+ if (!m_requestHeaders.isEmpty()) {
+ const struct curl_slist* headers = m_requestHeaders.head();
+ curl_easy_setopt(m_handle, CURLOPT_HTTPHEADER, headers);
+ }
}
void CurlHandle::enableHttpGetRequest()
@@ -508,21 +514,11 @@
curl_easy_setopt(m_handle, CURLOPT_COOKIELIST, cookieList);
}
-struct curl_slist* CurlHandle::getCookieList()
+void CurlHandle::fetchCookieList(CurlSList &cookies) const
{
- clearCookieList();
-
- curl_easy_getinfo(m_handle, CURLINFO_COOKIELIST, &m_cookieList);
-
- return m_cookieList;
+ curl_easy_getinfo(m_handle, CURLINFO_COOKIELIST, static_cast<struct curl_slist**>(cookies));
}
-void CurlHandle::clearCookieList()
-{
- if (!m_cookieList)
- curl_slist_free_all(m_cookieList);
-}
-
void CurlHandle::enableProxyIfExists()
{
auto& proxy = CurlContext::singleton().proxyInfo();
@@ -564,7 +560,7 @@
curl_easy_setopt(m_handle, CURLOPT_SSL_CTX_FUNCTION, callbackFunc);
}
-URL CurlHandle::getEffectiveURL()
+URL CurlHandle::getEffectiveURL() const
{
CURLcode errCd = CURLE_FAILED_INIT;
char* url = ""
Modified: trunk/Source/WebCore/platform/network/curl/CurlContext.h (219605 => 219606)
--- trunk/Source/WebCore/platform/network/curl/CurlContext.h 2017-07-18 07:20:33 UTC (rev 219605)
+++ trunk/Source/WebCore/platform/network/curl/CurlContext.h 2017-07-18 07:20:34 UTC (rev 219606)
@@ -102,6 +102,8 @@
const String url() const;
};
+ static const char* const errorDomain;
+
static CurlContext& singleton()
{
static CurlContext shared;
@@ -167,10 +169,36 @@
CURLM* m_multiHandle { nullptr };
};
+// CurlSList -------------------------------------------------
+class CurlSList {
+public:
+ CurlSList() { }
+ ~CurlSList() { clear(); }
+ operator struct curl_slist** () { return &m_list; }
+
+ const struct curl_slist* head() const { return m_list; }
+ bool isEmpty() const { return m_list; }
+ void clear()
+ {
+ if (m_list) {
+ curl_slist_free_all(m_list);
+ m_list = nullptr;
+ }
+ }
+
+ void append(const char* str) { m_list = curl_slist_append(m_list, str); }
+ void append(const String& str) { append(str.latin1().data()); }
+
+private:
+ struct curl_slist* m_list { nullptr };
+};
+
// CurlHandle -------------------------------------------------
+class HTTPHeaderMap;
+
class CurlHandle {
WTF_MAKE_NONCOPYABLE(CurlHandle);
@@ -193,13 +221,20 @@
CURLcode perform();
CURLcode pause(int);
+ CURLcode errorCode() const { return m_errorCode; }
+ void setErrorCode(CURLcode errorCode) { m_errorCode = errorCode; }
+
+ const String errorDescription() const;
+
void enableShareHandle();
- void setPrivateData(void* userData);
+ void* privateData() const { return m_privateData; }
+ void setPrivateData(void* userData) { m_privateData = userData; }
+
void setUrl(const String&);
const char* url() const { return m_url; }
- void clearRequestHeaders();
+ void appendRequestHeaders(const HTTPHeaderMap&);
void appendRequestHeader(const String&, const String&);
void appendRequestHeader(const String&);
void enableRequestHeaders();
@@ -231,7 +266,7 @@
void enableCookieJarIfExists();
void setCookieList(const char*);
- struct curl_slist* getCookieList();
+ void fetchCookieList(CurlSList &cookies) const;
void enableProxyIfExists();
@@ -244,7 +279,7 @@
void setSslCtxCallbackFunction(curl_ssl_ctx_callback, void*);
// Status
- URL getEffectiveURL();
+ URL getEffectiveURL() const;
CURLcode getPrimaryPort(long&);
CURLcode getResponseCode(long&);
CURLcode getContentLenghtDownload(long long&);
@@ -260,16 +295,16 @@
private:
void clearUrl();
- void clearCookieList();
static int expectedSizeOfCurlOffT();
CURL* m_handle { nullptr };
char m_errorBuffer[CURL_ERROR_SIZE] { };
+ CURLcode m_errorCode;
char* m_url { nullptr };
- struct curl_slist* m_requestHeaders { nullptr };
- struct curl_slist* m_cookieList { nullptr };
+ void* m_privateData { nullptr };
+ CurlSList m_requestHeaders;
};
}
Modified: trunk/Source/WebCore/platform/network/curl/CurlDownload.cpp (219605 => 219606)
--- trunk/Source/WebCore/platform/network/curl/CurlDownload.cpp 2017-07-18 07:20:33 UTC (rev 219605)
+++ trunk/Source/WebCore/platform/network/curl/CurlDownload.cpp 2017-07-18 07:20:34 UTC (rev 219606)
@@ -86,13 +86,27 @@
bool CurlDownload::start()
{
- ref(); // CurlJobManager will call deref when the download has finished.
- return CurlJobManager::singleton().add(m_curlHandle.handle());
+ 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;
}
bool CurlDownload::cancel()
{
- return CurlJobManager::singleton().remove(m_curlHandle.handle());
+ CurlJobManager::singleton().cancel(m_job);
+ return true;
}
String CurlDownload::getTempPath() const
@@ -145,28 +159,7 @@
void CurlDownload::addHeaders(const ResourceRequest& request)
{
LockHolder locker(m_mutex);
-
- m_curlHandle.clearRequestHeaders();
-
- if (request.httpHeaderFields().size() > 0) {
- HTTPHeaderMap customHeaders = request.httpHeaderFields();
- HTTPHeaderMap::const_iterator end = customHeaders.end();
- for (HTTPHeaderMap::const_iterator it = customHeaders.begin(); it != end; ++it) {
- const String& value = it->value;
- String headerString(it->key);
- if (value.isEmpty())
- // Insert the ; to tell curl that this header has an empty value.
- headerString.append(";");
- else {
- headerString.append(": ");
- headerString.append(value);
- }
-
- m_curlHandle.appendRequestHeader(headerString);
- }
-
- m_curlHandle.enableRequestHeaders();
- }
+ m_curlHandle.appendRequestHeaders(request.httpHeaderFields());
}
void CurlDownload::didReceiveHeader(const String& header)
@@ -201,9 +194,7 @@
{
LockHolder locker(m_mutex);
- RefPtr<CurlDownload> protectedThis(this);
-
- callOnMainThread([this, size, protectedThis] {
+ callOnMainThread([this, size, protectedThis = makeRef(*this)] {
didReceiveDataOfLength(size);
});
@@ -234,12 +225,13 @@
void CurlDownload::didFail()
{
closeFile();
+ {
+ LockHolder locker(m_mutex);
- LockHolder locker(m_mutex);
+ if (m_deletesFileUponFailure)
+ deleteFile(m_tempPath);
+ }
- if (m_deletesFileUponFailure)
- deleteFile(m_tempPath);
-
if (m_listener)
m_listener->didFail();
}
@@ -292,29 +284,6 @@
download->didReceiveResponse();
}
-CurlJobAction CurlDownload::handleCurlMsg(CURLMsg* msg)
-{
- switch (msg->msg) {
- case CURLMSG_DONE: {
- if (msg->data.result == CURLE_OK) {
- callOnMainThread([this] {
- didFinish();
- deref(); // This matches the ref() in CurlDownload::start().
- });
- } else {
- callOnMainThread([this] {
- didFail();
- deref(); // This matches the ref() in CurlDownload::start().
- });
- }
- return CurlJobAction::Finished;
- }
- default: {
- return CurlJobAction::None;
- }
- }
}
-}
-
#endif
Modified: trunk/Source/WebCore/platform/network/curl/CurlDownload.h (219605 => 219606)
--- trunk/Source/WebCore/platform/network/curl/CurlDownload.h 2017-07-18 07:20:33 UTC (rev 219605)
+++ trunk/Source/WebCore/platform/network/curl/CurlDownload.h 2017-07-18 07:20:34 UTC (rev 219606)
@@ -50,7 +50,7 @@
virtual void didFail() { }
};
-class CurlDownload : public ThreadSafeRefCounted<CurlDownload>, public CurlJob {
+class CurlDownload : public ThreadSafeRefCounted<CurlDownload> {
public:
CurlDownload();
~CurlDownload();
@@ -72,8 +72,6 @@
void setDestination(const String& destination) { m_destination = destination; }
- virtual CurlJobAction handleCurlMsg(CURLMsg*);
-
private:
void closeFile();
void moveFileToDestination();
@@ -100,7 +98,7 @@
static void receivedResponseCallback(CurlDownload*);
CurlHandle m_curlHandle;
-
+ CurlJobTicket m_job;
String m_tempPath;
String m_destination;
WebCore::PlatformFileHandle m_tempHandle { invalidPlatformFileHandle };
Modified: trunk/Source/WebCore/platform/network/curl/CurlJobManager.cpp (219605 => 219606)
--- trunk/Source/WebCore/platform/network/curl/CurlJobManager.cpp 2017-07-18 07:20:33 UTC (rev 219605)
+++ trunk/Source/WebCore/platform/network/curl/CurlJobManager.cpp 2017-07-18 07:20:34 UTC (rev 219606)
@@ -29,6 +29,7 @@
#if USE(CURL)
#include "CurlJobManager.h"
+#include <iterator>
#include <wtf/MainThread.h>
#include <wtf/text/CString.h>
@@ -36,49 +37,117 @@
namespace WebCore {
-CurlJobManager::CurlJobManager()
-{
+/*
+ * 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();
-CurlJobManager::~CurlJobManager()
+ CURLMcode retval = addHandle(curl->handle());
+ // @FIXME error logging
+ if (retval == CURLM_OK)
+ m_jobs.set(job.ticket(), WTFMove(job));
+ }
+
+ void cancel(CurlJobTicket ticket)
+ {
+ complete(ticket, [](auto&& job) { job.cancel(); });
+ }
+
+ void complete(CurlJobTicket ticket, Action action)
+ {
+ 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);
+ }
+ }
+
+ bool isEmpty() const { return m_jobs.isEmpty(); }
+
+ bool withJob(CurlJobTicket ticket, WTF::Function<void(JobMap::iterator)> callback)
+ {
+ auto found = m_jobs.find(ticket);
+ if (found == m_jobs.end())
+ return false;
+
+ callback(found);
+ return true;
+ }
+
+ bool withCurlHandle(CurlJobTicket ticket, WTF::Function<void(CurlHandle&)> callback)
+ {
+ return withJob(ticket, [&callback](JobMap::iterator it) {
+ callback(*it->value.curlHandle());
+ });
+ }
+
+private:
+ JobMap m_jobs;
+};
+
+void CurlJob::invoke(CurlJobResult result)
{
- stopThread();
+ callOnMainThread([job = WTFMove(m_job), result] {
+ job(result);
+ });
}
-bool CurlJobManager::add(CURL* curlHandle)
+CurlJobTicket CurlJobManager::add(CurlHandle& curl, Callback callback)
{
ASSERT(isMainThread());
+ CurlJobTicket ticket = static_cast<CurlJobTicket>(curl.handle());
+
{
LockHolder locker(m_mutex);
- m_pendingHandleList.append(curlHandle);
+
+ if (isActiveJob(ticket))
+ return ticket;
+
+
+ m_pendingJobs.append(CurlJob { &curl, WTFMove(callback) });
+ m_activeJobs.add(ticket);
}
startThreadIfNeeded();
- return true;
+ return ticket;
}
-bool CurlJobManager::remove(CURL* curlHandle)
+bool CurlJobManager::cancel(CurlJobTicket job)
{
- LockHolder locker(m_mutex);
+ ASSERT(isMainThread());
- m_removedHandleList.append(curlHandle);
+ if (m_runThread) {
+ LockHolder locker(m_mutex);
+ if (!isActiveJob(job))
+ return false;
+
+ m_cancelledTickets.append(job);
+ m_activeJobs.remove(job);
+ }
+
return true;
}
-int CurlJobManager::getActiveCount() const
+void CurlJobManager::callOnJobThread(WTF::Function<void()>&& callback)
{
- LockHolder locker(m_mutex);
- return m_activeHandleList.size();
-}
+ LockHolder locker { m_mutex };
-int CurlJobManager::getPendingCount() const
-{
- LockHolder locker(m_mutex);
- return m_pendingHandleList.size();
+ m_taskQueue.append(std::make_unique<WTF::Function<void()>>(WTFMove(callback)));
}
void CurlJobManager::startThreadIfNeeded()
@@ -85,10 +154,12 @@
{
ASSERT(isMainThread());
- if (!runThread()) {
+ LockHolder locker(m_mutex);
+ if (!m_runThread) {
if (m_thread)
m_thread->waitForCompletion();
- setRunThread(true);
+
+ m_runThread = true;
m_thread = Thread::create("curlThread", [this] {
workerThread();
});
@@ -95,9 +166,19 @@
}
}
+void CurlJobManager::stopThreadIfNoMoreJobRunning()
+{
+ ASSERT(!isMainThread());
+
+ LockHolder locker(m_mutex);
+
+ if (m_pendingJobs.isEmpty())
+ m_runThread = false;
+}
+
void CurlJobManager::stopThread()
{
- setRunThread(false);
+ m_runThread = false;
if (m_thread) {
m_thread->waitForCompletion();
@@ -105,54 +186,32 @@
}
}
-void CurlJobManager::stopThreadIfIdle()
+bool CurlJobManager::updateJobs(CurlJobList& jobs)
{
- if (!getActiveCount() && !getPendingCount())
- setRunThread(false);
-}
+ ASSERT(!isMainThread());
-void CurlJobManager::updateHandleList()
-{
- LockHolder locker(m_mutex);
+ Vector<CurlJob> pendingJobs;
+ Vector<CurlJobTicket> cancelledTickets;
+ {
+ LockHolder locker(m_mutex);
+ if (!m_runThread)
+ return false;
- // Remove curl easy handles from multi list
- int size = m_removedHandleList.size();
- for (int i = 0; i < size; i++) {
- removeFromCurl(m_removedHandleList[0]);
- m_removedHandleList.remove(0);
+ pendingJobs = WTFMove(m_pendingJobs);
+ cancelledTickets = WTFMove(m_cancelledTickets);
}
- // Add pending curl easy handles to multi list
- size = m_pendingHandleList.size();
- for (int i = 0; i < size; i++) {
- addToCurl(m_pendingHandleList[0]);
- m_pendingHandleList.remove(0);
- }
-}
+ for (auto& job : pendingJobs)
+ jobs.append(job);
-bool CurlJobManager::addToCurl(CURL* curlHandle)
-{
- CURLMcode retval = m_curlMultiHandle.addHandle(curlHandle);
- if (retval == CURLM_OK) {
- m_activeHandleList.append(curlHandle);
- return true;
+ for (auto& ticket : cancelledTickets)
+ jobs.cancel(ticket);
+
+ for (auto& callback : m_taskQueue.takeAllMessages()) {
+ (*callback)();
}
- return false;
-}
-bool CurlJobManager::removeFromCurl(CURL* curlHandle)
-{
- int handlePos = m_activeHandleList.find(curlHandle);
-
- if (handlePos < 0)
- return true;
-
- CURLMcode retval = m_curlMultiHandle.removeHandle(curlHandle);
- if (retval == CURLM_OK) {
- m_activeHandleList.remove(handlePos);
- return true;
- }
- return false;
+ return true;
}
void CurlJobManager::workerThread()
@@ -159,10 +218,9 @@
{
ASSERT(!isMainThread());
- while (runThread()) {
+ CurlJobList jobs;
- updateHandleList();
-
+ while (updateJobs(jobs)) {
// Retry 'select' if it was interrupted by a process signal.
int rc = 0;
do {
@@ -177,7 +235,7 @@
timeout.tv_sec = 0;
timeout.tv_usec = selectTimeoutMS * 1000; // select waits microseconds
- m_curlMultiHandle.getFdSet(fdread, fdwrite, fdexcep, maxfd);
+ jobs.getFdSet(fdread, fdwrite, fdexcep, maxfd);
// When the 3 file descriptors are empty, winsock will return -1
// and bail out, stopping the file download. So make sure we
@@ -186,26 +244,49 @@
rc = ::select(maxfd + 1, &fdread, &fdwrite, &fdexcep, &timeout);
} while (rc == -1 && errno == EINTR);
- int runningHandles = 0;
- while (m_curlMultiHandle.perform(runningHandles) == CURLM_CALL_MULTI_PERFORM) { }
+ int activeCount = 0;
+ while (jobs.perform(activeCount) == CURLM_CALL_MULTI_PERFORM) { }
- int messagesInQueue = 0;
- CURLMsg* msg = m_curlMultiHandle.readInfo(messagesInQueue);
- if (!msg)
- continue;
+ // check the curl messages indicating completed transfers
+ // and free their resources
+ while (true) {
+ int messagesInQueue = 0;
+ CURLMsg* msg = jobs.readInfo(messagesInQueue);
+ if (!msg)
+ break;
+ if (msg->msg == CURLMSG_DONE) {
+ auto ticket = static_cast<CurlJobTicket>(msg->easy_handle);
+ CURLcode result = msg->data.result;
- CurlJob* job = nullptr;
- CURLcode err = curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &job);
- UNUSED_PARAM(err);
- ASSERT(job);
+ {
+ LockHolder locker(m_mutex);
+ m_activeJobs.remove(ticket);
+ }
- CurlJobAction action = ""
+ jobs.complete(ticket, [result](auto& job) {
+ job.curlHandle()->setErrorCode(result);
- if (action == CurlJobAction::Finished)
- removeFromCurl(msg->easy_handle);
+ 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();
+ }
- stopThreadIfIdle();
+ if (jobs.isEmpty()) {
+ stopThreadIfNoMoreJobRunning();
+ if (!m_runThread)
+ break;
+ }
}
}
Modified: trunk/Source/WebCore/platform/network/curl/CurlJobManager.h (219605 => 219606)
--- trunk/Source/WebCore/platform/network/curl/CurlJobManager.h 2017-07-18 07:20:33 UTC (rev 219605)
+++ trunk/Source/WebCore/platform/network/curl/CurlJobManager.h 2017-07-18 07:20:34 UTC (rev 219606)
@@ -28,11 +28,16 @@
#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 PLATFORM(WIN)
+#if OS(WINDOWS)
#include <windows.h>
#include <winsock2.h>
#endif
@@ -40,59 +45,86 @@
namespace WebCore {
-enum class CurlJobAction { None, Finished };
+enum class CurlJobResult { Done, Error, Cancelled };
+using CurlJobTicket = void*;
+using CurlJobCallback = WTF::Function<void(CurlJobResult)>;
+using CurlJobTask = WTF::Function<void(CurlHandle&)>;
+class CurlJobList;
+
class CurlJob {
+ WTF_MAKE_NONCOPYABLE(CurlJob);
public:
- virtual CurlJobAction handleCurlMsg(CURLMsg*) = 0;
+ 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() { }
+
+ 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);
};
+
class CurlJobManager {
+ WTF_MAKE_NONCOPYABLE(CurlJobManager);
+ using Callback = CurlJobCallback;
+
public:
+
static CurlJobManager& singleton()
{
- // Since it's a static variable, if the class has already been created,
- // It won't be created again.
- // And it **is** thread-safe in C++11.
-
static CurlJobManager shared;
return shared;
}
- CurlJobManager();
- virtual ~CurlJobManager();
+ CurlJobManager() = default;
+ ~CurlJobManager() { stopThread(); }
- bool add(CURL* curlHandle);
- bool remove(CURL* curlHandle);
+ CurlJobTicket add(CurlHandle&, Callback);
+ bool cancel(CurlJobTicket);
+ void callOnJobThread(WTF::Function<void()>&&);
- int getActiveCount() const;
- int getPendingCount() const;
-
private:
void startThreadIfNeeded();
+ void stopThreadIfNoMoreJobRunning();
void stopThread();
- void stopThreadIfIdle();
- void updateHandleList();
+ bool updateJobs(CurlJobList& jobs);
+ bool isActiveJob(CurlJobTicket job) const { return m_activeJobs.contains(job); }
- bool runThread() const { LockHolder locker(m_mutex); return m_runThread; }
- void setRunThread(bool runThread) { LockHolder locker(m_mutex); m_runThread = runThread; }
-
- bool addToCurl(CURL* curlHandle);
- bool removeFromCurl(CURL* curlHandle);
-
void workerThread();
RefPtr<Thread> m_thread;
- Vector<CURL*> m_pendingHandleList;
- Vector<CURL*> m_activeHandleList;
- Vector<CURL*> m_removedHandleList;
+ Vector<CurlJob> m_pendingJobs;
+ HashSet<CurlJobTicket> m_activeJobs;
+ Vector<CurlJobTicket> m_cancelledTickets;
+ MessageQueue<WTF::Function<void()>> m_taskQueue;
mutable Lock m_mutex;
- bool m_runThread { false };
-
- CurlMultiHandle m_curlMultiHandle;
-
- friend class CurlJob;
+ bool m_runThread { };
};
}
Modified: trunk/Source/WebCore/platform/network/curl/ResourceError.h (219605 => 219606)
--- trunk/Source/WebCore/platform/network/curl/ResourceError.h 2017-07-18 07:20:33 UTC (rev 219605)
+++ trunk/Source/WebCore/platform/network/curl/ResourceError.h 2017-07-18 07:20:34 UTC (rev 219606)
@@ -23,9 +23,9 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef ResourceError_h
-#define ResourceError_h
+#pragma once
+#include "CurlContext.h"
#include "ResourceErrorBase.h"
#if PLATFORM(WIN)
@@ -33,8 +33,6 @@
#include <winsock2.h>
#endif
-#include <curl/curl.h>
-
namespace WebCore {
class ResourceError : public ResourceErrorBase
@@ -42,16 +40,20 @@
public:
ResourceError(Type type = Type::Null)
: ResourceErrorBase(type)
- , m_sslErrors(0)
{
}
ResourceError(const String& domain, int errorCode, const URL& failingURL, const String& localizedDescription, Type type = Type::General)
: ResourceErrorBase(domain, errorCode, failingURL, localizedDescription, type)
- , m_sslErrors(0)
{
}
+ ResourceError(const CurlHandle& curl, unsigned sslErrors)
+ : ResourceErrorBase(CurlContext::errorDomain, curl.errorCode(), curl.getEffectiveURL(), curl.errorDescription(), Type::General)
+ , m_sslErrors { sslErrors }
+ {
+ }
+
unsigned sslErrors() const { return m_sslErrors; }
void setSSLErrors(unsigned sslVerifyResult) { m_sslErrors = sslVerifyResult; }
bool hasSSLConnectError() const { return errorCode() == CURLE_SSL_CONNECT_ERROR; }
@@ -60,9 +62,8 @@
friend class ResourceErrorBase;
void doPlatformIsolatedCopy(const ResourceError&) { }
- unsigned m_sslErrors;
+ unsigned m_sslErrors { 0 };
};
}
-#endif // ResourceError_h_
Modified: trunk/Source/WebCore/platform/network/curl/ResourceHandleCurl.cpp (219605 => 219606)
--- trunk/Source/WebCore/platform/network/curl/ResourceHandleCurl.cpp 2017-07-18 07:20:33 UTC (rev 219605)
+++ trunk/Source/WebCore/platform/network/curl/ResourceHandleCurl.cpp 2017-07-18 07:20:34 UTC (rev 219606)
@@ -35,13 +35,12 @@
#include "CredentialStorage.h"
#include "CurlCacheManager.h"
#include "CurlContext.h"
+#include "CurlJobManager.h"
#include "FileSystem.h"
#include "Logging.h"
#include "MIMETypeRegistry.h"
#include "NetworkingContext.h"
-#include "NotImplemented.h"
#include "ResourceHandleInternal.h"
-#include "ResourceHandleManager.h"
#include "SSLHandle.h"
#include "SynchronousLoaderClient.h"
#include <wtf/text/Base64.h>
@@ -55,11 +54,12 @@
ResourceHandle::~ResourceHandle()
{
- cancel();
}
bool ResourceHandle::start()
{
+ ASSERT(isMainThread());
+
// 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
@@ -68,24 +68,274 @@
if (d->m_context && !d->m_context->isValid())
return false;
- ResourceHandleManager::sharedInstance()->add(this);
+ initialize();
+
+ m_job = CurlJobManager::singleton().add(d->m_curlHandle, [this, protectedThis = makeRef(*this)](CurlJobResult result) {
+ ASSERT(isMainThread());
+
+ switch (result) {
+ case CurlJobResult::Done:
+ didFinish();
+ break;
+
+ case CurlJobResult::Error:
+ didFail();
+ break;
+
+ case CurlJobResult::Cancelled:
+ break;
+ }
+ });
+ ASSERT(m_job);
+
return true;
}
void ResourceHandle::cancel()
{
- ResourceHandleManager::sharedInstance()->cancel(this);
+ d->m_cancelled = true;
+ CurlJobManager::singleton().cancel(m_job);
}
+void ResourceHandle::initialize()
+{
+ CurlContext& context = CurlContext::singleton();
+
+ URL url = ""
+
+ // Remove any fragment part, otherwise curl will send it as part of the request.
+ url.removeFragmentIdentifier();
+
+ ResourceHandleInternal* d = getInternal();
+ 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.
+ d->m_response.setMimeType(MIMETypeRegistry::getMIMETypeForPath(url));
+ }
+
+ if (d->m_defersLoading) {
+ CURLcode error = d->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
+ d->m_curlHandle.enableVerboseIfUsed();
+ d->m_curlHandle.enableStdErrIfUsed();
+#endif
+
+ d->m_curlHandle.setSslVerifyPeer(CurlHandle::VerifyPeerEnable);
+ d->m_curlHandle.setSslVerifyHost(CurlHandle::VerifyHostStrictNameCheck);
+ d->m_curlHandle.setPrivateData(this);
+ d->m_curlHandle.setWriteCallbackFunction(writeCallback, this);
+ d->m_curlHandle.setHeaderCallbackFunction(headerCallback, this);
+ d->m_curlHandle.enableAutoReferer();
+ d->m_curlHandle.enableFollowLocation();
+ d->m_curlHandle.enableHttpAuthentication(CURLAUTH_ANY);
+ d->m_curlHandle.enableShareHandle();
+ d->m_curlHandle.enableTimeout();
+ d->m_curlHandle.enableAllowedProtocols();
+ setSSLClientCertificate(this);
+
+ if (CurlContext::singleton().shouldIgnoreSSLErrors())
+ d->m_curlHandle.setSslVerifyPeer(CurlHandle::VerifyPeerDisable);
+ else
+ setSSLVerifyOptions(this);
+
+ d->m_curlHandle.enableCAInfoIfExists();
+
+ d->m_curlHandle.enableAcceptEncoding();
+ d->m_curlHandle.setUrl(urlString);
+ d->m_curlHandle.enableCookieJarIfExists();
+
+ if (firstRequest().httpHeaderFields().size()) {
+ auto customHeaders = firstRequest().httpHeaderFields();
+ auto& cache = CurlCacheManager::getInstance();
+
+ bool hasCacheHeaders = customHeaders.contains(HTTPHeaderName::IfModifiedSince) || customHeaders.contains(HTTPHeaderName::IfNoneMatch);
+ if (!hasCacheHeaders && cache.isCached(url)) {
+ cache.addCacheEntryClient(url, this);
+
+ // append additional cache information
+ for (auto entry : cache.requestHeaders(url))
+ customHeaders.set(entry.key, entry.value);
+
+ d->m_addedCacheValidationHeaders = true;
+ }
+
+ d->m_curlHandle.appendRequestHeaders(customHeaders);
+ }
+
+ String method = firstRequest().httpMethod();
+ if ("GET" == method)
+ d->m_curlHandle.enableHttpGetRequest();
+ else if ("POST" == method)
+ setupPOST();
+ else if ("PUT" == method)
+ setupPUT();
+ else if ("HEAD" == method)
+ d->m_curlHandle.enableHttpHeadRequest();
+ else {
+ d->m_curlHandle.setHttpCustomRequest(method);
+ setupPUT();
+ }
+
+ d->m_curlHandle.enableRequestHeaders();
+
+ applyAuthentication();
+
+ d->m_curlHandle.enableProxyIfExists();
+}
+
+void ResourceHandle::applyAuthentication()
+{
+ ResourceRequest& request = firstRequest();
+ // m_user/m_pass are credentials given manually, for instance, by the arguments passed to XMLHttpRequest.open().
+ ResourceHandleInternal* d = getInternal();
+
+ String partition = request.cachePartition();
+
+ if (shouldUseCredentialStorage()) {
+ if (d->m_user.isEmpty() && d->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.
+ d->m_initialCredential = CredentialStorage::defaultCredentialStorage().get(partition, request.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(d->m_user, d->m_pass, CredentialPersistenceNone), request.url());
+ }
+ }
+
+ String user = d->m_user;
+ String password = d->m_pass;
+
+ if (!d->m_initialCredential.isEmpty()) {
+ user = d->m_initialCredential.user();
+ password = d->m_initialCredential.password();
+ d->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
+ d->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 ResourceHandle::setupPUT()
+{
+ d->m_curlHandle.enableHttpPutRequest();
+
+ // Disable the Expect: 100 continue header
+ d->m_curlHandle.appendRequestHeader("Expect:");
+
+ size_t numElements = getFormElementsCount(this);
+ if (!numElements)
+ return;
+
+ setupFormData(false);
+}
+
+void ResourceHandle::setupPOST()
+{
+ d->m_curlHandle.enableHttpPostRequest();
+
+ size_t numElements = getFormElementsCount(this);
+ if (!numElements)
+ return;
+
+ // Do not stream for simple POST data
+ if (numElements == 1) {
+ firstRequest().httpBody()->flatten(d->m_postBytes);
+ if (d->m_postBytes.size())
+ d->m_curlHandle.setPostFields(d->m_postBytes.data(), d->m_postBytes.size());
+ return;
+ }
+
+ setupFormData(true);
+}
+
+void ResourceHandle::setupFormData(bool isPostRequest)
+{
+ Vector<FormDataElement> elements = firstRequest().httpBody()->elements();
+ size_t numElements = elements.size();
+
+ static const long long maxCurlOffT = d->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)
+ d->m_curlHandle.appendRequestHeader("Transfer-Encoding: chunked");
+ else {
+ if (isPostRequest)
+ d->m_curlHandle.setPostFieldLarge(size);
+ else
+ d->m_curlHandle.setInFileSizeLarge(size);
+ }
+
+ d->m_curlHandle.setReadCallbackFunction(readCallback, this);
+}
+
#if OS(WINDOWS)
void ResourceHandle::setHostAllowsAnyHTTPSCertificate(const String& host)
{
+ ASSERT(isMainThread());
+
allowsAnyHTTPSCertificateHosts(host);
}
void ResourceHandle::setClientCertificateInfo(const String& host, const String& certificate, const String& key)
{
+ ASSERT(isMainThread());
+
if (fileExists(certificate))
addAllowedClientCertificate(host, certificate, key);
else
@@ -104,41 +354,73 @@
void ResourceHandle::platformSetDefersLoading(bool defers)
{
- if (!d->m_curlHandle.handle())
+ 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 (m_job) {
+ CurlJobManager::singleton().callOnJobThread(WTFMove(action));
+ } else {
+ action();
+ }
+}
+
+void ResourceHandle::didFinish()
+{
+#if ENABLE(WEB_TIMING)
+ calculateWebTimingInformations();
+#endif
+ if (d->m_cancelled)
return;
- 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)
+ if (!d->m_response.responseFired()) {
+ handleLocalReceiveResponse();
+ if (d->m_cancelled)
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_multipartHandle)
+ d->m_multipartHandle->contentEnded();
+
+ if (client()) {
+ client()->didFinishLoading(this);
+ CurlCacheManager::getInstance().didFinishLoading(*this);
+ }
}
-bool ResourceHandle::shouldUseCredentialStorage()
+void ResourceHandle::didFail()
{
- return (!client() || client()->shouldUseCredentialStorage(this)) && firstRequest().url().protocolIsInHTTPFamily();
+ if (d->m_cancelled)
+ return;
+ URL url = ""
+ if (client()) {
+ client()->didFail(this, ResourceError(d->m_curlHandle, d->m_sslErrors));
+ CurlCacheManager::getInstance().didFail(*this);
+ }
}
-void ResourceHandle::platformLoadResourceSynchronously(NetworkingContext* context, const ResourceRequest& request, StoredCredentials, ResourceError& error, ResourceResponse& response, Vector<char>& data)
+bool ResourceHandle::shouldUseCredentialStorage()
{
- SynchronousLoaderClient client;
- RefPtr<ResourceHandle> handle = adoptRef(new ResourceHandle(context, request, &client, false, false));
-
- handle.get()->dispatchSynchronousJob();
-
- error = client.error();
- data.swap(client.mutableData());
- response = client.response();
+ return (!client() || client()->shouldUseCredentialStorage(this)) && firstRequest().url().protocolIsInHTTPFamily();
}
void ResourceHandle::didReceiveAuthenticationChallenge(const AuthenticationChallenge& challenge)
{
+ ASSERT(isMainThread());
+
String partition = firstRequest().cachePartition();
if (!d->m_user.isNull() && !d->m_pass.isNull()) {
@@ -188,6 +470,8 @@
void ResourceHandle::receivedCredential(const AuthenticationChallenge& challenge, const Credential& credential)
{
+ ASSERT(isMainThread());
+
if (challenge != d->m_currentWebChallenge)
return;
@@ -211,6 +495,8 @@
void ResourceHandle::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge& challenge)
{
+ ASSERT(isMainThread());
+
if (challenge != d->m_currentWebChallenge)
return;
@@ -220,6 +506,8 @@
void ResourceHandle::receivedCancellation(const AuthenticationChallenge& challenge)
{
+ ASSERT(isMainThread());
+
if (challenge != d->m_currentWebChallenge)
return;
@@ -237,10 +525,8 @@
ASSERT_NOT_REACHED();
}
-const char* const errorDomainCurl = "CurlErrorDomain";
-
#if ENABLE(WEB_TIMING)
-static void calculateWebTimingInformations(ResourceHandleInternal* d)
+void ResourceHandle::calculateWebTimingInformations()
{
double preTransferTime = 0;
double dnslookupTime = 0;
@@ -263,8 +549,10 @@
}
#endif
-static void handleLocalReceiveResponse(ResourceHandle* job, ResourceHandleInternal* d)
+void ResourceHandle::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.
@@ -273,48 +561,11 @@
URL url = ""
ASSERT(url.isValid());
d->m_response.setURL(url);
- if (d->client())
- d->client()->didReceiveResponse(job, ResourceResponse(d->m_response));
+ if (client())
+ client()->didReceiveResponse(this, ResourceResponse(d->m_response));
d->m_response.setResponseFired(true);
}
-// called with data after all headers have been processed via headerCallback
-static size_t writeCallback(char* ptr, size_t size, size_t nmemb, void* data)
-{
- ResourceHandle* job = static_cast<ResourceHandle*>(data);
- ResourceHandleInternal* d = job->getInternal();
- 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;
-
- if (!d->m_response.responseFired()) {
- handleLocalReceiveResponse(job, d);
- if (d->m_cancelled)
- return 0;
- }
-
- if (d->m_multipartHandle)
- d->m_multipartHandle->contentReceived(static_cast<const char*>(ptr), totalSize);
- else if (d->client()) {
- d->client()->didReceiveData(job, ptr, totalSize, 0);
- CurlCacheManager::getInstance().didReceiveData(*job, ptr, totalSize);
- }
-
- return totalSize;
-}
-
inline static bool isHttpInfo(int statusCode)
{
return 100 <= statusCode && statusCode < 200;
@@ -439,6 +690,166 @@
return true;
}
+size_t ResourceHandle::willPrepareSendData(char* ptr, size_t blockSize, size_t numberOfBlocks)
+{
+ if (!d->m_formDataStream.hasMoreElements())
+ return 0;
+
+ size_t size = d->m_formDataStream.read(ptr, blockSize, numberOfBlocks);
+
+ // Something went wrong so cancel the job.
+ if (!size) {
+ cancel();
+ return 0;
+ }
+
+ return size;
+
+}
+
+void ResourceHandle::didReceiveHeaderLine(const String& header)
+{
+ int splitPos = header.find(":");
+ if (splitPos != notFound) {
+ String key = header.left(splitPos).stripWhiteSpace();
+ String value = header.substring(splitPos + 1).stripWhiteSpace();
+
+ if (isAppendableHeader(key))
+ d->m_response.addHTTPHeaderField(key, value);
+ else
+ d->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;
+ d->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());
+ d->m_response.setHTTPStatusText(status.stripWhiteSpace());
+ }
+ }
+}
+
+void ResourceHandle::didReceiveAllHeaders(long httpCode, long long contentLength)
+{
+ ASSERT(isMainThread());
+
+ d->m_response.setExpectedContentLength(contentLength);
+
+ d->m_response.setURL(d->m_curlHandle.getEffectiveURL());
+
+ d->m_response.setHTTPStatusCode(httpCode);
+ d->m_response.setMimeType(extractMIMETypeFromMediaType(d->m_response.httpHeaderField(HTTPHeaderName::ContentType)).convertToASCIILowercase());
+ d->m_response.setTextEncodingName(extractCharsetFromMediaType(d->m_response.httpHeaderField(HTTPHeaderName::ContentType)));
+
+ if (d->m_response.isMultipart()) {
+ String boundary;
+ bool parsed = MultipartHandle::extractBoundary(d->m_response.httpHeaderField(HTTPHeaderName::ContentType), boundary);
+ if (parsed)
+ d->m_multipartHandle = std::make_unique<MultipartHandle>(this, boundary);
+ }
+
+ // HTTP redirection
+ if (isHttpRedirect(httpCode)) {
+ String location = d->m_response.httpHeaderField(HTTPHeaderName::Location);
+ if (!location.isEmpty()) {
+ URL newURL = URL(firstRequest().url(), location);
+
+ ResourceRequest redirectedRequest = firstRequest();
+ redirectedRequest.setURL(newURL);
+ ResourceResponse response = d->m_response;
+ if (client())
+ client()->willSendRequest(this, WTFMove(redirectedRequest), WTFMove(response));
+
+ firstRequest().setURL(newURL);
+
+ return;
+ }
+ } else if (isHttpAuthentication(httpCode)) {
+ ProtectionSpace protectionSpace;
+ if (getProtectionSpace(this, d->m_response, protectionSpace)) {
+ Credential credential;
+ AuthenticationChallenge challenge(protectionSpace, credential, d->m_authFailureCount, d->m_response, ResourceError());
+ challenge.setAuthenticationClient(this);
+ this->didReceiveAuthenticationChallenge(challenge);
+ d->m_authFailureCount++;
+ return;
+ }
+ }
+
+ if (client()) {
+ if (isHttpNotModified(httpCode)) {
+ const String& url = ""
+ if (CurlCacheManager::getInstance().getCachedResponse(url, d->m_response)) {
+ if (d->m_addedCacheValidationHeaders) {
+ d->m_response.setHTTPStatusCode(200);
+ d->m_response.setHTTPStatusText("OK");
+ }
+ }
+ }
+ client()->didReceiveResponse(this, ResourceResponse(d->m_response));
+ CurlCacheManager::getInstance().didReceiveResponse(*this, d->m_response);
+ }
+
+ d->m_response.setResponseFired(true);
+}
+
+void ResourceHandle::didReceiveContentData()
+{
+ ASSERT(isMainThread());
+
+ if (!d->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 (d->m_multipartHandle)
+ d->m_multipartHandle->contentReceived(static_cast<const char*>(ptr), size);
+ else if (client()) {
+ client()->didReceiveData(this, ptr, size, 0);
+ CurlCacheManager::getInstance().didReceiveData(*this, 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 ResourceHandle::readCallback(char* ptr, size_t size, size_t nmemb, void* data)
+{
+ ASSERT(!isMainThread());
+
+ ResourceHandle* job = static_cast<ResourceHandle*>(data);
+ ResourceHandleInternal* d = job->getInternal();
+
+ 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 job->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.
@@ -448,8 +859,10 @@
* update the ResourceResponse and then send it away.
*
*/
-static size_t headerCallback(char* ptr, size_t size, size_t nmemb, void* data)
+size_t ResourceHandle::headerCallback(char* ptr, size_t size, size_t nmemb, void* data)
{
+ ASSERT(!isMainThread());
+
ResourceHandle* job = static_cast<ResourceHandle*>(data);
ResourceHandleInternal* d = job->getInternal();
if (d->m_cancelled)
@@ -459,7 +872,6 @@
ASSERT(!d->m_defersLoading);
size_t totalSize = size * nmemb;
- ResourceHandleClient* client = d->client();
String header(static_cast<const char*>(ptr), totalSize);
@@ -470,7 +882,7 @@
* The HTTP standard requires to use \r\n but for compatibility it recommends to
* accept also \n.
*/
- if (header == String("\r\n") || header == String("\n")) {
+ if (header == AtomicString("\r\n") || header == AtomicString("\n")) {
long httpCode = 0;
d->m_curlHandle.getResponseCode(httpCode);
@@ -486,108 +898,24 @@
long long contentLength = 0;
d->m_curlHandle.getContentLenghtDownload(contentLength);
- d->m_response.setExpectedContentLength(contentLength);
- d->m_response.setURL(d->m_curlHandle.getEffectiveURL());
+ callOnMainThread([job = RefPtr<ResourceHandle>(job), httpCode, contentLength] {
+ if (!job->d->m_cancelled)
+ job->didReceiveAllHeaders(httpCode, contentLength);
+ });
+ } else
+ job->didReceiveHeaderLine(header);
- d->m_response.setHTTPStatusCode(httpCode);
- d->m_response.setMimeType(extractMIMETypeFromMediaType(d->m_response.httpHeaderField(HTTPHeaderName::ContentType)).convertToASCIILowercase());
- d->m_response.setTextEncodingName(extractCharsetFromMediaType(d->m_response.httpHeaderField(HTTPHeaderName::ContentType)));
-
- if (d->m_response.isMultipart()) {
- String boundary;
- bool parsed = MultipartHandle::extractBoundary(d->m_response.httpHeaderField(HTTPHeaderName::ContentType), boundary);
- if (parsed)
- d->m_multipartHandle = std::make_unique<MultipartHandle>(job, boundary);
- }
-
- // HTTP redirection
- if (isHttpRedirect(httpCode)) {
- String location = d->m_response.httpHeaderField(HTTPHeaderName::Location);
- if (!location.isEmpty()) {
- URL newURL = URL(job->firstRequest().url(), location);
-
- ResourceRequest redirectedRequest = job->firstRequest();
- redirectedRequest.setURL(newURL);
- ResourceResponse response = d->m_response;
- if (client)
- client->willSendRequest(job, WTFMove(redirectedRequest), WTFMove(response));
-
- d->m_firstRequest.setURL(newURL);
-
- return totalSize;
- }
- } else if (isHttpAuthentication(httpCode)) {
- ProtectionSpace protectionSpace;
- if (getProtectionSpace(job, d->m_response, protectionSpace)) {
- Credential credential;
- AuthenticationChallenge challenge(protectionSpace, credential, d->m_authFailureCount, d->m_response, ResourceError());
- challenge.setAuthenticationClient(job);
- job->didReceiveAuthenticationChallenge(challenge);
- d->m_authFailureCount++;
- return totalSize;
- }
- }
-
- if (client) {
- if (isHttpNotModified(httpCode)) {
- const String& url = ""
- if (CurlCacheManager::getInstance().getCachedResponse(url, d->m_response)) {
- if (d->m_addedCacheValidationHeaders) {
- d->m_response.setHTTPStatusCode(200);
- d->m_response.setHTTPStatusText("OK");
- }
- }
- }
- client->didReceiveResponse(job, ResourceResponse(d->m_response));
- CurlCacheManager::getInstance().didReceiveResponse(*job, d->m_response);
- }
- d->m_response.setResponseFired(true);
-
- } else {
- int splitPos = header.find(":");
- if (splitPos != -1) {
- String key = header.left(splitPos).stripWhiteSpace();
- String value = header.substring(splitPos + 1).stripWhiteSpace();
-
- if (isAppendableHeader(key))
- d->m_response.addHTTPHeaderField(key, value);
- else
- d->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;
- d->m_curlHandle.getResponseCode(httpCode);
-
- String httpCodeString = String::number(httpCode);
- int statusCodePos = header.find(httpCodeString);
-
- if (statusCodePos != -1) {
- // The status text is after the status code.
- String status = header.substring(statusCodePos + httpCodeString.length());
- d->m_response.setHTTPStatusText(status.stripWhiteSpace());
- }
-
- }
- }
-
return totalSize;
}
-/* 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 readCallback(char* ptr, size_t size, size_t nmemb, void* data)
+// called with data after all headers have been processed via headerCallback
+size_t ResourceHandle::writeCallback(char* ptr, size_t size, size_t nmemb, void* data)
{
+ ASSERT(!isMainThread());
+
ResourceHandle* job = static_cast<ResourceHandle*>(data);
ResourceHandleInternal* d = job->getInternal();
-
if (d->m_cancelled)
return 0;
@@ -594,112 +922,83 @@
// We should never be called when deferred loading is activated.
ASSERT(!d->m_defersLoading);
- if (!size || !nmemb)
- return 0;
+ size_t totalSize = size * nmemb;
- if (!d->m_formDataStream.hasMoreElements())
- 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 = d->m_curlHandle.getResponseCode(httpCode);
+ if (CURLE_OK == errCd && httpCode >= 300 && httpCode < 400)
+ return totalSize;
- size_t sent = d->m_formDataStream.read(ptr, size, nmemb);
+ bool shouldCall { false };
+ {
+ LockHolder locker(job->m_receivedBufferMutex);
+
+ if (job->m_receivedBuffer.isEmpty())
+ shouldCall = true;
+
+ job->m_receivedBuffer.append(ptr, totalSize);
+ }
- // Something went wrong so cancel the job.
- if (!sent)
- job->cancel();
+ if (shouldCall) {
+ callOnMainThread([job = RefPtr<ResourceHandle>(job)] {
+ if (!job->d->m_cancelled)
+ job->didReceiveContentData();
+ });
+ }
- return sent;
+ return totalSize;
}
-static inline size_t getFormElementsCount(ResourceHandle* job)
+// sync loader
+
+void ResourceHandle::platformLoadResourceSynchronously(NetworkingContext* context, const ResourceRequest& request, StoredCredentials, ResourceError& error, ResourceResponse& response, Vector<char>& data)
{
- RefPtr<FormData> formData = job->firstRequest().httpBody();
+ ASSERT(isMainThread());
- if (!formData)
- return 0;
+ SynchronousLoaderClient client;
+ RefPtr<ResourceHandle> handle = adoptRef(new ResourceHandle(context, request, &client, false, false));
- // 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));
+ handle.get()->dispatchSynchronousJob();
- return size;
+ error = client.error();
+ data.swap(client.mutableData());
+ response = client.response();
}
-static void setupFormData(ResourceHandle* job, bool isPostRequest)
+void ResourceHandle::dispatchSynchronousJob()
{
- ResourceHandleInternal* d = job->getInternal();
- Vector<FormDataElement> elements = job->firstRequest().httpBody()->elements();
- size_t numElements = elements.size();
+ URL kurl = firstRequest().url();
- static const long long maxCurlOffT = d->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();
+ if (kurl.protocolIsData()) {
+ handleDataURL();
+ return;
}
- // cURL guesses that we want chunked encoding as long as we specify the header
- if (chunkedTransfer)
- d->m_curlHandle.appendRequestHeader("Transfer-Encoding: chunked");
- else {
- if (isPostRequest)
- d->m_curlHandle.setPostFieldLarge(size);
- else
- d->m_curlHandle.setInFileSizeLarge(size);
- }
-
- d->m_curlHandle.setReadCallbackFunction(readCallback, job);
-}
-
-void ResourceHandle::setupPUT()
-{
ResourceHandleInternal* d = getInternal();
- d->m_curlHandle.enableHttpPutRequest();
- // Disable the Expect: 100 continue header
- d->m_curlHandle.appendRequestHeader("Expect:");
+ // 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.
+ d->m_defersLoading = false;
- size_t numElements = getFormElementsCount(this);
- if (!numElements)
- return;
+ initialize();
- setupFormData(this, false);
-}
+ // curl_easy_perform blocks until the transfert is finished.
+ CURLcode ret = d->m_curlHandle.perform();
-void ResourceHandle::setupPOST()
-{
- ResourceHandleInternal* d = getInternal();
- d->m_curlHandle.enableHttpPostRequest();
+#if ENABLE(WEB_TIMING)
+ calculateWebTimingInformations();
+#endif
- size_t numElements = getFormElementsCount(this);
- if (!numElements)
- return;
-
- // Do not stream for simple POST data
- if (numElements == 1) {
- firstRequest().httpBody()->flatten(d->m_postBytes);
- if (d->m_postBytes.size())
- d->m_curlHandle.setPostFields(d->m_postBytes.data(), d->m_postBytes.size());
- return;
+ if (client()) {
+ if (ret != CURLE_OK)
+ client()->didFail(this, ResourceError(d->m_curlHandle, d->m_sslErrors));
+ else
+ client()->didReceiveResponse(this, ResourceResponse(d->m_response));
}
-
- setupFormData(this, true);
}
void ResourceHandle::handleDataURL()
@@ -763,218 +1062,6 @@
client()->didFinishLoading(this);
}
-void ResourceHandle::dispatchSynchronousJob()
-{
- URL kurl = firstRequest().url();
-
- if (kurl.protocolIsData()) {
- handleDataURL();
- return;
- }
-
- ResourceHandleInternal* d = getInternal();
-
- // 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.
- d->m_defersLoading = false;
-
- initialize();
-
- // curl_easy_perform blocks until the transfert is finished.
- CURLcode ret = d->m_curlHandle.perform();
-
- if (ret != CURLE_OK) {
- ResourceError error(ASCIILiteral(errorDomainCurl), ret, kurl, String(curl_easy_strerror(ret)));
- error.setSSLErrors(d->m_sslErrors);
- d->client()->didFail(this, error);
- } else {
- if (d->client())
- d->client()->didReceiveResponse(this, ResourceResponse(d->m_response));
- }
-
-#if ENABLE(WEB_TIMING)
- calculateWebTimingInformations(d);
-#endif
-}
-
-void ResourceHandle::applyAuthentication()
-{
- ResourceRequest& request = firstRequest();
- // m_user/m_pass are credentials given manually, for instance, by the arguments passed to XMLHttpRequest.open().
- ResourceHandleInternal* d = getInternal();
-
- String partition = request.cachePartition();
-
- if (shouldUseCredentialStorage()) {
- if (d->m_user.isEmpty() && d->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.
- d->m_initialCredential = CredentialStorage::defaultCredentialStorage().get(partition, request.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(d->m_user, d->m_pass, CredentialPersistenceNone), request.url());
- }
- }
-
- String user = d->m_user;
- String password = d->m_pass;
-
- if (!d->m_initialCredential.isEmpty()) {
- user = d->m_initialCredential.user();
- password = d->m_initialCredential.password();
- d->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
- d->m_curlHandle.setHttpAuthUserPass(user, password);
-}
-
-void ResourceHandle::initialize()
-{
- CurlContext& context = CurlContext::singleton();
-
- URL url = ""
-
- // Remove any fragment part, otherwise curl will send it as part of the request.
- url.removeFragmentIdentifier();
-
- ResourceHandleInternal* d = getInternal();
- 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.
- d->m_response.setMimeType(MIMETypeRegistry::getMIMETypeForPath(url));
- }
-
- if (d->m_defersLoading) {
- CURLcode error = d->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
- d->m_curlHandle.enableVerboseIfUsed();
- d->m_curlHandle.enableStdErrIfUsed();
-#endif
-
- d->m_curlHandle.setSslVerifyPeer(CurlHandle::VerifyPeerEnable);
- d->m_curlHandle.setSslVerifyHost(CurlHandle::VerifyHostStrictNameCheck);
- d->m_curlHandle.setPrivateData(this);
- d->m_curlHandle.setWriteCallbackFunction(writeCallback, this);
- d->m_curlHandle.setHeaderCallbackFunction(headerCallback, this);
- d->m_curlHandle.enableAutoReferer();
- d->m_curlHandle.enableFollowLocation();
- d->m_curlHandle.enableHttpAuthentication(CURLAUTH_ANY);
- d->m_curlHandle.enableShareHandle();
- d->m_curlHandle.enableTimeout();
- d->m_curlHandle.enableAllowedProtocols();
- setSSLClientCertificate(this);
-
- if (CurlContext::singleton().shouldIgnoreSSLErrors())
- d->m_curlHandle.setSslVerifyPeer(CurlHandle::VerifyPeerDisable);
- else
- setSSLVerifyOptions(this);
-
- d->m_curlHandle.enableCAInfoIfExists();
-
- d->m_curlHandle.enableAcceptEncoding();
- d->m_curlHandle.setUrl(urlString);
- d->m_curlHandle.enableCookieJarIfExists();
-
- d->m_curlHandle.clearRequestHeaders();
-
- if (firstRequest().httpHeaderFields().size() > 0) {
- HTTPHeaderMap customHeaders = firstRequest().httpHeaderFields();
-
- bool hasCacheHeaders = customHeaders.contains(HTTPHeaderName::IfModifiedSince) || customHeaders.contains(HTTPHeaderName::IfNoneMatch);
- if (!hasCacheHeaders && CurlCacheManager::getInstance().isCached(url)) {
- CurlCacheManager::getInstance().addCacheEntryClient(url, this);
- HTTPHeaderMap& requestHeaders = CurlCacheManager::getInstance().requestHeaders(url);
-
- // append additional cache information
- HTTPHeaderMap::const_iterator it = requestHeaders.begin();
- HTTPHeaderMap::const_iterator end = requestHeaders.end();
- while (it != end) {
- customHeaders.set(it->key, it->value);
- ++it;
- }
- d->m_addedCacheValidationHeaders = true;
- }
-
- for (auto customHeader : customHeaders)
- d->m_curlHandle.appendRequestHeader(customHeader.key, customHeader.value);
- }
-
- String method = firstRequest().httpMethod();
- if ("GET" == method)
- d->m_curlHandle.enableHttpGetRequest();
- else if ("POST" == method)
- setupPOST();
- else if ("PUT" == method)
- setupPUT();
- else if ("HEAD" == method)
- d->m_curlHandle.enableHttpHeadRequest();
- else {
- d->m_curlHandle.setHttpCustomRequest(method);
- setupPUT();
- }
-
- d->m_curlHandle.enableRequestHeaders();
-
- applyAuthentication();
-
- d->m_curlHandle.enableProxyIfExists();
-}
-
-void ResourceHandle::handleCurlMsg(CURLMsg* msg)
-{
- ResourceHandleInternal* d = getInternal();
-
- if (CURLE_OK == msg->data.result) {
-#if ENABLE(WEB_TIMING)
- calculateWebTimingInformations(d);
-#endif
- if (!d->m_response.responseFired()) {
- handleLocalReceiveResponse(this, d);
- if (d->m_cancelled)
- return;
- }
-
- if (d->m_multipartHandle)
- d->m_multipartHandle->contentEnded();
-
- if (d->client()) {
- d->client()->didFinishLoading(this);
- CurlCacheManager::getInstance().didFinishLoading(*this);
- }
- } else {
- URL url = ""
-#ifndef NDEBUG
- fprintf(stderr, "Curl ERROR for url='', error: '%s'\n", url.string().utf8().data(), curl_easy_strerror(msg->data.result));
-#endif
- if (d->client()) {
- ResourceError resourceError(ASCIILiteral(errorDomainCurl), msg->data.result, url, String(curl_easy_strerror(msg->data.result)));
- resourceError.setSSLErrors(d->m_sslErrors);
- d->client()->didFail(this, resourceError);
- CurlCacheManager::getInstance().didFail(*this);
- }
- }
-}
-
} // namespace WebCore
#endif
Deleted: trunk/Source/WebCore/platform/network/curl/ResourceHandleManager.cpp (219605 => 219606)
--- trunk/Source/WebCore/platform/network/curl/ResourceHandleManager.cpp 2017-07-18 07:20:33 UTC (rev 219605)
+++ trunk/Source/WebCore/platform/network/curl/ResourceHandleManager.cpp 2017-07-18 07:20:34 UTC (rev 219606)
@@ -1,252 +0,0 @@
-/*
- * Copyright (C) 2004, 2006 Apple Inc. All rights reserved.
- * Copyright (C) 2006 Michael Emmel [email protected]
- * Copyright (C) 2007 Alp Toker <[email protected]>
- * Copyright (C) 2007 Holger Hans Peter Freyther
- * Copyright (C) 2008 Collabora Ltd.
- * Copyright (C) 2008 Nuanti Ltd.
- * Copyright (C) 2009 Appcelerator Inc.
- * Copyright (C) 2009 Brent Fulgham <[email protected]>
- * Copyright (C) 2010 Patrick Gansterer <[email protected]>
- * Copyright (C) 2013 Peter Gal <[email protected]>, University of Szeged
- * Copyright (C) 2013 Alex Christensen <[email protected]>
- * Copyright (C) 2013 University of Szeged
- * 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 "ResourceHandleManager.h"
-
-#if USE(CURL)
-
-#include "CredentialStorage.h"
-#include "HTTPHeaderNames.h"
-#include "HTTPParsers.h"
-#include "MultipartHandle.h"
-#include "ResourceError.h"
-#include "ResourceHandle.h"
-#include "ResourceHandleClient.h"
-#include "ResourceHandleInternal.h"
-#include "ResourceRequest.h"
-#include "ResourceResponse.h"
-#include "SSLHandle.h"
-#include "TextEncoding.h"
-#include <wtf/text/CString.h>
-#include <wtf/text/StringView.h>
-
-#if OS(WINDOWS)
-#else
-#include <sys/param.h>
-#define MAX_PATH MAXPATHLEN
-#endif
-
-#include <errno.h>
-#include <stdio.h>
-#if ENABLE(WEB_TIMING)
-#include <wtf/CurrentTime.h>
-#endif
-#include <wtf/Lock.h>
-#include <wtf/Threading.h>
-#include <wtf/Vector.h>
-
-namespace WebCore {
-
-const int selectTimeoutMS = 5;
-static const Seconds pollTime { 50_ms };
-const int maxRunningJobs = 128;
-
-ResourceHandleManager::ResourceHandleManager()
- : m_downloadTimer(*this, &ResourceHandleManager::downloadTimerCallback)
- , m_runningJobs(0)
-{
-
-}
-
-ResourceHandleManager::~ResourceHandleManager()
-{
-
-}
-
-ResourceHandleManager* ResourceHandleManager::sharedInstance()
-{
- static ResourceHandleManager* sharedInstance = 0;
- if (!sharedInstance)
- sharedInstance = new ResourceHandleManager();
- return sharedInstance;
-}
-
-void ResourceHandleManager::downloadTimerCallback()
-{
- startScheduledJobs();
-
- fd_set fdread;
- fd_set fdwrite;
- fd_set fdexcep;
- int maxfd = 0;
-
- struct timeval timeout;
- timeout.tv_sec = 0;
- timeout.tv_usec = selectTimeoutMS * 1000; // select waits microseconds
-
- // Retry 'select' if it was interrupted by a process signal.
- int rc = 0;
- do {
- m_curlMultiHandle.getFdSet(fdread, fdwrite, fdexcep, maxfd);
-
- // When the 3 file descriptors are empty, winsock will return -1
- // and bail out, stopping the file download. So make sure we
- // have valid file descriptors before calling select.
- if (maxfd >= 0)
- rc = ::select(maxfd + 1, &fdread, &fdwrite, &fdexcep, &timeout);
- } while (rc == -1 && errno == EINTR);
-
- if (-1 == rc) {
-#ifndef NDEBUG
- perror("bad: select() returned -1: ");
-#endif
- return;
- }
-
- int runningHandles = 0;
- while (m_curlMultiHandle.perform(runningHandles) == CURLM_CALL_MULTI_PERFORM) { }
-
- // check the curl messages indicating completed transfers
- // and free their resources
- while (true) {
- int messagesInQueue = 0;
- CURLMsg* msg = m_curlMultiHandle.readInfo(messagesInQueue);
- if (!msg)
- break;
-
- // find the node which has same d->m_handle as completed transfer
- CURL* handle = msg->easy_handle;
- ASSERT(handle);
- ResourceHandle* job = nullptr;
- CURLcode err = curl_easy_getinfo(handle, CURLINFO_PRIVATE, &job);
- ASSERT_UNUSED(err, CURLE_OK == err);
- ASSERT(job);
- if (!job)
- continue;
- ResourceHandleInternal* d = job->getInternal();
-
- if (d->m_cancelled) {
- removeFromCurl(job);
- continue;
- }
-
- if (CURLMSG_DONE != msg->msg)
- continue;
-
- job->handleCurlMsg(msg);
- removeFromCurl(job);
- }
-
- bool started = startScheduledJobs(); // new jobs might have been added in the meantime
-
- if (!m_downloadTimer.isActive() && (started || (runningHandles > 0)))
- m_downloadTimer.startOneShot(pollTime);
-}
-
-void ResourceHandleManager::removeFromCurl(ResourceHandle* job)
-{
- ResourceHandleInternal* d = job->getInternal();
- ASSERT(d->m_curlHandle.handle());
- if (!d->m_curlHandle.handle())
- return;
- m_runningJobs--;
-
- m_curlMultiHandle.removeHandle(d->m_curlHandle.handle());
- job->deref();
-}
-
-void ResourceHandleManager::add(ResourceHandle* job)
-{
- // we can be called from within curl, so to avoid re-entrancy issues
- // schedule this job to be added the next time we enter curl download loop
- job->ref();
- m_resourceHandleList.append(job);
- if (!m_downloadTimer.isActive())
- m_downloadTimer.startOneShot(pollTime);
-}
-
-bool ResourceHandleManager::removeScheduledJob(ResourceHandle* job)
-{
- int size = m_resourceHandleList.size();
- for (int i = 0; i < size; i++) {
- if (job == m_resourceHandleList[i]) {
- m_resourceHandleList.remove(i);
- job->deref();
- return true;
- }
- }
- return false;
-}
-
-bool ResourceHandleManager::startScheduledJobs()
-{
- // TODO: Create a separate stack of jobs for each domain.
-
- bool started = false;
- while (!m_resourceHandleList.isEmpty() && m_runningJobs < maxRunningJobs) {
- ResourceHandle* job = m_resourceHandleList[0];
- m_resourceHandleList.remove(0);
- startJob(job);
- started = true;
- }
- return started;
-}
-
-void ResourceHandleManager::startJob(ResourceHandle* job)
-{
- job->initialize();
-
- m_runningJobs++;
-
- CURLMcode ret = m_curlMultiHandle.addHandle(job->getInternal()->m_curlHandle.handle());
-
- // don't call perform, because events must be async
- // timeout will occur and do curl_multi_perform
- if (ret && ret != CURLM_CALL_MULTI_PERFORM) {
-#ifndef NDEBUG
- fprintf(stderr, "Error %d starting job %s\n", ret, encodeWithURLEscapeSequences(job->firstRequest().url().string()).latin1().data());
-#endif
- job->cancel();
- return;
- }
-}
-
-void ResourceHandleManager::cancel(ResourceHandle* job)
-{
- if (removeScheduledJob(job))
- return;
-
- ResourceHandleInternal* d = job->getInternal();
- d->m_cancelled = true;
- if (!m_downloadTimer.isActive())
- m_downloadTimer.startOneShot(pollTime);
-}
-
-} // namespace WebCore
-
-#endif
Deleted: trunk/Source/WebCore/platform/network/curl/ResourceHandleManager.h (219605 => 219606)
--- trunk/Source/WebCore/platform/network/curl/ResourceHandleManager.h 2017-07-18 07:20:33 UTC (rev 219605)
+++ trunk/Source/WebCore/platform/network/curl/ResourceHandleManager.h 2017-07-18 07:20:34 UTC (rev 219606)
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2006 Apple Inc. All rights reserved.
- * Copyright (C) 2006 Michael Emmel [email protected]
- * 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.
- */
-
-#ifndef ResourceHandleManager_h
-#define ResourceHandleManager_h
-
-#include "CurlContext.h"
-#include "Frame.h"
-#include "Timer.h"
-#include "ResourceHandleClient.h"
-
-#if PLATFORM(WIN)
-#include <winsock2.h>
-#include <windows.h>
-#endif
-
-#include <wtf/Vector.h>
-#include <wtf/text/CString.h>
-#include <wtf/text/WTFString.h>
-
-namespace WebCore {
-
-class ResourceHandleManager {
-public:
- static ResourceHandleManager* sharedInstance();
- void add(ResourceHandle*);
- void cancel(ResourceHandle*);
-
-private:
- ResourceHandleManager();
- ~ResourceHandleManager();
- void downloadTimerCallback();
- void removeFromCurl(ResourceHandle*);
- bool removeScheduledJob(ResourceHandle*);
- void startJob(ResourceHandle*);
- bool startScheduledJobs();
-
- Timer m_downloadTimer;
- Vector<ResourceHandle*> m_resourceHandleList;
- int m_runningJobs;
-
- CurlMultiHandle m_curlMultiHandle;
-};
-
-}
-
-#endif