00001 #include "cpr/session.h"
00002
00003 #include <algorithm>
00004 #include <functional>
00005 #include <string>
00006
00007 #include <curl/curl.h>
00008
00009 #include "cpr/curlholder.h"
00010 #include "cpr/util.h"
00011
00012 namespace cpr {
00013
00014 class Session::Impl {
00015 public:
00016 Impl();
00017
00018 void SetUrl(const Url& url);
00019 void SetParameters(const Parameters& parameters);
00020 void SetParameters(Parameters&& parameters);
00021 void SetHeader(const Header& header);
00022 void SetTimeout(const Timeout& timeout);
00023 void SetAuth(const Authentication& auth);
00024 void SetDigest(const Digest& auth);
00025 void SetPayload(Payload&& payload);
00026 void SetPayload(const Payload& payload);
00027 void SetProxies(Proxies&& proxies);
00028 void SetProxies(const Proxies& proxies);
00029 void SetMultipart(Multipart&& multipart);
00030 void SetMultipart(const Multipart& multipart);
00031 void SetRedirect(const bool& redirect);
00032 void SetMaxRedirects(const MaxRedirects& max_redirects);
00033 void SetCookies(const Cookies& cookies);
00034 void SetBody(Body&& body);
00035 void SetBody(const Body& body);
00036 void SetLowSpeed(const LowSpeed& low_speed);
00037 void SetVerifySsl(const VerifySsl& verify);
00038
00039 Response Delete();
00040 Response Get();
00041 Response Head();
00042 Response Options();
00043 Response Patch();
00044 Response Post();
00045 Response Put();
00046
00047 private:
00048 std::unique_ptr<CurlHolder, std::function<void(CurlHolder*)>> curl_;
00049 Url url_;
00050 Parameters parameters_;
00051 Proxies proxies_;
00052
00053 Response makeRequest(CURL* curl);
00054 static void freeHolder(CurlHolder* holder);
00055 static CurlHolder* newHolder();
00056 };
00057
00058 Session::Impl::Impl() {
00059 curl_ = std::unique_ptr<CurlHolder, std::function<void(CurlHolder*)>>(newHolder(),
00060 &Impl::freeHolder);
00061 auto curl = curl_->handle;
00062 if (curl) {
00063
00064 auto version_info = curl_version_info(CURLVERSION_NOW);
00065 auto version = std::string{"curl/"} + std::string{version_info->version};
00066 curl_easy_setopt(curl, CURLOPT_USERAGENT, version.data());
00067 curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
00068 curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L);
00069 curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 50L);
00070 curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_->error);
00071 curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "");
00072 #ifdef CPR_CURL_NOSIGNAL
00073 curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
00074 #endif
00075 #if LIBCURL_VERSION_MAJOR >= 7
00076 #if LIBCURL_VERSION_MINOR >= 25
00077 #if LIBCURL_VERSION_PATCH >= 0
00078 curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L);
00079 #endif
00080 #endif
00081 #endif
00082 }
00083 }
00084
00085 void Session::Impl::freeHolder(CurlHolder* holder) {
00086 curl_easy_cleanup(holder->handle);
00087 curl_slist_free_all(holder->chunk);
00088 curl_formfree(holder->formpost);
00089 delete holder;
00090 }
00091
00092 CurlHolder* Session::Impl::newHolder() {
00093 CurlHolder* holder = new CurlHolder();
00094 holder->handle = curl_easy_init();
00095 return holder;
00096 }
00097
00098 void Session::Impl::SetUrl(const Url& url) {
00099 url_ = url;
00100 }
00101
00102 void Session::Impl::SetParameters(const Parameters& parameters) {
00103 parameters_ = parameters;
00104 }
00105
00106 void Session::Impl::SetParameters(Parameters&& parameters) {
00107 parameters_ = std::move(parameters);
00108 }
00109
00110 void Session::Impl::SetHeader(const Header& header) {
00111 auto curl = curl_->handle;
00112 if (curl) {
00113 struct curl_slist* chunk = NULL;
00114 for (auto item = header.cbegin(); item != header.cend(); ++item) {
00115 auto header_string = std::string{item->first};
00116 if (item->second.empty()) {
00117 header_string += ";";
00118 } else {
00119 header_string += ": " + item->second;
00120 }
00121 chunk = curl_slist_append(chunk, header_string.data());
00122 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);
00123 curl_->chunk = chunk;
00124 }
00125 }
00126 }
00127
00128 void Session::Impl::SetTimeout(const Timeout& timeout) {
00129 auto curl = curl_->handle;
00130 if (curl) {
00131 curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, timeout.Milliseconds());
00132 }
00133 }
00134
00135 void Session::Impl::SetAuth(const Authentication& auth) {
00136 auto curl = curl_->handle;
00137 if (curl) {
00138 curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
00139 curl_easy_setopt(curl, CURLOPT_USERPWD, auth.GetAuthString());
00140 }
00141 }
00142
00143 void Session::Impl::SetDigest(const Digest& auth) {
00144 auto curl = curl_->handle;
00145 if (curl) {
00146 curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
00147 curl_easy_setopt(curl, CURLOPT_USERPWD, auth.GetAuthString());
00148 }
00149 }
00150
00151 void Session::Impl::SetPayload(Payload&& payload) {
00152 auto curl = curl_->handle;
00153 if (curl) {
00154 curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, payload.content.length());
00155 curl_easy_setopt(curl, CURLOPT_COPYPOSTFIELDS, payload.content.data());
00156 }
00157 }
00158
00159 void Session::Impl::SetPayload(const Payload& payload) {
00160 auto curl = curl_->handle;
00161 if (curl) {
00162 curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, payload.content.length());
00163 curl_easy_setopt(curl, CURLOPT_POSTFIELDS, payload.content.data());
00164 }
00165 }
00166
00167 void Session::Impl::SetProxies(const Proxies& proxies) {
00168 proxies_ = proxies;
00169 }
00170
00171 void Session::Impl::SetProxies(Proxies&& proxies) {
00172 proxies_ = std::move(proxies);
00173 }
00174
00175 void Session::Impl::SetMultipart(Multipart&& multipart) {
00176 auto curl = curl_->handle;
00177 if (curl) {
00178 struct curl_httppost* formpost = NULL;
00179 struct curl_httppost* lastptr = NULL;
00180
00181 for (auto& part : multipart.parts) {
00182 std::vector<struct curl_forms> formdata;
00183 formdata.push_back({CURLFORM_COPYNAME, part.name.data()});
00184 if (part.is_buffer) {
00185 formdata.push_back({CURLFORM_BUFFER, part.value.data()});
00186 formdata.push_back(
00187 {CURLFORM_COPYCONTENTS, reinterpret_cast<const char*>(part.data)});
00188 formdata.push_back(
00189 {CURLFORM_CONTENTSLENGTH, reinterpret_cast<const char*>(part.datalen)});
00190 } else if (part.is_file) {
00191 formdata.push_back({CURLFORM_FILE, part.value.data()});
00192 } else {
00193 formdata.push_back({CURLFORM_COPYCONTENTS, part.value.data()});
00194 }
00195 if (!part.content_type.empty()) {
00196 formdata.push_back({CURLFORM_CONTENTTYPE, part.content_type.data()});
00197 }
00198 formdata.push_back({CURLFORM_END, nullptr});
00199 curl_formadd(&formpost, &lastptr, CURLFORM_ARRAY, formdata.data(), CURLFORM_END);
00200 }
00201 curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
00202
00203 curl_formfree(curl_->formpost);
00204 curl_->formpost = formpost;
00205 }
00206 }
00207
00208 void Session::Impl::SetMultipart(const Multipart& multipart) {
00209 auto curl = curl_->handle;
00210 if (curl) {
00211 struct curl_httppost* formpost = NULL;
00212 struct curl_httppost* lastptr = NULL;
00213
00214 for (auto& part : multipart.parts) {
00215 std::vector<struct curl_forms> formdata;
00216 formdata.push_back({CURLFORM_PTRNAME, part.name.data()});
00217 if (part.is_buffer) {
00218 formdata.push_back({CURLFORM_BUFFER, part.value.data()});
00219 formdata.push_back({CURLFORM_BUFFERPTR, reinterpret_cast<const char*>(part.data)});
00220 formdata.push_back(
00221 {CURLFORM_BUFFERLENGTH, reinterpret_cast<const char*>(part.datalen)});
00222 } else if (part.is_file) {
00223 formdata.push_back({CURLFORM_FILE, part.value.data()});
00224 } else {
00225 formdata.push_back({CURLFORM_PTRCONTENTS, part.value.data()});
00226 }
00227 if (!part.content_type.empty()) {
00228 formdata.push_back({CURLFORM_CONTENTTYPE, part.content_type.data()});
00229 }
00230 formdata.push_back({CURLFORM_END, nullptr});
00231 curl_formadd(&formpost, &lastptr, CURLFORM_ARRAY, formdata.data(), CURLFORM_END);
00232 }
00233 curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
00234
00235 curl_formfree(curl_->formpost);
00236 curl_->formpost = formpost;
00237 }
00238 }
00239
00240 void Session::Impl::SetRedirect(const bool& redirect) {
00241 auto curl = curl_->handle;
00242 if (curl) {
00243 curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, std::int32_t(redirect));
00244 }
00245 }
00246
00247 void Session::Impl::SetMaxRedirects(const MaxRedirects& max_redirects) {
00248 auto curl = curl_->handle;
00249 if (curl) {
00250 curl_easy_setopt(curl, CURLOPT_MAXREDIRS, max_redirects.number_of_redirects);
00251 }
00252 }
00253
00254 void Session::Impl::SetCookies(const Cookies& cookies) {
00255 auto curl = curl_->handle;
00256 if (curl) {
00257 curl_easy_setopt(curl, CURLOPT_COOKIELIST, "ALL");
00258 curl_easy_setopt(curl, CURLOPT_COOKIE, cookies.GetEncoded().data());
00259 }
00260 }
00261
00262 void Session::Impl::SetBody(Body&& body) {
00263 auto curl = curl_->handle;
00264 if (curl) {
00265 curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, body.length());
00266 curl_easy_setopt(curl, CURLOPT_COPYPOSTFIELDS, body.data());
00267 }
00268 }
00269
00270 void Session::Impl::SetBody(const Body& body) {
00271 auto curl = curl_->handle;
00272 if (curl) {
00273 curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, body.length());
00274 curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.data());
00275 }
00276 }
00277
00278 void Session::Impl::SetLowSpeed(const LowSpeed& low_speed) {
00279 auto curl = curl_->handle;
00280 if (curl) {
00281 curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, low_speed.limit);
00282 curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, low_speed.time);
00283 }
00284 }
00285
00286 void Session::Impl::SetVerifySsl(const VerifySsl& verify) {
00287 auto curl = curl_->handle;
00288 if (curl) {
00289 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, verify ? 1L : 0L);
00290 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, verify ? 2L : 0L);
00291 }
00292 }
00293
00294 Response Session::Impl::Delete() {
00295 auto curl = curl_->handle;
00296 if (curl) {
00297 curl_easy_setopt(curl, CURLOPT_HTTPGET, 0L);
00298 curl_easy_setopt(curl, CURLOPT_NOBODY, 0L);
00299 curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
00300 }
00301
00302 return makeRequest(curl);
00303 }
00304
00305 Response Session::Impl::Get() {
00306 auto curl = curl_->handle;
00307 if (curl) {
00308 curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, NULL);
00309 curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
00310 curl_easy_setopt(curl, CURLOPT_POST, 0L);
00311 curl_easy_setopt(curl, CURLOPT_NOBODY, 0L);
00312 }
00313
00314 return makeRequest(curl);
00315 }
00316
00317 Response Session::Impl::Head() {
00318 auto curl = curl_->handle;
00319 if (curl) {
00320 curl_easy_setopt(curl, CURLOPT_HTTPGET, 0L);
00321 curl_easy_setopt(curl, CURLOPT_POST, 0L);
00322 curl_easy_setopt(curl, CURLOPT_NOBODY, 1L);
00323 }
00324
00325 return makeRequest(curl);
00326 }
00327
00328 Response Session::Impl::Options() {
00329 auto curl = curl_->handle;
00330 if (curl) {
00331 curl_easy_setopt(curl, CURLOPT_HTTPGET, 0L);
00332 curl_easy_setopt(curl, CURLOPT_POST, 0L);
00333 curl_easy_setopt(curl, CURLOPT_NOBODY, 0L);
00334 curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "OPTIONS");
00335 }
00336
00337 return makeRequest(curl);
00338 }
00339
00340 Response Session::Impl::Patch() {
00341 auto curl = curl_->handle;
00342 if (curl) {
00343 curl_easy_setopt(curl, CURLOPT_NOBODY, 0L);
00344 curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PATCH");
00345 }
00346
00347 return makeRequest(curl);
00348 }
00349
00350 Response Session::Impl::Post() {
00351 auto curl = curl_->handle;
00352 if (curl) {
00353 curl_easy_setopt(curl, CURLOPT_HTTPGET, 0L);
00354 curl_easy_setopt(curl, CURLOPT_NOBODY, 0L);
00355 }
00356
00357 return makeRequest(curl);
00358 }
00359
00360 Response Session::Impl::Put() {
00361 auto curl = curl_->handle;
00362 if (curl) {
00363 curl_easy_setopt(curl, CURLOPT_NOBODY, 0L);
00364 curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");
00365 }
00366
00367 return makeRequest(curl);
00368 }
00369
00370 Response Session::Impl::makeRequest(CURL* curl) {
00371 if (!parameters_.content.empty()) {
00372 Url new_url{url_ + "?" + parameters_.content};
00373 curl_easy_setopt(curl, CURLOPT_URL, new_url.data());
00374 } else {
00375 curl_easy_setopt(curl, CURLOPT_URL, url_.data());
00376 }
00377
00378 auto protocol = url_.substr(0, url_.find(':'));
00379 if (proxies_.has(protocol)) {
00380 curl_easy_setopt(curl, CURLOPT_PROXY, proxies_[protocol].data());
00381 } else {
00382 curl_easy_setopt(curl, CURLOPT_PROXY, "");
00383 }
00384
00385 curl_->error[0] = '\0';
00386
00387 std::string response_string;
00388 std::string header_string;
00389 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, cpr::util::writeFunction);
00390 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_string);
00391 curl_easy_setopt(curl, CURLOPT_HEADERDATA, &header_string);
00392
00393 auto curl_error = curl_easy_perform(curl);
00394
00395 char* raw_url;
00396 long response_code;
00397 double elapsed;
00398 curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
00399 curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &elapsed);
00400 curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &raw_url);
00401
00402 Error error(curl_error, curl_->error);
00403
00404 Cookies cookies;
00405 struct curl_slist* raw_cookies;
00406 curl_easy_getinfo(curl, CURLINFO_COOKIELIST, &raw_cookies);
00407 for (struct curl_slist* nc = raw_cookies; nc; nc = nc->next) {
00408 auto tokens = cpr::util::split(nc->data, '\t');
00409 auto value = tokens.back();
00410 tokens.pop_back();
00411 cookies[tokens.back()] = value;
00412 }
00413 curl_slist_free_all(raw_cookies);
00414
00415 auto header = cpr::util::parseHeader(header_string);
00416 return Response{static_cast<std::int32_t>(response_code),
00417 response_string,
00418 header,
00419 raw_url,
00420 elapsed,
00421 cookies,
00422 error};
00423 }
00424
00425
00426 Session::Session() : pimpl_{ new Impl{} } {}
00427 Session::~Session() {}
00428 void Session::SetUrl(const Url& url) { pimpl_->SetUrl(url); }
00429 void Session::SetParameters(const Parameters& parameters) { pimpl_->SetParameters(parameters); }
00430 void Session::SetParameters(Parameters&& parameters) { pimpl_->SetParameters(std::move(parameters)); }
00431 void Session::SetHeader(const Header& header) { pimpl_->SetHeader(header); }
00432 void Session::SetTimeout(const Timeout& timeout) { pimpl_->SetTimeout(timeout); }
00433 void Session::SetAuth(const Authentication& auth) { pimpl_->SetAuth(auth); }
00434 void Session::SetDigest(const Digest& auth) { pimpl_->SetDigest(auth); }
00435 void Session::SetPayload(const Payload& payload) { pimpl_->SetPayload(payload); }
00436 void Session::SetPayload(Payload&& payload) { pimpl_->SetPayload(std::move(payload)); }
00437 void Session::SetProxies(const Proxies& proxies) { pimpl_->SetProxies(proxies); }
00438 void Session::SetProxies(Proxies&& proxies) { pimpl_->SetProxies(std::move(proxies)); }
00439 void Session::SetMultipart(const Multipart& multipart) { pimpl_->SetMultipart(multipart); }
00440 void Session::SetMultipart(Multipart&& multipart) { pimpl_->SetMultipart(std::move(multipart)); }
00441 void Session::SetRedirect(const bool& redirect) { pimpl_->SetRedirect(redirect); }
00442 void Session::SetMaxRedirects(const MaxRedirects& max_redirects) { pimpl_->SetMaxRedirects(max_redirects); }
00443 void Session::SetCookies(const Cookies& cookies) { pimpl_->SetCookies(cookies); }
00444 void Session::SetBody(const Body& body) { pimpl_->SetBody(body); }
00445 void Session::SetBody(Body&& body) { pimpl_->SetBody(std::move(body)); }
00446 void Session::SetLowSpeed(const LowSpeed& low_speed) { pimpl_->SetLowSpeed(low_speed); }
00447 void Session::SetVerifySsl(const VerifySsl& verify) { pimpl_->SetVerifySsl(verify); }
00448 void Session::SetOption(const Url& url) { pimpl_->SetUrl(url); }
00449 void Session::SetOption(const Parameters& parameters) { pimpl_->SetParameters(parameters); }
00450 void Session::SetOption(Parameters&& parameters) { pimpl_->SetParameters(std::move(parameters)); }
00451 void Session::SetOption(const Header& header) { pimpl_->SetHeader(header); }
00452 void Session::SetOption(const Timeout& timeout) { pimpl_->SetTimeout(timeout); }
00453 void Session::SetOption(const Authentication& auth) { pimpl_->SetAuth(auth); }
00454 void Session::SetOption(const Digest& auth) { pimpl_->SetDigest(auth); }
00455 void Session::SetOption(const Payload& payload) { pimpl_->SetPayload(payload); }
00456 void Session::SetOption(Payload&& payload) { pimpl_->SetPayload(std::move(payload)); }
00457 void Session::SetOption(const Proxies& proxies) { pimpl_->SetProxies(proxies); }
00458 void Session::SetOption(Proxies&& proxies) { pimpl_->SetProxies(std::move(proxies)); }
00459 void Session::SetOption(const Multipart& multipart) { pimpl_->SetMultipart(multipart); }
00460 void Session::SetOption(Multipart&& multipart) { pimpl_->SetMultipart(std::move(multipart)); }
00461 void Session::SetOption(const bool& redirect) { pimpl_->SetRedirect(redirect); }
00462 void Session::SetOption(const MaxRedirects& max_redirects) { pimpl_->SetMaxRedirects(max_redirects); }
00463 void Session::SetOption(const Cookies& cookies) { pimpl_->SetCookies(cookies); }
00464 void Session::SetOption(const Body& body) { pimpl_->SetBody(body); }
00465 void Session::SetOption(Body&& body) { pimpl_->SetBody(std::move(body)); }
00466 void Session::SetOption(const LowSpeed& low_speed) { pimpl_->SetLowSpeed(low_speed); }
00467 void Session::SetOption(const VerifySsl& verify) { pimpl_->SetVerifySsl(verify); }
00468 Response Session::Delete() { return pimpl_->Delete(); }
00469 Response Session::Get() { return pimpl_->Get(); }
00470 Response Session::Head() { return pimpl_->Head(); }
00471 Response Session::Options() { return pimpl_->Options(); }
00472 Response Session::Patch() { return pimpl_->Patch(); }
00473 Response Session::Post() { return pimpl_->Post(); }
00474 Response Session::Put() { return pimpl_->Put(); }
00475
00476
00477 }