Yes I rewrote the Curl code to use the new callback api's so you can
use select in the main thread.

Also the Pleyo people have done some work I've not fully integrated so
their work is worth looking at.

I ran into some issues and dropped back for the time being to a full
load on each call. Its a nice debugging feature anyway.

Also the latest curl uses asynchronous DNS resolution a huge win.

The biggest problem with Curl right now is that completion messages
still have to be polled for I was going to talk with the curl guys and
patch so you can register a callback.

Attached is my current curl the new stuff is turned off and I'm just loading.

Also this has some nice code in it esp for gtk that binds curl to the
gtk event loop.

http://www.gnomefiles.org/app.php/gCurl

If you read the code and think about the problem that the completion
messages get put on a queue inside curl
and you have to poll for them then you will see why I really want to
fix this in curl.

Also I seemed to be crashing inside curl sometimes with this turned on.


On Nov 12, 2007 12:11 AM, Alp Toker <[EMAIL PROTECTED]> wrote:
> Mike Emmel wrote:
> > Here is my autoconf build files
> >
> > They are for my current  projects but I think they could readily be
> > cleaned up to b used with the standard build.
> > I found that having a single Makefile did not incur any performance 
> > problems.
>
> Mike, just had a look over this and it's looking like a good start. Thanks!
>
> Was wondering, do you have any fixes to the Cairo graphics or CURL http
> backends in your tree, or anything that might be useful to WebKit upstream?
>
> If you provide your HTTP fixes, for example, I'll have more time to fix
> the remaining Cairo SVG bugs, which you can then pull back into your
> private branch, so everyone wins.
>
/*
 * Copyright (C) 2004, 2006 Apple Computer, Inc.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 */

#ifndef ResourceHandle_h
#define ResourceHandle_h

#include "AuthenticationChallenge.h"
#include "HTTPHeaderMap.h"
#include <wtf/OwnPtr.h>


#if PLATFORM(WIN)
typedef unsigned long DWORD;
typedef unsigned long DWORD_PTR;
typedef void* LPVOID;
typedef LPVOID HINTERNET;
typedef unsigned WPARAM;
typedef long LPARAM;
typedef struct HWND__* HWND;
typedef _W64 long LONG_PTR;
typedef LONG_PTR LRESULT;
#endif


#if PLATFORM(MAC)
#include <wtf/RetainPtr.h>
#ifdef __OBJC__
@class NSData;
@class NSError;
@class NSURLConnection;
@class WebCoreResourceHandleAsDelegate;
#else
class NSData;
class NSError;
class NSURLConnection;
class WebCoreResourceHandleAsDelegate;
typedef struct objc_object *id;
#endif
#endif

#if USE(CFNETWORK)
typedef struct _CFURLConnection* CFURLConnectionRef;
typedef int CFHTTPCookieStorageAcceptPolicy;
typedef struct OpaqueCFHTTPCookieStorage* CFHTTPCookieStorageRef;
#endif

#if USE(CURL)
typedef struct CURLMsg CURLMsg;
#endif

namespace WebCore {

class AuthenticationChallenge;
class Credential;
class FormData;
class Frame;
class KURL;
class ResourceError;
class ResourceHandleClient;
class ResourceHandleInternal;
class ResourceRequest;
class ResourceResponse;
class SharedBuffer;
class SubresourceLoader;
class SubresourceLoaderClient;

template <typename T> class Timer;

class ResourceHandle : public Shared<ResourceHandle> {
private:
    ResourceHandle(const ResourceRequest&, ResourceHandleClient*, bool defersLoading, bool mightDownloadFromHandle);

public:
    // FIXME: should not need the Frame
    static PassRefPtr<ResourceHandle> create(const ResourceRequest&, ResourceHandleClient*, Frame*, bool defersLoading, bool mightDownloadFromHandle = false);

    static void loadResourceSynchronously(const ResourceRequest&, ResourceError&, ResourceResponse&, Vector<char>& data);
    static bool willLoadFromCache(ResourceRequest&);
    
    ~ResourceHandle();

#if PLATFORM(MAC) || USE(CFNETWORK)
    void didReceiveAuthenticationChallenge(const AuthenticationChallenge&);
    void receivedCredential(const AuthenticationChallenge&, const Credential&);
    void receivedRequestToContinueWithoutCredential(const AuthenticationChallenge&);
    void receivedCancellation(const AuthenticationChallenge&);
#endif
#if PLATFORM(MAC)
    void didCancelAuthenticationChallenge(const AuthenticationChallenge&);
    NSURLConnection *connection() const;
    WebCoreResourceHandleAsDelegate *delegate();
    void releaseDelegate();
#elif USE(CFNETWORK)
    static CFRunLoopRef loaderRunLoop();
    CFURLConnectionRef connection() const;
    CFURLConnectionRef releaseConnectionForDownload();

