Title: [220939] trunk/Source/WebCore
Revision
220939
Author
[email protected]
Date
2017-08-18 16:02:51 -0700 (Fri, 18 Aug 2017)

Log Message

[Curl] Improve multi-threaded networking
https://bugs.webkit.org/show_bug.cgi?id=175713

Patch by Daewoong Jang <[email protected]> on 2017-08-18
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.

Modified Paths

Added Paths

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
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to