Changeset 29423 in webkit


Ignore:
Timestamp:
Jan 11, 2008 5:36:02 PM (16 years ago)
Author:
alp@webkit.org
Message:

2008-01-11 Luca Bruno <lethalman88@gmail.com>

Reviewed by Alp Toker.

http://bugs.webkit.org/show_bug.cgi?id=16729
[cURL] Allow multiple files for upload

  • platform/network/ResourceHandleInternal.h: (WebCore::ResourceHandleInternal::ResourceHandleInternal):
  • platform/network/curl/ResourceHandleCurl.cpp: (WebCore::ResourceHandleInternal::~ResourceHandleInternal):
  • platform/network/curl/ResourceHandleManager.cpp: (WebCore::readCallback): added (WebCore::ResourceHandleManager::setupPOST): setup for streaming the POST (WebCore::ResourceHandleManager::startJob): (WebCore::ResourceHandleManager::cancel): revert the previous patch for regression
  • platform/network/curl/ResourceHandleManager.h:
Location:
trunk/WebCore
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/WebCore/ChangeLog

    r29422 r29423  
     12008-01-11  Luca Bruno  <lethalman88@gmail.com>
     2
     3        Reviewed by Alp Toker.
     4
     5        http://bugs.webkit.org/show_bug.cgi?id=16729
     6        [cURL] Allow multiple files for upload
     7
     8        * platform/network/ResourceHandleInternal.h:
     9        (WebCore::ResourceHandleInternal::ResourceHandleInternal):
     10        * platform/network/curl/ResourceHandleCurl.cpp:
     11        (WebCore::ResourceHandleInternal::~ResourceHandleInternal):
     12        * platform/network/curl/ResourceHandleManager.cpp:
     13        (WebCore::readCallback): added
     14        (WebCore::ResourceHandleManager::setupPOST): setup for streaming the POST
     15        (WebCore::ResourceHandleManager::startJob):
     16        (WebCore::ResourceHandleManager::cancel): revert the previous patch for regression
     17        * platform/network/curl/ResourceHandleManager.h:
     18
    1192008-01-11  Christian Dywan  <christian@imendio.com>
    220
  • trunk/WebCore/platform/network/ResourceHandleInternal.h

    r29222 r29423  
    9797            , m_handle(0)
    9898            , m_url(0)
    99             , m_fileName(0)
    10099            , m_customHeaders(0)
    101100            , m_cancelled(false)
     101            , m_file(0)
     102            , m_formDataElementIndex(0)
     103            , m_formDataElementDataOffset(0)
    102104#endif
    103105#if PLATFORM(QT)
     
    150152        CURL* m_handle;
    151153        char* m_url;
    152         char* m_fileName;
    153154        struct curl_slist* m_customHeaders;       
    154         Vector<char> m_postBytes;
    155155        ResourceResponse m_response;
    156156        bool m_cancelled;
     157
     158        FILE* m_file;
     159        size_t m_formDataElementIndex;
     160        size_t m_formDataElementDataOffset;
     161        Vector<char> m_postBytes;
    157162#endif
    158163#if PLATFORM(QT)
  • trunk/WebCore/platform/network/curl/ResourceHandleCurl.cpp

    r29352 r29423  
    3939{
    4040    free(m_url);
    41     free(m_fileName);
    4241    if (m_customHeaders)
    4342        curl_slist_free_all(m_customHeaders);
     43    if (m_file)
     44        fclose(m_file);
    4445}
    4546
  • trunk/WebCore/platform/network/curl/ResourceHandleManager.cpp

    r29366 r29423  
    3232
    3333#include "CString.h"
     34#include "FileSystem.h"
    3435#include "MIMETypeRegistry.h"
    3536#include "NotImplemented.h"
     
    192193}
    193194
     195/* This is called to obtain HTTP POST or PUT data.
     196   Iterate through FormData elements and upload files.
     197   Carefully respect the given buffer size and fill the rest of the data at the next calls.
     198*/
     199size_t readCallback(void* ptr, size_t size, size_t nmemb, void* data)
     200{
     201    ResourceHandle* job = static_cast<ResourceHandle*>(data);
     202    ResourceHandleInternal* d = job->getInternal();
     203    if (d->m_cancelled)
     204        return 0;
     205
     206    size_t sent = 0;
     207    size_t toSend = size * nmemb;
     208    if (!toSend)
     209        return 0;
     210
     211    Vector<FormDataElement> elements = job->request().httpBody()->elements();
     212    if (d->m_formDataElementIndex >= elements.size())
     213        return 0;
     214
     215    FormDataElement element = elements[d->m_formDataElementIndex];
     216
     217    if (element.m_type == FormDataElement::encodedFile) {
     218        if (!d->m_file)
     219            d->m_file = fopen(element.m_filename.utf8().data(), "rb");
     220
     221        if (!d->m_file) {
     222            // FIXME: show a user error?
     223#ifndef NDEBUG
     224            printf("Failed while trying to open %s for upload\n", element.m_filename.utf8().data());
     225#endif
     226            job->cancel();
     227            return 0;
     228        }
     229
     230        sent = fread(ptr, size, nmemb, d->m_file);
     231        if (!size && ferror(d->m_file)) {
     232            // FIXME: show a user error?
     233#ifndef NDEBUG
     234            printf("Failed while trying to read %s for upload\n", element.m_filename.utf8().data());
     235#endif
     236            job->cancel();
     237            return 0;
     238        }
     239        if (feof(d->m_file)) {
     240            fclose(d->m_file);
     241            d->m_file = 0;
     242            d->m_formDataElementIndex++;
     243        }
     244    } else {
     245        size_t elementSize = element.m_data.size() - d->m_formDataElementDataOffset;
     246        sent = elementSize > toSend ? toSend : elementSize;
     247        memcpy(ptr, element.m_data.data() + d->m_formDataElementDataOffset, sent);
     248        if (elementSize > sent)
     249            d->m_formDataElementDataOffset += sent;
     250        else {
     251            d->m_formDataElementDataOffset = 0;
     252            d->m_formDataElementIndex++;
     253        }
     254    }
     255
     256    return sent;
     257}
     258
    194259void ResourceHandleManager::downloadTimerCallback(Timer<ResourceHandleManager>* timer)
    195260{
     
    289354}
    290355
    291 void ResourceHandleManager::setupPUT(ResourceHandle*)
     356void ResourceHandleManager::setupPUT(ResourceHandle*, struct curl_slist**)
    292357{
    293358    notImplemented();
    294359}
    295360
    296 void ResourceHandleManager::setupPOST(ResourceHandle* job)
     361/* Calculate the length of the POST.
     362   Force chunked data transfer if size of files can't be obtained.
     363 */
     364void ResourceHandleManager::setupPOST(ResourceHandle* job, struct curl_slist** headers)
    297365{
    298366    ResourceHandleInternal* d = job->getInternal();
    299 
    300     job->request().httpBody()->flatten(d->m_postBytes);
    301     if (d->m_postBytes.size() != 0) {
    302         curl_easy_setopt(d->m_handle, CURLOPT_POST, TRUE);
    303         curl_easy_setopt(d->m_handle, CURLOPT_POSTFIELDSIZE, d->m_postBytes.size());
    304         curl_easy_setopt(d->m_handle, CURLOPT_POSTFIELDS, d->m_postBytes.data());
    305     }
    306 
    307367    Vector<FormDataElement> elements = job->request().httpBody()->elements();
    308     size_t size = elements.size();
    309     struct curl_httppost* lastItem = 0;
    310     struct curl_httppost* post = 0;
    311     for (size_t i = 0; i < size; i++) {
    312         if (elements[i].m_type != FormDataElement::encodedFile)
    313             continue;
    314         CString cstring = elements[i].m_filename.utf8();
    315         ASSERT(!d->m_fileName);
    316         d->m_fileName = strdup(cstring.data());
    317 
    318         // Fill in the file upload field
    319         curl_formadd(&post, &lastItem, CURLFORM_COPYNAME, "sendfile", CURLFORM_FILE, d->m_fileName, CURLFORM_END);
    320 
    321         // Fill in the filename field
    322         curl_formadd(&post, &lastItem, CURLFORM_COPYNAME, "filename", CURLFORM_COPYCONTENTS, d->m_fileName, CURLFORM_END);
    323 
    324         // FIXME: We should not add a "submit" field for each file uploaded. Review this code.
    325         // Fill in the submit field too, even if this is rarely needed
    326         curl_formadd(&post, &lastItem, CURLFORM_COPYNAME, "submit", CURLFORM_COPYCONTENTS, "send", CURLFORM_END);
    327 
    328         // FIXME: should we support more than one file?
    329         break;
    330     }
    331 
    332     if (post)
    333         curl_easy_setopt(d->m_handle, CURLOPT_HTTPPOST, post);
     368    size_t numElements = elements.size();
     369
     370    if (!numElements)
     371        return;
     372
     373    // Do not stream for simple POST data
     374    if (numElements == 1) {
     375        job->request().httpBody()->flatten(d->m_postBytes);
     376        if (d->m_postBytes.size() != 0) {
     377            curl_easy_setopt(d->m_handle, CURLOPT_POST, TRUE);
     378            curl_easy_setopt(d->m_handle, CURLOPT_POSTFIELDSIZE, d->m_postBytes.size());
     379            curl_easy_setopt(d->m_handle, CURLOPT_POSTFIELDS, d->m_postBytes.data());
     380        }
     381        return;
     382    }
     383
     384    // Obtain the total size of the POST
     385    static const long long maxCurlOffT = (1LL << (sizeof(curl_off_t) * 8 - 1)) - 1;
     386    curl_off_t size = 0;
     387    bool chunkedTransfer = false;
     388    for (size_t i = 0; i < numElements; i++) {
     389        FormDataElement element = elements[i];
     390        if (element.m_type == FormDataElement::encodedFile) {
     391            long long fileSizeResult;
     392            if (fileSize(element.m_filename, fileSizeResult)) {
     393                if (fileSizeResult > maxCurlOffT) {
     394                    // File size is too big for specifying it to cURL
     395                    chunkedTransfer = true;
     396                    break;
     397                }
     398                size += fileSizeResult;
     399            } else {
     400                chunkedTransfer = true;
     401                break;
     402            }
     403        } else
     404            size += elements[i].m_data.size();
     405    }
     406
     407    curl_easy_setopt(d->m_handle, CURLOPT_POST, TRUE);
     408
     409    // cURL guesses that we want chunked encoding as long as we specify the header
     410    if (chunkedTransfer)
     411        *headers = curl_slist_append(*headers, "Transfer-Encoding: chunked");
     412    else
     413        curl_easy_setopt(d->m_handle, CURLOPT_POSTFIELDSIZE_LARGE, size);
     414
     415    curl_easy_setopt(d->m_handle, CURLOPT_READFUNCTION, readCallback);
     416    curl_easy_setopt(d->m_handle, CURLOPT_READDATA, job);
    334417}
    335418
     
    346429{
    347430    int size = m_resourceHandleList.size();
    348     for (int i=0; i < size; i++) {
     431    for (int i = 0; i < size; i++) {
    349432        if (job == m_resourceHandleList[i]) {
    350433            m_resourceHandleList.remove(i);
     
    472555    }
    473556
     557    struct curl_slist* headers = 0;
    474558    if (job->request().httpHeaderFields().size() > 0) {
    475         struct curl_slist* headers = 0;
    476559        HTTPHeaderMap customHeaders = job->request().httpHeaderFields();
    477560        HTTPHeaderMap::const_iterator end = customHeaders.end();
     
    485568            headers = curl_slist_append(headers, headerLatin1.data());
    486569        }
    487         curl_easy_setopt(d->m_handle, CURLOPT_HTTPHEADER, headers);
    488         d->m_customHeaders = headers;
    489570    }
    490571
     
    492573        curl_easy_setopt(d->m_handle, CURLOPT_HTTPGET, TRUE);
    493574    else if ("POST" == job->request().httpMethod())
    494         setupPOST(job);
     575        setupPOST(job, &headers);
    495576    else if ("PUT" == job->request().httpMethod())
    496         setupPUT(job);
     577        setupPUT(job, &headers);
    497578    else if ("HEAD" == job->request().httpMethod())
    498579        curl_easy_setopt(d->m_handle, CURLOPT_NOBODY, TRUE);
     580
     581    if (headers) {
     582        curl_easy_setopt(d->m_handle, CURLOPT_HTTPHEADER, headers);
     583        d->m_customHeaders = headers;
     584    }
    499585
    500586    m_runningJobs++;
  • trunk/WebCore/platform/network/curl/ResourceHandleManager.h

    r28573 r29423  
    4545    void setCookieJarFileName(const char* cookieJarFileName);
    4646
    47     void setupPOST(ResourceHandle*);
    48     void setupPUT(ResourceHandle*);
     47    void setupPOST(ResourceHandle*, struct curl_slist**);
     48    void setupPUT(ResourceHandle*, struct curl_slist**);
    4949
    5050private:
Note: See TracChangeset for help on using the changeset viewer.