    static CFHTTPCookieStorageAcceptPolicy cookieStorageAcceptPolicy();
    static void setCookieStorageAcceptPolicy(CFHTTPCookieStorageAcceptPolicy);

    static CFHTTPCookieStorageRef cookieStorage();
    static void setCookieStorage(CFHTTPCookieStorageRef);

    static void setHostAllowsAnyHTTPSCertificate(const String&);
#endif
    PassRefPtr<SharedBuffer> bufferedData();
    static bool supportsBufferedData();
    
#if PLATFORM(MAC)
    id releaseProxy();
#endif

#if USE(WININET)
    void setHasReceivedResponse(bool = true);
    bool hasReceivedResponse() const;
    void fileLoadTimer(Timer<ResourceHandle>*);
    void onHandleCreated(LPARAM);
    void onRequestRedirected(LPARAM);
    void onRequestComplete(LPARAM);
    friend void __stdcall transferJobStatusCallback(HINTERNET, DWORD_PTR, DWORD, LPVOID, DWORD);
    friend LRESULT __stdcall ResourceHandleWndProc(HWND, unsigned message, WPARAM, LPARAM);
#endif

#if USE(CURL)
public:
    static void setCookieJarFileName(const char* cookieJarFileName);
    static char* cookieJarFileName();
    size_t write(void* ptr, size_t size, size_t nmemb);
    size_t header(char* ptr, size_t size, size_t nmemb);
    void checkMessages();
    void processMessage(CURLMsg* msg);
private:
    static void initCURL();
    bool  setupHandle();
    void  setupPUT();
    void  setupPOST();
    void finish();
public:
#endif

#if PLATFORM(GDK) || PLATFORM(QT) || USE(CURL)
    ResourceHandleInternal* getInternal() { return d.get(); }
#endif

    // Used to work around the fact that you don't get any more NSURLConnection callbacks until you return from the one you're in.
    static bool loadsBlocked();    
    
    void clearAuthentication();
    void cancel();

    // The client may be 0, in which case no callbacks will be made.
    ResourceHandleClient* client() const;
    void setClient(ResourceHandleClient*);

    void setDefersLoading(bool);
      
    const ResourceRequest& request() const;

    void fireBlockedFailure(Timer<ResourceHandle>*);

private:
    static bool portAllowed(const ResourceRequest&);
    
    void scheduleBlockedFailure();

    bool start(Frame*);
        
    OwnPtr<ResourceHandleInternal> d;
};

}

#endif // ResourceHandle_h
// -*- mode: c++; c-basic-offset: 4 -*-
/*
 * Copyright (C) 2004, 2006 Apple Computer, Inc.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 */

#ifndef ResourceHandleInternal_h
#define ResourceHandleInternal_h

#include "ResourceRequest.h"
#include "AuthenticationChallenge.h"

#if USE(CFNETWORK)
#include <CFNetwork/CFURLConnectionPriv.h>
#endif

#if USE(WININET)
#include <winsock2.h>
#include <windows.h>
#include "Timer.h"
#endif

#if USE(CURL)
#include <curl/curl.h>
#include "wtf/Vector.h"
#include "CString.h"
#endif

#if PLATFORM(QT)
class QWebFrame;
class QWebNetworkJob;
#endif

#if PLATFORM(MAC)
#ifdef __OBJC__
@class NSURLConnection;
#else
class NSURLConnection;
#endif
#endif

// The allocations and releases in ResourceHandleInternal are
// Cocoa-exception-free (either simple Foundation classes or
// WebCoreResourceLoaderImp which avoids doing work in dealloc).

namespace WebCore {
    class ResourceHandleClient;

    class ResourceHandleInternal : Noncopyable {
    public:
        ResourceHandleInternal(ResourceHandle* loader, const ResourceRequest& request, ResourceHandleClient* c, bool defersLoading, bool mightDownloadFromHandle)
            : m_client(c)
            , m_request(request)
            , status(0)
            , m_defersLoading(defersLoading)
            , m_mightDownloadFromHandle(mightDownloadFromHandle)
#if USE(CFNETWORK)
            , m_connection(0)
#endif
#if USE(WININET)
            , m_fileHandle(INVALID_HANDLE_VALUE)
            , m_fileLoadTimer(loader, &ResourceHandle::fileLoadTimer)
            , m_resourceHandle(0)
            , m_secondaryHandle(0)
            , m_jobId(0)
            , m_threadId(0)
            , m_writing(false)
            , m_formDataString(0)
            , m_formDataLength(0)
            , m_bytesRemainingToWrite(0)
            , m_hasReceivedResponse(false)
            , m_resend(false)
#endif
#if USE(CURL)
            , m_curlMultiHandle(0)
            , m_handle(0)
            , m_url(0)
            , m_useSimple(false)
            , m_customHeaders(0)
            , m_customPostHeaders(0)
            , m_filePost(0)
            , m_sentResponse(false)
            , m_loading(false)
            , m_callback(0)
            , m_finished(0)
#endif
#if PLATFORM(QT)
            , m_job(0)
            , m_frame(0)
#endif
#if PLATFORM(MAC)
            , m_currentMacChallenge(nil)
#elif USE(CFNETWORK)
            , m_currentCFChallenge(0)
#endif
        {
        }
        
