Changeset 218962 in webkit


Ignore:
Timestamp:
Jun 29, 2017 2:58:55 PM (7 years ago)
Author:
commit-queue@webkit.org
Message:

[Curl] ResourceHandleManager violate the class responsibility of ResourceHandle
https://bugs.webkit.org/show_bug.cgi?id=173630

Patch by Basuke Suzuki <Basuke Suzuki> on 2017-06-29
Reviewed by Alex Christensen.

  • platform/network/ResourceHandle.h:
  • platform/network/curl/ResourceHandleCurl.cpp:

(WebCore::ResourceHandle::platformLoadResourceSynchronously):
(WebCore::calculateWebTimingInformations):
(WebCore::handleLocalReceiveResponse):
(WebCore::writeCallback):
(WebCore::isHttpInfo):
(WebCore::isHttpRedirect):
(WebCore::isHttpAuthentication):
(WebCore::isHttpNotModified):
(WebCore::isAppendableHeader):
(WebCore::removeLeadingAndTrailingQuotes):
(WebCore::getProtectionSpace):
(WebCore::headerCallback):
(WebCore::readCallback):
(WebCore::getFormElementsCount):
(WebCore::setupFormData):
(WebCore::ResourceHandle::setupPUT):
(WebCore::ResourceHandle::setupPOST):
(WebCore::ResourceHandle::handleDataURL):
(WebCore::ResourceHandle::dispatchSynchronousJob):
(WebCore::ResourceHandle::applyAuthentication):
(WebCore::ResourceHandle::initialize):
(WebCore::ResourceHandle::handleCurlMsg):

  • platform/network/curl/ResourceHandleManager.cpp:

(WebCore::ResourceHandleManager::downloadTimerCallback):
(WebCore::ResourceHandleManager::startJob):
(WebCore::calculateWebTimingInformations): Deleted.
(WebCore::isHttpInfo): Deleted.
(WebCore::isHttpRedirect): Deleted.
(WebCore::isHttpAuthentication): Deleted.
(WebCore::isHttpNotModified): Deleted.
(WebCore::handleLocalReceiveResponse): Deleted.
(WebCore::writeCallback): Deleted.
(WebCore::isAppendableHeader): Deleted.
(WebCore::removeLeadingAndTrailingQuotes): Deleted.
(WebCore::getProtectionSpace): Deleted.
(WebCore::headerCallback): Deleted.
(WebCore::readCallback): Deleted.
(WebCore::getFormElementsCount): Deleted.
(WebCore::setupFormData): Deleted.
(WebCore::ResourceHandleManager::setupPUT): Deleted.
(WebCore::ResourceHandleManager::setupPOST): Deleted.
(WebCore::handleDataURL): Deleted.
(WebCore::ResourceHandleManager::dispatchSynchronousJob): Deleted.
(WebCore::ResourceHandleManager::applyAuthenticationToRequest): Deleted.
(WebCore::ResourceHandleManager::initializeHandle): Deleted.

  • platform/network/curl/ResourceHandleManager.h:
