// (C) Copyright 2015 by Autodesk, Inc. //============================================================================= // // namespace COMISO::cURLpp IMPLEMENTATION // //============================================================================= #include "cURLpp.hh" #if COMISO_DOCLOUD_AVAILABLE #include "CoMISo/Utils/CoMISoError.hh" #include #include #include DEB_module("cURLpp") namespace COMISO { namespace cURLpp { ////////////////////////////////////////////////////////////////////////// // Session Session::Session() { curl_global_init(CURL_GLOBAL_DEFAULT); } Session::~Session() { curl_global_cleanup(); } ////////////////////////////////////////////////////////////////////////// // Request Request::Request() : hnd_(curl_easy_init()), http_hdr_(nullptr) { COMISO_THROW_if(hnd_ == nullptr, DOCLOUD_REQUEST_INIT_FAILED); } Request::~Request() { curl_easy_cleanup(hnd_); if (http_hdr_ != nullptr) curl_slist_free_all(http_hdr_); } void Request::set_url(const char* _url) { curl_easy_setopt(hnd_, CURLOPT_URL, _url); } void Request::add_http_header(const char* _hdr) { http_hdr_ = curl_slist_append(http_hdr_, _hdr); } // TODO: REMOVE this security hole!! void Request::disable_ssl_verification() { /* * If you want to connect to a site who isn't using a certificate that is * signed by one of the certs in the CA bundle you have, you can skip the * verification of the server's certificate. This makes the connection * A LOT LESS SECURE. * * If you have a CA cert for the server stored someplace else than in the * default bundle, then the CURLOPT_CAPATH option might come handy for * you. */ curl_easy_setopt(hnd_, CURLOPT_SSL_VERIFYPEER, 0L); /* * If the site you're connecting to uses a different host name that what * they have mentioned in their server certificate's commonName (or * subjectAltName) fields, libcurl will refuse to connect. You can skip * this check, but this will make the connection less secure. */ curl_easy_setopt(hnd_, CURLOPT_SSL_VERIFYHOST, 0L); } void Request::perform() { DEB_enter_func; prepare(); // set the header we send to the server curl_easy_setopt(hnd_, CURLOPT_HTTPHEADER, http_hdr_); // set the write function to handle incoming data from the server curl_easy_setopt(hnd_, CURLOPT_WRITEFUNCTION, write_func); // set the string to store the incoming header data curl_easy_setopt(hnd_, CURLOPT_HEADERDATA, reinterpret_cast(&hdr_)); // set the string to store the incoming main body (data) curl_easy_setopt(hnd_, CURLOPT_WRITEDATA, reinterpret_cast(&bdy_)); // do the transmission for (int try_nmbr = 0; ; ++try_nmbr) // test exit conditions below { auto res = curl_easy_perform(hnd_); // Various errors can occur while performing the request, but a single // retry is usually sufficient to recover, we do up to 10 attempts. // CURLE_SSL_CONNECT_ERROR happens a lot with the DOcloud service // CURLE_COULDNT_CONNECT happens occasionally. if (res == CURLE_OK) break; // success, exit here else if (try_nmbr < 10) // retry DEB_warning(1, "curl_easy_perform() try #" << try_nmbr << " failed " "with code: " << res << ", message: " << curl_easy_strerror(res)) else COMISO_THROW(DOCLOUD_REQUEST_EXEC_FAILED) } DEB_line(6, "Received Header: " << hdr_); DEB_line(6, "Received Body: " << bdy_); finalize(); } size_t Request::write_func(const char* _ptr, const size_t _size, const size_t _nmemb, void* _str) { // TODO: not sure how much exception-safe (e.g. out of memory this is!) size_t n_add = _size * _nmemb; if (n_add == 0) return 0; auto& str = *reinterpret_cast(_str); str.append(_ptr, n_add); return n_add; } ////////////////////////////////////////////////////////////////////////// // Post void Post::prepare() { // set the post fields curl_easy_setopt(hnd_, CURLOPT_POSTFIELDS, post_.data()); curl_easy_setopt(hnd_, CURLOPT_POSTFIELDSIZE, post_.size()); } ////////////////////////////////////////////////////////////////////////// // Upload void Upload::prepare() { /* tell it to "upload" to the URL */ curl_easy_setopt(hnd_, CURLOPT_UPLOAD, 1L); size_t data_len = send_data(); /* and give the size of the upload (optional) */ curl_easy_setopt(hnd_, CURLOPT_INFILESIZE_LARGE, (curl_off_t)data_len); /* we want to use our own read function */ //curl_easy_setopt(hnd_, CURLOPT_READFUNCTION, read_func); /* enable verbose for easier tracing */ //curl_easy_setopt(hnd_, CURLOPT_VERBOSE, 1L); } void Upload::finalize() { DEB_enter_func; double rate, time; /* now extract transfer info */ curl_easy_getinfo(hnd_, CURLINFO_SPEED_UPLOAD, &rate); curl_easy_getinfo(hnd_, CURLINFO_TOTAL_TIME, &time); DEB_double_format("%.2f"); DEB_line(2, "Upload speed: " << rate / 1024. << "Kbps; Time: " << time << "s."); } ////////////////////////////////////////////////////////////////////////// // UploadFile UploadFile::~UploadFile() { if (file_ != nullptr) std::fclose(file_); } size_t UploadFile::send_data() { /* set where to read from (on Windows you need to use READFUNCTION too) */ file_ = std::fopen(filename_.data(), "rb"); curl_easy_setopt(hnd_, CURLOPT_READDATA, file_); return _filelength(fileno(file_)); } ////////////////////////////////////////////////////////////////////////// // UploadData size_t UploadData::send_data() { curl_easy_setopt(hnd_, CURLOPT_READFUNCTION, Buffer::copy); curl_easy_setopt(hnd_, CURLOPT_READDATA, &buf_); return buf_.length(); } size_t UploadData::Buffer::copy(void* _target, const size_t _elem_size, const size_t _n_elem, void* _from_buf) { auto dat = reinterpret_cast(_from_buf); size_t char_to_write = dat->len_ - dat->pos_; size_t buffer_len = _elem_size * _n_elem; if (char_to_write > buffer_len) char_to_write = buffer_len; std::copy(dat->ptr_ + dat->pos_, dat->ptr_ + dat->pos_ + char_to_write, (char*)_target); dat->pos_ += char_to_write; return char_to_write; } ////////////////////////////////////////////////////////////////////////// // Get void Get::prepare() { curl_easy_setopt(hnd_, CURLOPT_HTTPGET, 1L); } ////////////////////////////////////////////////////////////////////////// // Delete void Delete::prepare() { curl_easy_setopt(hnd_, CURLOPT_CUSTOMREQUEST, "DELETE"); } } // namespace cURLpp } // namespace COMISO #endif // COMISO_DOCLOUD_AVAILABLE