        ~ResourceHandleInternal();

        ResourceHandleClient* client() { return m_client; }
        ResourceHandleClient* m_client;
        
        ResourceRequest m_request;
        
        int status;

        bool m_defersLoading;
        bool m_mightDownloadFromHandle;
#if USE(CFNETWORK)
        RetainPtr<CFURLConnectionRef> m_connection;
#elif PLATFORM(MAC)
        RetainPtr<NSURLConnection> m_connection;
        RetainPtr<WebCoreResourceHandleAsDelegate> m_delegate;
        RetainPtr<id> m_proxy;
#endif
#if USE(WININET)
        HANDLE m_fileHandle;
        Timer<ResourceHandle> m_fileLoadTimer;
        HINTERNET m_resourceHandle;
        HINTERNET m_secondaryHandle;
        unsigned m_jobId;
        DWORD m_threadId;
        bool m_writing;
        char* m_formDataString;
        int m_formDataLength;
        int m_bytesRemainingToWrite;
        String m_postReferrer;
        bool m_hasReceivedResponse;
        bool m_resend;
#endif
#if USE(CURL)
    CURL* handle() { return m_handle; }
    void setHandle( CURL* a ) { m_handle = a; }
    const char* getURL() { return m_url; }
    void setURL(const char* url) { if (m_url) free(m_url); m_url = strdup(url); }
    void setHeaders(struct curl_slist* headers) { m_customHeaders = headers; }

    //TODO: group methods in a more coherent way
    bool loadsBlocked();
    void clearAuthentication();
    void cancel();
    PassRefPtr<WebCore::SharedBuffer> bufferedData();
    void setDefersLoading(bool defers);

    void  setupPUT();
    void  setupPOST();
    void  processMessage(CURLMsg* msg);
    size_t write(void* ptr, size_t size, size_t nmemb);
    size_t header(char* ptr, size_t size, size_t nmemb);

    CURLM* m_curlMultiHandle;
    CURL* m_handle;
    char* m_url;
    bool m_useSimple;
    struct curl_slist* m_customHeaders;
    struct curl_slist* m_customPostHeaders;
    struct curl_httppost* m_filePost;
    bool m_sentResponse;
    bool m_loading;
    void* m_callback;
    bool m_finished;
     /**
      * Stores entire response if job is m_useSimple
      */
    Vector<char> m_resultData;
    Vector<char> m_postBytes;
    Vector<CString> m_cstrings;
    ResourceResponse m_response;
    ResourceError m_error;
#endif

#if PLATFORM(QT)
        QWebNetworkJob *m_job;
        QWebFrame *m_frame;
#endif
#if PLATFORM(MAC)
        NSURLAuthenticationChallenge *m_currentMacChallenge;
#endif
#if USE(CFNETWORK)
        CFURLAuthChallengeRef m_currentCFChallenge;
#endif
        AuthenticationChallenge m_currentWebChallenge;
    };

} // namespace WebCore

#endif // ResourceHandleInternal_h
/**
 * @file ResourceHandleCurl.cpp *
 * BAL Implementation of transfer job, with curl
 */

#include "config.h"
#include "config.h"
#include "ResourceHandle.h"
#include "ResourceHandleClient.h"
#include "DocLoader.h"
#include "NotImplemented.h"
#include "ResourceHandleInternal.h"
#include "DeprecatedString.h"
#include "FormData.h"
#include "wtf/HashSet.h"
#include "PlatformString.h"
#include "CString.h"
#include "Frame.h"
#include "Page.h"
#include "wtf/Vector.h"
#include "MIMETypeRegistry.h"
#include <curl/curl.h>
#include <plugins/DOM/GPDOM.h>


