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