diff --git a/Makefile.am b/Makefile.am index 5cba018d..fc3f2353 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,5 +1,5 @@ ACLOCAL_AMFLAGS=-I m4 -CPPFLAGS=-Iinclude +CPPFLAGS=-Iinclude -std=c++11 check_PROGRAMS = test-program pkginclude_HEADERS = include/restclient-cpp/restclient.h include/restclient-cpp/version.h include/restclient-cpp/connection.h include/restclient-cpp/helpers.h BUILT_SOURCES = include/restclient-cpp/version.h diff --git a/include/restclient-cpp/connection.h b/include/restclient-cpp/connection.h index 087b6b4f..d04bf64a 100644 --- a/include/restclient-cpp/connection.h +++ b/include/restclient-cpp/connection.h @@ -22,6 +22,8 @@ */ namespace RestClient { +typedef std::map PostData; + /** * @brief Connection object for advanced usage */ @@ -65,6 +67,7 @@ class Connection { double preTransferTime; double startTransferTime; double redirectTime; + std::string effectiveUrl; int redirectCount; } RequestInfo; /** @@ -135,6 +138,12 @@ class Connection { // set to not use signals void SetNoSignal(bool no); + // set whether the peer certificate should be validated + void SetPeerVerify(bool peerVerify); + + // set whether the host certificate should be validated + void SetHostVerify(bool hostVerify); + // set whether to follow redirects void FollowRedirects(bool follow); @@ -180,11 +189,22 @@ class Connection { RestClient::Response get(const std::string& uri); RestClient::Response post(const std::string& uri, const std::string& data); + RestClient::Response post(const std::string& url, + const RestClient::PostData& data); RestClient::Response put(const std::string& uri, const std::string& data); RestClient::Response del(const std::string& uri); RestClient::Response head(const std::string& uri); + RestClient::Response getLastResponse(); + + // Cookie handler methods + void setCookies(const Cookies& cookies); + const Cookies& getCookies()const; + void setCookie(std::string key, std::string value); + std::string getCookie(std::string key)const; + void clearCookies(); + private: CURL* curlHandle; std::string baseUrl; @@ -192,6 +212,8 @@ class Connection { int timeout; bool followRedirects; bool noSignal; + bool hostVerify; + bool peerVerify; struct { std::string username; std::string password; @@ -205,6 +227,8 @@ class Connection { std::string keyPassword; std::string uriProxy; RestClient::Response performCurlRequest(const std::string& uri); + RestClient::Response lastResponse; + Cookies cookies; }; }; // namespace RestClient diff --git a/include/restclient-cpp/restclient.h b/include/restclient-cpp/restclient.h index 75dc7c6e..e3c2195e 100644 --- a/include/restclient-cpp/restclient.h +++ b/include/restclient-cpp/restclient.h @@ -25,6 +25,7 @@ namespace RestClient { * public data definitions */ typedef std::map HeaderFields; +typedef std::map Cookies; /** @struct Response * @brief This structure represents the HTTP response data @@ -39,6 +40,7 @@ typedef struct { int code; std::string body; HeaderFields headers; + Cookies cookies; } Response; // init and disable functions diff --git a/source/connection.cc b/source/connection.cc index e005ed7a..397a01e2 100644 --- a/source/connection.cc +++ b/source/connection.cc @@ -19,6 +19,26 @@ #include "restclient-cpp/helpers.h" #include "restclient-cpp/version.h" + +static std::string cookies_to_string(const RestClient::Cookies& cookies){ + + std::string retv; + for(auto entry:cookies){ + retv += entry.first + "=" + entry.second + "; "; + } + return retv; +} + +static std::string postdata_to_string(CURL* curlHandle, const RestClient::PostData& data){ + std::string retv; + for(auto entry:data){ + char* value = curl_easy_escape(curlHandle, entry.second.c_str(), entry.second.size()); + retv += entry.first + "=" + value + "&"; + curl_free(value); + } + return retv; +} + /** * @brief constructor for the Connection object * @@ -26,7 +46,7 @@ * */ RestClient::Connection::Connection(const std::string& baseUrl) - : lastRequest(), headerFields() { + :hostVerify(true),peerVerify(true) { this->curlHandle = curl_easy_init(); if (!this->curlHandle) { throw std::runtime_error("Couldn't initialize curl handle"); @@ -183,6 +203,14 @@ RestClient::Connection::SetNoSignal(bool no) { this->noSignal = no; } +void RestClient::Connection::SetPeerVerify(bool peerVerify) { + this->peerVerify = peerVerify; +} + +void RestClient::Connection::SetHostVerify(bool hostVerify) { + this->hostVerify = hostVerify; +} + /** * @brief set username and password for basic auth * @@ -282,11 +310,13 @@ RestClient::Response RestClient::Connection::performCurlRequest(const std::string& uri) { // init return type RestClient::Response ret = {}; + curl_easy_setopt(this->curlHandle, CURLOPT_COOKIEFILE, ""); std::string url = std::string(this->baseUrl + uri); std::string headerString; CURLcode res = CURLE_OK; curl_slist* headerList = NULL; + curl_slist* cookieList = NULL; /** set query URL */ curl_easy_setopt(this->curlHandle, CURLOPT_URL, url.c_str()); @@ -311,6 +341,14 @@ RestClient::Connection::performCurlRequest(const std::string& uri) { curl_easy_setopt(this->curlHandle, CURLOPT_HTTPHEADER, headerList); + if (!this->hostVerify) { + curl_easy_setopt(this->curlHandle, CURLOPT_SSL_VERIFYHOST, 0L); + } + + if (!this->peerVerify) { + curl_easy_setopt(this->curlHandle, CURLOPT_SSL_VERIFYPEER, 0L); + } + // set basic auth if configured if (this->basicAuth.username.length() > 0) { std::string authString = std::string(this->basicAuth.username + ":" + @@ -374,6 +412,12 @@ RestClient::Connection::performCurlRequest(const std::string& uri) { 1L); } + // set cookies + if(cookies.empty() == false){ + std::string cookie_data = cookies_to_string(cookies); + curl_easy_setopt(this->curlHandle, CURLOPT_COOKIE, cookie_data.c_str()); + } + res = curl_easy_perform(this->curlHandle); if (res != CURLE_OK) { switch (res) { @@ -411,11 +455,34 @@ RestClient::Connection::performCurlRequest(const std::string& uri) { &this->lastRequest.redirectTime); curl_easy_getinfo(this->curlHandle, CURLINFO_REDIRECT_COUNT, &this->lastRequest.redirectCount); + char* effectiveUrl; + curl_easy_getinfo(this->curlHandle, CURLINFO_EFFECTIVE_URL, + &effectiveUrl); + lastRequest.effectiveUrl = effectiveUrl; + // free header list curl_slist_free_all(headerList); // reset curl handle curl_easy_reset(this->curlHandle); - return ret; + + struct curl_slist* cookies; + curl_easy_getinfo(this->curlHandle, CURLINFO_COOKIELIST, &cookies); + struct curl_slist* i = cookies; + while (i) { + std::string c = i->data; + std::size_t pos1 = c.find('\t', c.find('\t', c.find('\t', c.find('\t', c.find('\t') + 1) + 1) + 1) + 1); + std::size_t pos2 = c.find('\t', pos1 + 1); + ret.cookies.insert(std::pair(c.substr(pos1 + 1, pos2 - pos1 - 1), c.substr(pos2 + 1))); + i = i->next; + } + curl_slist_free_all(cookies); + + this->lastResponse = ret; + return ret; +} + +RestClient::Response RestClient::Connection::getLastResponse() { + return this->lastResponse; } /** @@ -448,6 +515,22 @@ RestClient::Connection::post(const std::string& url, return this->performCurlRequest(url); } + + +RestClient::Response +RestClient::Connection::post(const std::string& url, + const RestClient::PostData& data) { + + std::string pdata = postdata_to_string(this->curlHandle, data); + + /** Now specify we want to POST data */ + curl_easy_setopt(this->curlHandle, CURLOPT_POST, 1L); + /** set post fields */ + curl_easy_setopt(this->curlHandle, CURLOPT_POSTFIELDS, pdata.c_str()); + curl_easy_setopt(this->curlHandle, CURLOPT_POSTFIELDSIZE, pdata.size()); + + return this->performCurlRequest(url); +} /** * @brief HTTP PUT method * @@ -514,3 +597,25 @@ RestClient::Connection::head(const std::string& url) { return this->performCurlRequest(url); } + + +// Cookie handler methods +void RestClient::Connection::setCookies(const Cookies& cookies){ + this->cookies = cookies; +} + +const RestClient::Cookies& RestClient::Connection::getCookies()const{ + return cookies; +} + +void RestClient::Connection::setCookie(std::string key, std::string value){ + cookies[key] = value; +} + +std::string RestClient::Connection::getCookie(std::string key)const{ + return cookies.at(key); +} + +void RestClient::Connection::clearCookies(){ + this->cookies.clear(); +}