namespace WebCore {


static char error_buffer[CURL_ERROR_SIZE];
static char* m_cookieJarFileName;
//static CURLM* m_curlMultiHandle; // FIXME: never freed
static CURLSH* m_curlShareHandle; // FIXME: never freed
static const double pollTimeSeconds = 0.0;
static const int selectTimeoutMS = 5;
static GPType* gpCallback;

struct HandleGPCallbackData {
    ResourceHandle* job;
    curl_socket_t s;
    int mask;
};



static void handleGPCallback( GPType* type, void* data)
{
    int handles = -1;
    dom_FileHandleDataSource_CallbackFunc* args = (dom_FileHandleDataSource_CallbackFunc*)data;
    HandleGPCallbackData* cd = (HandleGPCallbackData*)args->callData;
    assert(cd);
    ResourceHandleInternal* d = cd->job->getInternal();
//printf("===============>CURL handleGPCallback %p \n",data);
    /*FIXME: curl seems to not get all the work needed correct just do 
     * generic call
     */
#if 1
    // -1 used to just check messages
    if(cd->s != -1 ) {
        switch(cd->mask) {
            case CURL_CSELECT_OUT:
            {
                curl_multi_socket_action(d->m_curlMultiHandle, cd->s, CURL_CSELECT_OUT, &handles);
            }
            break;
            case CURL_CSELECT_IN:
            curl_multi_socket_action(d->m_curlMultiHandle, cd->s, CURL_CSELECT_IN, &handles);
            break;
            default:
            /*Pass 0 let curl decide what to do*/
            curl_multi_socket_action(d->m_curlMultiHandle, cd->s,0, &handles);
            break;
        }
    }else
        cd->job->checkMessages();
#endif
}

static size_t writeCallback(void* ptr, size_t size, size_t nmemb, void* obj)
{
    int handles = -1;
    ResourceHandle* job = static_cast<ResourceHandle*>(obj);
    assert(job);
    int res= job->write(ptr, size, nmemb);
    //Wakeup event queue to check for messages later
    dom_EventQueue_wakeup(dom_EventQueue_get());
    return res;
}

static size_t headerCallback(char* ptr, size_t size, size_t nmemb, void* obj)
{
    ResourceHandle* job = static_cast<ResourceHandle*>(obj);
    assert(job);
    int res= job->header(ptr, size, nmemb);
    //Wakeup event queue to check for messages later
    dom_EventQueue_wakeup(dom_EventQueue_get());
    return res;

}
/* this callback might very well be called multiple times for a single easy
   handle. Both to change the state of the 'what' but also to make it deal
   with more than one socket. */
static int socketCallback(CURL *easy,      /* easy handle */
                         curl_socket_t s, /* socket */
                         int action,        /* Why i was called */
                         void* userp, /* private pointer */
                         void *socketp)    /* "private" per socket pointer */
{
  CURLM* cm = userp;
  int handles = -1;
  ResourceHandle* job;
  curl_easy_getinfo(easy, CURLINFO_PRIVATE, &job);
  HandleGPCallbackData* cd = (HandleGPCallbackData*)socketp;
  if(!cd) {
    //FIXME: this ptr seems to be used after free/delete
    cd = (HandleGPCallbackData*)calloc(1,sizeof(HandleGPCallbackData));
    cd->job = job;
    cd->s = s;
  }
  switch (action) {
    case CURL_POLL_REMOVE:
    {
        curl_multi_assign(cm,s,0);
        cd = (HandleGPCallbackData*)dom_EventQueue_removeFileHandleDataSource(dom_EventQueue_get(),s);
        free(cd);
    }
    break;
    case CURL_POLL_NONE:
    break;
    case CURL_POLL_IN:
    {
        cd->mask = CURL_CSELECT_IN;
        curl_multi_assign(cm, s,cd);
        dom_EventQueue_addFileHandleDataSource(dom_EventQueue_get(),gpCallback,cd,s,GP_POLLIN);
    }
    break;
    case CURL_POLL_OUT:
    {
        cd->mask = CURL_CSELECT_OUT;
        curl_multi_assign(cm, s,cd);
        dom_EventQueue_addFileHandleDataSource(dom_EventQueue_get(),gpCallback,cd,s,GP_POLLOUT);
    }
    break;
    case CURL_POLL_INOUT:
    {
        cd->mask = CURL_CSELECT_IN | CURL_CSELECT_OUT;
        curl_multi_assign(cm, s,cd);
        dom_EventQueue_addFileHandleDataSource(dom_EventQueue_get(),gpCallback,cd,s,GP_POLLIN|GP_POLLOUT);
    }
    break;
    default:
    break;
  }
  return 0;
}

ResourceHandleInternal::~ResourceHandleInternal()
{
    dom_EventQueue_removeCallback(dom_EventQueue_get(),(GPCallback*)m_callback);
    free(((GPCallback*)m_callback)->data);
    free(m_callback);
    if(m_customHeaders)
        curl_slist_free_all(m_customHeaders);
    if(m_customPostHeaders)
        curl_slist_free_all(m_customPostHeaders);
    curl_multi_remove_handle(m_curlMultiHandle,m_handle);
    curl_easy_cleanup(m_handle);
    curl_multi_cleanup(m_curlMultiHandle);
    //debuging
    m_handle = 0;
}

void ResourceHandle::initCURL() {
    static bool initialized;
    if( initialized)
        return;
    m_cookieJarFileName = "/tmp/cookies-curl.txt";
    curl_global_init(CURL_GLOBAL_ALL);
    m_curlShareHandle = curl_share_init();
    curl_share_setopt(m_curlShareHandle, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE);
    curl_share_setopt(m_curlShareHandle, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
    GPRuntime_init();
    GPType_register("webkit.ResourceHandlerFunc(dom.FileHandleDataSource.CallbackFunc)");
    gpCallback = GPType_get("webkit.ResourceHandlerFunc");
    assert(gpCallback);
    gpCallback->function = handleGPCallback;
}

void ResourceHandle::setCookieJarFileName(const char* cookieJarFileName)
{
    if(m_cookieJarFileName)
        free(m_cookieJarFileName);
    m_cookieJarFileName = strdup(cookieJarFileName);
}

char* ResourceHandle::cookieJarFileName()
{ 
    return m_cookieJarFileName;
}

/**
 * check for messages 
 */
void ResourceHandle::checkMessages()
{
  int handles = 0;
  //immediate mode for testing
  //multi mode is crashing right now
 
  if(d->m_finished)
    return;
#if 1 
  if(true) {
    //printf("%p====================> ResourceHandle::checkMessages %s\n",this,d->m_url);
    curl_multi_remove_handle(d->m_curlMultiHandle,d->m_handle);
    curl_easy_perform(d->m_handle);
    //printf("DONE %p====================> ResourceHandle::checkMessages %s\n",this,d->m_url);
    finish();
    return;
  }
#endif

  while (CURLM_CALL_MULTI_PERFORM == curl_multi_socket_all(d->m_curlMultiHandle, &handles));
//printf("====================> ResourceHandle::checkMessages handles=%d\n",handles);
  int messagesInQueue = 0;
  // check the curl messages indicating completed transfers
  // and free their resources
   while (CURLMsg* msg = curl_multi_info_read(d->m_curlMultiHandle, &messagesInQueue)) {
        CURL* handle = msg->easy_handle;
//printf("====================> ResourceHandle::checkMessages queue=%d\n",messagesInQueue);
        assert(handle);
        ResourceHandle* job;
        curl_easy_getinfo(handle, CURLINFO_PRIVATE, &job);
        job->processMessage(msg);
   }
}

bool ResourceHandle::start(Frame* frame)
{
    int handles = 0;
    ASSERT(frame);
    ref();
    initCURL();
    if (!frame)
        return true;

    Page *page = frame->page();
    // If we are no longer attached to a Page, this must be an attempted load from an
    // onUnload handler, so let's just block it.
    if (!page)
        return true;

    d->m_curlMultiHandle = curl_multi_init();
    //curl_multi_setopt(d->m_curlMultiHandle,CURLMOPT_PIPELINING,1);
    curl_multi_setopt(d->m_curlMultiHandle, CURLMOPT_SOCKETFUNCTION, socketCallback);
    curl_multi_setopt(d->m_curlMultiHandle, CURLMOPT_SOCKETDATA,this);
    //run previous handles before mucking with multi    
    setupHandle();
    CURLMcode ret = curl_multi_add_handle(d->m_curlMultiHandle, d->m_handle);
    //make sure we run once since a transfer can finish and not add a socket 
    d->m_callback = calloc(1,sizeof(GPCallback));
    HandleGPCallbackData* messageCallbackData = (HandleGPCallbackData*)calloc(1,sizeof(HandleGPCallbackData));
    messageCallbackData->job = this;
    messageCallbackData->s = -1;
    ((GPCallback*)d->m_callback)->type = gpCallback;
    ((GPCallback*)d->m_callback)->data = messageCallbackData;
    dom_EventQueue_appendCallback(dom_EventQueue_get(),(GPCallback*)d->m_callback);
    dom_EventQueue_wakeup(dom_EventQueue_get());
    return false;
}

bool ResourceHandle::setupHandle() 
{
    // check for (probably) broken requests
    if (d->m_request.httpMethod() != "GET" && 
            d->m_request.httpMethod() != "POST" && 
            d->m_request.httpMethod() != "PUT") {
        notImplemented();
        return false;
    }
    KURL url = request().url();
    DeprecatedString surl = url.url();
    d->m_loading = true;
    d->m_response.setUrl(url);

    //remove any query part sent to a local file
    //allows http style get options to be handled by a local file
    if (request().url().isLocalFile()) {
        DeprecatedString query = url.query();
        if (!query.isEmpty()) {
            unsigned int idx = surl.find(query);
            surl = surl.left(idx);
        }
        d->m_response.setMimeType(MIMETypeRegistry::getMIMETypeForPath(String(surl)));
        d->m_response.setIsNull(false);
    }
    d->m_handle = curl_easy_init();
    // url ptr must remain valid through the request
    d->m_url =  strdup(surl.ascii());
    curl_easy_setopt(d->m_handle, CURLOPT_PRIVATE, this);
    curl_easy_setopt(d->m_handle, CURLOPT_ERRORBUFFER, error_buffer);
    curl_easy_setopt(d->m_handle, CURLOPT_WRITEFUNCTION, writeCallback);
    curl_easy_setopt(d->m_handle, CURLOPT_WRITEDATA, this);
    curl_easy_setopt(d->m_handle, CURLOPT_HEADERFUNCTION, headerCallback);
    curl_easy_setopt(d->m_handle, CURLOPT_WRITEHEADER, this);
    curl_easy_setopt(d->m_handle, CURLOPT_FOLLOWLOCATION, 1);
    curl_easy_setopt(d->m_handle, CURLOPT_MAXREDIRS, 10);
    curl_easy_setopt(d->m_handle, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
    // enable gzip and deflate through Accept-Encoding:
    curl_easy_setopt(d->m_handle, CURLOPT_ENCODING, "");
    curl_easy_setopt(d->m_handle, CURLOPT_URL, d->m_url);

    //xmlHttpRequest see xml/xmlhttprequest.cpp
    HTTPHeaderMap customHeaders =  d->m_request.httpHeaderFields();
    if (!customHeaders.isEmpty()) {
        struct curl_slist *slist=NULL;
        HTTPHeaderMap::const_iterator end = customHeaders.end();
        for (HTTPHeaderMap::const_iterator it = customHeaders.begin(); it != end; ++it) {
            String key = it->first;
            String value = it->second;
            String pair = key+": "+value;
            CString cstring = pair.utf8();
            //save for later cleanup
            d->m_cstrings.append(cstring);
            slist = curl_slist_append(slist, cstring.data());
        }
        curl_easy_setopt(d->m_handle, CURLOPT_HTTPHEADER, slist);
        d->m_customHeaders = slist;
    }
    //default to get
    curl_easy_setopt(d->m_handle, CURLOPT_HTTPGET, TRUE);
    if (d->m_request.httpMethod() == "POST")
        setupPOST();
    else if (d->m_request.httpMethod() == "PUT")
        setupPUT();
    else if (d->m_request.httpMethod() == "HEAD")
        curl_easy_setopt(d->m_handle, CURLOPT_NOBODY, TRUE);

    if (cookieJarFileName()) {
        curl_easy_setopt(d->m_handle, CURLOPT_COOKIEFILE, cookieJarFileName());
        curl_easy_setopt(d->m_handle, CURLOPT_COOKIEJAR, cookieJarFileName());
    }
}

ResourceHandle::~ResourceHandle()
{
//printf("ResourceHandle::~ResourceHandle ================ %s \n",d->m_url);    
}

void  ResourceHandle::setupPUT()
{
    if (d->m_request.httpMethod() != "PUT")
        return;
    curl_easy_setopt(d->m_handle, CURLOPT_UPLOAD, TRUE) ;
    struct curl_slist *slist=NULL;
    //FIXME: hmmm disable Expect: 100-continue
    //slist = curl_slist_append(slist,"Expect:");
    //just supprot Http 1.1 chuncked encoding now
    slist = curl_slist_append(slist, "Transfer-Encoding: chunked");
    curl_easy_setopt(d->m_handle, CURLOPT_HTTPHEADER, slist);
    d->m_customPostHeaders = slist;
}

void  ResourceHandle::setupPOST()
{
    if (d->m_request.httpMethod() != "POST")
        return;
    curl_easy_setopt(d->m_handle, CURLOPT_POST, true);
    //disable Expect: 100-continue
    {
        struct curl_slist *slist = NULL;
        slist = curl_slist_append(slist, "Expect:");
        curl_easy_setopt(d->m_handle, CURLOPT_HTTPHEADER, slist);
        d->m_customPostHeaders = slist;
    }

    //first add any data
    static CString data;
    data = request().httpBody()->flattenToString().latin1();
    
    d->m_request.setHTTPHeaderField("PropagateHttpHeader", "true");
    d->m_request.setHTTPContentType("Content-Type: application/x-www-form-urlencoded");
    if (data.length() != 0) {
        curl_easy_setopt(d->m_handle, CURLOPT_POSTFIELDS, data.data());
        curl_easy_setopt(d->m_handle, CURLOPT_POSTFIELDSIZE, data.length());
    }

    Vector<FormDataElement> elements = request().httpBody()->elements();
    int size = elements.size();
    struct curl_httppost *lastptr = NULL;

    for (int i = 0; i < size; i++) {
        if (elements[i].m_type == FormDataElement::encodedFile) {
            CString cstring =  elements[i].m_filename.utf8();
            //keep ref here so its not freed
            d->m_cstrings.append(cstring);
            const char *filename = cstring.data();

            /* Fill in the file upload field */
            curl_formadd(&d->m_filePost,
                         &lastptr,
                         CURLFORM_COPYNAME, "sendfile",
                         CURLFORM_FILE, filename,
                         CURLFORM_END);

            /* Fill in the filename field */
            curl_formadd(&d->m_filePost,
                         &lastptr,
                         CURLFORM_COPYNAME, "filename",
                         CURLFORM_COPYCONTENTS, filename,
                         CURLFORM_END);

            /* Fill in the submit field too, even if this is rarely needed */
            curl_formadd(&d->m_filePost,
                         &lastptr,
                         CURLFORM_COPYNAME, "submit",
                         CURLFORM_COPYCONTENTS, "send",
                         CURLFORM_END);
        }
    }
}


void ResourceHandle::cancel()
{
    //FIXME: send error on cancel ?
    //if(client())
    // client()->didFail(this,ResourceError());
    if (!d->m_sentResponse) {
        d->m_sentResponse = true;
    }
    finish();
    return;
}



size_t ResourceHandle::write(void* ptr, size_t size, size_t nmemb)
{
//printf("CURL writeCallback ================ %s \n",d->m_url);    
  int messagesInQueue = 0;
  // check the curl messages indicating completed transfers
  // and free their resources
//printf("=====================>write callback check for messages \n");
#if 0
   while (CURLMsg* msg = curl_multi_info_read(d->m_curlMultiHandle, &messagesInQueue)) {
        CURL* handle = msg->easy_handle;
        assert(handle);
        ResourceHandle* job;
        curl_easy_getinfo(handle, CURLINFO_PRIVATE, &job);
        job->processMessage(msg);
   }
#endif
    long code = 0;
    CURLcode res = curl_easy_getinfo(d->m_handle, CURLINFO_RESPONSE_CODE, &code);
    // avoid having redirection messages in the body ! see http://google.com/ for an example
	if (code >= 300 && code < 400) {
        return size*nmemb;
	}


    if (!d->m_sentResponse) {
        d->m_sentResponse = true;
        d->m_response.setIsNull(false);
        if(client())
            client()->didReceiveResponse(this, d->m_response);
    }

    int totalSize = size * nmemb;
    if(client())
        client()->didReceiveData(this, static_cast<char*> (ptr), totalSize, totalSize);

    if (d->m_useSimple) {
        d->m_resultData.append(static_cast<char*> (ptr), totalSize);
    }

    return totalSize;
}

size_t ResourceHandle::header(char* ptr, size_t size, size_t nmemb)
{
    d->m_response.setIsNull(false);
    int realsize = size * nmemb;
    String header = String(ptr, realsize);

    // Means 'end of header'
    if (realsize == 2 && header.contains('\n')) {
        d->m_sentResponse = true;
		d->m_response.setIsNull(false);
        if(client())
            client()->didReceiveResponse(this, d->m_response);
        return realsize;
    }

    int index = header.find(':');
    String name = header.substring(0, index + 1).stripWhiteSpace();
    String value = header.substring(index + 1).stripWhiteSpace();

    //FIXME: this should not be const
    const HTTPHeaderMap map = d->m_response.httpHeaderFields();
    const_cast<HTTPHeaderMap*> (&map)->add(name, value);

    // Reset CURLHeader and set status code
    if (header.contains("HTTP", true)) {
        String status = header.substring(9, realsize).left(3);
        d->m_response.setHTTPStatusCode(status.toInt());
        // Set URL for Response
        if (d->m_response.url().isEmpty()) {
            d->m_response.setUrl(KURL(d->m_url));
            int index = d->m_response.url().url().findRev('/');
            //use remove because right() method from deprecatedString seems to have a bug!
            String filename = d->m_response.url().url().remove(0, index + 1);
            if (!filename.isEmpty())
                d->m_response.setSuggestedFilename(filename);
            else {
                String defaultFilename = String("index.html");
                d->m_response.setSuggestedFilename(defaultFilename);
            }
        }
        return realsize;
    }

    // Set Mime type and eventually charset for Response
    if (header.contains("Content-Type: ", false)) {
        String content = header.substring(14, realsize);

        int ret = content.find("\r");
        if (ret >= 0)
            content.truncate(ret);

        int index = content.find(';');
        if (index >= 0) {
            String charset = content.substring(index + 1, content.length());
            int equal = charset.find("=");
            if (equal >= 0)
                charset.remove(0, equal + 1);
            d->m_response.setTextEncodingName(charset);

            content.truncate(index);
        }

        d->m_response.setMimeType(content);
        return realsize;
    }

    // Set expected length
    if (header.contains("Content-Length: ", false)) {
        String length = header.substring(16, realsize);
        d->m_response.setExpectedContentLength(length.toInt());
        return realsize;
    }

	// Set Location
    if (header.contains("Location: ", false)) {
        String location = header.substring(10, realsize);
        int ret = location.find("\r");
        if (ret >= 0)
            location.truncate(ret);

		d->m_request.setURL(location.deprecatedString());
		// a redirection occured, we must change the request or else we will
		// only have the main document with all dependencies messed up
		// mixing previous base url with new paths. It will end up with a
		// serie of 404 errors, so change the request now.
		if (client())
			client()->willSendRequest(this, d->m_request, /*const ResourceResponse& redirectResponse*/d->m_response);
        return realsize;
    }

    if (header.contains("Set-Cookie: ", false)) {
        int ret = header.find("\r");
        if (ret >= 0)
            header.truncate(ret);

        // Add url to cookies parameters. This url will be extracted from params in observe().
        //BAL::getBIObserverService()->notifyObserver(String("Set-Cookie"), header.substring(12, realsize)+"; URL="+getURL());
    }

    return realsize;
}

void  ResourceHandle::processMessage(CURLMsg* msg)
{
//printf("=======>ResourceHandle::processMessage %s:%s\n",curl_easy_strerror(msg->data.result),d->m_url);
    if(d->m_finished)
        return;
    if (msg->msg == CURLMSG_DONE) {
        // find the node which has same d->m_handle as completed transfer
        if (msg->data.result != CURLE_OK) {
            char *handle_url = 0;
            curl_easy_getinfo(d->m_handle, CURLINFO_EFFECTIVE_URL, &handle_url);
            const char *error =  curl_easy_strerror(msg->data.result);
            free(handle_url);
#ifndef NDEBUG
            //printf("ResourceHandle::processMessage %s \n",error);
#endif
            // Set error code to '1' as it is not a HTTP error.
            KURL url(d->m_url);
            ResourceError jobError(url.host(), 1, url.url(), String(error));
            //FIXME: make error descriptive
            if(client()) {
                client()->didFail(this, jobError); //didFail release resources.
                //Important return here since client is no longer valid
                //event though we hold a ref
            }
            deref();
            return;
        }
        finish();
    }
}

void  ResourceHandle::finish()
{
    //empty page if true
    //FIXME: should we really do this here on a cancelled page ?
    d->m_finished = true;
//printf("=================CURL finished %s \n",d->m_url);
    if (!d->m_sentResponse) {
        d->m_sentResponse = true;
        d->m_response.setIsNull(false);
        if(client())
            client()->didReceiveResponse(this, d->m_response);
    }
    if(client())
        client()->didFinishLoading(this);
    deref();
}


void ResourceHandle::loadResourceSynchronously(const ResourceRequest& request, ResourceError& error,
        ResourceResponse& response, Vector<char>& data)
{
    CURLcode res = CURLE_OK;
    RefPtr<ResourceHandle> job = ResourceHandle::create(request,0,0,false,true);
    ResourceHandleInternal* d = job->getInternal();
//printf("CURL loadResourceSynchronously ================ %s \n",d->m_url);    
    job->setupHandle();
    d->m_useSimple = true;
	if (d->m_handle) {
        curl_multi_remove_handle(d->m_curlMultiHandle,d->m_handle);
        res = curl_easy_perform(d->m_handle);
    }

    //FIXME: make error descriptive this may be broken
    if(res != CURLE_OK ) {
        error = ResourceError();
	}
    Vector<char> results = Vector<char> (d->m_resultData);
    d->m_resultData.clear();
    response = d->m_response;
    job = 0;
}

bool ResourceHandle::willLoadFromCache(ResourceRequest&)
{
    notImplemented();
    return false;
}

bool ResourceHandle::loadsBlocked()
{
    notImplemented();
    return false;
}

PassRefPtr<SharedBuffer> ResourceHandle::bufferedData()
{
    return 0;
}

bool ResourceHandle::supportsBufferedData()
{
    return false;
}

void ResourceHandle::setDefersLoading(bool defers)
{
    d->m_defersLoading = defers;
    notImplemented();
}

} // namespace BAL
// -*- mode: c++; c-basic-offset: 4 -*-
/*
 * Copyright (C) 2003, 2006 Apple Computer, Inc.  All rights reserved.
 * Copyright (C) 2006 Samuel Weinig <[EMAIL PROTECTED]>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 */

#ifndef ResourceRequest_h
#define ResourceRequest_h

#include "ResourceRequestBase.h"

namespace WebCore {

    struct ResourceRequest : ResourceRequestBase {

        ResourceRequest(const String& url) 
            : ResourceRequestBase(KURL(url.deprecatedString()), UseProtocolCachePolicy)
        {
        }

        ResourceRequest(const KURL& url) 
            : ResourceRequestBase(url, UseProtocolCachePolicy)
        {
        }

        ResourceRequest(const KURL& url, const String& referrer, ResourceRequestCachePolicy policy = UseProtocolCachePolicy) 
            : ResourceRequestBase(url, policy)
        {
            setHTTPReferrer(referrer);
        }
        
        ResourceRequest()
            : ResourceRequestBase(KURL(), UseProtocolCachePolicy)
        {
        }
        
    private:
        friend class ResourceRequestBase;

        void doUpdatePlatformRequest() {}
        void doUpdateResourceRequest() {}
    };

} // namespace WebCore

#endif // ResourceRequest_h
_______________________________________________
webkit-dev mailing list
webkit-dev@lists.webkit.org
http://lists.webkit.org/mailman/listinfo/webkit-dev

Reply via email to