httpcurl.cpp

来自「khtml在gtk上的移植版本」· C++ 代码 · 共 549 行

CPP
549
字号
/* * Copyright (c) 2004 Nokia. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 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. * * Neither the name of Nokia nor the names of its contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 THE * COPYRIGHT OWNER 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. */#include <assert.h>#include <string.h>#include <glib.h>#include <curl/curl.h>#include "osb.h" // URLProtectionSpaec#include "HttpCurl.h"#include "GLibHelpers.h"class CurlRequest : public HttpRequest{    friend class CurlFactoryS;public:    CurlRequest(CurlFactoryS *p, const gchar * aurl, Type atype, CURLM* multi);    ~CurlRequest();    void execute();    void stop();    void error(int curlerror);    void data(const char* data, int len);    void header(const char* data, int len);    void finish();          void authenticate(const gchar * user, const gchar * pass);    void setProxy(const gchar * proxyURL);    void setPostData(const gchar * contentType, GByteArray *);   protected:    bool shouldAuthenticate() { return m_authenticate; }        /** Authenticates the request. */    void doAuth();        void cleanup();private:        CURLM *curlMultiHandle;	// not freed    CURL *handle;    gchar *password;    const gchar *curl_referrer;    struct HttpPost* curl_post;    struct HttpPost* curl_post_last;    GByteArray * postData;    bool headerEnd;    bool m_authenticate;    gchar * proxy;    CurlFactoryS* parent;};extern "C" {staticgbooleanevent_cb(gpointer user_data){    assert(user_data);    CurlFactoryS* self = static_cast<CurlFactoryS*>(user_data);    return self->event();}}static bool curlInitialized = false;CurlFactoryS::CurlFactoryS()    : proxy(0){    if (!curlInitialized) {	curl_global_init(CURL_GLOBAL_ALL);	curlInitialized = true;    }    curl_version_info_data *curl_info;    curl_info = curl_version_info(CURLVERSION_NOW);    curl_protocols = curl_info->protocols;    FD_ZERO(&fdread);    FD_ZERO(&fdwrite);    FD_ZERO(&fdexcep);    gsId = g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE,			       callbackTimeoutMS,  // milliseconds			       (GSourceFunc)& ::event_cb, //callback			       this, //data			       NULL); //notify    curlMultiHandle = curl_multi_init();}CurlFactoryS::~CurlFactoryS(){    if (gsId) {	GSource* s = g_main_context_find_source_by_id(NULL, gsId);	if (s) g_source_destroy(s);    }    if (curlMultiHandle)	curl_multi_cleanup(curlMultiHandle);    if (curlInitialized) {	curl_global_cleanup();	curlInitialized = false;    }    g_free(proxy);}void CurlFactoryS::setProxy(const gchar * proto, const gchar * proxyURL){    assignToString(&proxy, proxyURL);}bool CurlFactoryS::canProvide(const gchar * url) const{    int i = 0;    while(curl_protocols[i]) {	if (g_str_has_prefix(url, curl_protocols[i]))	    return true;	++i;    }        return false;}HttpRequest* CurlFactoryS::createRequest(HttpRequestListener* listener,                                         OSB::URLCredentialStorage* credentials,					 const gchar * url,					 HttpRequest::Type type){    if (!curlMultiHandle) {	return 0;    }    CurlRequest* req = new CurlRequest(this, url, type, curlMultiHandle);    req->setListener(listener);        if (proxy)	req->setProxy(proxy);    OSB::URLProtectionSpace space( url,  "",                                   OSB::URLProtectionSpace::Default,                                    OSB::URLProtectionSpace::NoProxy);        const OSB::URLCredential * cred = credentials->defaultCredential(space);    if (cred) {        req->authenticate(cred->user(), cred->password());    }        return req;}void CurlFactoryS::update(){    FD_ZERO(&fdread);    FD_ZERO(&fdwrite);    FD_ZERO(&fdexcep);    curl_multi_fdset(curlMultiHandle, &fdread, &fdwrite, &fdexcep, &maxfd);}/** timeout event callback *  @return FALSE if timeout is to be removed, *          TRUE if timeout is to be occur again */gboolean CurlFactoryS::event(){    update();    int nrunning;    struct timeval timeout;    int retval;    timeout.tv_sec = 0;    timeout.tv_usec = selectTimeoutMS * 1000;	// select waits microseconds    retval = ::select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);    switch(retval) {    case -1:			// select error#if DEBUG		g_printerr("%s, select error(?)",__PRETTY_FUNCTION__);#endif	/* fallthrough*/    case 0:			// select timeout	/* fallthrough. this can be the first perform to be made */    default: 			// 1+ descriptors have data	while(CURLM_CALL_MULTI_PERFORM == curl_multi_perform(curlMultiHandle, &nrunning));	break;    }    // check the curl messages indicating completed transfers    // and free their resources    CurlRequest* req;    CURLMsg* msg;    CURL *handle;    int nmsgs;    while ((msg = curl_multi_info_read(curlMultiHandle, &nmsgs)))    {	handle = msg->easy_handle;	if (msg->msg == CURLMSG_DONE) {	    	    // find the node which has same handle as completed transfer	    assert(handle);            	    curl_easy_getinfo(handle, CURLINFO_PRIVATE, &req);	    assert(req); //fixme: assert->if ?                        	    // if found, delete it	    if (req) {                switch (msg->data.result) {		case CURLE_OK:		{                    // use this to authenticate                              int respCode = -1;                    curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &respCode);                                if (respCode == HTTP_AUTH_REQUIRED &&                        req->shouldAuthenticate()) {                        // reset -- be careful not to call finish.                        // another thing also is to validate whether                        // data should be shown prior                         req->cleanup();                        // authenticate                        req->doAuth();                        // execute query again.                        req->execute();                    } else {                                 req->finish();                    }                    break;		}		default:		{		    req->error(msg->data.result);		    req->finish();		    break;		} 		}	    } else {#if DEBUG				g_printerr("CurlRequest not found, eventhough curl handle finished");#endif		assert(0);			    }	}    }    return TRUE;}CurlFactoryS& CurlFactory(){    static CurlFactoryS single;    return single;}extern "C" {static size_twrite_cb(void *ptr, size_t size, size_t nmemb, void *data){    assert(data);    int realsize = size * nmemb;    CurlRequest *req = static_cast<CurlRequest*> (data);    req->data((char*)ptr, realsize);    return realsize;}static size_theader_cb(char *ptr, size_t size, size_t nmemb, void* data){    assert(data);    int realsize = size * nmemb;    CurlRequest *req = static_cast<CurlRequest*> (data);    req->header((char*)ptr, realsize);    return realsize;}}CurlRequest::CurlRequest(CurlFactoryS* p, 			 const gchar * aurl, 			 HttpRequest::Type atype, 			 CURLM *multi)    :HttpRequest(aurl, atype)    , curlMultiHandle(multi)    , handle(NULL)    , password(NULL)    , curl_referrer(NULL)    , curl_post(NULL)    , curl_post_last(NULL)    , postData(NULL)    , headerEnd(false)    , m_authenticate(true)    , proxy(NULL)    , parent(p)    {    assert(curlMultiHandle);    assert(parent);}CurlRequest::~CurlRequest(){    cleanup();    g_byte_array_free(postData, true);    g_free(password);    g_free(proxy);    curlMultiHandle = 0;}void CurlRequest::cleanup(){    if (handle) {	curl_multi_remove_handle(curlMultiHandle, handle);	curl_easy_cleanup(handle);	handle = NULL;    }    }void CurlRequest::setProxy(const gchar * proxyURL){    if (handle) 	return;    assignToString(&proxy, proxyURL);    }void CurlRequest::setPostData(const gchar * contentType, GByteArray * data) {    if (strcmp(contentType, "application/x-www-form-urlencoded") ||        strncmp(contentType, "multipart/form-data", 19)) {        postData = g_byte_array_new();        memcpy( postData->data, data->data, data->len );        postData->len = data->len;    } else { #if DEBUG	g_printerr("Content type '%s' not supported", contentType);#endif	assert(0);    }}void CurlRequest::authenticate(const gchar * user, const gchar * pass){    if (handle)	return;    if (this->password)        g_free(this->password);    if (user && pass)        this->password = g_strdup_printf("%s:%s", user, pass);}void CurlRequest::doAuth() {    m_authenticate = listener->authenticate(this);}void CurlRequest::execute(){    if (handle)	return; 		// already running    handle = curl_easy_init();    curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, ::write_cb);    curl_easy_setopt(handle, CURLOPT_WRITEDATA, this);    curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, ::header_cb);    curl_easy_setopt(handle, CURLOPT_WRITEHEADER, this);    curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1);    curl_easy_setopt(handle, CURLOPT_MAXREDIRS, 10);    // url ptr must remain valid through the request    curl_easy_setopt(handle, CURLOPT_URL, m_url);     if (this->password) {        curl_easy_setopt(handle, CURLOPT_HTTPAUTH, CURLAUTH_ANY);        curl_easy_setopt(handle, CURLOPT_USERPWD, this->password);    }        // must remain valid or not ?    if (proxy)	curl_easy_setopt(handle, CURLOPT_PROXY, proxy);    curl_easy_setopt(handle, CURLOPT_PRIVATE, this);    if (type() == HttpRequest::POST) {	curl_easy_setopt(handle, CURLOPT_POSTFIELDS, postData->data);	curl_easy_setopt(handle, CURLOPT_POSTFIELDSIZE, postData->len);    }    started();    CURLMcode ret = curl_multi_add_handle(curlMultiHandle, handle);    if (ret == CURLM_CALL_MULTI_PERFORM) {	// don't call perform, because events must be async	// timeout will occur and do curl_multi_perform    } else if (ret == CURLM_OK) {    } else {	cleanup();	HttpRequest::error();    }}void CurlRequest::stop(){    cleanup();    listener->finished(this);}void CurlRequest::error(int curlerror){#if DEBUG    g_printerr("curl error while loading url:'%s' errorno:%d", m_url, curlerror);#endif		        HttpRequest::error();	}void CurlRequest::data(const char* data, int len){    if (!headerEnd) {	listener->headersEnd(this);	headerEnd = true;    }    // use this to check whether authentication is needed.     int status = -1;    curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &status);                    // if we are in middle of authenticating, do nothing.    if (status != HTTP_AUTH_REQUIRED || !m_authenticate) {        listener->data(this, data, len);    }}#define HTTP_LOCATION "Location"#define HTTP_CONTENT_TYPE "Content-Type"#define HTTP_CONTENT_LENGTH "Content-Length"#define HTTP_REFRESH "Http-Refresh"#define HTTP_WWW_AUTHENTICATE "WWW-Authenticate"HttpHeader* parseHeader(const gchar* data, int len){    int i=0;    while(i<len) {	if (data[i] == ':') break;	++i;    }    if (i==len) return 0;        // these are freed    gchar * key = g_strstrip(g_strndup(data, i));    gchar * value = g_strstrip(g_strndup(&data[i+1], len - i - 1));    HttpHeader * header;    if (!strcmp(key, HTTP_LOCATION)) {	header = new HttpHeaderLocation(value);    } else if (!strcmp(key, HTTP_CONTENT_TYPE)) {	header = new HttpHeaderContentType(value);    } else if (!strcmp(key, HTTP_CONTENT_LENGTH)) {	header = new HttpHeaderContentLength(value);    } else if (!strcmp(key, HTTP_REFRESH)) {        header = new HttpHeaderRefresh(value);    } else {        header = new HttpHeader(HttpHeader::Unknown, key, value);    }    g_free(key);    g_free(value);    return header;}void CurlRequest::header(const char* data, int len){    HttpHeader *hdr = parseHeader(data, len);    if (hdr) {        if ( hdr->key() == HTTP_WWW_AUTHENTICATE)        {            const gchar * value = hdr->value();            char * realm = g_strrstr(value, "realm=" );            m_authScheme = g_strstrip(g_strndup(value, realm - value));            m_authRealm = g_strstrip(g_strdup(realm+6));        }                     listener->header(this, hdr);	delete hdr;    }}void CurlRequest::finish(){    cleanup();    finished();}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?