/* * Copyright (c) 2005 Kimmo Kinnunen . All Rights Reserved. */ #include #include #include #include #include "HttpLibsoup.h" #include "osb.h" // URLProtectionSpace // trampolined callbacks from libsoup extern "C" { void LibsoupRequest__headersEnd(SoupMessage *req, gpointer user_data); void LibsoupRequest__chunkEnd(SoupMessage *req, gpointer user_data); void LibsoupRequest__requestEnd(SoupMessage *req, gpointer user_data); void LibsoupRequest__soupComplete(SoupMessage *req, gpointer user_data); } /* * LibsoupFactory */ const gchar* const LibsoupFactory::protocols[] = {"http", "https", 0}; /** * @return singleton factory object to use to create * Http Requests */ LibsoupFactory& getLibsoupFactory() { static LibsoupFactory single; return single; } class LibsoupRequest : public HttpRequest { friend class LibsoupFactory; public: LibsoupRequest(LibsoupFactory *aOwner, HttpRequestListener* aListener, const gchar* aUrl, const gchar* aCookies, Method atype, bool aIsSyncronized = false); ~LibsoupRequest(); 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 setPostData(const gchar* contentType, GByteArray *); SoupMessage* getSoupMessage() { return m_msg; } void setIsSynchronized(bool flag) { m_sync = flag; } public: // trampolined callbacks from libsoup void headersEnd(); void chunkEnd(); void requestEnd(); void soupComplete(); private: LibsoupFactory* m_owner; SoupSession* m_session; // not owned SoupMessage* m_msg; SoupUri* m_currentURI; bool m_sync; bool m_finished; }; LibsoupFactory::LibsoupFactory() { m_session = soup_session_async_new_with_options (NULL, NULL); m_syncSession = soup_session_sync_new_with_options (NULL, NULL); } LibsoupFactory::~LibsoupFactory() { g_object_unref(m_session); g_object_unref(m_syncSession); } void LibsoupFactory::setProxy(const gchar * proto, const gchar * proxyURL) { assert(proto); assert(proxyURL); if (g_ascii_strcasecmp(proto, "http") || g_ascii_strcasecmp(proto, "https")){ SoupUri* uri_proxy = soup_uri_new(proxyURL); if (uri_proxy) { GValue gv_proxy; g_value_init(&gv_proxy, G_TYPE_POINTER); g_value_set_pointer(&gv_proxy, uri_proxy); g_object_set_property(G_OBJECT(m_session), SOUP_SESSION_PROXY_URI, &gv_proxy); g_object_set_property(G_OBJECT(m_syncSession), SOUP_SESSION_PROXY_URI, &gv_proxy); soup_uri_free(uri_proxy); } } } bool LibsoupFactory::canProvide(const gchar* url) const { assert(url); for (int i = 0; protocols[i]; i++) { if (g_str_has_prefix(url, protocols[i])) return true; } return false; } HttpRequest* LibsoupFactory::createRequest(HttpRequestListener* listener, OSB::URLCredentialStorage* credentials, const gchar* url, const gchar* cookies, HttpRequest::Method method) { assert(credentials); assert(url); assert(cookies); assert(m_session); LibsoupRequest* req = new LibsoupRequest(this, listener, url, cookies, method); 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; } HttpRequest* LibsoupFactory::createSynchronizedRequest(HttpRequestListener* aListener, OSB::URLCredentialStorage* aCredentials, const gchar* aUrl, const gchar* aCookies, HttpRequest::Method aMethod) { assert(aCredentials); assert(aUrl); assert(aCookies); assert(m_syncSession); LibsoupRequest* req = new LibsoupRequest(this, aListener, aUrl, aCookies, aMethod, true); OSB::URLProtectionSpace space(aUrl, "", OSB::URLProtectionSpace::Default, OSB::URLProtectionSpace::NoProxy); const OSB::URLCredential* cred = aCredentials->defaultCredential(space); if (cred) { req->authenticate(cred->user(), cred->password()); } return req; } /* * trampoline functions for c <-> c++ communication */ extern "C" { void LibsoupRequest__headersEnd(SoupMessage *req, gpointer user_data) { assert(user_data); reinterpret_cast(user_data)->headersEnd(); } void LibsoupRequest__chunkEnd(SoupMessage *req, gpointer user_data) { assert(user_data); reinterpret_cast(user_data)->chunkEnd(); } void LibsoupRequest__requestEnd(SoupMessage *req, gpointer user_data) { assert(user_data); reinterpret_cast(user_data)->requestEnd(); } void LibsoupRequest__soupComplete(SoupMessage *req, gpointer user_data) { assert(user_data); reinterpret_cast(user_data)->soupComplete(); } } LibsoupRequest::LibsoupRequest(LibsoupFactory* aOwner, HttpRequestListener* aListener, const gchar* aUrl, const gchar* aCookies, HttpRequest::Method aType, bool aIsSynchronized) :HttpRequest(aListener, aUrl, aCookies, aType) ,m_owner(aOwner) ,m_sync(aIsSynchronized) ,m_finished(false) { const char* soupMethod = 0; switch(aType){ case HttpRequest::GET: soupMethod = SOUP_METHOD_GET; break; case HttpRequest::POST: soupMethod = SOUP_METHOD_POST; break; } m_currentURI = soup_uri_new(aUrl); m_msg = soup_message_new_from_uri(soupMethod, m_currentURI); g_object_ref(m_msg); soup_message_set_flags(m_msg, SOUP_MESSAGE_NO_REDIRECT | SOUP_MESSAGE_OVERWRITE_CHUNKS); soup_message_add_handler(m_msg, SOUP_HANDLER_PRE_BODY, LibsoupRequest__headersEnd, this); soup_message_add_handler(m_msg, SOUP_HANDLER_BODY_CHUNK, LibsoupRequest__chunkEnd, this); soup_message_add_handler(m_msg, SOUP_HANDLER_POST_BODY, LibsoupRequest__requestEnd, this); } LibsoupRequest::~LibsoupRequest() { stop(); g_object_unref(m_msg); soup_uri_free(m_currentURI); } void LibsoupRequest::setPostData(const gchar* contentType, GByteArray* data) { } void LibsoupRequest::authenticate(const gchar* user, const gchar* pass) { } void LibsoupRequest::execute() { if (m_agent) { soup_message_add_header(m_msg->request_headers, "User-Agent", m_agent); } m_listener->started(this); if (m_sync) m_session = m_owner->getSyncSession(); else m_session = m_owner->getSession(); soup_session_queue_message(m_session, m_msg, LibsoupRequest__soupComplete, this); } void LibsoupRequest::stop() { if (!m_finished) { soup_message_set_status(m_msg, SOUP_STATUS_CANCELLED); soup_session_cancel_message(m_owner->getSession(), m_msg); } } void LibsoupRequest::headersEnd() { const char* str; if ((SOUP_STATUS_IS_REDIRECTION (m_msg->status_code))) { } else { str = soup_message_get_header (m_msg->response_headers, "Content-Type"); if (str) { HttpHeaderContentType ct(str); m_listener->header(this, &ct); } str = soup_message_get_header (m_msg->response_headers, "Content-Length"); if (str){ HttpHeaderContentLength cl(str); m_listener->header(this, &cl); } str = soup_message_get_header (m_msg->response_headers, "Http-Refresh"); if (str){ HttpHeaderRefresh hr(str); m_listener->header(this, &hr); } str = soup_message_get_header (m_msg->response_headers, "Set-Cookie"); if (str) { HttpHeaderSetCookie sc(str); m_listener->header(this, &sc); } m_listener->headersEnd(this, m_msg->status_code); } } void LibsoupRequest::chunkEnd() { if (!SOUP_STATUS_IS_REDIRECTION (m_msg->status_code)) { m_listener->data(this, m_msg->response.body, m_msg->response.length); } } void LibsoupRequest::requestEnd() { } void LibsoupRequest::soupComplete() { if (m_msg->status_code == SOUP_STATUS_CANCELLED){ // do nothing, cancelled by us } else if (SOUP_STATUS_IS_REDIRECTION (m_msg->status_code)){ const char* str; str = soup_message_get_header(m_msg->response_headers, "Location"); if (str) { SoupUri* newURI = soup_uri_new_with_base (m_currentURI, str); if (newURI) { HttpHeaderLocation loc(str); m_listener->header(this, &loc); soup_message_set_uri (m_msg, newURI); soup_uri_free (m_currentURI); m_currentURI = newURI; soup_session_requeue_message(m_session, m_msg); } else { // error } } } else { m_finished = true; m_listener->finished(this); } }