Location:
trunk/Source/WebCore
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r218961 r218962  
     12017-06-29  Basuke Suzuki  <Basuke.Suzuki@sony.com>
     2
     3        [Curl] ResourceHandleManager violate the class responsibility of ResourceHandle
     4        https://bugs.webkit.org/show_bug.cgi?id=173630
     5
     6        Reviewed by Alex Christensen.
     7
     8        * platform/network/ResourceHandle.h:
     9        * platform/network/curl/ResourceHandleCurl.cpp:
     10        (WebCore::ResourceHandle::platformLoadResourceSynchronously):
     11        (WebCore::calculateWebTimingInformations):
     12        (WebCore::handleLocalReceiveResponse):
     13        (WebCore::writeCallback):
     14        (WebCore::isHttpInfo):
     15        (WebCore::isHttpRedirect):
     16        (WebCore::isHttpAuthentication):
     17        (WebCore::isHttpNotModified):
     18        (WebCore::isAppendableHeader):
     19        (WebCore::removeLeadingAndTrailingQuotes):
     20        (WebCore::getProtectionSpace):
     21        (WebCore::headerCallback):
     22        (WebCore::readCallback):
     23        (WebCore::getFormElementsCount):
     24        (WebCore::setupFormData):
     25        (WebCore::ResourceHandle::setupPUT):
     26        (WebCore::ResourceHandle::setupPOST):
     27        (WebCore::ResourceHandle::handleDataURL):
     28        (WebCore::ResourceHandle::dispatchSynchronousJob):
     29        (WebCore::ResourceHandle::applyAuthentication):
     30        (WebCore::ResourceHandle::initialize):
     31        (WebCore::ResourceHandle::handleCurlMsg):
     32        * platform/network/curl/ResourceHandleManager.cpp:
     33        (WebCore::ResourceHandleManager::downloadTimerCallback):
     34        (WebCore::ResourceHandleManager::startJob):
     35        (WebCore::calculateWebTimingInformations): Deleted.
     36        (WebCore::isHttpInfo): Deleted.
     37        (WebCore::isHttpRedirect): Deleted.
     38        (WebCore::isHttpAuthentication): Deleted.
     39        (WebCore::isHttpNotModified): Deleted.
     40        (WebCore::handleLocalReceiveResponse): Deleted.
     41        (WebCore::writeCallback): Deleted.
     42        (WebCore::isAppendableHeader): Deleted.
     43        (WebCore::removeLeadingAndTrailingQuotes): Deleted.
     44        (WebCore::getProtectionSpace): Deleted.
     45        (WebCore::headerCallback): Deleted.
     46        (WebCore::readCallback): Deleted.
     47        (WebCore::getFormElementsCount): Deleted.
     48        (WebCore::setupFormData): Deleted.
     49        (WebCore::ResourceHandleManager::setupPUT): Deleted.
     50        (WebCore::ResourceHandleManager::setupPOST): Deleted.
     51        (WebCore::handleDataURL): Deleted.
     52        (WebCore::ResourceHandleManager::dispatchSynchronousJob): Deleted.
     53        (WebCore::ResourceHandleManager::applyAuthenticationToRequest): Deleted.
     54        (WebCore::ResourceHandleManager::initializeHandle): Deleted.
     55        * platform/network/curl/ResourceHandleManager.h:
     56
    1572017-06-29  Said Abou-Hallawa  <sabouhallawa@apple.com>
    258
  • trunk/Source/WebCore/platform/network/ResourceHandle.h

    r218799 r218962  
    6969#endif
    7070
     71#if USE(CURL)
     72#include "CurlJobManager.h"
     73#endif
     74
    7175namespace WTF {
    7276class SchedulePair;
     
    153157#endif
    154158
    155 #if PLATFORM(WIN) && USE(CURL)
     159#if OS(WINDOWS) && USE(CURL)
    156160    static void setHostAllowsAnyHTTPSCertificate(const String&);
    157161    static void setClientCertificateInfo(const String&, const String&, const String&);
    158162#endif
    159163
    160 #if PLATFORM(WIN) && USE(CURL) && USE(CF)
     164#if OS(WINDOWS) && USE(CURL) && USE(CF)
    161165    static void setClientCertificate(const String& host, CFDataRef);
    162166#endif
     
    182186#endif
    183187
     188#if USE(CURL)
     189    void initialize();
     190    void handleDataURL();
     191    void handleCurlMsg(CURLMsg*);
     192#endif
     193
    184194    bool hasAuthenticationChallenge() const;
    185195    void clearAuthentication();
     
    283293#endif
    284294
     295#if USE(CURL)
     296    void dispatchSynchronousJob();
     297
     298    void setupPOST(struct curl_slist**);
     299    void setupPUT(struct curl_slist**);
     300
     301    void applyAuthentication();
     302
     303    char m_curlErrorBuffer[CURL_ERROR_SIZE];
     304#endif
     305
    285306    friend class ResourceHandleInternal;
    286307    std::unique_ptr<ResourceHandleInternal> d;
  • trunk/Source/WebCore/platform/network/curl/ResourceHandleCurl.cpp

    r218142 r218962  
    3333#include "CachedResourceLoader.h"
    3434#include "CredentialStorage.h"
     35#include "CurlCacheManager.h"
     36#include "CurlContext.h"
    3537#include "FileSystem.h"
    3638#include "Logging.h"
     39#include "MIMETypeRegistry.h"
    3740#include "NetworkingContext.h"
    3841#include "NotImplemented.h"
     
    4144#include "SSLHandle.h"
    4245#include "SynchronousLoaderClient.h"
     46#include <wtf/text/Base64.h>
    4347
    4448namespace WebCore {
     
    7579}
    7680
    77 #if PLATFORM(WIN)
     81#if OS(WINDOWS)
    7882
    7983void ResourceHandle::setHostAllowsAnyHTTPSCertificate(const String& host)
     
    9296#endif
    9397
    94 #if PLATFORM(WIN) && USE(CF)
     98#if OS(WINDOWS) && USE(CF)
    9599
    96100void ResourceHandle::setClientCertificate(const String&, CFDataRef)
     
    128132    RefPtr<ResourceHandle> handle = adoptRef(new ResourceHandle(context, request, &client, false, false));
    129133
    130     ResourceHandleManager* manager = ResourceHandleManager::sharedInstance();
    131 
    132     manager->dispatchSynchronousJob(handle.get());
     134    handle.get()->dispatchSynchronousJob();
    133135
    134136    error = client.error();
     
    242244}
    243245
     246const char* const errorDomainCurl = "CurlErrorDomain";
     247
     248#if ENABLE(WEB_TIMING)
     249static void calculateWebTimingInformations(ResourceHandleInternal* d)
     250{
     251    double preTransferTime = 0;
     252    double dnslookupTime = 0;
     253    double connectTime = 0;
     254    double appConnectTime = 0;
     255
     256    curl_easy_getinfo(d->m_handle, CURLINFO_NAMELOOKUP_TIME, &dnslookupTime);
     257    curl_easy_getinfo(d->m_handle, CURLINFO_CONNECT_TIME, &connectTime);
     258    curl_easy_getinfo(d->m_handle, CURLINFO_APPCONNECT_TIME, &appConnectTime);
     259    curl_easy_getinfo(d->m_handle, CURLINFO_PRETRANSFER_TIME, &preTransferTime);
     260
     261    d->m_response.deprecatedNetworkLoadMetrics().domainLookupStart = Seconds(0);
     262    d->m_response.deprecatedNetworkLoadMetrics().domainLookupEnd = Seconds(dnslookupTime);
     263
     264    d->m_response.deprecatedNetworkLoadMetrics().connectStart = Seconds(dnslookupTime);
     265    d->m_response.deprecatedNetworkLoadMetrics().connectEnd = Seconds(connectTime);
     266
     267    d->m_response.deprecatedNetworkLoadMetrics().requestStart = Seconds(connectTime);
     268    d->m_response.deprecatedNetworkLoadMetrics().responseStart = Seconds(preTransferTime);
     269
     270    if (appConnectTime)
     271        d->m_response.deprecatedNetworkLoadMetrics().secureConnectionStart = Seconds(connectTime);
     272}
     273#endif
     274
     275static void handleLocalReceiveResponse(CURL* handle, ResourceHandle* job, ResourceHandleInternal* d)
     276{
     277    // since the code in headerCallback will not have run for local files
     278    // the code to set the URL and fire didReceiveResponse is never run,
     279    // which means the ResourceLoader's response does not contain the URL.
     280    // Run the code here for local files to resolve the issue.
     281    // TODO: See if there is a better approach for handling this.
     282    URL url = CurlContext::singleton().getEffectiveURL(handle);
     283    ASSERT(url.isValid());
     284    d->m_response.setURL(url);
     285    if (d->client())
     286        d->client()->didReceiveResponse(job, ResourceResponse(d->m_response));
     287    d->m_response.setResponseFired(true);
     288}
     289
     290
     291// called with data after all headers have been processed via headerCallback
     292static size_t writeCallback(void* ptr, size_t size, size_t nmemb, void* data)
     293{
     294    ResourceHandle* job = static_cast<ResourceHandle*>(data);
     295    ResourceHandleInternal* d = job->getInternal();
     296    if (d->m_cancelled)
     297        return 0;
     298
     299    // We should never be called when deferred loading is activated.
     300    ASSERT(!d->m_defersLoading);
     301
     302    size_t totalSize = size * nmemb;
     303
     304    // this shouldn't be necessary but apparently is. CURL writes the data
     305    // of html page even if it is a redirect that was handled internally
     306    // can be observed e.g. on gmail.com
     307    CURL* h = d->m_handle;
     308    long httpCode = 0;
     309    CURLcode err = curl_easy_getinfo(h, CURLINFO_RESPONSE_CODE, &httpCode);
     310    if (CURLE_OK == err && httpCode >= 300 && httpCode < 400)
     311        return totalSize;
     312
     313    if (!d->m_response.responseFired()) {
     314        handleLocalReceiveResponse(h, job, d);
     315        if (d->m_cancelled)
     316            return 0;
     317    }
     318
     319    if (d->m_multipartHandle)
     320        d->m_multipartHandle->contentReceived(static_cast<const char*>(ptr), totalSize);
     321    else if (d->client()) {
     322        d->client()->didReceiveData(job, static_cast<char*>(ptr), totalSize, 0);
     323        CurlCacheManager::getInstance().didReceiveData(*job, static_cast<char*>(ptr), totalSize);
     324    }
     325
     326    return totalSize;
     327}
     328
     329inline static bool isHttpInfo(int statusCode)
     330{
     331    return 100 <= statusCode && statusCode < 200;
     332}
     333
     334inline static bool isHttpRedirect(int statusCode)
     335{
     336    return 300 <= statusCode && statusCode < 400 && statusCode != 304;
     337}
     338
     339inline static bool isHttpAuthentication(int statusCode)
     340{
     341    return statusCode == 401;
     342}
     343
     344inline static bool isHttpNotModified(int statusCode)
     345{
     346    return statusCode == 304;
     347}
     348
     349static bool isAppendableHeader(const String &key)
     350{
     351    static const char* appendableHeaders[] = {
     352        "access-control-allow-headers",
     353        "access-control-allow-methods",
     354        "access-control-allow-origin",
     355        "access-control-expose-headers",
     356        "allow",
     357        "cache-control",
     358        "connection",
     359        "content-encoding",
     360        "content-language",
     361        "if-match",
     362        "if-none-match",
     363        "keep-alive",
     364        "pragma",
     365        "proxy-authenticate",
     366        "public",
     367        "server",
     368        "set-cookie",
     369        "te",
     370        "trailer",
     371        "transfer-encoding",
     372        "upgrade",
     373        "user-agent",
     374        "vary",
     375        "via",
     376        "warning",
     377        "www-authenticate"
     378    };
     379
     380    // Custom headers start with 'X-', and need no further checking.
     381    if (key.startsWith("x-", /* caseSensitive */ false))
     382        return true;
     383
     384    for (auto& header : appendableHeaders) {
     385        if (equalIgnoringASCIICase(key, header))
     386            return true;
     387    }
     388
     389    return false;
     390}
     391
     392static void removeLeadingAndTrailingQuotes(String& value)
     393{
     394    unsigned length = value.length();
     395    if (value.startsWith('"') && value.endsWith('"') && length > 1)
     396        value = value.substring(1, length - 2);
     397}
     398
     399static bool getProtectionSpace(CURL* h, const ResourceResponse& response, ProtectionSpace& protectionSpace)
     400{
     401    CURLcode err;
     402
     403    long port = 0;
     404    err = curl_easy_getinfo(h, CURLINFO_PRIMARY_PORT, &port);
     405    if (err != CURLE_OK)
     406        return false;
     407
     408    long availableAuth = CURLAUTH_NONE;
     409    err = curl_easy_getinfo(h, CURLINFO_HTTPAUTH_AVAIL, &availableAuth);
     410    if (err != CURLE_OK)
     411        return false;
     412
     413    URL url = CurlContext::singleton().getEffectiveURL(h);
     414    if (!url.isValid())
     415        return false;
     416
     417    String host = url.host();
     418    StringView protocol = url.protocol();
     419
     420    String realm;
     421
     422    const String authHeader = response.httpHeaderField(HTTPHeaderName::Authorization);
     423    const String realmString = "realm=";
     424    int realmPos = authHeader.find(realmString);
     425    if (realmPos > 0) {
     426        realm = authHeader.substring(realmPos + realmString.length());
     427        realm = realm.left(realm.find(','));
     428        removeLeadingAndTrailingQuotes(realm);
     429    }
     430
     431    ProtectionSpaceServerType serverType = ProtectionSpaceServerHTTP;
     432    if (protocol == "https")
     433        serverType = ProtectionSpaceServerHTTPS;
     434
     435    ProtectionSpaceAuthenticationScheme authScheme = ProtectionSpaceAuthenticationSchemeUnknown;
     436
     437    if (availableAuth & CURLAUTH_BASIC)
     438        authScheme = ProtectionSpaceAuthenticationSchemeHTTPBasic;
     439    if (availableAuth & CURLAUTH_DIGEST)
     440        authScheme = ProtectionSpaceAuthenticationSchemeHTTPDigest;
     441    if (availableAuth & CURLAUTH_GSSNEGOTIATE)
     442        authScheme = ProtectionSpaceAuthenticationSchemeNegotiate;
     443    if (availableAuth & CURLAUTH_NTLM)
     444        authScheme = ProtectionSpaceAuthenticationSchemeNTLM;
     445
     446    protectionSpace = ProtectionSpace(host, port, serverType, realm, authScheme);
     447
     448    return true;
     449}
     450
     451/*
     452* This is being called for each HTTP header in the response. This includes '\r\n'
     453* for the last line of the header.
     454*
     455* We will add each HTTP Header to the ResourceResponse and on the termination
     456* of the header (\r\n) we will parse Content-Type and Content-Disposition and
     457* update the ResourceResponse and then send it away.
     458*
     459*/
     460static size_t headerCallback(char* ptr, size_t size, size_t nmemb, void* data)
     461{
     462    ResourceHandle* job = static_cast<ResourceHandle*>(data);
     463    ResourceHandleInternal* d = job->getInternal();
     464    if (d->m_cancelled)
     465        return 0;
     466
     467    // We should never be called when deferred loading is activated.
     468    ASSERT(!d->m_defersLoading);
     469
     470    size_t totalSize = size * nmemb;
     471    ResourceHandleClient* client = d->client();
     472
     473    String header(static_cast<const char*>(ptr), totalSize);
     474
     475    /*
     476    * a) We can finish and send the ResourceResponse
     477    * b) We will add the current header to the HTTPHeaderMap of the ResourceResponse
     478    *
     479    * The HTTP standard requires to use \r\n but for compatibility it recommends to
     480    * accept also \n.
     481    */
     482    if (header == String("\r\n") || header == String("\n")) {
     483        CURL* h = d->m_handle;
     484
     485        long httpCode = 0;
     486        curl_easy_getinfo(h, CURLINFO_RESPONSE_CODE, &httpCode);
     487
     488        if (!httpCode) {
     489            // Comes here when receiving 200 Connection Established. Just return.
     490            return totalSize;
     491        }
     492        if (isHttpInfo(httpCode)) {
     493            // Just return when receiving http info, e.g. HTTP/1.1 100 Continue.
     494            // If not, the request might be cancelled, because the MIME type will be empty for this response.
     495            return totalSize;
     496        }
     497
     498        double contentLength = 0;
     499        curl_easy_getinfo(h, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &contentLength);
     500        d->m_response.setExpectedContentLength(static_cast<long long int>(contentLength));
     501
     502        d->m_response.setURL(CurlContext::singleton().getEffectiveURL(h));
     503
     504        d->m_response.setHTTPStatusCode(httpCode);
     505        d->m_response.setMimeType(extractMIMETypeFromMediaType(d->m_response.httpHeaderField(HTTPHeaderName::ContentType)).convertToASCIILowercase());
     506        d->m_response.setTextEncodingName(extractCharsetFromMediaType(d->m_response.httpHeaderField(HTTPHeaderName::ContentType)));
     507
     508        if (d->m_response.isMultipart()) {
     509            String boundary;
     510            bool parsed = MultipartHandle::extractBoundary(d->m_response.httpHeaderField(HTTPHeaderName::ContentType), boundary);
     511            if (parsed)
     512                d->m_multipartHandle = std::make_unique<MultipartHandle>(job, boundary);
     513        }
     514
     515        // HTTP redirection
     516        if (isHttpRedirect(httpCode)) {
     517            String location = d->m_response.httpHeaderField(HTTPHeaderName::Location);
     518            if (!location.isEmpty()) {
     519                URL newURL = URL(job->firstRequest().url(), location);
     520
     521                ResourceRequest redirectedRequest = job->firstRequest();
     522                redirectedRequest.setURL(newURL);
     523                ResourceResponse response = d->m_response;
     524                if (client)
     525                    client->willSendRequest(job, WTFMove(redirectedRequest), WTFMove(response));
     526
     527                d->m_firstRequest.setURL(newURL);
     528
     529                return totalSize;
     530            }
     531        } else if (isHttpAuthentication(httpCode)) {
     532            ProtectionSpace protectionSpace;
     533            if (getProtectionSpace(d->m_handle, d->m_response, protectionSpace)) {
     534                Credential credential;
     535                AuthenticationChallenge challenge(protectionSpace, credential, d->m_authFailureCount, d->m_response, ResourceError());
     536                challenge.setAuthenticationClient(job);
     537                job->didReceiveAuthenticationChallenge(challenge);
     538                d->m_authFailureCount++;
     539                return totalSize;
     540            }
     541        }
     542
     543        if (client) {
     544            if (isHttpNotModified(httpCode)) {
     545                const String& url = job->firstRequest().url().string();
     546                if (CurlCacheManager::getInstance().getCachedResponse(url, d->m_response)) {
     547                    if (d->m_addedCacheValidationHeaders) {
     548                        d->m_response.setHTTPStatusCode(200);
     549                        d->m_response.setHTTPStatusText("OK");
     550                    }
     551                }
     552            }
     553            client->didReceiveResponse(job, ResourceResponse(d->m_response));
     554            CurlCacheManager::getInstance().didReceiveResponse(*job, d->m_response);
     555        }
     556        d->m_response.setResponseFired(true);
     557
     558    } else {
     559        int splitPos = header.find(":");
     560        if (splitPos != -1) {
     561            String key = header.left(splitPos).stripWhiteSpace();
     562            String value = header.substring(splitPos + 1).stripWhiteSpace();
     563
     564            if (isAppendableHeader(key))
     565                d->m_response.addHTTPHeaderField(key, value);
     566            else
     567                d->m_response.setHTTPHeaderField(key, value);
     568        } else if (header.startsWith("HTTP", false)) {
     569            // This is the first line of the response.
     570            // Extract the http status text from this.
     571            //
     572            // If the FOLLOWLOCATION option is enabled for the curl handle then
     573            // curl will follow the redirections internally. Thus this header callback
     574            // will be called more than one time with the line starting "HTTP" for one job.
     575            long httpCode = 0;
     576            curl_easy_getinfo(d->m_handle, CURLINFO_RESPONSE_CODE, &httpCode);
     577
     578            String httpCodeString = String::number(httpCode);
     579            int statusCodePos = header.find(httpCodeString);
     580
     581            if (statusCodePos != -1) {
     582                // The status text is after the status code.
     583                String status = header.substring(statusCodePos + httpCodeString.length());
     584                d->m_response.setHTTPStatusText(status.stripWhiteSpace());
     585            }
     586
     587        }
     588    }
     589
     590    return totalSize;
     591}
     592
     593/* This is called to obtain HTTP POST or PUT data.
     594Iterate through FormData elements and upload files.
     595Carefully respect the given buffer size and fill the rest of the data at the next calls.
     596*/
     597size_t readCallback(void* ptr, size_t size, size_t nmemb, void* data)
     598{
     599    ResourceHandle* job = static_cast<ResourceHandle*>(data);
     600    ResourceHandleInternal* d = job->getInternal();
     601
     602    if (d->m_cancelled)
     603        return 0;
     604
     605    // We should never be called when deferred loading is activated.
     606    ASSERT(!d->m_defersLoading);
     607
     608    if (!size || !nmemb)
     609        return 0;
     610
     611    if (!d->m_formDataStream.hasMoreElements())
     612        return 0;
     613
     614    size_t sent = d->m_formDataStream.read(ptr, size, nmemb);
     615
     616    // Something went wrong so cancel the job.
     617    if (!sent)
     618        job->cancel();
     619
     620    return sent;
     621}
     622
     623static inline size_t getFormElementsCount(ResourceHandle* job)
     624{
     625    RefPtr<FormData> formData = job->firstRequest().httpBody();
     626
     627    if (!formData)
     628        return 0;
     629
     630    // Resolve the blob elements so the formData can correctly report it's size.
     631    formData = formData->resolveBlobReferences();
     632    size_t size = formData->elements().size();
     633    job->firstRequest().setHTTPBody(WTFMove(formData));
     634
     635    return size;
     636}
     637
     638static void setupFormData(ResourceHandle* job, CURLoption sizeOption, struct curl_slist** headers)
     639{
     640    ResourceHandleInternal* d = job->getInternal();
     641    Vector<FormDataElement> elements = job->firstRequest().httpBody()->elements();
     642    size_t numElements = elements.size();
     643
     644    // The size of a curl_off_t could be different in WebKit and in cURL depending on
     645    // compilation flags of both.
     646    static int expectedSizeOfCurlOffT = 0;
     647    if (!expectedSizeOfCurlOffT) {
     648        curl_version_info_data* infoData = curl_version_info(CURLVERSION_NOW);
     649        if (infoData->features & CURL_VERSION_LARGEFILE)
     650            expectedSizeOfCurlOffT = sizeof(long long);
     651        else
     652            expectedSizeOfCurlOffT = sizeof(int);
     653    }
     654
     655    static const long long maxCurlOffT = (1LL << (expectedSizeOfCurlOffT * 8 - 1)) - 1;
     656    // Obtain the total size of the form data
     657    curl_off_t size = 0;
     658    bool chunkedTransfer = false;
     659    for (size_t i = 0; i < numElements; i++) {
     660        FormDataElement element = elements[i];
     661        if (element.m_type == FormDataElement::Type::EncodedFile) {
     662            long long fileSizeResult;
     663            if (getFileSize(element.m_filename, fileSizeResult)) {
     664                if (fileSizeResult > maxCurlOffT) {
     665                    // File size is too big for specifying it to cURL
     666                    chunkedTransfer = true;
     667                    break;
     668                }
     669                size += fileSizeResult;
     670            } else {
     671                chunkedTransfer = true;
     672                break;
     673            }
     674        } else
     675            size += elements[i].m_data.size();
     676    }
     677
     678    // cURL guesses that we want chunked encoding as long as we specify the header
     679    if (chunkedTransfer)
     680        *headers = curl_slist_append(*headers, "Transfer-Encoding: chunked");
     681    else {
     682        if (sizeof(long long) == expectedSizeOfCurlOffT)
     683            curl_easy_setopt(d->m_handle, sizeOption, (long long)size);
     684        else
     685            curl_easy_setopt(d->m_handle, sizeOption, (int)size);
     686    }
     687
     688    curl_easy_setopt(d->m_handle, CURLOPT_READFUNCTION, readCallback);
     689    curl_easy_setopt(d->m_handle, CURLOPT_READDATA, job);
     690}
     691
     692void ResourceHandle::setupPUT(struct curl_slist** headers)
     693{
     694    ResourceHandleInternal* d = getInternal();
     695    curl_easy_setopt(d->m_handle, CURLOPT_UPLOAD, TRUE);
     696    curl_easy_setopt(d->m_handle, CURLOPT_INFILESIZE, 0);
     697
     698    // Disable the Expect: 100 continue header
     699    *headers = curl_slist_append(*headers, "Expect:");
     700
     701    size_t numElements = getFormElementsCount(this);
     702    if (!numElements)
     703        return;
     704
     705    setupFormData(this, CURLOPT_INFILESIZE_LARGE, headers);
     706}
     707
     708void ResourceHandle::setupPOST(struct curl_slist** headers)
     709{
     710    ResourceHandleInternal* d = getInternal();
     711    curl_easy_setopt(d->m_handle, CURLOPT_POST, TRUE);
     712    curl_easy_setopt(d->m_handle, CURLOPT_POSTFIELDSIZE, 0);
     713
     714    size_t numElements = getFormElementsCount(this);
     715    if (!numElements)
     716        return;
     717
     718    // Do not stream for simple POST data
     719    if (numElements == 1) {
     720        firstRequest().httpBody()->flatten(d->m_postBytes);
     721        if (d->m_postBytes.size()) {
     722            curl_easy_setopt(d->m_handle, CURLOPT_POSTFIELDSIZE, d->m_postBytes.size());
     723            curl_easy_setopt(d->m_handle, CURLOPT_POSTFIELDS, d->m_postBytes.data());
     724        }
     725        return;
     726    }
     727
     728    setupFormData(this, CURLOPT_POSTFIELDSIZE_LARGE, headers);
     729}
     730
     731void ResourceHandle::handleDataURL()
     732{
     733    ASSERT(firstRequest().url().protocolIsData());
     734    String url = firstRequest().url().string();
     735
     736    ASSERT(client());
     737
     738    int index = url.find(',');
     739    if (index == -1) {
     740        client()->cannotShowURL(this);
     741        return;
     742    }
     743
     744    String mediaType = url.substring(5, index - 5);
     745    String data = url.substring(index + 1);
     746
     747    bool base64 = mediaType.endsWith(";base64", false);
     748    if (base64)
     749        mediaType = mediaType.left(mediaType.length() - 7);
     750
     751    if (mediaType.isEmpty())
     752        mediaType = "text/plain";
     753
     754    String mimeType = extractMIMETypeFromMediaType(mediaType);
     755    String charset = extractCharsetFromMediaType(mediaType);
     756
     757    if (charset.isEmpty())
     758        charset = "US-ASCII";
     759
     760    ResourceResponse response;
     761    response.setMimeType(mimeType);
     762    response.setTextEncodingName(charset);
     763    response.setURL(firstRequest().url());
     764
     765    if (base64) {
     766        data = decodeURLEscapeSequences(data);
     767        client()->didReceiveResponse(this, WTFMove(response));
     768
     769        // didReceiveResponse might cause the client to be deleted.
     770        if (client()) {
     771            Vector<char> out;
     772            if (base64Decode(data, out, Base64IgnoreSpacesAndNewLines) && out.size() > 0)
     773                client()->didReceiveData(this, out.data(), out.size(), 0);
     774        }
     775    } else {
     776        TextEncoding encoding(charset);
     777        data = decodeURLEscapeSequences(data, encoding);
     778        client()->didReceiveResponse(this, WTFMove(response));
     779
     780        // didReceiveResponse might cause the client to be deleted.
     781        if (client()) {
     782            CString encodedData = encoding.encode(data, URLEncodedEntitiesForUnencodables);
     783            if (encodedData.length())
     784                client()->didReceiveData(this, encodedData.data(), encodedData.length(), 0);
     785        }
     786    }
     787
     788    if (client())
     789        client()->didFinishLoading(this);
     790}
     791
     792void ResourceHandle::dispatchSynchronousJob()
     793{
     794    URL kurl = firstRequest().url();
     795
     796    if (kurl.protocolIsData()) {
     797        handleDataURL();
     798        return;
     799    }
     800
     801    ResourceHandleInternal* d = getInternal();
     802
     803    // If defersLoading is true and we call curl_easy_perform
     804    // on a paused handle, libcURL would do the transfert anyway
     805    // and we would assert so force defersLoading to be false.
     806    d->m_defersLoading = false;
     807
     808    initialize();
     809
     810    // curl_easy_perform blocks until the transfert is finished.
     811    CURLcode ret = curl_easy_perform(d->m_handle);
     812
     813    if (ret != CURLE_OK) {
     814        ResourceError error(ASCIILiteral(errorDomainCurl), ret, kurl, String(curl_easy_strerror(ret)));
     815        error.setSSLErrors(d->m_sslErrors);
     816        d->client()->didFail(this, error);
     817    } else {
     818        if (d->client())
     819            d->client()->didReceiveResponse(this, ResourceResponse(d->m_response));
     820    }
     821
     822#if ENABLE(WEB_TIMING)
     823    calculateWebTimingInformations(d);
     824#endif
     825
     826    curl_easy_cleanup(d->m_handle);
     827}
     828
     829void ResourceHandle::applyAuthentication()
     830{
     831    ResourceRequest& request = firstRequest();
     832    // m_user/m_pass are credentials given manually, for instance, by the arguments passed to XMLHttpRequest.open().
     833    ResourceHandleInternal* d = getInternal();
     834
     835    String partition = request.cachePartition();
     836
     837    if (shouldUseCredentialStorage()) {
     838        if (d->m_user.isEmpty() && d->m_pass.isEmpty()) {
     839            // <rdar://problem/7174050> - For URLs that match the paths of those previously challenged for HTTP Basic authentication,
     840            // try and reuse the credential preemptively, as allowed by RFC 2617.
     841            d->m_initialCredential = CredentialStorage::defaultCredentialStorage().get(partition, request.url());
     842        } else {
     843            // If there is already a protection space known for the URL, update stored credentials
     844            // before sending a request. This makes it possible to implement logout by sending an
     845            // XMLHttpRequest with known incorrect credentials, and aborting it immediately (so that
     846            // an authentication dialog doesn't pop up).
     847            CredentialStorage::defaultCredentialStorage().set(partition, Credential(d->m_user, d->m_pass, CredentialPersistenceNone), request.url());
     848        }
     849    }
     850
     851    String user = d->m_user;
     852    String password = d->m_pass;
     853
     854    if (!d->m_initialCredential.isEmpty()) {
     855        user = d->m_initialCredential.user();
     856        password = d->m_initialCredential.password();
     857        curl_easy_setopt(d->m_handle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
     858    }
     859
     860    // It seems we need to set CURLOPT_USERPWD even if username and password is empty.
     861    // Otherwise cURL will not automatically continue with a new request after a 401 response.
     862
     863    // curl CURLOPT_USERPWD expects username:password
     864    String userpass = user + ":" + password;
     865    curl_easy_setopt(d->m_handle, CURLOPT_USERPWD, userpass.utf8().data());
     866}
     867
     868void ResourceHandle::initialize()
     869{
     870    CurlContext& context = CurlContext::singleton();
     871
     872    static const int allowedProtocols = CURLPROTO_FILE | CURLPROTO_FTP | CURLPROTO_FTPS | CURLPROTO_HTTP | CURLPROTO_HTTPS;
     873    URL url = firstRequest().url();
     874
     875    // Remove any fragment part, otherwise curl will send it as part of the request.
     876    url.removeFragmentIdentifier();
     877
     878    ResourceHandleInternal* d = getInternal();
     879    String urlString = url.string();
     880
     881    if (url.isLocalFile()) {
     882        // Remove any query part sent to a local file.
     883        if (!url.query().isEmpty()) {
     884            // By setting the query to a null string it'll be removed.
     885            url.setQuery(String());
     886            urlString = url.string();
     887        }
     888        // Determine the MIME type based on the path.
     889        d->m_response.setMimeType(MIMETypeRegistry::getMIMETypeForPath(url));
     890    }
     891
     892    d->m_handle = curl_easy_init();
     893
     894    if (d->m_defersLoading) {
     895        CURLcode error = curl_easy_pause(d->m_handle, CURLPAUSE_ALL);
     896        // If we did not pause the handle, we would ASSERT in the
     897        // header callback. So just assert here.
     898        ASSERT_UNUSED(error, error == CURLE_OK);
     899    }
     900#ifndef NDEBUG
     901    if (context.isVerbose())
     902        curl_easy_setopt(d->m_handle, CURLOPT_VERBOSE, 1);
     903    if (context.getLogFile())
     904        curl_easy_setopt(d->m_handle, CURLOPT_STDERR, context.getLogFile());
     905#endif
     906    curl_easy_setopt(d->m_handle, CURLOPT_SSL_VERIFYPEER, 1L);
     907    curl_easy_setopt(d->m_handle, CURLOPT_SSL_VERIFYHOST, 2L);
     908    curl_easy_setopt(d->m_handle, CURLOPT_PRIVATE, this);
     909    curl_easy_setopt(d->m_handle, CURLOPT_ERRORBUFFER, m_curlErrorBuffer);
     910    curl_easy_setopt(d->m_handle, CURLOPT_WRITEFUNCTION, writeCallback);
     911    curl_easy_setopt(d->m_handle, CURLOPT_WRITEDATA, this);
     912    curl_easy_setopt(d->m_handle, CURLOPT_HEADERFUNCTION, headerCallback);
     913    curl_easy_setopt(d->m_handle, CURLOPT_WRITEHEADER, this);
     914    curl_easy_setopt(d->m_handle, CURLOPT_AUTOREFERER, 1);
     915    curl_easy_setopt(d->m_handle, CURLOPT_FOLLOWLOCATION, 1);
     916    curl_easy_setopt(d->m_handle, CURLOPT_MAXREDIRS, 10);
     917    curl_easy_setopt(d->m_handle, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
     918    curl_easy_setopt(d->m_handle, CURLOPT_SHARE, CurlContext::singleton().curlShareHandle());
     919    curl_easy_setopt(d->m_handle, CURLOPT_DNS_CACHE_TIMEOUT, 60 * 5); // 5 minutes
     920    curl_easy_setopt(d->m_handle, CURLOPT_PROTOCOLS, allowedProtocols);
     921    curl_easy_setopt(d->m_handle, CURLOPT_REDIR_PROTOCOLS, allowedProtocols);
     922    setSSLClientCertificate(this);
     923
     924    if (context.shouldIgnoreSSLErrors())
     925        curl_easy_setopt(d->m_handle, CURLOPT_SSL_VERIFYPEER, false);
     926    else
     927        setSSLVerifyOptions(this);
     928
     929    const char* certificate = context.getCertificatePath();
     930    if (certificate)
     931        curl_easy_setopt(d->m_handle, CURLOPT_CAINFO, certificate);
     932
     933    // enable gzip and deflate through Accept-Encoding:
     934    curl_easy_setopt(d->m_handle, CURLOPT_ENCODING, "");
     935
     936    // url must remain valid through the request
     937    ASSERT(!d->m_url);
     938
     939    // url is in ASCII so latin1() will only convert it to char* without character translation.
     940    d->m_url = fastStrDup(urlString.latin1().data());
     941    curl_easy_setopt(d->m_handle, CURLOPT_URL, d->m_url);
     942
     943    const char* cookieJar = context.getCookieJarFileName();
     944    if (cookieJar)
     945        curl_easy_setopt(d->m_handle, CURLOPT_COOKIEJAR, cookieJar);
     946
     947    struct curl_slist* headers = 0;
     948    if (firstRequest().httpHeaderFields().size() > 0) {
     949        HTTPHeaderMap customHeaders = firstRequest().httpHeaderFields();
     950
     951        bool hasCacheHeaders = customHeaders.contains(HTTPHeaderName::IfModifiedSince) || customHeaders.contains(HTTPHeaderName::IfNoneMatch);
     952        if (!hasCacheHeaders && CurlCacheManager::getInstance().isCached(url)) {
     953            CurlCacheManager::getInstance().addCacheEntryClient(url, this);
     954            HTTPHeaderMap& requestHeaders = CurlCacheManager::getInstance().requestHeaders(url);
     955
     956            // append additional cache information
     957            HTTPHeaderMap::const_iterator it = requestHeaders.begin();
     958            HTTPHeaderMap::const_iterator end = requestHeaders.end();
     959            while (it != end) {
     960                customHeaders.set(it->key, it->value);
     961                ++it;
     962            }
     963            d->m_addedCacheValidationHeaders = true;
     964        }
     965
     966        HTTPHeaderMap::const_iterator end = customHeaders.end();
     967        for (HTTPHeaderMap::const_iterator it = customHeaders.begin(); it != end; ++it) {
     968            String key = it->key;
     969            String value = it->value;
     970            String headerString(key);
     971            if (value.isEmpty()) {
     972                // Insert the ; to tell curl that this header has an empty value.
     973                headerString.append(";");
     974            } else {
     975                headerString.append(": ");
     976                headerString.append(value);
     977            }
     978            CString headerLatin1 = headerString.latin1();
     979            headers = curl_slist_append(headers, headerLatin1.data());
     980        }
     981    }
     982
     983    String method = firstRequest().httpMethod();
     984    if ("GET" == method)
     985        curl_easy_setopt(d->m_handle, CURLOPT_HTTPGET, TRUE);
     986    else if ("POST" == method)
     987        setupPOST(&headers);
     988    else if ("PUT" == method)
     989        setupPUT(&headers);
     990    else if ("HEAD" == method)
     991        curl_easy_setopt(d->m_handle, CURLOPT_NOBODY, TRUE);
     992    else {
     993        curl_easy_setopt(d->m_handle, CURLOPT_CUSTOMREQUEST, method.ascii().data());
     994        setupPUT(&headers);
     995    }
     996
     997    if (headers) {
     998        curl_easy_setopt(d->m_handle, CURLOPT_HTTPHEADER, headers);
     999        d->m_customHeaders = headers;
     1000    }
     1001
     1002    applyAuthentication();
     1003
     1004    // Set proxy options if we have them.
     1005    auto& proxy = CurlContext::singleton().proxyInfo();
     1006    if (proxy.type != CurlProxyType::Invalid) {
     1007        curl_easy_setopt(d->m_handle, CURLOPT_PROXY, proxy.url().utf8().data());
     1008        curl_easy_setopt(d->m_handle, CURLOPT_PROXYTYPE, proxy.type);
     1009    }
     1010}
     1011
     1012void ResourceHandle::handleCurlMsg(CURLMsg* msg)
     1013{
     1014    ResourceHandleInternal* d = getInternal();
     1015
     1016    if (CURLE_OK == msg->data.result) {
     1017#if ENABLE(WEB_TIMING)
     1018        calculateWebTimingInformations(d);
     1019#endif
     1020        if (!d->m_response.responseFired()) {
     1021            handleLocalReceiveResponse(d->m_handle, this, d);
     1022            if (d->m_cancelled)
     1023                return;
     1024        }
     1025
     1026        if (d->m_multipartHandle)
     1027            d->m_multipartHandle->contentEnded();
     1028
     1029        if (d->client()) {
     1030            d->client()->didFinishLoading(this);
     1031            CurlCacheManager::getInstance().didFinishLoading(*this);
     1032        }
     1033    } else {
     1034        URL url = CurlContext::singleton().getEffectiveURL(d->m_handle);
     1035#ifndef NDEBUG
     1036        fprintf(stderr, "Curl ERROR for url='%s', error: '%s'\n", url.string().utf8().data(), curl_easy_strerror(msg->data.result));
     1037#endif
     1038        if (d->client()) {
     1039            ResourceError resourceError(ASCIILiteral(errorDomainCurl), msg->data.result, url, String(curl_easy_strerror(msg->data.result)));
     1040            resourceError.setSSLErrors(d->m_sslErrors);
     1041            d->client()->didFail(this, resourceError);
     1042            CurlCacheManager::getInstance().didFail(*this);
     1043        }
     1044    }
     1045}
     1046
    2441047} // namespace WebCore
    2451048
  • trunk/Source/WebCore/platform/network/curl/ResourceHandleManager.cpp

    r218947 r218962  
    4242
    4343#include "CredentialStorage.h"
    44 #include "CurlCacheManager.h"
    4544#include "CurlContext.h"
    4645#include "HTTPHeaderNames.h"
    4746#include "HTTPParsers.h"
    48 #include "MIMETypeRegistry.h"
    4947#include "MultipartHandle.h"
    5048#include "ResourceError.h"
     
    5654#include "SSLHandle.h"
    5755#include "TextEncoding.h"
    58 #include <wtf/text/Base64.h>
    5956#include <wtf/text/CString.h>
    6057#include <wtf/text/StringView.h>
     
    8077static const Seconds pollTime { 50_ms };
    8178const int maxRunningJobs = 128;
    82 const char* const errorDomainCurl = "CurlErrorDomain";
    83 
    84 #if ENABLE(WEB_TIMING)
    85 static void calculateWebTimingInformations(ResourceHandleInternal* d)
    86 {
    87     double preTransferTime = 0;
    88     double dnslookupTime = 0;
    89     double connectTime = 0;
    90     double appConnectTime = 0;
    91 
    92     curl_easy_getinfo(d->m_handle, CURLINFO_NAMELOOKUP_TIME, &dnslookupTime);
    93     curl_easy_getinfo(d->m_handle, CURLINFO_CONNECT_TIME, &connectTime);
    94     curl_easy_getinfo(d->m_handle, CURLINFO_APPCONNECT_TIME, &appConnectTime);
    95     curl_easy_getinfo(d->m_handle, CURLINFO_PRETRANSFER_TIME, &preTransferTime);
    96 
    97     d->m_response.deprecatedNetworkLoadMetrics().domainLookupStart = Seconds(0);
    98     d->m_response.deprecatedNetworkLoadMetrics().domainLookupEnd = Seconds(dnslookupTime);
    99 
    100     d->m_response.deprecatedNetworkLoadMetrics().connectStart = Seconds(dnslookupTime);
    101     d->m_response.deprecatedNetworkLoadMetrics().connectEnd = Seconds(connectTime);
    102 
    103     d->m_response.deprecatedNetworkLoadMetrics().requestStart = Seconds(connectTime);
    104     d->m_response.deprecatedNetworkLoadMetrics().responseStart = Seconds(preTransferTime);
    105 
    106     if (appConnectTime)
    107         d->m_response.deprecatedNetworkLoadMetrics().secureConnectionStart = Seconds(connectTime);
    108 }
    109 #endif
    110 
    111 inline static bool isHttpInfo(int statusCode)
    112 {
    113     return 100 <= statusCode && statusCode < 200;
    114 }
    115 
    116 inline static bool isHttpRedirect(int statusCode)
    117 {
    118     return 300 <= statusCode && statusCode < 400 && statusCode != 304;
    119 }
    120 
    121 inline static bool isHttpAuthentication(int statusCode)
    122 {
    123     return statusCode == 401;
    124 }
    125 
    126 inline static bool isHttpNotModified(int statusCode)
    127 {
    128     return statusCode == 304;
    129 }
    13079
    13180ResourceHandleManager::ResourceHandleManager()
     
    14796        sharedInstance = new ResourceHandleManager();
    14897    return sharedInstance;
    149 }
    150 
    151 static void handleLocalReceiveResponse (CURL* handle, ResourceHandle* job, ResourceHandleInternal* d)
    152 {
    153     // since the code in headerCallback will not have run for local files
    154     // the code to set the URL and fire didReceiveResponse is never run,
    155     // which means the ResourceLoader's response does not contain the URL.
    156     // Run the code here for local files to resolve the issue.
    157     // TODO: See if there is a better approach for handling this.
    158     URL url = CurlContext::singleton().getEffectiveURL(handle);
    159     ASSERT(url.isValid());
    160     d->m_response.setURL(url);
    161      if (d->client())
    162          d->client()->didReceiveResponse(job, ResourceResponse(d->m_response));
    163      d->m_response.setResponseFired(true);
    164 }
    165 
    166 
    167 // called with data after all headers have been processed via headerCallback
    168 static size_t writeCallback(void* ptr, size_t size, size_t nmemb, void* data)
    169 {
    170     ResourceHandle* job = static_cast<ResourceHandle*>(data);
    171     ResourceHandleInternal* d = job->getInternal();
    172     if (d->m_cancelled)
    173         return 0;
    174 
    175     // We should never be called when deferred loading is activated.
    176     ASSERT(!d->m_defersLoading);
    177 
    178     size_t totalSize = size * nmemb;
    179 
    180     // this shouldn't be necessary but apparently is. CURL writes the data
    181     // of html page even if it is a redirect that was handled internally
    182     // can be observed e.g. on gmail.com
    183     CURL* h = d->m_handle;
    184     long httpCode = 0;
    185     CURLcode err = curl_easy_getinfo(h, CURLINFO_RESPONSE_CODE, &httpCode);
    186     if (CURLE_OK == err && httpCode >= 300 && httpCode < 400)
    187         return totalSize;
    188 
    189     if (!d->m_response.responseFired()) {
    190         handleLocalReceiveResponse(h, job, d);
    191         if (d->m_cancelled)
    192             return 0;
    193     }
    194 
    195     if (d->m_multipartHandle)
    196         d->m_multipartHandle->contentReceived(static_cast<const char*>(ptr), totalSize);
    197     else if (d->client()) {
    198         d->client()->didReceiveData(job, static_cast<char*>(ptr), totalSize, 0);
    199         CurlCacheManager::getInstance().didReceiveData(*job, static_cast<char*>(ptr), totalSize);
    200     }
    201 
    202     return totalSize;
    203 }
    204 
    205 static bool isAppendableHeader(const String &key)
    206 {
    207     static const char* appendableHeaders[] = {
    208         "access-control-allow-headers",
    209         "access-control-allow-methods",
    210         "access-control-allow-origin",
    211         "access-control-expose-headers",
    212         "allow",
    213         "cache-control",
    214         "connection",
    215         "content-encoding",
    216         "content-language",
    217         "if-match",
    218         "if-none-match",
    219         "keep-alive",
    220         "pragma",
    221         "proxy-authenticate",
    222         "public",
    223         "server",
    224         "set-cookie",
    225         "te",
    226         "trailer",
    227         "transfer-encoding",
    228         "upgrade",
    229         "user-agent",
    230         "vary",
    231         "via",
    232         "warning",
    233         "www-authenticate"
    234     };
    235 
    236     // Custom headers start with 'X-', and need no further checking.
    237     if (key.startsWith("x-", /* caseSensitive */ false))
    238         return true;
    239 
    240     for (auto& header : appendableHeaders) {
    241         if (equalIgnoringASCIICase(key, header))
    242             return true;
    243     }
    244 
    245     return false;
    246 }
    247 
    248 static void removeLeadingAndTrailingQuotes(String& value)
    249 {
    250     unsigned length = value.length();
    251     if (value.startsWith('"') && value.endsWith('"') && length > 1)
    252         value = value.substring(1, length-2);
    253 }
    254 
    255 static bool getProtectionSpace(CURL* h, const ResourceResponse& response, ProtectionSpace& protectionSpace)
    256 {
    257     CURLcode err;
    258 
    259     long port = 0;
    260     err = curl_easy_getinfo(h, CURLINFO_PRIMARY_PORT, &port);
    261     if (err != CURLE_OK)
    262         return false;
    263 
    264     long availableAuth = CURLAUTH_NONE;
    265     err = curl_easy_getinfo(h, CURLINFO_HTTPAUTH_AVAIL, &availableAuth);
    266     if (err != CURLE_OK)
    267         return false;
    268 
    269     URL url = CurlContext::singleton().getEffectiveURL(h);
    270     if (!url.isValid())
    271         return false;
    272 
    273     String host = url.host();
    274     StringView protocol = url.protocol();
    275 
    276     String realm;
    277 
    278     const String authHeader = response.httpHeaderField(HTTPHeaderName::Authorization);
    279     const String realmString = "realm=";
    280     int realmPos = authHeader.find(realmString);
    281     if (realmPos > 0) {
    282         realm = authHeader.substring(realmPos + realmString.length());
    283         realm = realm.left(realm.find(','));
    284         removeLeadingAndTrailingQuotes(realm);
    285     }
    286 
    287     ProtectionSpaceServerType serverType = ProtectionSpaceServerHTTP;
    288     if (protocol == "https")
    289         serverType = ProtectionSpaceServerHTTPS;
    290 
    291     ProtectionSpaceAuthenticationScheme authScheme = ProtectionSpaceAuthenticationSchemeUnknown;
    292 
    293     if (availableAuth & CURLAUTH_BASIC)
    294         authScheme = ProtectionSpaceAuthenticationSchemeHTTPBasic;
    295     if (availableAuth & CURLAUTH_DIGEST)
    296         authScheme = ProtectionSpaceAuthenticationSchemeHTTPDigest;
    297     if (availableAuth & CURLAUTH_GSSNEGOTIATE)
    298         authScheme = ProtectionSpaceAuthenticationSchemeNegotiate;
    299     if (availableAuth & CURLAUTH_NTLM)
    300         authScheme = ProtectionSpaceAuthenticationSchemeNTLM;
    301 
    302     protectionSpace = ProtectionSpace(host, port, serverType, realm, authScheme);
    303 
    304     return true;
    305 }
    306 
    307 /*
    308  * This is being called for each HTTP header in the response. This includes '\r\n'
    309  * for the last line of the header.
    310  *
    311  * We will add each HTTP Header to the ResourceResponse and on the termination
    312  * of the header (\r\n) we will parse Content-Type and Content-Disposition and
    313  * update the ResourceResponse and then send it away.
    314  *
    315  */
    316 static size_t headerCallback(char* ptr, size_t size, size_t nmemb, void* data)
    317 {
    318     ResourceHandle* job = static_cast<ResourceHandle*>(data);
    319     ResourceHandleInternal* d = job->getInternal();
    320     if (d->m_cancelled)
    321         return 0;
    322 
    323     // We should never be called when deferred loading is activated.
    324     ASSERT(!d->m_defersLoading);
    325 
    326     size_t totalSize = size * nmemb;
    327     ResourceHandleClient* client = d->client();
    328 
    329     String header(static_cast<const char*>(ptr), totalSize);
    330 
    331     /*
    332      * a) We can finish and send the ResourceResponse
    333      * b) We will add the current header to the HTTPHeaderMap of the ResourceResponse
    334      *
    335      * The HTTP standard requires to use \r\n but for compatibility it recommends to
    336      * accept also \n.
    337      */
    338     if (header == String("\r\n") || header == String("\n")) {
    339         CURL* h = d->m_handle;
    340 
    341         long httpCode = 0;
    342         curl_easy_getinfo(h, CURLINFO_RESPONSE_CODE, &httpCode);
    343 
    344         if (!httpCode) {
    345             // Comes here when receiving 200 Connection Established. Just return.
    346             return totalSize;
    347         }
    348         if (isHttpInfo(httpCode)) {
    349             // Just return when receiving http info, e.g. HTTP/1.1 100 Continue.
    350             // If not, the request might be cancelled, because the MIME type will be empty for this response.
    351             return totalSize;
    352         }
    353 
    354         double contentLength = 0;
    355         curl_easy_getinfo(h, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &contentLength);
    356         d->m_response.setExpectedContentLength(static_cast<long long int>(contentLength));
    357 
    358         d->m_response.setURL(CurlContext::singleton().getEffectiveURL(h));
    359 
    360         d->m_response.setHTTPStatusCode(httpCode);
    361         d->m_response.setMimeType(extractMIMETypeFromMediaType(d->m_response.httpHeaderField(HTTPHeaderName::ContentType)).convertToASCIILowercase());
    362         d->m_response.setTextEncodingName(extractCharsetFromMediaType(d->m_response.httpHeaderField(HTTPHeaderName::ContentType)));
    363 
    364         if (d->m_response.isMultipart()) {
    365             String boundary;
    366             bool parsed = MultipartHandle::extractBoundary(d->m_response.httpHeaderField(HTTPHeaderName::ContentType), boundary);
    367             if (parsed)
    368                 d->m_multipartHandle = std::make_unique<MultipartHandle>(job, boundary);
    369         }
    370 
    371         // HTTP redirection
    372         if (isHttpRedirect(httpCode)) {
    373             String location = d->m_response.httpHeaderField(HTTPHeaderName::Location);
    374             if (!location.isEmpty()) {
    375                 URL newURL = URL(job->firstRequest().url(), location);
    376 
    377                 ResourceRequest redirectedRequest = job->firstRequest();
    378                 redirectedRequest.setURL(newURL);
    379                 ResourceResponse response = d->m_response;
    380                 if (client)
    381                     client->willSendRequest(job, WTFMove(redirectedRequest), WTFMove(response));
    382 
    383                 d->m_firstRequest.setURL(newURL);
    384 
    385                 return totalSize;
    386             }
    387         } else if (isHttpAuthentication(httpCode)) {
    388             ProtectionSpace protectionSpace;
    389             if (getProtectionSpace(d->m_handle, d->m_response, protectionSpace)) {
    390                 Credential credential;
    391                 AuthenticationChallenge challenge(protectionSpace, credential, d->m_authFailureCount, d->m_response, ResourceError());
    392                 challenge.setAuthenticationClient(job);
    393                 job->didReceiveAuthenticationChallenge(challenge);
    394                 d->m_authFailureCount++;
    395                 return totalSize;
    396             }
    397         }
    398 
    399         if (client) {
    400             if (isHttpNotModified(httpCode)) {
    401                 const String& url = job->firstRequest().url().string();
    402                 if (CurlCacheManager::getInstance().getCachedResponse(url, d->m_response)) {
    403                     if (d->m_addedCacheValidationHeaders) {
    404                         d->m_response.setHTTPStatusCode(200);
    405                         d->m_response.setHTTPStatusText("OK");
    406                     }
    407                 }
    408             }
    409             client->didReceiveResponse(job, ResourceResponse(d->m_response));
    410             CurlCacheManager::getInstance().didReceiveResponse(*job, d->m_response);
    411         }
    412         d->m_response.setResponseFired(true);
    413 
    414     } else {
    415         int splitPos = header.find(":");
    416         if (splitPos != -1) {
    417             String key = header.left(splitPos).stripWhiteSpace();
    418             String value = header.substring(splitPos + 1).stripWhiteSpace();
    419 
    420             if (isAppendableHeader(key))
    421                 d->m_response.addHTTPHeaderField(key, value);
    422             else
    423                 d->m_response.setHTTPHeaderField(key, value);
    424         } else if (header.startsWith("HTTP", false)) {
    425             // This is the first line of the response.
    426             // Extract the http status text from this.
    427             //
    428             // If the FOLLOWLOCATION option is enabled for the curl handle then
    429             // curl will follow the redirections internally. Thus this header callback
    430             // will be called more than one time with the line starting "HTTP" for one job.
    431             long httpCode = 0;
    432             curl_easy_getinfo(d->m_handle, CURLINFO_RESPONSE_CODE, &httpCode);
    433 
    434             String httpCodeString = String::number(httpCode);
    435             int statusCodePos = header.find(httpCodeString);
    436 
    437             if (statusCodePos != -1) {
    438                 // The status text is after the status code.
    439                 String status = header.substring(statusCodePos + httpCodeString.length());
    440                 d->m_response.setHTTPStatusText(status.stripWhiteSpace());
    441             }
    442 
    443         }
    444     }
    445 
    446     return totalSize;
    447 }
    448 
    449 /* This is called to obtain HTTP POST or PUT data.
    450    Iterate through FormData elements and upload files.
    451    Carefully respect the given buffer size and fill the rest of the data at the next calls.
    452 */
    453 size_t readCallback(void* ptr, size_t size, size_t nmemb, void* data)
    454 {
    455     ResourceHandle* job = static_cast<ResourceHandle*>(data);
    456     ResourceHandleInternal* d = job->getInternal();
    457 
    458     if (d->m_cancelled)
    459         return 0;
    460 
    461     // We should never be called when deferred loading is activated.
    462     ASSERT(!d->m_defersLoading);
    463 
    464     if (!size || !nmemb)
    465         return 0;
    466 
    467     if (!d->m_formDataStream.hasMoreElements())
    468         return 0;
    469 
    470     size_t sent = d->m_formDataStream.read(ptr, size, nmemb);
    471 
    472     // Something went wrong so cancel the job.
    473     if (!sent)
    474         job->cancel();
    475 
    476     return sent;
    47798}
    47899
     
    542163            continue;
    543164
    544 
    545         if (CURLE_OK == msg->data.result) {
    546 #if ENABLE(WEB_TIMING)
    547             calculateWebTimingInformations(d);
    548 #endif
    549             if (!d->m_response.responseFired()) {
    550                 handleLocalReceiveResponse(d->m_handle, job, d);
    551                 if (d->m_cancelled) {
    552                     removeFromCurl(job);
    553                     continue;
    554                 }
    555             }
    556 
    557             if (d->m_multipartHandle)
    558                 d->m_multipartHandle->contentEnded();
    559 
    560             if (d->client()) {
    561                 d->client()->didFinishLoading(job);
    562                 CurlCacheManager::getInstance().didFinishLoading(*job);
    563             }
    564         } else {
    565             URL url = CurlContext::singleton().getEffectiveURL(d->m_handle);
    566 #ifndef NDEBUG
    567             fprintf(stderr, "Curl ERROR for url='%s', error: '%s'\n", url.string().utf8().data(), curl_easy_strerror(msg->data.result));
    568 #endif
    569             if (d->client()) {
    570                 ResourceError resourceError(ASCIILiteral(errorDomainCurl), msg->data.result, url, String(curl_easy_strerror(msg->data.result)));
    571                 resourceError.setSSLErrors(d->m_sslErrors);
    572                 d->client()->didFail(job, resourceError);
    573                 CurlCacheManager::getInstance().didFail(*job);
    574             }
    575         }
    576 
     165        job->handleCurlMsg(msg);
    577166        removeFromCurl(job);
    578167    }
     
    595184    d->m_handle = 0;
    596185    job->deref();
    597 }
    598 
    599 static inline size_t getFormElementsCount(ResourceHandle* job)
    600 {
    601     RefPtr<FormData> formData = job->firstRequest().httpBody();
    602 
    603     if (!formData)
    604         return 0;
    605 
    606     // Resolve the blob elements so the formData can correctly report it's size.
    607     formData = formData->resolveBlobReferences();
    608     size_t size = formData->elements().size();
    609     job->firstRequest().setHTTPBody(WTFMove(formData));
    610 
    611     return size;
    612 }
    613 
    614 static void setupFormData(ResourceHandle* job, CURLoption sizeOption, struct curl_slist** headers)
    615 {
    616     ResourceHandleInternal* d = job->getInternal();
    617     Vector<FormDataElement> elements = job->firstRequest().httpBody()->elements();
    618     size_t numElements = elements.size();
    619 
    620     // The size of a curl_off_t could be different in WebKit and in cURL depending on
    621     // compilation flags of both.
    622     static int expectedSizeOfCurlOffT = 0;
    623     if (!expectedSizeOfCurlOffT) {
    624         curl_version_info_data *infoData = curl_version_info(CURLVERSION_NOW);
    625         if (infoData->features & CURL_VERSION_LARGEFILE)
    626             expectedSizeOfCurlOffT = sizeof(long long);
    627         else
    628             expectedSizeOfCurlOffT = sizeof(int);
    629     }
    630 
    631     static const long long maxCurlOffT = (1LL << (expectedSizeOfCurlOffT * 8 - 1)) - 1;
    632     // Obtain the total size of the form data
    633     curl_off_t size = 0;
    634     bool chunkedTransfer = false;
    635     for (size_t i = 0; i < numElements; i++) {
    636         FormDataElement element = elements[i];
    637         if (element.m_type == FormDataElement::Type::EncodedFile) {
    638             long long fileSizeResult;
    639             if (getFileSize(element.m_filename, fileSizeResult)) {
    640                 if (fileSizeResult > maxCurlOffT) {
    641                     // File size is too big for specifying it to cURL
    642                     chunkedTransfer = true;
    643                     break;
    644                 }
    645                 size += fileSizeResult;
    646             } else {
    647                 chunkedTransfer = true;
    648                 break;
    649             }
    650         } else
    651             size += elements[i].m_data.size();
    652     }
    653 
    654     // cURL guesses that we want chunked encoding as long as we specify the header
    655     if (chunkedTransfer)
    656         *headers = curl_slist_append(*headers, "Transfer-Encoding: chunked");
    657     else {
    658         if (sizeof(long long) == expectedSizeOfCurlOffT)
    659             curl_easy_setopt(d->m_handle, sizeOption, (long long)size);
    660         else
    661             curl_easy_setopt(d->m_handle, sizeOption, (int)size);
    662     }
    663 
    664     curl_easy_setopt(d->m_handle, CURLOPT_READFUNCTION, readCallback);
    665     curl_easy_setopt(d->m_handle, CURLOPT_READDATA, job);
    666 }
    667 
    668 void ResourceHandleManager::setupPUT(ResourceHandle* job, struct curl_slist** headers)
    669 {
    670     ResourceHandleInternal* d = job->getInternal();
    671     curl_easy_setopt(d->m_handle, CURLOPT_UPLOAD, TRUE);
    672     curl_easy_setopt(d->m_handle, CURLOPT_INFILESIZE, 0);
    673 
    674     // Disable the Expect: 100 continue header
    675     *headers = curl_slist_append(*headers, "Expect:");
    676 
    677     size_t numElements = getFormElementsCount(job);
    678     if (!numElements)
    679         return;
    680 
    681     setupFormData(job, CURLOPT_INFILESIZE_LARGE, headers);
    682 }
    683 
    684 void ResourceHandleManager::setupPOST(ResourceHandle* job, struct curl_slist** headers)
    685 {
    686     ResourceHandleInternal* d = job->getInternal();
    687     curl_easy_setopt(d->m_handle, CURLOPT_POST, TRUE);
    688     curl_easy_setopt(d->m_handle, CURLOPT_POSTFIELDSIZE, 0);
    689 
    690     size_t numElements = getFormElementsCount(job);
    691     if (!numElements)
    692         return;
    693 
    694     // Do not stream for simple POST data
    695     if (numElements == 1) {
    696         job->firstRequest().httpBody()->flatten(d->m_postBytes);
    697         if (d->m_postBytes.size()) {
    698             curl_easy_setopt(d->m_handle, CURLOPT_POSTFIELDSIZE, d->m_postBytes.size());
    699             curl_easy_setopt(d->m_handle, CURLOPT_POSTFIELDS, d->m_postBytes.data());
    700         }
    701         return;
    702     }
    703 
    704     setupFormData(job, CURLOPT_POSTFIELDSIZE_LARGE, headers);
    705186}
    706187
     
    742223}
    743224
    744 static void handleDataURL(ResourceHandle* handle)
    745 {
    746     ASSERT(handle->firstRequest().url().protocolIsData());
    747     String url = handle->firstRequest().url().string();
    748 
    749     ASSERT(handle);
    750     ASSERT(handle->client());
    751 
    752     int index = url.find(',');
    753     if (index == -1) {
    754         handle->client()->cannotShowURL(handle);
    755         return;
    756     }
    757 
    758     String mediaType = url.substring(5, index - 5);
    759     String data = url.substring(index + 1);
    760 
    761     bool base64 = mediaType.endsWith(";base64", false);
    762     if (base64)
    763         mediaType = mediaType.left(mediaType.length() - 7);
    764 
    765     if (mediaType.isEmpty())
    766         mediaType = "text/plain";
    767 
    768     String mimeType = extractMIMETypeFromMediaType(mediaType);
    769     String charset = extractCharsetFromMediaType(mediaType);
    770 
    771     if (charset.isEmpty())
    772         charset = "US-ASCII";
    773 
    774     ResourceResponse response;
    775     response.setMimeType(mimeType);
    776     response.setTextEncodingName(charset);
    777     response.setURL(handle->firstRequest().url());
    778 
    779     if (base64) {
    780         data = decodeURLEscapeSequences(data);
    781         handle->client()->didReceiveResponse(handle, WTFMove(response));
    782 
    783         // didReceiveResponse might cause the client to be deleted.
    784         if (handle->client()) {
    785             Vector<char> out;
    786             if (base64Decode(data, out, Base64IgnoreSpacesAndNewLines) && out.size() > 0)
    787                 handle->client()->didReceiveData(handle, out.data(), out.size(), 0);
    788         }
    789     } else {
    790         TextEncoding encoding(charset);
    791         data = decodeURLEscapeSequences(data, encoding);
    792         handle->client()->didReceiveResponse(handle, WTFMove(response));
    793 
    794         // didReceiveResponse might cause the client to be deleted.
    795         if (handle->client()) {
    796             CString encodedData = encoding.encode(data, URLEncodedEntitiesForUnencodables);
    797             if (encodedData.length())
    798                 handle->client()->didReceiveData(handle, encodedData.data(), encodedData.length(), 0);
    799         }
    800     }
    801 
    802     if (handle->client())
    803         handle->client()->didFinishLoading(handle);
    804 }
    805 
    806 void ResourceHandleManager::dispatchSynchronousJob(ResourceHandle* job)
    807 {
    808     URL kurl = job->firstRequest().url();
    809 
    810     if (kurl.protocolIsData()) {
    811         handleDataURL(job);
    812         return;
    813     }
    814 
    815     ResourceHandleInternal* handle = job->getInternal();
    816 
    817     // If defersLoading is true and we call curl_easy_perform
    818     // on a paused handle, libcURL would do the transfert anyway
    819     // and we would assert so force defersLoading to be false.
    820     handle->m_defersLoading = false;
    821 
    822     initializeHandle(job);
    823 
    824     // curl_easy_perform blocks until the transfert is finished.
    825     CURLcode ret =  curl_easy_perform(handle->m_handle);
    826 
    827     if (ret != CURLE_OK) {
    828         ResourceError error(ASCIILiteral(errorDomainCurl), ret, kurl, String(curl_easy_strerror(ret)));
    829         error.setSSLErrors(handle->m_sslErrors);
    830         handle->client()->didFail(job, error);
    831     } else {
    832         if (handle->client())
    833             handle->client()->didReceiveResponse(job, ResourceResponse(handle->m_response));
    834     }
    835 
    836 #if ENABLE(WEB_TIMING)
    837     calculateWebTimingInformations(handle);
    838 #endif
    839 
    840     curl_easy_cleanup(handle->m_handle);
    841 }
    842 
    843225void ResourceHandleManager::startJob(ResourceHandle* job)
    844226{
     
    846228
    847229    if (url.protocolIsData()) {
    848         handleDataURL(job);
     230        job->handleDataURL();
    849231        job->deref();
    850232        return;
    851233    }
    852234
    853     initializeHandle(job);
     235    job->initialize();
    854236
    855237    m_runningJobs++;
     
    866248}
    867249
    868 void ResourceHandleManager::applyAuthenticationToRequest(ResourceHandle* handle, ResourceRequest& request)
    869 {
    870     // m_user/m_pass are credentials given manually, for instance, by the arguments passed to XMLHttpRequest.open().
    871     ResourceHandleInternal* d = handle->getInternal();
    872 
    873     String partition = handle->firstRequest().cachePartition();
    874 
    875     if (handle->shouldUseCredentialStorage()) {
    876         if (d->m_user.isEmpty() && d->m_pass.isEmpty()) {
    877             // <rdar://problem/7174050> - For URLs that match the paths of those previously challenged for HTTP Basic authentication,
    878             // try and reuse the credential preemptively, as allowed by RFC 2617.
    879             d->m_initialCredential = CredentialStorage::defaultCredentialStorage().get(partition, request.url());
    880         } else {
    881             // If there is already a protection space known for the URL, update stored credentials
    882             // before sending a request. This makes it possible to implement logout by sending an
    883             // XMLHttpRequest with known incorrect credentials, and aborting it immediately (so that
    884             // an authentication dialog doesn't pop up).
    885             CredentialStorage::defaultCredentialStorage().set(partition, Credential(d->m_user, d->m_pass, CredentialPersistenceNone), request.url());
    886         }
    887     }
    888 
    889     String user = d->m_user;
    890     String password = d->m_pass;
    891 
    892     if (!d->m_initialCredential.isEmpty()) {
    893         user = d->m_initialCredential.user();
    894         password = d->m_initialCredential.password();
    895         curl_easy_setopt(d->m_handle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
    896     }
    897 
    898     // It seems we need to set CURLOPT_USERPWD even if username and password is empty.
    899     // Otherwise cURL will not automatically continue with a new request after a 401 response.
    900 
    901     // curl CURLOPT_USERPWD expects username:password
    902     String userpass = user + ":" + password;
    903     curl_easy_setopt(d->m_handle, CURLOPT_USERPWD, userpass.utf8().data());
    904 }
    905 
    906 void ResourceHandleManager::initializeHandle(ResourceHandle* job)
    907 {
    908     CurlContext& context = CurlContext::singleton();
    909 
    910     static const int allowedProtocols = CURLPROTO_FILE | CURLPROTO_FTP | CURLPROTO_FTPS | CURLPROTO_HTTP | CURLPROTO_HTTPS;
    911     URL url = job->firstRequest().url();
    912 
    913     // Remove any fragment part, otherwise curl will send it as part of the request.
    914     url.removeFragmentIdentifier();
    915 
    916     ResourceHandleInternal* d = job->getInternal();
    917     String urlString = url.string();
    918 
    919     if (url.isLocalFile()) {
    920         // Remove any query part sent to a local file.
    921         if (!url.query().isEmpty()) {
    922             // By setting the query to a null string it'll be removed.
    923             url.setQuery(String());
    924             urlString = url.string();
    925         }
    926         // Determine the MIME type based on the path.
    927         d->m_response.setMimeType(MIMETypeRegistry::getMIMETypeForPath(url));
    928     }
    929 
    930     d->m_handle = curl_easy_init();
    931 
    932     if (d->m_defersLoading) {
    933         CURLcode error = curl_easy_pause(d->m_handle, CURLPAUSE_ALL);
    934         // If we did not pause the handle, we would ASSERT in the
    935         // header callback. So just assert here.
    936         ASSERT_UNUSED(error, error == CURLE_OK);
    937     }
    938 #ifndef NDEBUG
    939     if (context.isVerbose())
    940         curl_easy_setopt(d->m_handle, CURLOPT_VERBOSE, 1);
    941     if (context.getLogFile())
    942         curl_easy_setopt(d->m_handle, CURLOPT_STDERR, context.getLogFile());
    943 #endif
    944     curl_easy_setopt(d->m_handle, CURLOPT_SSL_VERIFYPEER, 1L);
    945     curl_easy_setopt(d->m_handle, CURLOPT_SSL_VERIFYHOST, 2L);
    946     curl_easy_setopt(d->m_handle, CURLOPT_PRIVATE, job);
    947     curl_easy_setopt(d->m_handle, CURLOPT_ERRORBUFFER, m_curlErrorBuffer);
    948     curl_easy_setopt(d->m_handle, CURLOPT_WRITEFUNCTION, writeCallback);
    949     curl_easy_setopt(d->m_handle, CURLOPT_WRITEDATA, job);
    950     curl_easy_setopt(d->m_handle, CURLOPT_HEADERFUNCTION, headerCallback);
    951     curl_easy_setopt(d->m_handle, CURLOPT_WRITEHEADER, job);
    952     curl_easy_setopt(d->m_handle, CURLOPT_AUTOREFERER, 1);
    953     curl_easy_setopt(d->m_handle, CURLOPT_FOLLOWLOCATION, 1);
    954     curl_easy_setopt(d->m_handle, CURLOPT_MAXREDIRS, 10);
    955     curl_easy_setopt(d->m_handle, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
    956     curl_easy_setopt(d->m_handle, CURLOPT_SHARE, CurlContext::singleton().curlShareHandle());
    957     curl_easy_setopt(d->m_handle, CURLOPT_DNS_CACHE_TIMEOUT, 60 * 5); // 5 minutes
    958     curl_easy_setopt(d->m_handle, CURLOPT_PROTOCOLS, allowedProtocols);
    959     curl_easy_setopt(d->m_handle, CURLOPT_REDIR_PROTOCOLS, allowedProtocols);
    960     setSSLClientCertificate(job);
    961 
    962     if (context.shouldIgnoreSSLErrors())
    963         curl_easy_setopt(d->m_handle, CURLOPT_SSL_VERIFYPEER, false);
    964     else
    965         setSSLVerifyOptions(job);
    966 
    967     const char* certificate = context.getCertificatePath();
    968     if (certificate)
    969         curl_easy_setopt(d->m_handle, CURLOPT_CAINFO, certificate);
    970 
    971     // enable gzip and deflate through Accept-Encoding:
    972     curl_easy_setopt(d->m_handle, CURLOPT_ENCODING, "");
    973 
    974     // url must remain valid through the request
    975     ASSERT(!d->m_url);
    976 
    977     // url is in ASCII so latin1() will only convert it to char* without character translation.
    978     d->m_url = fastStrDup(urlString.latin1().data());
    979     curl_easy_setopt(d->m_handle, CURLOPT_URL, d->m_url);
    980 
    981     const char* cookieJar = context.getCookieJarFileName();
    982     if (cookieJar)
    983         curl_easy_setopt(d->m_handle, CURLOPT_COOKIEJAR, cookieJar);
    984 
    985     struct curl_slist* headers = 0;
    986     if (job->firstRequest().httpHeaderFields().size() > 0) {
    987         HTTPHeaderMap customHeaders = job->firstRequest().httpHeaderFields();
    988 
    989         bool hasCacheHeaders = customHeaders.contains(HTTPHeaderName::IfModifiedSince) || customHeaders.contains(HTTPHeaderName::IfNoneMatch);
    990         if (!hasCacheHeaders && CurlCacheManager::getInstance().isCached(url)) {
    991             CurlCacheManager::getInstance().addCacheEntryClient(url, job);
    992             HTTPHeaderMap& requestHeaders = CurlCacheManager::getInstance().requestHeaders(url);
    993 
    994             // append additional cache information
    995             HTTPHeaderMap::const_iterator it = requestHeaders.begin();
    996             HTTPHeaderMap::const_iterator end = requestHeaders.end();
    997             while (it != end) {
    998                 customHeaders.set(it->key, it->value);
    999                 ++it;
    1000             }
    1001             d->m_addedCacheValidationHeaders = true;
    1002         }
    1003 
    1004         HTTPHeaderMap::const_iterator end = customHeaders.end();
    1005         for (HTTPHeaderMap::const_iterator it = customHeaders.begin(); it != end; ++it) {
    1006             String key = it->key;
    1007             String value = it->value;
    1008             String headerString(key);
    1009             if (value.isEmpty())
    1010                 // Insert the ; to tell curl that this header has an empty value.
    1011                 headerString.append(";");
    1012             else {
    1013                 headerString.append(": ");
    1014                 headerString.append(value);
    1015             }
    1016             CString headerLatin1 = headerString.latin1();
    1017             headers = curl_slist_append(headers, headerLatin1.data());
    1018         }
    1019     }
    1020 
    1021     String method = job->firstRequest().httpMethod();
    1022     if ("GET" == method)
    1023         curl_easy_setopt(d->m_handle, CURLOPT_HTTPGET, TRUE);
    1024     else if ("POST" == method)
    1025         setupPOST(job, &headers);
    1026     else if ("PUT" == method)
    1027         setupPUT(job, &headers);
    1028     else if ("HEAD" == method)
    1029         curl_easy_setopt(d->m_handle, CURLOPT_NOBODY, TRUE);
    1030     else {
    1031         curl_easy_setopt(d->m_handle, CURLOPT_CUSTOMREQUEST, method.ascii().data());
    1032         setupPUT(job, &headers);
    1033     }
    1034 
    1035     if (headers) {
    1036         curl_easy_setopt(d->m_handle, CURLOPT_HTTPHEADER, headers);
    1037         d->m_customHeaders = headers;
    1038     }
    1039 
    1040     applyAuthenticationToRequest(job, job->firstRequest());
    1041 
    1042     // Set proxy options if we have them.
    1043     auto& proxy = CurlContext::singleton().proxyInfo();
    1044     if (proxy.type != CurlProxyType::Invalid) {
    1045         curl_easy_setopt(d->m_handle, CURLOPT_PROXY, proxy.url().utf8().data());
    1046         curl_easy_setopt(d->m_handle, CURLOPT_PROXYTYPE, proxy.type);
    1047     }
    1048 }
    1049 
    1050250void ResourceHandleManager::cancel(ResourceHandle* job)
    1051251{
  • trunk/Source/WebCore/platform/network/curl/ResourceHandleManager.h

    r218947 r218962  
    5151    void cancel(ResourceHandle*);
    5252
    53     void dispatchSynchronousJob(ResourceHandle*);
    54 
    55     void setupPOST(ResourceHandle*, struct curl_slist**);
    56     void setupPUT(ResourceHandle*, struct curl_slist**);
    57 
    5853private:
    5954    ResourceHandleManager();
     
    6459    void startJob(ResourceHandle*);
    6560    bool startScheduledJobs();
    66     void applyAuthenticationToRequest(ResourceHandle*, ResourceRequest&);
    67 
    68     void initializeHandle(ResourceHandle*);
    6961
    7062    Timer m_downloadTimer;
    7163    CURLM* m_curlMultiHandle;
    72     char m_curlErrorBuffer[CURL_ERROR_SIZE];
    7364    Vector<ResourceHandle*> m_resourceHandleList;
    7465    int m_runningJobs;
Note: See TracChangeset for help on using the changeset viewer.