Changeset 220939 in webkit


Ignore:
Timestamp:
Aug 18, 2017 4:02:51 PM (7 years ago)
Author:
commit-queue@webkit.org
Message:

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

Patch by Daewoong Jang <daewoong.jang@navercorp.com> 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.
Location:
trunk/Source/WebCore
Files:
1 added
10 edited
1 copied

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r220937 r220939  
     12017-08-18  Daewoong Jang  <daewoong.jang@navercorp.com>
     2
     3        [Curl] Improve multi-threaded networking
     4        https://bugs.webkit.org/show_bug.cgi?id=175713
     5
     6        Reviewed by Alex Christensen.
     7
     8        * platform/Curl.cmake:
     9        * platform/network/ResourceHandleInternal.h:
     10        (WebCore::ResourceHandleInternal::ResourceHandleInternal):
     11        (WebCore::ResourceHandleInternal::m_failureTimer): Deleted.
     12        * platform/network/curl/CurlCacheManager.cpp:
     13        (WebCore::CurlCacheManager::didReceiveResponse):
     14        * platform/network/curl/CurlDownload.cpp:
     15        (WebCore::CurlDownload::init):
     16        (WebCore::CurlDownload::start):
     17        (WebCore::CurlDownload::cancel):
     18        (WebCore::CurlDownload::retain):
     19        (WebCore::CurlDownload::release):
     20        (WebCore::CurlDownload::setupRequest):
     21        (WebCore::CurlDownload::notifyFinish):
     22        (WebCore::CurlDownload::notifyFail):
     23        * platform/network/curl/CurlDownload.h:
     24        * platform/network/curl/CurlJobManager.cpp:
     25        (WebCore::CurlJobList::isEmpty const):
     26        (WebCore::CurlJobList::startJobs):
     27        (WebCore::CurlJobList::finishJobs):
     28        (WebCore::CurlJobList::notifyResult):
     29        (WebCore::CurlJobManager::add):
     30        (WebCore::CurlJobManager::cancel):
     31        (WebCore::CurlJobManager::callOnJobThread):
     32        (WebCore::CurlJobManager::startThreadIfNeeded):
     33        (WebCore::CurlJobManager::updateJobList):
     34        (WebCore::CurlJobManager::workerThread):
     35        (WebCore::CurlJobList::append): Deleted.
     36        (WebCore::CurlJobList::cancel): Deleted.
     37        (WebCore::CurlJobList::complete): Deleted.
     38        (WebCore::CurlJobList::withJob): Deleted.
     39        (WebCore::CurlJobList::withCurlHandle): Deleted.
     40        (WebCore::CurlJob::invoke): Deleted.
     41        (WebCore::CurlJobManager::updateJobs): Deleted.
     42        * platform/network/curl/CurlJobManager.h:
     43        (WebCore::CurlJob::CurlJob): Deleted.
     44        (WebCore::CurlJob::~CurlJob): Deleted.
     45        (WebCore::CurlJob::operator=): Deleted.
     46        (WebCore::CurlJob::curlHandle const): Deleted.
     47        (WebCore::CurlJob::ticket const): Deleted.
     48        (WebCore::CurlJob::finished): Deleted.
     49        (WebCore::CurlJob::error): Deleted.
     50        (WebCore::CurlJob::cancel): Deleted.
     51        (WebCore::CurlJobManager::isActiveJob const): Deleted.
     52        * platform/network/curl/MultipartHandle.cpp:
     53        (WebCore::MultipartHandle::didReceiveData):
     54        * platform/network/curl/ResourceHandleCurl.cpp:
     55        (WebCore::ResourceHandleInternal::~ResourceHandleInternal):
     56        (WebCore::ResourceHandle::start):
     57        (WebCore::ResourceHandle::cancel):
     58        (WebCore::ResourceHandle::platformSetDefersLoading):
     59        (WebCore::ResourceHandle::didReceiveAuthenticationChallenge):
     60        (WebCore::ResourceHandle::receivedCredential):
     61        (WebCore::ResourceHandle::receivedRequestToContinueWithoutCredential):
     62        (WebCore::ResourceHandle::platformLoadResourceSynchronously):
     63        (WebCore::ResourceHandleInternal::initialize): Deleted.
     64        (WebCore::ResourceHandleInternal::applyAuthentication): Deleted.
     65        (WebCore::getFormElementsCount): Deleted.
     66        (WebCore::ResourceHandleInternal::setupPUT): Deleted.
     67        (WebCore::ResourceHandleInternal::setupPOST): Deleted.
     68        (WebCore::ResourceHandleInternal::setupFormData): Deleted.
     69        (WebCore::ResourceHandleInternal::didFinish): Deleted.
     70        (WebCore::ResourceHandleInternal::didFail): Deleted.
     71        (WebCore::ResourceHandleInternal::calculateWebTimingInformations): Deleted.
     72        (WebCore::ResourceHandleInternal::handleLocalReceiveResponse): Deleted.
     73        (WebCore::isHttpInfo): Deleted.
     74        (WebCore::isHttpRedirect): Deleted.
     75        (WebCore::isHttpAuthentication): Deleted.
     76        (WebCore::isHttpNotModified): Deleted.
     77        (WebCore::isAppendableHeader): Deleted.
     78        (WebCore::removeLeadingAndTrailingQuotes): Deleted.
     79        (WebCore::getProtectionSpace): Deleted.
     80        (WebCore::ResourceHandleInternal::willPrepareSendData): Deleted.
     81        (WebCore::ResourceHandleInternal::didReceiveHeaderLine): Deleted.
     82        (WebCore::ResourceHandleInternal::didReceiveAllHeaders): Deleted.
     83        (WebCore::ResourceHandleInternal::didReceiveContentData): Deleted.
     84        (WebCore::ResourceHandleInternal::readCallback): Deleted.
     85        (WebCore::ResourceHandleInternal::headerCallback): Deleted.
     86        (WebCore::ResourceHandleInternal::writeCallback): Deleted.
     87        (WebCore::ResourceHandleInternal::dispatchSynchronousJob): Deleted.
     88        (WebCore::ResourceHandleInternal::handleDataURL): Deleted.
     89        * platform/network/curl/ResourceHandleCurlDelegate.cpp: Added.
     90        (WebCore::ResourceHandleCurlDelegate::ResourceHandleCurlDelegate):
     91        (WebCore::ResourceHandleCurlDelegate::~ResourceHandleCurlDelegate):
     92        (WebCore::ResourceHandleCurlDelegate::hasHandle const):
     93        (WebCore::ResourceHandleCurlDelegate::releaseHandle):
     94        (WebCore::ResourceHandleCurlDelegate::start):
     95        (WebCore::ResourceHandleCurlDelegate::cancel):
     96        (WebCore::ResourceHandleCurlDelegate::setDefersLoading):
     97        (WebCore::ResourceHandleCurlDelegate::setAuthentication):
     98        (WebCore::ResourceHandleCurlDelegate::dispatchSynchronousJob):
     99        (WebCore::ResourceHandleCurlDelegate::retain):
     100        (WebCore::ResourceHandleCurlDelegate::release):
     101        (WebCore::ResourceHandleCurlDelegate::setupRequest):
     102        (WebCore::ResourceHandleCurlDelegate::notifyFinish):
     103        (WebCore::ResourceHandleCurlDelegate::notifyFail):
     104        (WebCore::ResourceHandleCurlDelegate::response):
     105        (WebCore::ResourceHandleCurlDelegate::setupAuthentication):
     106        (WebCore::removeLeadingAndTrailingQuotes):
     107        (WebCore::ResourceHandleCurlDelegate::getProtectionSpace):
     108        (WebCore::isHttpInfo):
     109        (WebCore::isHttpRedirect):
     110        (WebCore::isHttpAuthentication):
     111        (WebCore::isHttpNotModified):
     112        (WebCore::isAppendableHeader):
     113        (WebCore::ResourceHandleCurlDelegate::didReceiveHeaderLine):
     114        (WebCore::ResourceHandleCurlDelegate::didReceiveAllHeaders):
     115        (WebCore::ResourceHandleCurlDelegate::didReceiveContentData):
     116        (WebCore::ResourceHandleCurlDelegate::handleLocalReceiveResponse):
     117        (WebCore::ResourceHandleCurlDelegate::prepareSendData):
     118        (WebCore::ResourceHandleCurlDelegate::didFinish):
     119        (WebCore::ResourceHandleCurlDelegate::didFail):
     120        (WebCore::ResourceHandleCurlDelegate::handleDataURL):
     121        (WebCore::ResourceHandleCurlDelegate::setupPOST):
     122        (WebCore::ResourceHandleCurlDelegate::setupPUT):
     123        (WebCore::ResourceHandleCurlDelegate::getFormElementsCount):
     124        (WebCore::ResourceHandleCurlDelegate::setupFormData):
     125        (WebCore::ResourceHandleCurlDelegate::applyAuthentication):
     126        (WebCore::ResourceHandleCurlDelegate::setWebTimings):
     127        (WebCore::ResourceHandleCurlDelegate::didReceiveHeader):
     128        (WebCore::ResourceHandleCurlDelegate::didReceiveData):
     129        (WebCore::ResourceHandleCurlDelegate::willSendData):
     130        (WebCore::ResourceHandleCurlDelegate::didReceiveHeaderCallback):
     131        (WebCore::ResourceHandleCurlDelegate::didReceiveDataCallback):
     132        (WebCore::ResourceHandleCurlDelegate::willSendDataCallback):
     133        * platform/network/curl/ResourceHandleCurlDelegate.h: Added.
     134
    11352017-08-18  Ryosuke Niwa  <rniwa@webkit.org>
    2136
  • trunk/Source/WebCore/platform/Curl.cmake

    r219606 r220939  
    1616    platform/network/curl/ProxyServerCurl.cpp
    1717    platform/network/curl/ResourceHandleCurl.cpp
     18    platform/network/curl/ResourceHandleCurlDelegate.cpp
    1819    platform/network/curl/SSLHandle.cpp
    1920    platform/network/curl/SocketStreamHandleImplCurl.cpp
  • trunk/Source/WebCore/platform/network/ResourceHandleInternal.h

    r219954 r220939  
    4444
    4545#if USE(CURL)
    46 #include "CurlContext.h"
    47 #include "CurlJobManager.h"
    48 #include "FormDataStreamCurl.h"
    49 #include "MultipartHandle.h"
    50 #include <wtf/Lock.h>
     46#include "ResourceHandleCurlDelegate.h"
    5147#endif
    5248
     
    8884#if USE(CFURLCONNECTION)
    8985        , m_currentRequest(request)
    90 #endif
    91 #if USE(CURL)
    92         , m_handle { loader }
    93         , m_formDataStream { loader }
    9486#endif
    9587#if USE(SOUP)
     
    141133#endif
    142134#if USE(CURL)
    143     ResourceHandle* m_handle;
    144     CurlHandle m_curlHandle;
    145 
     135    RefPtr<ResourceHandleCurlDelegate> m_delegate;
    146136    ResourceResponse m_response;
    147     bool m_cancelled { false };
    148     unsigned short m_authFailureCount { 0 };
    149 
    150     FormDataStream m_formDataStream;
    151     unsigned m_sslErrors { 0 };
    152     Vector<char> m_postBytes;
    153 
    154     std::unique_ptr<MultipartHandle> m_multipartHandle;
    155     bool m_addedCacheValidationHeaders { false };
    156     CurlJobTicket m_job { nullptr };
    157 
    158     Vector<char> m_receivedBuffer;
    159     Lock m_receivedBufferMutex;
    160 
    161     void initialize();
    162     void applyAuthentication();
    163     void setupPOST();
    164     void setupPUT();
    165     void setupFormData(bool isPostRequest);
    166 
    167     void didFinish();
    168     void didFail();
    169 
    170     size_t willPrepareSendData(char* ptr, size_t blockSize, size_t numberOfBlocks);
    171     void didReceiveHeaderLine(const String& header);
    172     void didReceiveAllHeaders(long httpCode, long long contentLength);
    173     void didReceiveContentData();
    174 
    175     void handleLocalReceiveResponse();
    176 
    177     static size_t readCallback(char* ptr, size_t blockSize, size_t numberOfBlocks, void* data);
    178     static size_t headerCallback(char* ptr, size_t blockSize, size_t numberOfBlocks, void* data);
    179     static size_t writeCallback(char* ptr, size_t blockSize, size_t numberOfBlocks, void* data);
    180 
    181     void dispatchSynchronousJob();
    182     void handleDataURL();
    183 
    184     void calculateWebTimingInformations();
    185 
    186137#endif
    187138
  • trunk/Source/WebCore/platform/network/curl/CurlCacheManager.cpp

    r194496 r220939  
    200200
    201201    ResourceHandleInternal* d = job.getInternal();
    202     if (d->m_cancelled)
     202    if (!d->m_delegate->hasHandle())
    203203        return;
    204204
  • trunk/Source/WebCore/platform/network/curl/CurlDownload.cpp

    r220897 r220939  
    5959    LockHolder locker(m_mutex);
    6060
    61     setupRequest();
    62 
    6361    m_listener = listener;
    6462    m_url = url;
     
    7977bool CurlDownload::start()
    8078{
    81     m_job = CurlJobManager::singleton().add(m_curlHandle, [this, protectedThis = makeRef(*this)](CurlJobResult result) mutable {
    82         switch (result) {
    83         case CurlJobResult::Done:
    84             didFinish();
    85             break;
    86 
    87         case CurlJobResult::Error:
    88             didFail();
    89             break;
    90 
    91         case CurlJobResult::Cancelled:
    92             break;
    93         }
    94     });
    95     return true;
     79    m_job = CurlJobManager::singleton().add(m_curlHandle, *this);
     80    return !!m_job;
    9681}
    9782
    9883bool CurlDownload::cancel()
    9984{
    100     CurlJobManager::singleton().cancel(m_job);
     85    CurlJobTicket job = m_job;
     86    m_job = nullptr;
     87    CurlJobManager::singleton().cancel(job);
    10188    return true;
    10289}
     
    10895    response.setHTTPHeaderField(m_httpHeaderFieldName, m_httpHeaderFieldValue);
    10996    return response;
     97}
     98
     99void CurlDownload::retain()
     100{
     101    ref();
     102}
     103
     104void CurlDownload::release()
     105{
     106    deref();
    110107}
    111108
     
    124121    m_curlHandle.enableHttpAuthentication(CURLAUTH_ANY);
    125122    m_curlHandle.enableCAInfoIfExists();
     123}
     124
     125void CurlDownload::notifyFinish()
     126{
     127    callOnMainThread([protectedThis = makeRef(*this)] {
     128        if (!protectedThis->m_job)
     129            return;
     130        protectedThis->didFinish();
     131    });
     132}
     133
     134void CurlDownload::notifyFail()
     135{
     136    callOnMainThread([protectedThis = makeRef(*this)] {
     137        if (!protectedThis->m_job)
     138            return;
     139        protectedThis->didFail();
     140    });
    126141}
    127142
  • trunk/Source/WebCore/platform/network/curl/CurlDownload.h

    r220897 r220939  
    4545};
    4646
    47 class CurlDownload : public ThreadSafeRefCounted<CurlDownload> {
     47class CurlDownload : public ThreadSafeRefCounted<CurlDownload>, public CurlJobClient {
    4848public:
    4949    CurlDownload();
     
    6666
    6767private:
    68     void setupRequest();
     68    void retain() override;
     69    void release() override;
     70
     71    void setupRequest() override;
     72    void notifyFinish() override;
     73    void notifyFail() override;
    6974
    7075    void closeFile();
  • trunk/Source/WebCore/platform/network/curl/CurlJobManager.cpp

    r219606 r220939  
    22 * Copyright (C) 2013 Apple Inc.  All rights reserved.
    33 * Copyright (C) 2017 Sony Interactive Entertainment Inc.
     4 * Copyright (C) 2017 NAVER Corp.
    45 *
    56 * Redistribution and use in source and binary forms, with or without
     
    2627
    2728#include "config.h"
     29#include "CurlJobManager.h"
    2830
    2931#if USE(CURL)
    30 #include "CurlJobManager.h"
    31 
     32
     33#include "ResourceHandleCurlDelegate.h"
    3234#include <iterator>
    3335#include <wtf/MainThread.h>
     
    3739
    3840namespace WebCore {
     41
     42enum class CurlJobResult { Done, Error, Cancelled };
    3943
    4044/*
     
    4246 */
    4347class CurlJobList : public CurlMultiHandle {
    44     using Predicate = WTF::Function<bool(const CurlJob&)>;
    45     using Action = WTF::Function<void(CurlJob&)>;
    46     using JobMap = HashMap<CurlJobTicket, CurlJob>;
    47 
    4848public:
    49     void append(CurlJob& job)
    50     {
    51         CurlHandle* curl = job.curlHandle();
    52 
    53         CURLMcode retval = addHandle(curl->handle());
    54             // @FIXME error logging
    55         if (retval == CURLM_OK)
    56             m_jobs.set(job.ticket(), WTFMove(job));
    57     }
    58 
    59     void cancel(CurlJobTicket ticket)
    60     {
    61         complete(ticket, [](auto&& job) { job.cancel(); });
    62     }
    63 
    64     void complete(CurlJobTicket ticket, Action action)
    65     {
    66         auto found = m_jobs.find(ticket);
    67         if (found != m_jobs.end()) {
    68             auto job = WTFMove(found->value);
    69 
    70             removeHandle(job.curlHandle()->handle());
    71             action(job);
    72 
    73             m_jobs.remove(found);
    74         }
    75     }
    76 
    77     bool isEmpty() const { return m_jobs.isEmpty(); }
    78 
    79     bool withJob(CurlJobTicket ticket, WTF::Function<void(JobMap::iterator)> callback)
    80     {
    81         auto found = m_jobs.find(ticket);
    82         if (found == m_jobs.end())
    83             return false;
    84        
    85         callback(found);
    86         return true;
    87     }
    88 
    89     bool withCurlHandle(CurlJobTicket ticket, WTF::Function<void(CurlHandle&)> callback)
    90     {
    91         return withJob(ticket, [&callback](JobMap::iterator it) {
    92             callback(*it->value.curlHandle());
    93         });
     49    bool isEmpty() const { return m_activeJobs.isEmpty(); }
     50
     51    void startJobs(HashMap<CurlJobTicket, CurlJobClient*>&& jobs)
     52    {
     53        auto localJobs = WTFMove(jobs);
     54        for (auto& job : localJobs) {
     55            job.value->setupRequest();
     56            m_activeJobs.add(job.key, job.value);
     57            addHandle(job.key);
     58        }
     59    }
     60
     61    void finishJobs(HashSet<CurlJobTicket>&& tickets, CurlJobResult result)
     62    {
     63        auto localTickets = WTFMove(tickets);
     64        for (auto& ticket : localTickets) {
     65            if (!m_activeJobs.contains(ticket))
     66                continue;
     67            removeHandle(ticket);
     68            notifyResult(m_activeJobs.fastGet(ticket), result);
     69            m_activeJobs.remove(ticket);
     70        }
    9471    }
    9572
    9673private:
    97     JobMap m_jobs;
     74    void notifyResult(CurlJobClient* client, CurlJobResult result)
     75    {
     76        switch (result) {
     77        case CurlJobResult::Done:
     78            client->notifyFinish();
     79            break;
     80        case CurlJobResult::Error:
     81            client->notifyFail();
     82            break;
     83        case CurlJobResult::Cancelled:
     84            break;
     85        }
     86
     87        client->release();
     88    }
     89
     90    HashMap<CurlJobTicket, CurlJobClient*> m_activeJobs;
    9891};
    9992
    100 void CurlJob::invoke(CurlJobResult result)
    101 {
    102     callOnMainThread([job = WTFMove(m_job), result] {
    103         job(result);
    104     });
    105 }
    106 
    107 CurlJobTicket CurlJobManager::add(CurlHandle& curl, Callback callback)
     93CurlJobTicket CurlJobManager::add(CurlHandle& curl, CurlJobClient& client)
    10894{
    10995    ASSERT(isMainThread());
    11096
     97    client.retain();
     98
    11199    CurlJobTicket ticket = static_cast<CurlJobTicket>(curl.handle());
    112100
    113101    {
    114102        LockHolder locker(m_mutex);
    115 
    116         if (isActiveJob(ticket))
    117             return ticket;
    118 
    119 
    120         m_pendingJobs.append(CurlJob { &curl, WTFMove(callback) });
    121         m_activeJobs.add(ticket);
     103        m_cancelledTickets.remove(ticket);
     104        m_pendingJobs.add(ticket, &client);
    122105    }
    123106
     
    127110}
    128111
    129 bool CurlJobManager::cancel(CurlJobTicket job)
     112void CurlJobManager::cancel(CurlJobTicket job)
    130113{
    131114    ASSERT(isMainThread());
    132115
    133     if (m_runThread) {
    134         LockHolder locker(m_mutex);
    135 
    136         if (!isActiveJob(job))
    137             return false;
    138        
    139         m_cancelledTickets.append(job);
    140         m_activeJobs.remove(job);
    141     }
    142 
    143     return true;
    144 }
    145 
    146 void CurlJobManager::callOnJobThread(WTF::Function<void()>&& callback)
    147 {
    148     LockHolder locker { m_mutex };
    149 
    150     m_taskQueue.append(std::make_unique<WTF::Function<void()>>(WTFMove(callback)));
     116    LockHolder locker(m_mutex);
     117    m_cancelledTickets.add(job);
     118}
     119
     120void CurlJobManager::callOnJobThread(WTF::Function<void()>&& task)
     121{
     122    LockHolder locker(m_mutex);
     123    m_taskQueue.append(WTFMove(task));
    151124}
    152125
     
    163136        m_thread = Thread::create("curlThread", [this] {
    164137            workerThread();
     138            m_runThread = false;
    165139        });
    166140    }
     
    187161}
    188162
    189 bool CurlJobManager::updateJobs(CurlJobList& jobs)
     163void CurlJobManager::updateJobList(CurlJobList& jobs)
    190164{
    191165    ASSERT(!isMainThread());
    192166
    193     Vector<CurlJob> pendingJobs;
    194     Vector<CurlJobTicket> cancelledTickets;
     167    HashMap<CurlJobTicket, CurlJobClient*> pendingJobs;
     168    HashSet<CurlJobTicket> cancelledTickets;
     169    Vector<WTF::Function<void()>> taskQueue;
     170
    195171    {
    196172        LockHolder locker(m_mutex);
    197         if (!m_runThread)
    198             return false;
    199173
    200174        pendingJobs = WTFMove(m_pendingJobs);
    201175        cancelledTickets = WTFMove(m_cancelledTickets);
    202     }
    203 
    204     for (auto& job : pendingJobs)
    205         jobs.append(job);
    206 
    207     for (auto& ticket : cancelledTickets)
    208         jobs.cancel(ticket);
    209 
    210     for (auto& callback : m_taskQueue.takeAllMessages()) {
    211         (*callback)();
    212     }
    213 
    214     return true;
     176        taskQueue = WTFMove(m_taskQueue);
     177    }
     178
     179    jobs.startJobs(WTFMove(pendingJobs));
     180    jobs.finishJobs(WTFMove(cancelledTickets), CurlJobResult::Cancelled);
     181    jobs.finishJobs(WTFMove(m_finishedTickets), CurlJobResult::Done);
     182    jobs.finishJobs(WTFMove(m_failedTickets), CurlJobResult::Error);
     183
     184    for (auto& task : taskQueue)
     185        task();
    215186}
    216187
     
    221192    CurlJobList jobs;
    222193
    223     while (updateJobs(jobs)) {
     194    while (m_runThread) {
     195        updateJobList(jobs);
     196
    224197        // Retry 'select' if it was interrupted by a process signal.
    225198        int rc = 0;
     
    256229                break;
    257230
    258             if (msg->msg == CURLMSG_DONE) {
    259                 auto ticket = static_cast<CurlJobTicket>(msg->easy_handle);
    260                 CURLcode result = msg->data.result;
    261 
    262                 {
    263                     LockHolder locker(m_mutex);
    264                     m_activeJobs.remove(ticket);
    265                 }
    266 
    267                 jobs.complete(ticket, [result](auto& job) {
    268                     job.curlHandle()->setErrorCode(result);
    269 
    270                     bool done = result == CURLE_OK;
    271                     if (done)
    272                         job.finished();
    273                     else {
    274                         URL url = job.curlHandle()->getEffectiveURL();
    275 #ifndef NDEBUG
    276                         fprintf(stderr, "Curl ERROR for url='%s', error: '%s'\n", url.string().utf8().data(), job.curlHandle()->errorDescription().utf8().data());
     231            ASSERT(msg->msg == CURLMSG_DONE);
     232            auto ticket = static_cast<CurlJobTicket>(msg->easy_handle);
     233            if (msg->data.result == CURLE_OK)
     234                m_finishedTickets.add(ticket);
     235            else
     236                m_failedTickets.add(ticket);
     237        }
     238
     239        if (jobs.isEmpty())
     240            stopThreadIfNoMoreJobRunning();
     241    }
     242}
     243
     244}
     245
    277246#endif
    278                         job.error();
    279                     }
    280                 });
    281             } else
    282                 ASSERT_NOT_REACHED();
    283         }
    284 
    285         if (jobs.isEmpty()) {
    286             stopThreadIfNoMoreJobRunning();
    287             if (!m_runThread)
    288                 break;
    289         }
    290     }
    291 }
    292 
    293 }
    294 
    295 #endif
  • trunk/Source/WebCore/platform/network/curl/CurlJobManager.h

    r219606 r220939  
    22 * Copyright (C) 2013 Apple Inc.  All rights reserved.
    33 * Copyright (C) 2017 Sony Interactive Entertainment Inc.
     4 * Copyright (C) 2017 NAVER Corp.
    45 *
    56 * Redistribution and use in source and binary forms, with or without
     
    2829
    2930#include "CurlContext.h"
    30 
    31 #include <wtf/Function.h>
    3231#include <wtf/HashMap.h>
    3332#include <wtf/HashSet.h>
    3433#include <wtf/Lock.h>
    35 #include <wtf/MessageQueue.h>
    3634#include <wtf/Noncopyable.h>
    3735#include <wtf/Threading.h>
    38 #include <wtf/Vector.h>
    39 
    40 #if OS(WINDOWS)
    41 #include <windows.h>
    42 #include <winsock2.h>
    43 #endif
    44 
    4536
    4637namespace WebCore {
    4738
    48 enum class CurlJobResult { Done, Error, Cancelled };
     39class CurlJobList;
    4940using CurlJobTicket = void*;
    50 using CurlJobCallback = WTF::Function<void(CurlJobResult)>;
    51 using CurlJobTask = WTF::Function<void(CurlHandle&)>;
    5241
    53 class CurlJobList;
     42class CurlJobClient {
     43public:
     44    virtual void retain() = 0;
     45    virtual void release() = 0;
    5446
    55 class CurlJob {
    56     WTF_MAKE_NONCOPYABLE(CurlJob);
    57 public:
    58     CurlJob() { }
    59     CurlJob(CurlHandle* curl, CurlJobCallback job)
    60         : m_curl { curl }, m_job { WTFMove(job) } { }
    61     CurlJob(CurlJob&& other)
    62     {
    63         m_curl = other.m_curl;
    64         other.m_curl = nullptr;
    65         m_job = WTFMove(other.m_job);
    66     }
    67     ~CurlJob() { }
    68 
    69     CurlJob& operator=(CurlJob&& other)
    70     {
    71         m_curl = other.m_curl;
    72         other.m_curl = nullptr;
    73         m_job = WTFMove(other.m_job);
    74         return *this;
    75     }
    76 
    77     CurlHandle* curlHandle() const { return m_curl; }
    78     CurlJobTicket ticket() const { return static_cast<CurlJobTicket>(m_curl->handle()); }
    79 
    80     void finished() { invoke(CurlJobResult::Done); }
    81     void error() { invoke(CurlJobResult::Error); }
    82     void cancel() { invoke(CurlJobResult::Cancelled); }
    83 
    84 private:
    85     CurlHandle* m_curl;
    86     CurlJobCallback m_job;
    87 
    88     void invoke(CurlJobResult);
     47    virtual void setupRequest() = 0;
     48    virtual void notifyFinish() = 0;
     49    virtual void notifyFail() = 0;
    8950};
    90 
    9151
    9252class CurlJobManager {
    9353    WTF_MAKE_NONCOPYABLE(CurlJobManager);
    94     using Callback = CurlJobCallback;
    95 
    9654public:
    97 
    9855    static CurlJobManager& singleton()
    9956    {
     
    10562    ~CurlJobManager() { stopThread(); }
    10663
    107     CurlJobTicket add(CurlHandle&, Callback);
    108     bool cancel(CurlJobTicket);
     64    CurlJobTicket add(CurlHandle&, CurlJobClient&);
     65    void cancel(CurlJobTicket);
     66
    10967    void callOnJobThread(WTF::Function<void()>&&);
    11068
     
    11472    void stopThread();
    11573
    116     bool updateJobs(CurlJobList& jobs);
    117     bool isActiveJob(CurlJobTicket job) const { return m_activeJobs.contains(job); }
     74    void updateJobList(CurlJobList&);
    11875
    11976    void workerThread();
    12077
    12178    RefPtr<Thread> m_thread;
    122     Vector<CurlJob> m_pendingJobs;
    123     HashSet<CurlJobTicket> m_activeJobs;
    124     Vector<CurlJobTicket> m_cancelledTickets;
    125     MessageQueue<WTF::Function<void()>> m_taskQueue;
     79    HashMap<CurlJobTicket, CurlJobClient*> m_pendingJobs;
     80    HashSet<CurlJobTicket> m_cancelledTickets;
     81    HashSet<CurlJobTicket> m_finishedTickets;
     82    HashSet<CurlJobTicket> m_failedTickets;
     83    Vector<WTF::Function<void()>> m_taskQueue;
    12684    mutable Lock m_mutex;
    127     bool m_runThread { };
     85    bool m_runThread { false };
    12886};
    12987
  • trunk/Source/WebCore/platform/network/curl/MultipartHandle.cpp

    r201954 r220939  
    322322    ResourceHandleInternal* d = m_resourceHandle->getInternal();
    323323
    324     if (d->m_cancelled) {
     324    if (!d->m_delegate->hasHandle()) {
    325325        // Request has been canceled, so we'll go to the end state.
    326326        m_state = End;
  • trunk/Source/WebCore/platform/network/curl/ResourceHandleCurl.cpp

    r220897 r220939  
    44 * Copyright (C) 2017 Sony Interactive Entertainment Inc.
    55 * All rights reserved.
     6 * Copyright (C) 2017 NAVER Corp.
    67 *
    78 * Redistribution and use in source and binary forms, with or without
     
    5051ResourceHandleInternal::~ResourceHandleInternal()
    5152{
     53    if (m_delegate)
     54        m_delegate->releaseHandle();
    5255}
    5356
     
    5962{
    6063    ASSERT(isMainThread());
     64
     65    CurlContext::singleton();
    6166
    6267    // The frame could be null if the ResourceHandle is not associated to any
     
    6873        return false;
    6974
    70     d->initialize();
    71 
    72     d->m_job = CurlJobManager::singleton().add(d->m_curlHandle, [this, protectedThis = makeRef(*this)](CurlJobResult result) {
    73         ASSERT(isMainThread());
    74 
    75         switch (result) {
    76         case CurlJobResult::Done:
    77             d->didFinish();
    78             break;
    79 
    80         case CurlJobResult::Error:
    81             d->didFail();
    82             break;
    83 
    84         case CurlJobResult::Cancelled:
    85             break;
    86         }
    87     });
    88     ASSERT(d->m_job);
    89 
    90     return true;
     75    d->m_delegate = adoptRef(new ResourceHandleCurlDelegate(this));
     76    return d->m_delegate->start();
    9177}
    9278
    9379void ResourceHandle::cancel()
    9480{
    95     d->m_cancelled = true;
    96     CurlJobManager::singleton().cancel(d->m_job);
    97 }
    98 
    99 void ResourceHandleInternal::initialize()
    100 {
    101     CurlContext& context = CurlContext::singleton();
    102 
    103     URL url = m_firstRequest.url();
    104 
    105     // Remove any fragment part, otherwise curl will send it as part of the request.
    106     url.removeFragmentIdentifier();
    107 
    108     String urlString = url.string();
    109 
    110     if (url.isLocalFile()) {
    111         // Remove any query part sent to a local file.
    112         if (!url.query().isEmpty()) {
    113             // By setting the query to a null string it'll be removed.
    114             url.setQuery(String());
    115             urlString = url.string();
    116         }
    117         // Determine the MIME type based on the path.
    118         m_response.setMimeType(MIMETypeRegistry::getMIMETypeForPath(url));
    119     }
    120 
    121     if (m_defersLoading) {
    122         CURLcode error = m_curlHandle.pause(CURLPAUSE_ALL);
    123         // If we did not pause the handle, we would ASSERT in the
    124         // header callback. So just assert here.
    125         ASSERT_UNUSED(error, error == CURLE_OK);
    126     }
    127 
    128 #ifndef NDEBUG
    129     m_curlHandle.enableVerboseIfUsed();
    130     m_curlHandle.enableStdErrIfUsed();
    131 #endif
    132 
    133     m_curlHandle.initialize();
    134     m_curlHandle.setSslVerifyPeer(CurlHandle::VerifyPeerEnable);
    135     m_curlHandle.setSslVerifyHost(CurlHandle::VerifyHostStrictNameCheck);
    136     m_curlHandle.setPrivateData(this);
    137     m_curlHandle.setWriteCallbackFunction(writeCallback, this);
    138     m_curlHandle.setHeaderCallbackFunction(headerCallback, this);
    139     m_curlHandle.enableAutoReferer();
    140     m_curlHandle.enableFollowLocation();
    141     m_curlHandle.enableHttpAuthentication(CURLAUTH_ANY);
    142     m_curlHandle.enableShareHandle();
    143     m_curlHandle.enableTimeout();
    144     m_curlHandle.enableAllowedProtocols();
    145     auto certificate = getSSLClientCertificate(m_firstRequest.url().host());
    146     if (certificate) {
    147         m_curlHandle.setSslCert((*certificate).first.utf8().data());
    148         m_curlHandle.setSslCertType("P12");
    149         m_curlHandle.setSslKeyPassword((*certificate).second.utf8().data());
    150     }
    151 
    152     if (CurlContext::singleton().shouldIgnoreSSLErrors())
    153         m_curlHandle.setSslVerifyPeer(CurlHandle::VerifyPeerDisable);
    154     else
    155         setSSLVerifyOptions(m_curlHandle);
    156 
    157     m_curlHandle.enableCAInfoIfExists();
    158 
    159     m_curlHandle.enableAcceptEncoding();
    160     m_curlHandle.setUrl(urlString);
    161     m_curlHandle.enableCookieJarIfExists();
    162 
    163     if (m_firstRequest.httpHeaderFields().size()) {
    164         auto customHeaders = m_firstRequest.httpHeaderFields();
    165         auto& cache = CurlCacheManager::getInstance();
    166 
    167         bool hasCacheHeaders = customHeaders.contains(HTTPHeaderName::IfModifiedSince) || customHeaders.contains(HTTPHeaderName::IfNoneMatch);
    168         if (!hasCacheHeaders && cache.isCached(url)) {
    169             cache.addCacheEntryClient(url, m_handle);
    170 
    171             // append additional cache information
    172             for (auto entry : cache.requestHeaders(url))
    173                 customHeaders.set(entry.key, entry.value);
    174 
    175             m_addedCacheValidationHeaders = true;
    176         }
    177 
    178         m_curlHandle.appendRequestHeaders(customHeaders);
    179     }
    180 
    181     String method = m_firstRequest.httpMethod();
    182     if ("GET" == method)
    183         m_curlHandle.enableHttpGetRequest();
    184     else if ("POST" == method)
    185         setupPOST();
    186     else if ("PUT" == method)
    187         setupPUT();
    188     else if ("HEAD" == method)
    189         m_curlHandle.enableHttpHeadRequest();
    190     else {
    191         m_curlHandle.setHttpCustomRequest(method);
    192         setupPUT();
    193     }
    194 
    195     m_curlHandle.enableRequestHeaders();
    196 
    197     applyAuthentication();
    198 
    199     m_curlHandle.enableProxyIfExists();
    200 }
    201 
    202 void ResourceHandleInternal::applyAuthentication()
    203 {
    204     // m_user/m_pass are credentials given manually, for instance, by the arguments passed to XMLHttpRequest.open().
    205     String partition = m_firstRequest.cachePartition();
    206 
    207     if (m_handle->shouldUseCredentialStorage()) {
    208         if (m_user.isEmpty() && m_pass.isEmpty()) {
    209             // <rdar://problem/7174050> - For URLs that match the paths of those previously challenged for HTTP Basic authentication,
    210             // try and reuse the credential preemptively, as allowed by RFC 2617.
    211             m_initialCredential = CredentialStorage::defaultCredentialStorage().get(partition, m_firstRequest.url());
    212         } else {
    213             // If there is already a protection space known for the URL, update stored credentials
    214             // before sending a request. This makes it possible to implement logout by sending an
    215             // XMLHttpRequest with known incorrect credentials, and aborting it immediately (so that
    216             // an authentication dialog doesn't pop up).
    217             CredentialStorage::defaultCredentialStorage().set(partition, Credential(m_user, m_pass, CredentialPersistenceNone), m_firstRequest.url());
    218         }
    219     }
    220 
    221     String user = m_user;
    222     String password = m_pass;
    223 
    224     if (!m_initialCredential.isEmpty()) {
    225         user = m_initialCredential.user();
    226         password = m_initialCredential.password();
    227         m_curlHandle.enableHttpAuthentication(CURLAUTH_BASIC);
    228     }
    229 
    230     // It seems we need to set CURLOPT_USERPWD even if username and password is empty.
    231     // Otherwise cURL will not automatically continue with a new request after a 401 response.
    232 
    233     // curl CURLOPT_USERPWD expects username:password
    234     m_curlHandle.setHttpAuthUserPass(user, password);
    235 }
    236 
    237 static inline size_t getFormElementsCount(ResourceHandle* job)
    238 {
    239     RefPtr<FormData> formData = job->firstRequest().httpBody();
    240 
    241     if (!formData)
    242         return 0;
    243 
    244     // Resolve the blob elements so the formData can correctly report it's size.
    245     formData = formData->resolveBlobReferences();
    246     size_t size = formData->elements().size();
    247     job->firstRequest().setHTTPBody(WTFMove(formData));
    248 
    249     return size;
    250 }
    251 
    252 void ResourceHandleInternal::setupPUT()
    253 {
    254     m_curlHandle.enableHttpPutRequest();
    255 
    256     // Disable the Expect: 100 continue header
    257     m_curlHandle.appendRequestHeader("Expect:");
    258 
    259     size_t numElements = getFormElementsCount(m_handle);
    260     if (!numElements)
    261         return;
    262 
    263     setupFormData(false);
    264 }
    265 
    266 void ResourceHandleInternal::setupPOST()
    267 {
    268     m_curlHandle.enableHttpPostRequest();
    269 
    270     size_t numElements = getFormElementsCount(m_handle);
    271     if (!numElements)
    272         return;
    273 
    274     // Do not stream for simple POST data
    275     if (numElements == 1) {
    276         m_firstRequest.httpBody()->flatten(m_postBytes);
    277         if (m_postBytes.size())
    278             m_curlHandle.setPostFields(m_postBytes.data(), m_postBytes.size());
    279         return;
    280     }
    281 
    282     setupFormData(true);
    283 }
    284 
    285 void ResourceHandleInternal::setupFormData(bool isPostRequest)
    286 {
    287     Vector<FormDataElement> elements = m_firstRequest.httpBody()->elements();
    288     size_t numElements = elements.size();
    289 
    290     static const long long maxCurlOffT = m_curlHandle.maxCurlOffT();
    291 
    292     // Obtain the total size of the form data
    293     curl_off_t size = 0;
    294     bool chunkedTransfer = false;
    295     for (size_t i = 0; i < numElements; i++) {
    296         FormDataElement element = elements[i];
    297         if (element.m_type == FormDataElement::Type::EncodedFile) {
    298             long long fileSizeResult;
    299             if (getFileSize(element.m_filename, fileSizeResult)) {
    300                 if (fileSizeResult > maxCurlOffT) {
    301                     // File size is too big for specifying it to cURL
    302                     chunkedTransfer = true;
    303                     break;
    304                 }
    305                 size += fileSizeResult;
    306             } else {
    307                 chunkedTransfer = true;
    308                 break;
    309             }
    310         } else
    311             size += elements[i].m_data.size();
    312     }
    313 
    314     // cURL guesses that we want chunked encoding as long as we specify the header
    315     if (chunkedTransfer)
    316         m_curlHandle.appendRequestHeader("Transfer-Encoding: chunked");
    317     else {
    318         if (isPostRequest)
    319             m_curlHandle.setPostFieldLarge(size);
    320         else
    321             m_curlHandle.setInFileSizeLarge(size);
    322     }
    323 
    324     m_curlHandle.setReadCallbackFunction(readCallback, this);
     81    d->m_delegate->cancel();
    32582}
    32683
     
    358115    ASSERT(isMainThread());
    359116
    360     auto action = [defers, this, protectedThis = makeRef(*this)]() {
    361         if (defers) {
    362             CURLcode error = d->m_curlHandle.pause(CURLPAUSE_ALL);
    363             // If we could not defer the handle, so don't do it.
    364             if (error != CURLE_OK)
    365                 return;
    366         } else {
    367             CURLcode error = d->m_curlHandle.pause(CURLPAUSE_CONT);
    368             if (error != CURLE_OK) {
    369                 // Restarting the handle has failed so just cancel it.
    370                 cancel();
    371             }
    372         }
    373     };
    374 
    375     if (d->m_job) {
    376         CurlJobManager::singleton().callOnJobThread(WTFMove(action));
    377     } else {
    378         action();
    379     }
    380 }
    381 
    382 void ResourceHandleInternal::didFinish()
    383 {
    384     calculateWebTimingInformations();
    385 
    386     if (m_cancelled)
    387         return;
    388 
    389     if (!m_response.responseFired()) {
    390         handleLocalReceiveResponse();
    391         if (m_cancelled)
    392             return;
    393     }
    394 
    395     if (m_multipartHandle)
    396         m_multipartHandle->contentEnded();
    397 
    398     if (client()) {
    399         client()->didFinishLoading(m_handle);
    400         CurlCacheManager::getInstance().didFinishLoading(*m_handle);
    401     }
    402 }
    403 
    404 void ResourceHandleInternal::didFail()
    405 {
    406     if (m_cancelled)
    407         return;
    408     URL url = m_curlHandle.getEffectiveURL();
    409     if (client()) {
    410         client()->didFail(m_handle, ResourceError(CurlContext::errorDomain, m_curlHandle.errorCode(), m_curlHandle.getEffectiveURL(), m_curlHandle.errorDescription(), m_curlHandle.getSslErrors()));
    411         CurlCacheManager::getInstance().didFail(*m_handle);
    412     }
     117    d->m_delegate->setDefersLoading(defers);
    413118}
    414119
     
    432137        CredentialStorage::defaultCredentialStorage().set(partition, credential, challenge.protectionSpace(), urlToStore);
    433138       
    434         d->m_curlHandle.setHttpAuthUserPass(credential.user(), credential.password());
     139        d->m_delegate->setAuthentication(credential.user(), credential.password());
    435140
    436141        d->m_user = String();
     
    457162                }
    458163
    459                 d->m_curlHandle.setHttpAuthUserPass(credential.user(), credential.password());
     164                d->m_delegate->setAuthentication(credential.user(), credential.password());
    460165                return;
    461166            }
     
    490195    }
    491196
    492     d->m_curlHandle.setHttpAuthUserPass(credential.user(), credential.password());
     197    d->m_delegate->setAuthentication(credential.user(), credential.password());
    493198    clearAuthentication();
    494199}
     
    501206        return;
    502207
    503     d->m_curlHandle.setHttpAuthUserPass("", "");
     208    d->m_delegate->setAuthentication("", "");
    504209    clearAuthentication();
    505210}
     
    526231}
    527232
    528 void ResourceHandleInternal::calculateWebTimingInformations()
    529 {
    530     double preTransferTime = 0;
    531     double dnslookupTime = 0;
    532     double connectTime = 0;
    533     double appConnectTime = 0;
    534 
    535     m_curlHandle.getTimes(preTransferTime, dnslookupTime, connectTime, appConnectTime);
    536 
    537     m_response.deprecatedNetworkLoadMetrics().domainLookupStart = Seconds(0);
    538     m_response.deprecatedNetworkLoadMetrics().domainLookupEnd = Seconds(dnslookupTime);
    539 
    540     m_response.deprecatedNetworkLoadMetrics().connectStart = Seconds(dnslookupTime);
    541     m_response.deprecatedNetworkLoadMetrics().connectEnd = Seconds(connectTime);
    542 
    543     m_response.deprecatedNetworkLoadMetrics().requestStart = Seconds(connectTime);
    544     m_response.deprecatedNetworkLoadMetrics().responseStart = Seconds(preTransferTime);
    545 
    546     if (appConnectTime)
    547         m_response.deprecatedNetworkLoadMetrics().secureConnectionStart = Seconds(connectTime);
    548 }
    549 
    550 void ResourceHandleInternal::handleLocalReceiveResponse()
    551 {
    552     ASSERT(isMainThread());
    553 
    554     // since the code in headerCallback will not have run for local files
    555     // the code to set the URL and fire didReceiveResponse is never run,
    556     // which means the ResourceLoader's response does not contain the URL.
    557     // Run the code here for local files to resolve the issue.
    558     // TODO: See if there is a better approach for handling this.
    559     URL url = m_curlHandle.getEffectiveURL();
    560     ASSERT(url.isValid());
    561     m_response.setURL(url);
    562     if (client())
    563         client()->didReceiveResponse(m_handle, ResourceResponse(m_response));
    564     m_response.setResponseFired(true);
    565 }
    566 
    567 inline static bool isHttpInfo(int statusCode)
    568 {
    569     return 100 <= statusCode && statusCode < 200;
    570 }
    571 
    572 inline static bool isHttpRedirect(int statusCode)
    573 {
    574     return 300 <= statusCode && statusCode < 400 && statusCode != 304;
    575 }
    576 
    577 inline static bool isHttpAuthentication(int statusCode)
    578 {
    579     return statusCode == 401;
    580 }
    581 
    582 inline static bool isHttpNotModified(int statusCode)
    583 {
    584     return statusCode == 304;
    585 }
    586 
    587 static bool isAppendableHeader(const String &key)
    588 {
    589     static const char* appendableHeaders[] = {
    590         "access-control-allow-headers",
    591         "access-control-allow-methods",
    592         "access-control-allow-origin",
    593         "access-control-expose-headers",
    594         "allow",
    595         "cache-control",
    596         "connection",
    597         "content-encoding",
    598         "content-language",
    599         "if-match",
    600         "if-none-match",
    601         "keep-alive",
    602         "pragma",
    603         "proxy-authenticate",
    604         "public",
    605         "server",
    606         "set-cookie",
    607         "te",
    608         "trailer",
    609         "transfer-encoding",
    610         "upgrade",
    611         "user-agent",
    612         "vary",
    613         "via",
    614         "warning",
    615         "www-authenticate"
    616     };
    617 
    618     // Custom headers start with 'X-', and need no further checking.
    619     if (key.startsWith("x-", /* caseSensitive */ false))
    620         return true;
    621 
    622     for (auto& header : appendableHeaders) {
    623         if (equalIgnoringASCIICase(key, header))
    624             return true;
    625     }
    626 
    627     return false;
    628 }
    629 
    630 static void removeLeadingAndTrailingQuotes(String& value)
    631 {
    632     unsigned length = value.length();
    633     if (value.startsWith('"') && value.endsWith('"') && length > 1)
    634         value = value.substring(1, length - 2);
    635 }
    636 
    637 static bool getProtectionSpace(ResourceHandle* job, const ResourceResponse& response, ProtectionSpace& protectionSpace)
    638 {
    639     ResourceHandleInternal* d = job->getInternal();
    640 
    641     CURLcode err;
    642 
    643     long port = 0;
    644     err = d->m_curlHandle.getPrimaryPort(port);
    645     if (err != CURLE_OK)
    646         return false;
    647 
    648     long availableAuth = CURLAUTH_NONE;
    649     err = d->m_curlHandle.getHttpAuthAvail(availableAuth);
    650     if (err != CURLE_OK)
    651         return false;
    652 
    653     URL url = d->m_curlHandle.getEffectiveURL();
    654     if (!url.isValid())
    655         return false;
    656 
    657     String host = url.host();
    658     StringView protocol = url.protocol();
    659 
    660     String realm;
    661 
    662     const String authHeader = response.httpHeaderField(HTTPHeaderName::Authorization);
    663     const String realmString = "realm=";
    664     int realmPos = authHeader.find(realmString);
    665     if (realmPos > 0) {
    666         realm = authHeader.substring(realmPos + realmString.length());
    667         realm = realm.left(realm.find(','));
    668         removeLeadingAndTrailingQuotes(realm);
    669     }
    670 
    671     ProtectionSpaceServerType serverType = ProtectionSpaceServerHTTP;
    672     if (protocol == "https")
    673         serverType = ProtectionSpaceServerHTTPS;
    674 
    675     ProtectionSpaceAuthenticationScheme authScheme = ProtectionSpaceAuthenticationSchemeUnknown;
    676 
    677     if (availableAuth & CURLAUTH_BASIC)
    678         authScheme = ProtectionSpaceAuthenticationSchemeHTTPBasic;
    679     if (availableAuth & CURLAUTH_DIGEST)
    680         authScheme = ProtectionSpaceAuthenticationSchemeHTTPDigest;
    681     if (availableAuth & CURLAUTH_GSSNEGOTIATE)
    682         authScheme = ProtectionSpaceAuthenticationSchemeNegotiate;
    683     if (availableAuth & CURLAUTH_NTLM)
    684         authScheme = ProtectionSpaceAuthenticationSchemeNTLM;
    685 
    686     protectionSpace = ProtectionSpace(host, port, serverType, realm, authScheme);
    687 
    688     return true;
    689 }
    690 
    691 size_t ResourceHandleInternal::willPrepareSendData(char* ptr, size_t blockSize, size_t numberOfBlocks)
    692 {
    693     if (!m_formDataStream.hasMoreElements())
    694         return 0;
    695 
    696     size_t size = m_formDataStream.read(ptr, blockSize, numberOfBlocks);
    697 
    698     // Something went wrong so cancel the job.
    699     if (!size) {
    700         m_handle->cancel();
    701         return 0;
    702     }
    703 
    704     return size;
    705 
    706 }
    707 
    708 void ResourceHandleInternal::didReceiveHeaderLine(const String& header)
    709 {
    710     ASSERT(isMainThread());
    711 
    712     auto splitPosition = header.find(":");
    713     if (splitPosition != notFound) {
    714         String key = header.left(splitPosition).stripWhiteSpace();
    715         String value = header.substring(splitPosition + 1).stripWhiteSpace();
    716 
    717         if (isAppendableHeader(key))
    718             m_response.addHTTPHeaderField(key, value);
    719         else
    720             m_response.setHTTPHeaderField(key, value);
    721     } else if (header.startsWith("HTTP", false)) {
    722         // This is the first line of the response.
    723         // Extract the http status text from this.
    724         //
    725         // If the FOLLOWLOCATION option is enabled for the curl handle then
    726         // curl will follow the redirections internally. Thus this header callback
    727         // will be called more than one time with the line starting "HTTP" for one job.
    728         long httpCode = 0;
    729         m_curlHandle.getResponseCode(httpCode);
    730 
    731         String httpCodeString = String::number(httpCode);
    732         int statusCodePos = header.find(httpCodeString);
    733 
    734         if (statusCodePos != notFound) {
    735             // The status text is after the status code.
    736             String status = header.substring(statusCodePos + httpCodeString.length());
    737             m_response.setHTTPStatusText(status.stripWhiteSpace());
    738         }
    739     }
    740 }
    741 
    742 void ResourceHandleInternal::didReceiveAllHeaders(long httpCode, long long contentLength)
    743 {
    744     ASSERT(isMainThread());
    745 
    746     m_response.setExpectedContentLength(contentLength);
    747 
    748     m_response.setURL(m_curlHandle.getEffectiveURL());
    749 
    750     m_response.setHTTPStatusCode(httpCode);
    751     m_response.setMimeType(extractMIMETypeFromMediaType(m_response.httpHeaderField(HTTPHeaderName::ContentType)).convertToASCIILowercase());
    752     m_response.setTextEncodingName(extractCharsetFromMediaType(m_response.httpHeaderField(HTTPHeaderName::ContentType)));
    753 
    754     if (m_response.isMultipart()) {
    755         String boundary;
    756         bool parsed = MultipartHandle::extractBoundary(m_response.httpHeaderField(HTTPHeaderName::ContentType), boundary);
    757         if (parsed)
    758             m_multipartHandle = std::make_unique<MultipartHandle>(m_handle, boundary);
    759     }
    760 
    761     // HTTP redirection
    762     if (isHttpRedirect(httpCode)) {
    763         String location = m_response.httpHeaderField(HTTPHeaderName::Location);
    764         if (!location.isEmpty()) {
    765             URL newURL = URL(m_firstRequest.url(), location);
    766 
    767             ResourceRequest redirectedRequest = m_firstRequest;
    768             redirectedRequest.setURL(newURL);
    769             ResourceResponse response = m_response;
    770             if (client())
    771                 client()->willSendRequest(m_handle, WTFMove(redirectedRequest), WTFMove(response));
    772 
    773             m_firstRequest.setURL(newURL);
    774 
    775             return;
    776         }
    777     } else if (isHttpAuthentication(httpCode)) {
    778         ProtectionSpace protectionSpace;
    779         if (getProtectionSpace(m_handle, m_response, protectionSpace)) {
    780             Credential credential;
    781             AuthenticationChallenge challenge(protectionSpace, credential, m_authFailureCount, m_response, ResourceError());
    782             challenge.setAuthenticationClient(m_handle);
    783             m_handle->didReceiveAuthenticationChallenge(challenge);
    784             m_authFailureCount++;
    785             return;
    786         }
    787     }
    788 
    789     if (client()) {
    790         if (isHttpNotModified(httpCode)) {
    791             const String& url = m_firstRequest.url().string();
    792             if (CurlCacheManager::getInstance().getCachedResponse(url, m_response)) {
    793                 if (m_addedCacheValidationHeaders) {
    794                     m_response.setHTTPStatusCode(200);
    795                     m_response.setHTTPStatusText("OK");
    796                 }
    797             }
    798         }
    799         client()->didReceiveResponse(m_handle, ResourceResponse(m_response));
    800         CurlCacheManager::getInstance().didReceiveResponse(*m_handle, m_response);
    801     }
    802 
    803     m_response.setResponseFired(true);
    804 }
    805 
    806 void ResourceHandleInternal::didReceiveContentData()
    807 {
    808     ASSERT(isMainThread());
    809 
    810     if (!m_response.responseFired())
    811         handleLocalReceiveResponse();
    812 
    813     Vector<char> buffer;
    814     {
    815         LockHolder locker { m_receivedBufferMutex };
    816         buffer = WTFMove(m_receivedBuffer);
    817     }
    818 
    819     char* ptr = buffer.begin();
    820     size_t size = buffer.size();
    821 
    822     if (m_multipartHandle)
    823         m_multipartHandle->contentReceived(static_cast<const char*>(ptr), size);
    824     else if (client()) {
    825         client()->didReceiveData(m_handle, ptr, size, 0);
    826         CurlCacheManager::getInstance().didReceiveData(*m_handle, ptr, size);
    827     }
    828 }
    829 
    830 /* This is called to obtain HTTP POST or PUT data.
    831 Iterate through FormData elements and upload files.
    832 Carefully respect the given buffer size and fill the rest of the data at the next calls.
    833 */
    834 size_t ResourceHandleInternal::readCallback(char* ptr, size_t size, size_t nmemb, void* data)
    835 {
    836     ASSERT(!isMainThread());
    837 
    838     ResourceHandleInternal* d = static_cast<ResourceHandleInternal*>(data);
    839 
    840     if (d->m_cancelled)
    841         return 0;
    842 
    843     // We should never be called when deferred loading is activated.
    844     ASSERT(!d->m_defersLoading);
    845 
    846     if (!size || !nmemb)
    847         return 0;
    848 
    849     return d->willPrepareSendData(ptr, size, nmemb);
    850 }
    851 
    852 /*
    853 * This is being called for each HTTP header in the response. This includes '\r\n'
    854 * for the last line of the header.
    855 *
    856 * We will add each HTTP Header to the ResourceResponse and on the termination
    857 * of the header (\r\n) we will parse Content-Type and Content-Disposition and
    858 * update the ResourceResponse and then send it away.
    859 *
    860 */
    861 size_t ResourceHandleInternal::headerCallback(char* ptr, size_t size, size_t nmemb, void* data)
    862 {
    863     ASSERT(!isMainThread());
    864 
    865     ResourceHandleInternal* d = static_cast<ResourceHandleInternal*>(data);
    866     ResourceHandle* job = d->m_handle;
    867 
    868     if (d->m_cancelled)
    869         return 0;
    870 
    871     // We should never be called when deferred loading is activated.
    872     ASSERT(!d->m_defersLoading);
    873 
    874     size_t totalSize = size * nmemb;
    875 
    876     String header(static_cast<const char*>(ptr), totalSize);
    877 
    878     /*
    879     * a) We can finish and send the ResourceResponse
    880     * b) We will add the current header to the HTTPHeaderMap of the ResourceResponse
    881     *
    882     * The HTTP standard requires to use \r\n but for compatibility it recommends to
    883     * accept also \n.
    884     */
    885     if (header == AtomicString("\r\n") || header == AtomicString("\n")) {
    886         long httpCode = 0;
    887         d->m_curlHandle.getResponseCode(httpCode);
    888 
    889         if (!httpCode) {
    890             // Comes here when receiving 200 Connection Established. Just return.
    891             return totalSize;
    892         }
    893         if (isHttpInfo(httpCode)) {
    894             // Just return when receiving http info, e.g. HTTP/1.1 100 Continue.
    895             // If not, the request might be cancelled, because the MIME type will be empty for this response.
    896             return totalSize;
    897         }
    898 
    899         long long contentLength = 0;
    900         d->m_curlHandle.getContentLenghtDownload(contentLength);
    901 
    902         callOnMainThread([job = RefPtr<ResourceHandle>(job), d, httpCode, contentLength] {
    903             if (!d->m_cancelled)
    904                 d->didReceiveAllHeaders(httpCode, contentLength);
    905         });
    906     } else {
    907         callOnMainThread([job = RefPtr<ResourceHandle>(job), d, header] {
    908             if (!d->m_cancelled)
    909                 d->didReceiveHeaderLine(header);
    910         });
    911     }
    912 
    913     return totalSize;
    914 }
    915 
    916 // called with data after all headers have been processed via headerCallback
    917 size_t ResourceHandleInternal::writeCallback(char* ptr, size_t size, size_t nmemb, void* data)
    918 {
    919     ASSERT(!isMainThread());
    920 
    921     ResourceHandleInternal* d = static_cast<ResourceHandleInternal*>(data);
    922     ResourceHandle* job = d->m_handle;
    923 
    924     if (d->m_cancelled)
    925         return 0;
    926 
    927     // We should never be called when deferred loading is activated.
    928     ASSERT(!d->m_defersLoading);
    929 
    930     size_t totalSize = size * nmemb;
    931 
    932     // this shouldn't be necessary but apparently is. CURL writes the data
    933     // of html page even if it is a redirect that was handled internally
    934     // can be observed e.g. on gmail.com
    935     long httpCode = 0;
    936     CURLcode errCd = d->m_curlHandle.getResponseCode(httpCode);
    937     if (CURLE_OK == errCd && httpCode >= 300 && httpCode < 400)
    938         return totalSize;
    939 
    940     bool shouldCall { false };
    941     {
    942         LockHolder locker(d->m_receivedBufferMutex);
    943        
    944         if (d->m_receivedBuffer.isEmpty())
    945             shouldCall = true;
    946        
    947         d->m_receivedBuffer.append(ptr, totalSize);
    948     }
    949 
    950     if (shouldCall) {
    951         callOnMainThread([job = RefPtr<ResourceHandle>(job), d] {
    952             if (!d->m_cancelled)
    953                 d->didReceiveContentData();
    954         });
    955     }
    956 
    957     return totalSize;
    958 }
    959 
    960233// sync loader
    961234
     
    967240    RefPtr<ResourceHandle> handle = adoptRef(new ResourceHandle(context, request, &client, false, false));
    968241
    969     handle->d->dispatchSynchronousJob();
     242    handle->d->m_delegate->dispatchSynchronousJob();
    970243
    971244    error = client.error();
     
    974247}
    975248
    976 void ResourceHandleInternal::dispatchSynchronousJob()
    977 {
    978     URL kurl = m_firstRequest.url();
    979 
    980     if (kurl.protocolIsData()) {
    981         handleDataURL();
    982         return;
    983     }
    984 
    985     // If defersLoading is true and we call curl_easy_perform
    986     // on a paused handle, libcURL would do the transfert anyway
    987     // and we would assert so force defersLoading to be false.
    988     m_defersLoading = false;
    989 
    990     initialize();
    991 
    992     // curl_easy_perform blocks until the transfert is finished.
    993     CURLcode ret = m_curlHandle.perform();
    994 
    995     calculateWebTimingInformations();
    996 
    997     if (client()) {
    998         if (ret != CURLE_OK)
    999             client()->didFail(m_handle, ResourceError(CurlContext::errorDomain, m_curlHandle.errorCode(), m_curlHandle.getEffectiveURL(), m_curlHandle.errorDescription(), m_curlHandle.getSslErrors()));
    1000         else
    1001             client()->didReceiveResponse(m_handle, ResourceResponse(m_response));
    1002     }
    1003 }
    1004 
    1005 void ResourceHandleInternal::handleDataURL()
    1006 {
    1007     ASSERT(m_firstRequest.url().protocolIsData());
    1008     String url = m_firstRequest.url().string();
    1009 
    1010     ASSERT(client());
    1011 
    1012     int index = url.find(',');
    1013     if (index == -1) {
    1014         client()->cannotShowURL(m_handle);
    1015         return;
    1016     }
    1017 
    1018     String mediaType = url.substring(5, index - 5);
    1019     String data = url.substring(index + 1);
    1020 
    1021     bool base64 = mediaType.endsWith(";base64", false);
    1022     if (base64)
    1023         mediaType = mediaType.left(mediaType.length() - 7);
    1024 
    1025     if (mediaType.isEmpty())
    1026         mediaType = "text/plain";
    1027 
    1028     String mimeType = extractMIMETypeFromMediaType(mediaType);
    1029     String charset = extractCharsetFromMediaType(mediaType);
    1030 
    1031     if (charset.isEmpty())
    1032         charset = "US-ASCII";
    1033 
    1034     ResourceResponse response;
    1035     response.setMimeType(mimeType);
    1036     response.setTextEncodingName(charset);
    1037     response.setURL(m_firstRequest.url());
    1038 
    1039     if (base64) {
    1040         data = decodeURLEscapeSequences(data);
    1041         client()->didReceiveResponse(m_handle, WTFMove(response));
    1042 
    1043         // didReceiveResponse might cause the client to be deleted.
    1044         if (client()) {
    1045             Vector<char> out;
    1046             if (base64Decode(data, out, Base64IgnoreSpacesAndNewLines) && out.size() > 0)
    1047                 client()->didReceiveData(m_handle, out.data(), out.size(), 0);
    1048         }
    1049     } else {
    1050         TextEncoding encoding(charset);
    1051         data = decodeURLEscapeSequences(data, encoding);
    1052         client()->didReceiveResponse(m_handle, WTFMove(response));
    1053 
    1054         // didReceiveResponse might cause the client to be deleted.
    1055         if (client()) {
    1056             CString encodedData = encoding.encode(data, URLEncodedEntitiesForUnencodables);
    1057             if (encodedData.length())
    1058                 client()->didReceiveData(m_handle, encodedData.data(), encodedData.length(), 0);
    1059         }
    1060     }
    1061 
    1062     if (client())
    1063         client()->didFinishLoading(m_handle);
    1064 }
    1065 
    1066249} // namespace WebCore
    1067250
  • trunk/Source/WebCore/platform/network/curl/ResourceHandleCurlDelegate.cpp

    r220938 r220939  
    44 * Copyright (C) 2017 Sony Interactive Entertainment Inc.
    55 * All rights reserved.
     6 * Copyright (C) 2017 NAVER Corp. All rights reserved.
    67 *
    78 * Redistribution and use in source and binary forms, with or without
     
    2829
    2930#include "config.h"
    30 #include "ResourceHandle.h"
     31#include "ResourceHandleCurlDelegate.h"
    3132
    3233#if USE(CURL)
    3334
    34 #include "CachedResourceLoader.h"
    3535#include "CredentialStorage.h"
    3636#include "CurlCacheManager.h"
    37 #include "CurlContext.h"
    38 #include "CurlJobManager.h"
    39 #include "FileSystem.h"
    40 #include "Logging.h"
    4137#include "MIMETypeRegistry.h"
    42 #include "NetworkingContext.h"
     38#include "MultipartHandle.h"
     39#include "ResourceHandle.h"
    4340#include "ResourceHandleInternal.h"
    4441#include "SSLHandle.h"
    45 #include "SynchronousLoaderClient.h"
     42#include "TextEncoding.h"
     43#include "ThreadSafeDataBuffer.h"
     44#include "URL.h"
     45#include <wtf/MainThread.h>
    4646#include <wtf/text/Base64.h>
    4747
    4848namespace WebCore {
    4949
    50 ResourceHandleInternal::~ResourceHandleInternal()
    51 {
    52 }
    53 
    54 ResourceHandle::~ResourceHandle()
    55 {
    56 }
    57 
    58 bool ResourceHandle::start()
    59 {
    60     ASSERT(isMainThread());
    61 
    62     // The frame could be null if the ResourceHandle is not associated to any
    63     // Frame, e.g. if we are downloading a file.
    64     // If the frame is not null but the page is null this must be an attempted
    65     // load from an unload handler, so let's just block it.
    66     // If both the frame and the page are not null the context is valid.
    67     if (d->m_context && !d->m_context->isValid())
    68         return false;
    69 
    70     d->initialize();
    71 
    72     d->m_job = CurlJobManager::singleton().add(d->m_curlHandle, [this, protectedThis = makeRef(*this)](CurlJobResult result) {
    73         ASSERT(isMainThread());
    74 
    75         switch (result) {
    76         case CurlJobResult::Done:
    77             d->didFinish();
    78             break;
    79 
    80         case CurlJobResult::Error:
    81             d->didFail();
    82             break;
    83 
    84         case CurlJobResult::Cancelled:
    85             break;
    86         }
    87     });
    88     ASSERT(d->m_job);
    89 
    90     return true;
    91 }
    92 
    93 void ResourceHandle::cancel()
    94 {
    95     d->m_cancelled = true;
    96     CurlJobManager::singleton().cancel(d->m_job);
    97 }
    98 
    99 void ResourceHandleInternal::initialize()
     50ResourceHandleCurlDelegate::ResourceHandleCurlDelegate(ResourceHandle* handle)
     51    : m_handle(handle)
     52    , m_formDataStream(handle)
     53    , m_firstRequest(handle->firstRequest().isolatedCopy())
     54    , m_customHTTPHeaderFields(m_firstRequest.httpHeaderFields().isolatedCopy())
     55    , m_shouldUseCredentialStorage(handle->shouldUseCredentialStorage())
     56    , m_user(handle->getInternal()->m_user.isolatedCopy())
     57    , m_pass(handle->getInternal()->m_pass.isolatedCopy())
     58    , m_initialCredential(handle->getInternal()->m_initialCredential)
     59    , m_defersLoading(handle->getInternal()->m_defersLoading)
     60{
     61    const URL& url = m_firstRequest.url();
     62
     63    if (url.isLocalFile()) {
     64        // Determine the MIME type based on the path.
     65        response().setMimeType(MIMETypeRegistry::getMIMETypeForPath(url));
     66    }
     67
     68    if (m_customHTTPHeaderFields.size()) {
     69        auto& cache = CurlCacheManager::getInstance();
     70        bool hasCacheHeaders = m_customHTTPHeaderFields.contains(HTTPHeaderName::IfModifiedSince) || m_customHTTPHeaderFields.contains(HTTPHeaderName::IfNoneMatch);
     71        if (!hasCacheHeaders && cache.isCached(url)) {
     72            cache.addCacheEntryClient(url, m_handle);
     73            // append additional cache information
     74            for (auto entry : cache.requestHeaders(url))
     75                m_customHTTPHeaderFields.set(entry.key, entry.value);
     76            m_addedCacheValidationHeaders = true;
     77        }
     78    }
     79
     80    m_sslClientCertificate = getSSLClientCertificate(url.host());
     81
     82    setupAuthentication();
     83}
     84
     85ResourceHandleCurlDelegate::~ResourceHandleCurlDelegate()
     86{
     87}
     88
     89bool ResourceHandleCurlDelegate::hasHandle() const
     90{
     91    return !!m_handle;
     92}
     93
     94void ResourceHandleCurlDelegate::releaseHandle()
     95{
     96    m_handle = nullptr;
     97}
     98
     99bool ResourceHandleCurlDelegate::start()
     100{
     101    m_job = CurlJobManager::singleton().add(m_curlHandle, *this);
     102    return !!m_job;
     103}
     104
     105void ResourceHandleCurlDelegate::cancel()
     106{
     107    releaseHandle();
     108    CurlJobManager::singleton().cancel(m_job);
     109}
     110
     111void ResourceHandleCurlDelegate::setDefersLoading(bool defers)
     112{
     113    if (defers == m_defersLoading)
     114        return;
     115
     116    m_defersLoading = defers;
     117
     118    auto action = [protectedThis = makeRef(*this)]() {
     119        if (protectedThis->m_defersLoading) {
     120            CURLcode error = protectedThis->m_curlHandle.pause(CURLPAUSE_ALL);
     121            // If we could not defer the handle, so don't do it.
     122            if (error != CURLE_OK)
     123                return;
     124        } else {
     125            CURLcode error = protectedThis->m_curlHandle.pause(CURLPAUSE_CONT);
     126            if (error != CURLE_OK) {
     127                // Restarting the handle has failed so just cancel it.
     128                protectedThis->m_handle->cancel();
     129            }
     130        }
     131    };
     132
     133    CurlJobManager::singleton().callOnJobThread(WTFMove(action));
     134}
     135
     136void ResourceHandleCurlDelegate::setAuthentication(const String& user, const String& pass)
     137{
     138    auto action = [protectedThis = makeRef(*this), user = user.isolatedCopy(), pass = pass.isolatedCopy()]() {
     139        protectedThis->m_user = user;
     140        protectedThis->m_pass = pass;
     141        protectedThis->m_curlHandle.setHttpAuthUserPass(user, pass);
     142    };
     143
     144    CurlJobManager::singleton().callOnJobThread(WTFMove(action));
     145}
     146
     147void ResourceHandleCurlDelegate::dispatchSynchronousJob()
     148{
     149    URL kurl = m_firstRequest.url();
     150
     151    if (kurl.protocolIsData()) {
     152        handleDataURL();
     153        return;
     154    }
     155
     156    // If defersLoading is true and we call curl_easy_perform
     157    // on a paused handle, libcURL would do the transfert anyway
     158    // and we would assert so force defersLoading to be false.
     159    m_defersLoading = false;
     160
     161    setupRequest();
     162
     163    // curl_easy_perform blocks until the transfer is finished.
     164    CURLcode ret = m_curlHandle.perform();
     165
     166    double pretransferTime = 0;
     167    double dnsLookupTime = 0;
     168    double connectTime = 0;
     169    double appConnectTime = 0;
     170
     171    m_curlHandle.getTimes(pretransferTime, dnsLookupTime, connectTime, appConnectTime);
     172    setWebTimings(pretransferTime, dnsLookupTime, connectTime, appConnectTime);
     173
     174    if (m_handle->client()) {
     175        if (ret != CURLE_OK) {
     176            String domain = CurlContext::errorDomain;
     177            int errorCode = m_curlHandle.errorCode();
     178            URL failingURL = m_curlHandle.getEffectiveURL();
     179            String errorDescription = m_curlHandle.errorDescription();
     180            unsigned sslErrors = m_curlHandle.getSslErrors();
     181
     182            m_handle->client()->didFail(m_handle, ResourceError(domain, errorCode, failingURL, errorDescription, sslErrors));
     183        } else
     184            m_handle->client()->didReceiveResponse(m_handle, ResourceResponse(response()));
     185    }
     186}
     187
     188void ResourceHandleCurlDelegate::retain()
     189{
     190    ref();
     191}
     192
     193void ResourceHandleCurlDelegate::release()
     194{
     195    deref();
     196}
     197
     198void ResourceHandleCurlDelegate::setupRequest()
    100199{
    101200    CurlContext& context = CurlContext::singleton();
     
    107206
    108207    String urlString = url.string();
     208
     209    m_curlHandle.initialize();
    109210
    110211    if (url.isLocalFile()) {
     
    115216            urlString = url.string();
    116217        }
    117         // Determine the MIME type based on the path.
    118         m_response.setMimeType(MIMETypeRegistry::getMIMETypeForPath(url));
    119218    }
    120219
     
    131230#endif
    132231
    133     m_curlHandle.initialize();
    134232    m_curlHandle.setSslVerifyPeer(CurlHandle::VerifyPeerEnable);
    135233    m_curlHandle.setSslVerifyHost(CurlHandle::VerifyHostStrictNameCheck);
    136234    m_curlHandle.setPrivateData(this);
    137     m_curlHandle.setWriteCallbackFunction(writeCallback, this);
    138     m_curlHandle.setHeaderCallbackFunction(headerCallback, this);
     235    m_curlHandle.setWriteCallbackFunction(didReceiveDataCallback, this);
     236    m_curlHandle.setHeaderCallbackFunction(didReceiveHeaderCallback, this);
    139237    m_curlHandle.enableAutoReferer();
    140238    m_curlHandle.enableFollowLocation();
     
    143241    m_curlHandle.enableTimeout();
    144242    m_curlHandle.enableAllowedProtocols();
    145     auto certificate = getSSLClientCertificate(m_firstRequest.url().host());
    146     if (certificate) {
    147         m_curlHandle.setSslCert((*certificate).first.utf8().data());
     243
     244    if (m_sslClientCertificate) {
     245        m_curlHandle.setSslCert((*m_sslClientCertificate).first.utf8().data());
    148246        m_curlHandle.setSslCertType("P12");
    149         m_curlHandle.setSslKeyPassword((*certificate).second.utf8().data());
     247        m_curlHandle.setSslKeyPassword((*m_sslClientCertificate).second.utf8().data());
    150248    }
    151249
     
    161259    m_curlHandle.enableCookieJarIfExists();
    162260
    163     if (m_firstRequest.httpHeaderFields().size()) {
    164         auto customHeaders = m_firstRequest.httpHeaderFields();
    165         auto& cache = CurlCacheManager::getInstance();
    166 
    167         bool hasCacheHeaders = customHeaders.contains(HTTPHeaderName::IfModifiedSince) || customHeaders.contains(HTTPHeaderName::IfNoneMatch);
    168         if (!hasCacheHeaders && cache.isCached(url)) {
    169             cache.addCacheEntryClient(url, m_handle);
    170 
    171             // append additional cache information
    172             for (auto entry : cache.requestHeaders(url))
    173                 customHeaders.set(entry.key, entry.value);
    174 
    175             m_addedCacheValidationHeaders = true;
    176         }
    177 
    178         m_curlHandle.appendRequestHeaders(customHeaders);
    179     }
     261    if (m_customHTTPHeaderFields.size())
     262        m_curlHandle.appendRequestHeaders(m_customHTTPHeaderFields);
    180263
    181264    String method = m_firstRequest.httpMethod();
     
    200283}
    201284
    202 void ResourceHandleInternal::applyAuthentication()
     285void ResourceHandleCurlDelegate::notifyFinish()
     286{
     287    double pretransferTime = 0;
     288    double dnsLookupTime = 0;
     289    double connectTime = 0;
     290    double appConnectTime = 0;
     291
     292    m_curlHandle.getTimes(pretransferTime, dnsLookupTime, connectTime, appConnectTime);
     293
     294    callOnMainThread([protectedThis = makeRef(*this), pretransferTime, dnsLookupTime, connectTime, appConnectTime] {
     295        if (!protectedThis->m_handle)
     296            return;
     297        protectedThis->didFinish(pretransferTime, dnsLookupTime, connectTime, appConnectTime);
     298    });
     299}
     300
     301void ResourceHandleCurlDelegate::notifyFail()
     302{
     303    String domain = CurlContext::errorDomain;
     304    int errorCode = m_curlHandle.errorCode();
     305    URL failingURL = m_curlHandle.getEffectiveURL();
     306    String errorDescription = m_curlHandle.errorDescription();
     307    unsigned sslErrors = m_curlHandle.getSslErrors();
     308
     309    callOnMainThread([protectedThis = makeRef(*this), domain = domain.isolatedCopy(), errorCode, failingURL = failingURL.isolatedCopy(), errorDescription = errorDescription.isolatedCopy(), sslErrors] {
     310        if (!protectedThis->m_handle)
     311            return;
     312        protectedThis->didFail(domain, errorCode, failingURL, errorDescription, sslErrors);
     313    });
     314}
     315
     316ResourceResponse& ResourceHandleCurlDelegate::response()
     317{
     318    return m_handle->getInternal()->m_response;
     319}
     320
     321void ResourceHandleCurlDelegate::setupAuthentication()
    203322{
    204323    // m_user/m_pass are credentials given manually, for instance, by the arguments passed to XMLHttpRequest.open().
    205324    String partition = m_firstRequest.cachePartition();
    206325
    207     if (m_handle->shouldUseCredentialStorage()) {
     326    if (m_shouldUseCredentialStorage) {
    208327        if (m_user.isEmpty() && m_pass.isEmpty()) {
    209328            // <rdar://problem/7174050> - For URLs that match the paths of those previously challenged for HTTP Basic authentication,
     
    218337        }
    219338    }
    220 
    221     String user = m_user;
    222     String password = m_pass;
    223 
    224     if (!m_initialCredential.isEmpty()) {
    225         user = m_initialCredential.user();
    226         password = m_initialCredential.password();
    227         m_curlHandle.enableHttpAuthentication(CURLAUTH_BASIC);
    228     }
    229 
    230     // It seems we need to set CURLOPT_USERPWD even if username and password is empty.
    231     // Otherwise cURL will not automatically continue with a new request after a 401 response.
    232 
    233     // curl CURLOPT_USERPWD expects username:password
    234     m_curlHandle.setHttpAuthUserPass(user, password);
    235 }
    236 
    237 static inline size_t getFormElementsCount(ResourceHandle* job)
    238 {
    239     RefPtr<FormData> formData = job->firstRequest().httpBody();
    240 
    241     if (!formData)
    242         return 0;
    243 
    244     // Resolve the blob elements so the formData can correctly report it's size.
    245     formData = formData->resolveBlobReferences();
    246     size_t size = formData->elements().size();
    247     job->firstRequest().setHTTPBody(WTFMove(formData));
    248 
    249     return size;
    250 }
    251 
    252 void ResourceHandleInternal::setupPUT()
    253 {
    254     m_curlHandle.enableHttpPutRequest();
    255 
    256     // Disable the Expect: 100 continue header
    257     m_curlHandle.appendRequestHeader("Expect:");
    258 
    259     size_t numElements = getFormElementsCount(m_handle);
    260     if (!numElements)
    261         return;
    262 
    263     setupFormData(false);
    264 }
    265 
    266 void ResourceHandleInternal::setupPOST()
    267 {
    268     m_curlHandle.enableHttpPostRequest();
    269 
    270     size_t numElements = getFormElementsCount(m_handle);
    271     if (!numElements)
    272         return;
    273 
    274     // Do not stream for simple POST data
    275     if (numElements == 1) {
    276         m_firstRequest.httpBody()->flatten(m_postBytes);
    277         if (m_postBytes.size())
    278             m_curlHandle.setPostFields(m_postBytes.data(), m_postBytes.size());
    279         return;
    280     }
    281 
    282     setupFormData(true);
    283 }
    284 
    285 void ResourceHandleInternal::setupFormData(bool isPostRequest)
    286 {
    287     Vector<FormDataElement> elements = m_firstRequest.httpBody()->elements();
    288     size_t numElements = elements.size();
    289 
    290     static const long long maxCurlOffT = m_curlHandle.maxCurlOffT();
    291 
    292     // Obtain the total size of the form data
    293     curl_off_t size = 0;
    294     bool chunkedTransfer = false;
    295     for (size_t i = 0; i < numElements; i++) {
    296         FormDataElement element = elements[i];
    297         if (element.m_type == FormDataElement::Type::EncodedFile) {
    298             long long fileSizeResult;
    299             if (getFileSize(element.m_filename, fileSizeResult)) {
    300                 if (fileSizeResult > maxCurlOffT) {
    301                     // File size is too big for specifying it to cURL
    302                     chunkedTransfer = true;
    303                     break;
    304                 }
    305                 size += fileSizeResult;
    306             } else {
    307                 chunkedTransfer = true;
    308                 break;
    309             }
    310         } else
    311             size += elements[i].m_data.size();
    312     }
    313 
    314     // cURL guesses that we want chunked encoding as long as we specify the header
    315     if (chunkedTransfer)
    316         m_curlHandle.appendRequestHeader("Transfer-Encoding: chunked");
    317     else {
    318         if (isPostRequest)
    319             m_curlHandle.setPostFieldLarge(size);
    320         else
    321             m_curlHandle.setInFileSizeLarge(size);
    322     }
    323 
    324     m_curlHandle.setReadCallbackFunction(readCallback, this);
    325 }
    326 
    327 #if OS(WINDOWS)
    328 
    329 void ResourceHandle::setHostAllowsAnyHTTPSCertificate(const String& host)
    330 {
    331     ASSERT(isMainThread());
    332 
    333     allowsAnyHTTPSCertificateHosts(host);
    334 }
    335 
    336 void ResourceHandle::setClientCertificateInfo(const String& host, const String& certificate, const String& key)
    337 {
    338     ASSERT(isMainThread());
    339 
    340     if (fileExists(certificate))
    341         addAllowedClientCertificate(host, certificate, key);
    342     else
    343         LOG(Network, "Invalid client certificate file: %s!\n", certificate.latin1().data());
    344 }
    345 
    346 #endif
    347 
    348 #if OS(WINDOWS) && USE(CF)
    349 
    350 void ResourceHandle::setClientCertificate(const String&, CFDataRef)
    351 {
    352 }
    353 
    354 #endif
    355 
    356 void ResourceHandle::platformSetDefersLoading(bool defers)
    357 {
    358     ASSERT(isMainThread());
    359 
    360     auto action = [defers, this, protectedThis = makeRef(*this)]() {
    361         if (defers) {
    362             CURLcode error = d->m_curlHandle.pause(CURLPAUSE_ALL);
    363             // If we could not defer the handle, so don't do it.
    364             if (error != CURLE_OK)
    365                 return;
    366         } else {
    367             CURLcode error = d->m_curlHandle.pause(CURLPAUSE_CONT);
    368             if (error != CURLE_OK) {
    369                 // Restarting the handle has failed so just cancel it.
    370                 cancel();
    371             }
    372         }
    373     };
    374 
    375     if (d->m_job) {
    376         CurlJobManager::singleton().callOnJobThread(WTFMove(action));
    377     } else {
    378         action();
    379     }
    380 }
    381 
    382 void ResourceHandleInternal::didFinish()
    383 {
    384     calculateWebTimingInformations();
    385 
    386     if (m_cancelled)
    387         return;
    388 
    389     if (!m_response.responseFired()) {
    390         handleLocalReceiveResponse();
    391         if (m_cancelled)
    392             return;
    393     }
    394 
    395     if (m_multipartHandle)
    396         m_multipartHandle->contentEnded();
    397 
    398     if (client()) {
    399         client()->didFinishLoading(m_handle);
    400         CurlCacheManager::getInstance().didFinishLoading(*m_handle);
    401     }
    402 }
    403 
    404 void ResourceHandleInternal::didFail()
    405 {
    406     if (m_cancelled)
    407         return;
     339}
     340
     341static void removeLeadingAndTrailingQuotes(String& value)
     342{
     343    unsigned length = value.length();
     344    if (value.startsWith('"') && value.endsWith('"') && length > 1)
     345        value = value.substring(1, length - 2);
     346}
     347
     348bool ResourceHandleCurlDelegate::getProtectionSpace(const ResourceResponse& response, ProtectionSpace& protectionSpace)
     349{
     350    CURLcode err;
     351
     352    long port = 0;
     353    err = m_curlHandle.getPrimaryPort(port);
     354    if (err != CURLE_OK)
     355        return false;
     356
     357    long availableAuth = CURLAUTH_NONE;
     358    err = m_curlHandle.getHttpAuthAvail(availableAuth);
     359    if (err != CURLE_OK)
     360        return false;
     361
    408362    URL url = m_curlHandle.getEffectiveURL();
    409     if (client()) {
    410         client()->didFail(m_handle, ResourceError(CurlContext::errorDomain, m_curlHandle.errorCode(), m_curlHandle.getEffectiveURL(), m_curlHandle.errorDescription(), m_curlHandle.getSslErrors()));
    411         CurlCacheManager::getInstance().didFail(*m_handle);
    412     }
    413 }
    414 
    415 bool ResourceHandle::shouldUseCredentialStorage()
    416 {
    417     return (!client() || client()->shouldUseCredentialStorage(this)) && firstRequest().url().protocolIsInHTTPFamily();
    418 }
    419 
    420 void ResourceHandle::didReceiveAuthenticationChallenge(const AuthenticationChallenge& challenge)
    421 {
    422     ASSERT(isMainThread());
    423 
    424     String partition = firstRequest().cachePartition();
    425 
    426     if (!d->m_user.isNull() && !d->m_pass.isNull()) {
    427         Credential credential(d->m_user, d->m_pass, CredentialPersistenceNone);
    428 
    429         URL urlToStore;
    430         if (challenge.failureResponse().httpStatusCode() == 401)
    431             urlToStore = challenge.failureResponse().url();
    432         CredentialStorage::defaultCredentialStorage().set(partition, credential, challenge.protectionSpace(), urlToStore);
    433        
    434         d->m_curlHandle.setHttpAuthUserPass(credential.user(), credential.password());
    435 
    436         d->m_user = String();
    437         d->m_pass = String();
    438         // FIXME: Per the specification, the user shouldn't be asked for credentials if there were incorrect ones provided explicitly.
    439         return;
    440     }
    441 
    442     if (shouldUseCredentialStorage()) {
    443         if (!d->m_initialCredential.isEmpty() || challenge.previousFailureCount()) {
    444             // The stored credential wasn't accepted, stop using it.
    445             // There is a race condition here, since a different credential might have already been stored by another ResourceHandle,
    446             // but the observable effect should be very minor, if any.
    447             CredentialStorage::defaultCredentialStorage().remove(partition, challenge.protectionSpace());
    448         }
    449 
    450         if (!challenge.previousFailureCount()) {
    451             Credential credential = CredentialStorage::defaultCredentialStorage().get(partition, challenge.protectionSpace());
    452             if (!credential.isEmpty() && credential != d->m_initialCredential) {
    453                 ASSERT(credential.persistence() == CredentialPersistenceNone);
    454                 if (challenge.failureResponse().httpStatusCode() == 401) {
    455                     // Store the credential back, possibly adding it as a default for this directory.
    456                     CredentialStorage::defaultCredentialStorage().set(partition, credential, challenge.protectionSpace(), challenge.failureResponse().url());
    457                 }
    458 
    459                 d->m_curlHandle.setHttpAuthUserPass(credential.user(), credential.password());
    460                 return;
    461             }
    462         }
    463     }
    464 
    465     d->m_currentWebChallenge = challenge;
    466    
    467     if (client())
    468         client()->didReceiveAuthenticationChallenge(this, d->m_currentWebChallenge);
    469 }
    470 
    471 void ResourceHandle::receivedCredential(const AuthenticationChallenge& challenge, const Credential& credential)
    472 {
    473     ASSERT(isMainThread());
    474 
    475     if (challenge != d->m_currentWebChallenge)
    476         return;
    477 
    478     if (credential.isEmpty()) {
    479         receivedRequestToContinueWithoutCredential(challenge);
    480         return;
    481     }
    482 
    483     String partition = firstRequest().cachePartition();
    484 
    485     if (shouldUseCredentialStorage()) {
    486         if (challenge.failureResponse().httpStatusCode() == 401) {
    487             URL urlToStore = challenge.failureResponse().url();
    488             CredentialStorage::defaultCredentialStorage().set(partition, credential, challenge.protectionSpace(), urlToStore);
    489         }
    490     }
    491 
    492     d->m_curlHandle.setHttpAuthUserPass(credential.user(), credential.password());
    493     clearAuthentication();
    494 }
    495 
    496 void ResourceHandle::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge& challenge)
    497 {
    498     ASSERT(isMainThread());
    499 
    500     if (challenge != d->m_currentWebChallenge)
    501         return;
    502 
    503     d->m_curlHandle.setHttpAuthUserPass("", "");
    504     clearAuthentication();
    505 }
    506 
    507 void ResourceHandle::receivedCancellation(const AuthenticationChallenge& challenge)
    508 {
    509     ASSERT(isMainThread());
    510 
    511     if (challenge != d->m_currentWebChallenge)
    512         return;
    513 
    514     if (client())
    515         client()->receivedCancellation(this, challenge);
    516 }
    517 
    518 void ResourceHandle::receivedRequestToPerformDefaultHandling(const AuthenticationChallenge&)
    519 {
    520     ASSERT_NOT_REACHED();
    521 }
    522 
    523 void ResourceHandle::receivedChallengeRejection(const AuthenticationChallenge&)
    524 {
    525     ASSERT_NOT_REACHED();
    526 }
    527 
    528 void ResourceHandleInternal::calculateWebTimingInformations()
    529 {
    530     double preTransferTime = 0;
    531     double dnslookupTime = 0;
    532     double connectTime = 0;
    533     double appConnectTime = 0;
    534 
    535     m_curlHandle.getTimes(preTransferTime, dnslookupTime, connectTime, appConnectTime);
    536 
    537     m_response.deprecatedNetworkLoadMetrics().domainLookupStart = Seconds(0);
    538     m_response.deprecatedNetworkLoadMetrics().domainLookupEnd = Seconds(dnslookupTime);
    539 
    540     m_response.deprecatedNetworkLoadMetrics().connectStart = Seconds(dnslookupTime);
    541     m_response.deprecatedNetworkLoadMetrics().connectEnd = Seconds(connectTime);
    542 
    543     m_response.deprecatedNetworkLoadMetrics().requestStart = Seconds(connectTime);
    544     m_response.deprecatedNetworkLoadMetrics().responseStart = Seconds(preTransferTime);
    545 
    546     if (appConnectTime)
    547         m_response.deprecatedNetworkLoadMetrics().secureConnectionStart = Seconds(connectTime);
    548 }
    549 
    550 void ResourceHandleInternal::handleLocalReceiveResponse()
    551 {
    552     ASSERT(isMainThread());
    553 
    554     // since the code in headerCallback will not have run for local files
    555     // the code to set the URL and fire didReceiveResponse is never run,
    556     // which means the ResourceLoader's response does not contain the URL.
    557     // Run the code here for local files to resolve the issue.
    558     // TODO: See if there is a better approach for handling this.
    559     URL url = m_curlHandle.getEffectiveURL();
    560     ASSERT(url.isValid());
    561     m_response.setURL(url);
    562     if (client())
    563         client()->didReceiveResponse(m_handle, ResourceResponse(m_response));
    564     m_response.setResponseFired(true);
     363    if (!url.isValid())
     364        return false;
     365
     366    String host = url.host();
     367    StringView protocol = url.protocol();
     368
     369    String realm;
     370
     371    const String authHeader = response.httpHeaderField(HTTPHeaderName::Authorization);
     372    const String realmString = "realm=";
     373    int realmPos = authHeader.find(realmString);
     374    if (realmPos > 0) {
     375        realm = authHeader.substring(realmPos + realmString.length());
     376        realm = realm.left(realm.find(','));
     377        removeLeadingAndTrailingQuotes(realm);
     378    }
     379
     380    ProtectionSpaceServerType serverType = ProtectionSpaceServerHTTP;
     381    if (protocol == "https")
     382        serverType = ProtectionSpaceServerHTTPS;
     383
     384    ProtectionSpaceAuthenticationScheme authScheme = ProtectionSpaceAuthenticationSchemeUnknown;
     385
     386    if (availableAuth & CURLAUTH_BASIC)
     387        authScheme = ProtectionSpaceAuthenticationSchemeHTTPBasic;
     388    if (availableAuth & CURLAUTH_DIGEST)
     389        authScheme = ProtectionSpaceAuthenticationSchemeHTTPDigest;
     390    if (availableAuth & CURLAUTH_GSSNEGOTIATE)
     391        authScheme = ProtectionSpaceAuthenticationSchemeNegotiate;
     392    if (availableAuth & CURLAUTH_NTLM)
     393        authScheme = ProtectionSpaceAuthenticationSchemeNTLM;
     394
     395    protectionSpace = ProtectionSpace(host, port, serverType, realm, authScheme);
     396
     397    return true;
    565398}
    566399
     
    628461}
    629462
    630 static void removeLeadingAndTrailingQuotes(String& value)
    631 {
    632     unsigned length = value.length();
    633     if (value.startsWith('"') && value.endsWith('"') && length > 1)
    634         value = value.substring(1, length - 2);
    635 }
    636 
    637 static bool getProtectionSpace(ResourceHandle* job, const ResourceResponse& response, ProtectionSpace& protectionSpace)
    638 {
    639     ResourceHandleInternal* d = job->getInternal();
    640 
    641     CURLcode err;
    642 
    643     long port = 0;
    644     err = d->m_curlHandle.getPrimaryPort(port);
    645     if (err != CURLE_OK)
    646         return false;
    647 
    648     long availableAuth = CURLAUTH_NONE;
    649     err = d->m_curlHandle.getHttpAuthAvail(availableAuth);
    650     if (err != CURLE_OK)
    651         return false;
    652 
    653     URL url = d->m_curlHandle.getEffectiveURL();
    654     if (!url.isValid())
    655         return false;
    656 
    657     String host = url.host();
    658     StringView protocol = url.protocol();
    659 
    660     String realm;
    661 
    662     const String authHeader = response.httpHeaderField(HTTPHeaderName::Authorization);
    663     const String realmString = "realm=";
    664     int realmPos = authHeader.find(realmString);
    665     if (realmPos > 0) {
    666         realm = authHeader.substring(realmPos + realmString.length());
    667         realm = realm.left(realm.find(','));
    668         removeLeadingAndTrailingQuotes(realm);
    669     }
    670 
    671     ProtectionSpaceServerType serverType = ProtectionSpaceServerHTTP;
    672     if (protocol == "https")
    673         serverType = ProtectionSpaceServerHTTPS;
    674 
    675     ProtectionSpaceAuthenticationScheme authScheme = ProtectionSpaceAuthenticationSchemeUnknown;
    676 
    677     if (availableAuth & CURLAUTH_BASIC)
    678         authScheme = ProtectionSpaceAuthenticationSchemeHTTPBasic;
    679     if (availableAuth & CURLAUTH_DIGEST)
    680         authScheme = ProtectionSpaceAuthenticationSchemeHTTPDigest;
    681     if (availableAuth & CURLAUTH_GSSNEGOTIATE)
    682         authScheme = ProtectionSpaceAuthenticationSchemeNegotiate;
    683     if (availableAuth & CURLAUTH_NTLM)
    684         authScheme = ProtectionSpaceAuthenticationSchemeNTLM;
    685 
    686     protectionSpace = ProtectionSpace(host, port, serverType, realm, authScheme);
    687 
    688     return true;
    689 }
    690 
    691 size_t ResourceHandleInternal::willPrepareSendData(char* ptr, size_t blockSize, size_t numberOfBlocks)
    692 {
    693     if (!m_formDataStream.hasMoreElements())
    694         return 0;
    695 
    696     size_t size = m_formDataStream.read(ptr, blockSize, numberOfBlocks);
    697 
    698     // Something went wrong so cancel the job.
    699     if (!size) {
    700         m_handle->cancel();
    701         return 0;
    702     }
    703 
    704     return size;
    705 
    706 }
    707 
    708 void ResourceHandleInternal::didReceiveHeaderLine(const String& header)
     463void ResourceHandleCurlDelegate::didReceiveHeaderLine(const String& header)
    709464{
    710465    ASSERT(isMainThread());
     
    716471
    717472        if (isAppendableHeader(key))
    718             m_response.addHTTPHeaderField(key, value);
     473            response().addHTTPHeaderField(key, value);
    719474        else
    720             m_response.setHTTPHeaderField(key, value);
     475            response().setHTTPHeaderField(key, value);
    721476    } else if (header.startsWith("HTTP", false)) {
    722477        // This is the first line of the response.
     
    735490            // The status text is after the status code.
    736491            String status = header.substring(statusCodePos + httpCodeString.length());
    737             m_response.setHTTPStatusText(status.stripWhiteSpace());
    738         }
    739     }
    740 }
    741 
    742 void ResourceHandleInternal::didReceiveAllHeaders(long httpCode, long long contentLength)
     492            response().setHTTPStatusText(status.stripWhiteSpace());
     493        }
     494    }
     495}
     496
     497void ResourceHandleCurlDelegate::didReceiveAllHeaders(long httpCode, long long contentLength)
    743498{
    744499    ASSERT(isMainThread());
    745500
    746     m_response.setExpectedContentLength(contentLength);
    747 
    748     m_response.setURL(m_curlHandle.getEffectiveURL());
    749 
    750     m_response.setHTTPStatusCode(httpCode);
    751     m_response.setMimeType(extractMIMETypeFromMediaType(m_response.httpHeaderField(HTTPHeaderName::ContentType)).convertToASCIILowercase());
    752     m_response.setTextEncodingName(extractCharsetFromMediaType(m_response.httpHeaderField(HTTPHeaderName::ContentType)));
    753 
    754     if (m_response.isMultipart()) {
     501    response().setExpectedContentLength(contentLength);
     502    response().setURL(m_curlHandle.getEffectiveURL());
     503    response().setHTTPStatusCode(httpCode);
     504    response().setMimeType(extractMIMETypeFromMediaType(response().httpHeaderField(HTTPHeaderName::ContentType)).convertToASCIILowercase());
     505    response().setTextEncodingName(extractCharsetFromMediaType(response().httpHeaderField(HTTPHeaderName::ContentType)));
     506
     507    if (response().isMultipart()) {
    755508        String boundary;
    756         bool parsed = MultipartHandle::extractBoundary(m_response.httpHeaderField(HTTPHeaderName::ContentType), boundary);
     509        bool parsed = MultipartHandle::extractBoundary(response().httpHeaderField(HTTPHeaderName::ContentType), boundary);
    757510        if (parsed)
    758511            m_multipartHandle = std::make_unique<MultipartHandle>(m_handle, boundary);
     
    761514    // HTTP redirection
    762515    if (isHttpRedirect(httpCode)) {
    763         String location = m_response.httpHeaderField(HTTPHeaderName::Location);
     516        String location = response().httpHeaderField(HTTPHeaderName::Location);
    764517        if (!location.isEmpty()) {
    765518            URL newURL = URL(m_firstRequest.url(), location);
     
    767520            ResourceRequest redirectedRequest = m_firstRequest;
    768521            redirectedRequest.setURL(newURL);
    769             ResourceResponse response = m_response;
    770             if (client())
    771                 client()->willSendRequest(m_handle, WTFMove(redirectedRequest), WTFMove(response));
     522            ResourceResponse localResponse = response();
     523            if (m_handle->client())
     524                m_handle->client()->willSendRequest(m_handle, WTFMove(redirectedRequest), WTFMove(localResponse));
    772525
    773526            m_firstRequest.setURL(newURL);
     
    777530    } else if (isHttpAuthentication(httpCode)) {
    778531        ProtectionSpace protectionSpace;
    779         if (getProtectionSpace(m_handle, m_response, protectionSpace)) {
     532        if (getProtectionSpace(response(), protectionSpace)) {
    780533            Credential credential;
    781             AuthenticationChallenge challenge(protectionSpace, credential, m_authFailureCount, m_response, ResourceError());
     534            AuthenticationChallenge challenge(protectionSpace, credential, m_authFailureCount, response(), ResourceError());
    782535            challenge.setAuthenticationClient(m_handle);
    783536            m_handle->didReceiveAuthenticationChallenge(challenge);
     
    787540    }
    788541
    789     if (client()) {
     542    response().setResponseFired(true);
     543
     544    if (m_handle->client()) {
    790545        if (isHttpNotModified(httpCode)) {
    791546            const String& url = m_firstRequest.url().string();
    792             if (CurlCacheManager::getInstance().getCachedResponse(url, m_response)) {
     547            if (CurlCacheManager::getInstance().getCachedResponse(url, response())) {
    793548                if (m_addedCacheValidationHeaders) {
    794                     m_response.setHTTPStatusCode(200);
    795                     m_response.setHTTPStatusText("OK");
     549                    response().setHTTPStatusCode(200);
     550                    response().setHTTPStatusText("OK");
    796551                }
    797552            }
    798553        }
    799         client()->didReceiveResponse(m_handle, ResourceResponse(m_response));
    800         CurlCacheManager::getInstance().didReceiveResponse(*m_handle, m_response);
    801     }
    802 
    803     m_response.setResponseFired(true);
    804 }
    805 
    806 void ResourceHandleInternal::didReceiveContentData()
     554        CurlCacheManager::getInstance().didReceiveResponse(*m_handle, response());
     555        m_handle->client()->didReceiveResponse(m_handle, ResourceResponse(response()));
     556    }
     557}
     558
     559void ResourceHandleCurlDelegate::didReceiveContentData(ThreadSafeDataBuffer buffer)
    807560{
    808561    ASSERT(isMainThread());
    809562
    810     if (!m_response.responseFired())
     563    if (!response().responseFired())
    811564        handleLocalReceiveResponse();
    812565
    813     Vector<char> buffer;
    814     {
    815         LockHolder locker { m_receivedBufferMutex };
    816         buffer = WTFMove(m_receivedBuffer);
    817     }
    818 
    819     char* ptr = buffer.begin();
     566    const char* ptr = reinterpret_cast<const char*>(buffer.data()->begin());
    820567    size_t size = buffer.size();
    821568
    822569    if (m_multipartHandle)
    823         m_multipartHandle->contentReceived(static_cast<const char*>(ptr), size);
    824     else if (client()) {
    825         client()->didReceiveData(m_handle, ptr, size, 0);
     570        m_multipartHandle->contentReceived(ptr, size);
     571    else if (m_handle->client()) {
    826572        CurlCacheManager::getInstance().didReceiveData(*m_handle, ptr, size);
    827     }
    828 }
    829 
    830 /* This is called to obtain HTTP POST or PUT data.
    831 Iterate through FormData elements and upload files.
    832 Carefully respect the given buffer size and fill the rest of the data at the next calls.
    833 */
    834 size_t ResourceHandleInternal::readCallback(char* ptr, size_t size, size_t nmemb, void* data)
    835 {
    836     ASSERT(!isMainThread());
    837 
    838     ResourceHandleInternal* d = static_cast<ResourceHandleInternal*>(data);
    839 
    840     if (d->m_cancelled)
     573        m_handle->client()->didReceiveData(m_handle, ptr, size, 0);
     574    }
     575}
     576
     577void ResourceHandleCurlDelegate::handleLocalReceiveResponse()
     578{
     579    ASSERT(isMainThread());
     580
     581    // since the code in headerCallback will not have run for local files
     582    // the code to set the URL and fire didReceiveResponse is never run,
     583    // which means the ResourceLoader's response does not contain the URL.
     584    // Run the code here for local files to resolve the issue.
     585    // TODO: See if there is a better approach for handling this.
     586    URL url = m_curlHandle.getEffectiveURL();
     587    ASSERT(url.isValid());
     588    response().setURL(url);
     589    response().setResponseFired(true);
     590    if (m_handle->client())
     591        m_handle->client()->didReceiveResponse(m_handle, ResourceResponse(response()));
     592}
     593
     594void ResourceHandleCurlDelegate::prepareSendData(char* buffer, size_t blockSize, size_t numberOfBlocks)
     595{
     596    ASSERT(isMainThread());
     597    ASSERT(!m_sendBytes);
     598
     599    std::unique_lock<Lock> lock(m_workerThreadMutex);
     600
     601    if (!m_formDataStream.hasMoreElements())
     602        return;
     603
     604    size_t size = m_formDataStream.read(buffer, blockSize, numberOfBlocks);
     605    if (!size) {
     606        // Something went wrong so cancel the job.
     607        m_handle->cancel();
     608        return;
     609    }
     610
     611    m_sendBytes = size;
     612    m_workerThreadConditionVariable.notifyOne();
     613}
     614
     615void ResourceHandleCurlDelegate::didFinish(double pretransferTime, double dnsLookupTime, double connectTime, double appConnectTime)
     616{
     617    setWebTimings(pretransferTime, dnsLookupTime, connectTime, appConnectTime);
     618
     619    if (!m_handle)
     620        return;
     621
     622    if (!response().responseFired()) {
     623        handleLocalReceiveResponse();
     624        if (!m_handle)
     625            return;
     626    }
     627
     628    if (m_multipartHandle)
     629        m_multipartHandle->contentEnded();
     630
     631    if (m_handle->client()) {
     632        CurlCacheManager::getInstance().didFinishLoading(*m_handle);
     633        m_handle->client()->didFinishLoading(m_handle);
     634    }
     635}
     636
     637void ResourceHandleCurlDelegate::didFail(const String& domain, int errorCode, const URL& failingURL, const String& localizedDescription, unsigned sslErrors)
     638{
     639    if (!m_handle)
     640        return;
     641
     642    if (m_handle->client()) {
     643        CurlCacheManager::getInstance().didFail(*m_handle);
     644        m_handle->client()->didFail(m_handle, ResourceError(domain, errorCode, failingURL, localizedDescription, sslErrors));
     645    }
     646}
     647
     648void ResourceHandleCurlDelegate::handleDataURL()
     649{
     650    ASSERT(m_firstRequest.url().protocolIsData());
     651    String url = m_firstRequest.url().string();
     652
     653    ASSERT(m_handle->client());
     654
     655    int index = url.find(',');
     656    if (index == -1) {
     657        m_handle->client()->cannotShowURL(m_handle);
     658        return;
     659    }
     660
     661    String mediaType = url.substring(5, index - 5);
     662    String data = url.substring(index + 1);
     663
     664    bool base64 = mediaType.endsWith(";base64", false);
     665    if (base64)
     666        mediaType = mediaType.left(mediaType.length() - 7);
     667
     668    if (mediaType.isEmpty())
     669        mediaType = "text/plain";
     670
     671    String mimeType = extractMIMETypeFromMediaType(mediaType);
     672    String charset = extractCharsetFromMediaType(mediaType);
     673
     674    if (charset.isEmpty())
     675        charset = "US-ASCII";
     676
     677    ResourceResponse response;
     678    response.setMimeType(mimeType);
     679    response.setTextEncodingName(charset);
     680    response.setURL(m_firstRequest.url());
     681
     682    if (base64) {
     683        data = decodeURLEscapeSequences(data);
     684        m_handle->client()->didReceiveResponse(m_handle, WTFMove(response));
     685
     686        // didReceiveResponse might cause the client to be deleted.
     687        if (m_handle->client()) {
     688            Vector<char> out;
     689            if (base64Decode(data, out, Base64IgnoreSpacesAndNewLines) && out.size() > 0)
     690                m_handle->client()->didReceiveData(m_handle, out.data(), out.size(), 0);
     691        }
     692    } else {
     693        TextEncoding encoding(charset);
     694        data = decodeURLEscapeSequences(data, encoding);
     695        m_handle->client()->didReceiveResponse(m_handle, WTFMove(response));
     696
     697        // didReceiveResponse might cause the client to be deleted.
     698        if (m_handle->client()) {
     699            CString encodedData = encoding.encode(data, URLEncodedEntitiesForUnencodables);
     700            if (encodedData.length())
     701                m_handle->client()->didReceiveData(m_handle, encodedData.data(), encodedData.length(), 0);
     702        }
     703    }
     704
     705    if (m_handle->client())
     706        m_handle->client()->didFinishLoading(m_handle);
     707}
     708
     709void ResourceHandleCurlDelegate::setupPOST()
     710{
     711    m_curlHandle.enableHttpPostRequest();
     712
     713    size_t numElements = getFormElementsCount();
     714    if (!numElements)
     715        return;
     716
     717    // Do not stream for simple POST data
     718    if (numElements == 1) {
     719        m_firstRequest.httpBody()->flatten(m_postBytes);
     720        if (m_postBytes.size())
     721            m_curlHandle.setPostFields(m_postBytes.data(), m_postBytes.size());
     722        return;
     723    }
     724
     725    setupFormData(true);
     726}
     727
     728void ResourceHandleCurlDelegate::setupPUT()
     729{
     730    m_curlHandle.enableHttpPutRequest();
     731
     732    // Disable the Expect: 100 continue header
     733    m_curlHandle.appendRequestHeader("Expect:");
     734
     735    size_t numElements = getFormElementsCount();
     736    if (!numElements)
     737        return;
     738
     739    setupFormData(false);
     740}
     741
     742size_t ResourceHandleCurlDelegate::getFormElementsCount()
     743{
     744    RefPtr<FormData> formData = m_firstRequest.httpBody();
     745    if (!formData)
    841746        return 0;
    842747
    843     // We should never be called when deferred loading is activated.
    844     ASSERT(!d->m_defersLoading);
    845 
    846     if (!size || !nmemb)
    847         return 0;
    848 
    849     return d->willPrepareSendData(ptr, size, nmemb);
     748    // Resolve the blob elements so the formData can correctly report it's size.
     749    formData = formData->resolveBlobReferences();
     750    size_t size = formData->elements().size();
     751    m_firstRequest.setHTTPBody(WTFMove(formData));
     752    return size;
     753}
     754
     755void ResourceHandleCurlDelegate::setupFormData(bool isPostRequest)
     756{
     757    Vector<FormDataElement> elements = m_firstRequest.httpBody()->elements();
     758    size_t numElements = elements.size();
     759
     760    static const long long maxCurlOffT = m_curlHandle.maxCurlOffT();
     761
     762    // Obtain the total size of the form data
     763    curl_off_t size = 0;
     764    bool chunkedTransfer = false;
     765    for (size_t i = 0; i < numElements; i++) {
     766        FormDataElement element = elements[i];
     767        if (element.m_type == FormDataElement::Type::EncodedFile) {
     768            long long fileSizeResult;
     769            if (getFileSize(element.m_filename, fileSizeResult)) {
     770                if (fileSizeResult > maxCurlOffT) {
     771                    // File size is too big for specifying it to cURL
     772                    chunkedTransfer = true;
     773                    break;
     774                }
     775                size += fileSizeResult;
     776            } else {
     777                chunkedTransfer = true;
     778                break;
     779            }
     780        } else
     781            size += elements[i].m_data.size();
     782    }
     783
     784    // cURL guesses that we want chunked encoding as long as we specify the header
     785    if (chunkedTransfer)
     786        m_curlHandle.appendRequestHeader("Transfer-Encoding: chunked");
     787    else {
     788        if (isPostRequest)
     789            m_curlHandle.setPostFieldLarge(size);
     790        else
     791            m_curlHandle.setInFileSizeLarge(size);
     792    }
     793
     794    m_curlHandle.setReadCallbackFunction(willSendDataCallback, this);
     795}
     796
     797void ResourceHandleCurlDelegate::applyAuthentication()
     798{
     799    String user = m_user;
     800    String password = m_pass;
     801
     802    if (!m_initialCredential.isEmpty()) {
     803        user = m_initialCredential.user();
     804        password = m_initialCredential.password();
     805        m_curlHandle.enableHttpAuthentication(CURLAUTH_BASIC);
     806    }
     807
     808    // It seems we need to set CURLOPT_USERPWD even if username and password is empty.
     809    // Otherwise cURL will not automatically continue with a new request after a 401 response.
     810
     811    // curl CURLOPT_USERPWD expects username:password
     812    m_curlHandle.setHttpAuthUserPass(user, password);
     813}
     814
     815void ResourceHandleCurlDelegate::setWebTimings(double pretransferTime, double dnsLookupTime, double connectTime, double appConnectTime)
     816{
     817    response().deprecatedNetworkLoadMetrics().domainLookupStart = Seconds(0);
     818    response().deprecatedNetworkLoadMetrics().domainLookupEnd = Seconds(dnsLookupTime);
     819
     820    response().deprecatedNetworkLoadMetrics().connectStart = Seconds(dnsLookupTime);
     821    response().deprecatedNetworkLoadMetrics().connectEnd = Seconds(connectTime);
     822
     823    response().deprecatedNetworkLoadMetrics().requestStart = Seconds(connectTime);
     824    response().deprecatedNetworkLoadMetrics().responseStart = Seconds(pretransferTime);
     825
     826    if (appConnectTime)
     827        response().deprecatedNetworkLoadMetrics().secureConnectionStart = Seconds(connectTime);
    850828}
    851829
     
    859837*
    860838*/
    861 size_t ResourceHandleInternal::headerCallback(char* ptr, size_t size, size_t nmemb, void* data)
    862 {
    863     ASSERT(!isMainThread());
    864 
    865     ResourceHandleInternal* d = static_cast<ResourceHandleInternal*>(data);
    866     ResourceHandle* job = d->m_handle;
    867 
    868     if (d->m_cancelled)
     839size_t ResourceHandleCurlDelegate::didReceiveHeader(String&& header)
     840{
     841    if (!m_handle)
    869842        return 0;
    870843
    871     // We should never be called when deferred loading is activated.
    872     ASSERT(!d->m_defersLoading);
    873 
    874     size_t totalSize = size * nmemb;
    875 
    876     String header(static_cast<const char*>(ptr), totalSize);
     844    if (m_defersLoading)
     845        return 0;
    877846
    878847    /*
     
    885854    if (header == AtomicString("\r\n") || header == AtomicString("\n")) {
    886855        long httpCode = 0;
    887         d->m_curlHandle.getResponseCode(httpCode);
     856        m_curlHandle.getResponseCode(httpCode);
    888857
    889858        if (!httpCode) {
    890859            // Comes here when receiving 200 Connection Established. Just return.
    891             return totalSize;
     860            return header.length();
    892861        }
    893862        if (isHttpInfo(httpCode)) {
    894863            // Just return when receiving http info, e.g. HTTP/1.1 100 Continue.
    895864            // If not, the request might be cancelled, because the MIME type will be empty for this response.
    896             return totalSize;
     865            return header.length();
    897866        }
    898867
    899868        long long contentLength = 0;
    900         d->m_curlHandle.getContentLenghtDownload(contentLength);
    901 
    902         callOnMainThread([job = RefPtr<ResourceHandle>(job), d, httpCode, contentLength] {
    903             if (!d->m_cancelled)
    904                 d->didReceiveAllHeaders(httpCode, contentLength);
     869        m_curlHandle.getContentLenghtDownload(contentLength);
     870
     871        callOnMainThread([protectedThis = makeRef(*this), httpCode, contentLength] {
     872            if (!protectedThis->m_handle)
     873                return;
     874            protectedThis->didReceiveAllHeaders(httpCode, contentLength);
    905875        });
    906876    } else {
    907         callOnMainThread([job = RefPtr<ResourceHandle>(job), d, header] {
    908             if (!d->m_cancelled)
    909                 d->didReceiveHeaderLine(header);
     877        callOnMainThread([protectedThis = makeRef(*this), header = header.isolatedCopy() ] {
     878            if (!protectedThis->m_handle)
     879                return;
     880            protectedThis->didReceiveHeaderLine(header);
    910881        });
    911882    }
    912883
    913     return totalSize;
     884    return header.length();
    914885}
    915886
    916887// called with data after all headers have been processed via headerCallback
    917 size_t ResourceHandleInternal::writeCallback(char* ptr, size_t size, size_t nmemb, void* data)
    918 {
    919     ASSERT(!isMainThread());
    920 
    921     ResourceHandleInternal* d = static_cast<ResourceHandleInternal*>(data);
    922     ResourceHandle* job = d->m_handle;
    923 
    924     if (d->m_cancelled)
     888size_t ResourceHandleCurlDelegate::didReceiveData(ThreadSafeDataBuffer data)
     889{
     890    if (!m_handle)
    925891        return 0;
    926892
    927     // We should never be called when deferred loading is activated.
    928     ASSERT(!d->m_defersLoading);
    929 
    930     size_t totalSize = size * nmemb;
     893    if (m_defersLoading)
     894        return 0;
    931895
    932896    // this shouldn't be necessary but apparently is. CURL writes the data
     
    934898    // can be observed e.g. on gmail.com
    935899    long httpCode = 0;
    936     CURLcode errCd = d->m_curlHandle.getResponseCode(httpCode);
     900    CURLcode errCd = m_curlHandle.getResponseCode(httpCode);
    937901    if (CURLE_OK == errCd && httpCode >= 300 && httpCode < 400)
    938         return totalSize;
    939 
    940     bool shouldCall { false };
     902        return data.size();
     903
     904    if (!data.size())
     905        return 0;
     906
     907    callOnMainThread([protectedThis = makeRef(*this), data] {
     908        if (!protectedThis->m_handle)
     909            return;
     910        protectedThis->didReceiveContentData(data);
     911    });
     912
     913    return data.size();
     914}
     915
     916/* This is called to obtain HTTP POST or PUT data.
     917Iterate through FormData elements and upload files.
     918Carefully respect the given buffer blockSize and fill the rest of the data at the next calls.
     919*/
     920size_t ResourceHandleCurlDelegate::willSendData(char* buffer, size_t blockSize, size_t numberOfBlocks)
     921{
     922    ASSERT(!isMainThread());
     923
     924    if (!m_handle)
     925        return 0;
     926
     927    if (m_defersLoading)
     928        return 0;
     929
     930    if (!blockSize || !numberOfBlocks)
     931        return 0;
     932
    941933    {
    942         LockHolder locker(d->m_receivedBufferMutex);
    943        
    944         if (d->m_receivedBuffer.isEmpty())
    945             shouldCall = true;
    946        
    947         d->m_receivedBuffer.append(ptr, totalSize);
    948     }
    949 
    950     if (shouldCall) {
    951         callOnMainThread([job = RefPtr<ResourceHandle>(job), d] {
    952             if (!d->m_cancelled)
    953                 d->didReceiveContentData();
     934        std::unique_lock<Lock> lock(m_workerThreadMutex);
     935
     936        m_sendBytes = 0;
     937
     938        callOnMainThread([protectedThis = makeRef(*this), buffer, blockSize, numberOfBlocks] {
     939            if (!protectedThis->m_handle)
     940                return;
     941            protectedThis->prepareSendData(buffer, blockSize, numberOfBlocks);
    954942        });
    955     }
    956 
    957     return totalSize;
    958 }
    959 
    960 // sync loader
    961 
    962 void ResourceHandle::platformLoadResourceSynchronously(NetworkingContext* context, const ResourceRequest& request, StoredCredentials, ResourceError& error, ResourceResponse& response, Vector<char>& data)
    963 {
    964     ASSERT(isMainThread());
    965 
    966     SynchronousLoaderClient client;
    967     RefPtr<ResourceHandle> handle = adoptRef(new ResourceHandle(context, request, &client, false, false));
    968 
    969     handle->d->dispatchSynchronousJob();
    970 
    971     error = client.error();
    972     data.swap(client.mutableData());
    973     response = client.response();
    974 }
    975 
    976 void ResourceHandleInternal::dispatchSynchronousJob()
    977 {
    978     URL kurl = m_firstRequest.url();
    979 
    980     if (kurl.protocolIsData()) {
    981         handleDataURL();
    982         return;
    983     }
    984 
    985     // If defersLoading is true and we call curl_easy_perform
    986     // on a paused handle, libcURL would do the transfert anyway
    987     // and we would assert so force defersLoading to be false.
    988     m_defersLoading = false;
    989 
    990     initialize();
    991 
    992     // curl_easy_perform blocks until the transfert is finished.
    993     CURLcode ret = m_curlHandle.perform();
    994 
    995     calculateWebTimingInformations();
    996 
    997     if (client()) {
    998         if (ret != CURLE_OK)
    999             client()->didFail(m_handle, ResourceError(CurlContext::errorDomain, m_curlHandle.errorCode(), m_curlHandle.getEffectiveURL(), m_curlHandle.errorDescription(), m_curlHandle.getSslErrors()));
    1000         else
    1001             client()->didReceiveResponse(m_handle, ResourceResponse(m_response));
    1002     }
    1003 }
    1004 
    1005 void ResourceHandleInternal::handleDataURL()
    1006 {
    1007     ASSERT(m_firstRequest.url().protocolIsData());
    1008     String url = m_firstRequest.url().string();
    1009 
    1010     ASSERT(client());
    1011 
    1012     int index = url.find(',');
    1013     if (index == -1) {
    1014         client()->cannotShowURL(m_handle);
    1015         return;
    1016     }
    1017 
    1018     String mediaType = url.substring(5, index - 5);
    1019     String data = url.substring(index + 1);
    1020 
    1021     bool base64 = mediaType.endsWith(";base64", false);
    1022     if (base64)
    1023         mediaType = mediaType.left(mediaType.length() - 7);
    1024 
    1025     if (mediaType.isEmpty())
    1026         mediaType = "text/plain";
    1027 
    1028     String mimeType = extractMIMETypeFromMediaType(mediaType);
    1029     String charset = extractCharsetFromMediaType(mediaType);
    1030 
    1031     if (charset.isEmpty())
    1032         charset = "US-ASCII";
    1033 
    1034     ResourceResponse response;
    1035     response.setMimeType(mimeType);
    1036     response.setTextEncodingName(charset);
    1037     response.setURL(m_firstRequest.url());
    1038 
    1039     if (base64) {
    1040         data = decodeURLEscapeSequences(data);
    1041         client()->didReceiveResponse(m_handle, WTFMove(response));
    1042 
    1043         // didReceiveResponse might cause the client to be deleted.
    1044         if (client()) {
    1045             Vector<char> out;
    1046             if (base64Decode(data, out, Base64IgnoreSpacesAndNewLines) && out.size() > 0)
    1047                 client()->didReceiveData(m_handle, out.data(), out.size(), 0);
    1048         }
    1049     } else {
    1050         TextEncoding encoding(charset);
    1051         data = decodeURLEscapeSequences(data, encoding);
    1052         client()->didReceiveResponse(m_handle, WTFMove(response));
    1053 
    1054         // didReceiveResponse might cause the client to be deleted.
    1055         if (client()) {
    1056             CString encodedData = encoding.encode(data, URLEncodedEntitiesForUnencodables);
    1057             if (encodedData.length())
    1058                 client()->didReceiveData(m_handle, encodedData.data(), encodedData.length(), 0);
    1059         }
    1060     }
    1061 
    1062     if (client())
    1063         client()->didFinishLoading(m_handle);
     943
     944        m_workerThreadConditionVariable.wait(lock, [this] {
     945            return m_sendBytes;
     946        });
     947    }
     948
     949    return m_sendBytes;
     950}
     951
     952size_t ResourceHandleCurlDelegate::didReceiveHeaderCallback(char* ptr, size_t blockSize, size_t numberOfBlocks, void* data)
     953{
     954    return static_cast<ResourceHandleCurlDelegate*>(const_cast<void*>(data))->didReceiveHeader(String(static_cast<const char*>(ptr), blockSize * numberOfBlocks));
     955}
     956
     957size_t ResourceHandleCurlDelegate::didReceiveDataCallback(char* ptr, size_t blockSize, size_t numberOfBlocks, void* data)
     958{
     959    return static_cast<ResourceHandleCurlDelegate*>(const_cast<void*>(data))->didReceiveData(ThreadSafeDataBuffer::copyData(static_cast<const char*>(ptr), blockSize * numberOfBlocks));
     960}
     961
     962size_t ResourceHandleCurlDelegate::willSendDataCallback(char* ptr, size_t blockSize, size_t numberOfBlocks, void* data)
     963{
     964    return static_cast<ResourceHandleCurlDelegate*>(const_cast<void*>(data))->willSendData(ptr, blockSize, numberOfBlocks);
    1064965}
    1065966
Note: See TracChangeset for help on using the changeset viewer.