// License: Apache 2.0. See LICENSE file in root directory. // Copyright(c) 2020 Intel Corporation. All Rights Reserved. #ifdef CHECK_FOR_UPDATES #include #include #include #include #include #include #include #include #endif // CHECK_FOR_UPDATES #include "http-downloader.h" #include namespace rs2 { namespace http { #ifndef CHECK_FOR_UPDATES // Dummy functions http_downloader::http_downloader() {} http_downloader::~http_downloader() {} bool http_downloader::download_to_stream(const std::string& url, std::stringstream &output, user_callback_func_type user_callback_func) { return false; } bool http_downloader::download_to_file(const std::string& url, const std::string &file_name, user_callback_func_type user_callback_func) { return false; } bool http_downloader::download_to_bytes_vector(const std::string& url, std::vector &output, user_callback_func_type user_callback_func) { return false; } #else std::mutex initialize_mutex; static const curl_off_t HALF_SEC = 500000; // User call back function delay static const int CONNECT_TIMEOUT = 5L; // Libcurl connection timeout 5 [Sec] struct progress_data { curl_off_t last_run_time; user_callback_func_type user_callback_func; CURL *curl; }; size_t stream_write_callback(void *input_stream, size_t size, size_t nmemb, void *output_stream) { if (input_stream && output_stream) { std::string data((const char*)input_stream, (size_t)size * nmemb); *((std::stringstream*)output_stream) << data; return size * nmemb; } return 0; // Error } size_t vector_write_callback(void *input_stream, size_t size, size_t nmemb, void *output_vec) { uint8_t* source_bytes(static_cast(input_stream)); if (input_stream && output_vec) { int total_size((int)(size * nmemb)); while (total_size > 0) { static_cast *>(output_vec)->push_back(*source_bytes); source_bytes++; --total_size; } return size * nmemb; } return 0; // Error } size_t file_write_callback(void *input_stream, size_t size, size_t nmemb, void *output) { if (input_stream && output) { std::ofstream &out_stream(*static_cast (output)); size_t num_of_bytem(nmemb*size); out_stream.write((char *)input_stream, num_of_bytem); return size * nmemb; } return 0; // Error } // This function will be called if CURLOPT_NOPROGRESS is set to 0 // Return value: 0 = continue download / 1 = stop download int progress_callback(void *p, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) { progress_data *myp = static_cast(p); CURL *curl(myp->curl); curl_off_t curtime(0); if( curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME_T, &curtime) == CURLE_OK ) if (dltotal != 0 && (curtime - myp->last_run_time > HALF_SEC)) { myp->last_run_time = curtime; return myp->user_callback_func(static_cast(dlnow), static_cast(dltotal)) == callback_result::CONTINUE_DOWNLOAD ? 0 : 1; } return 0; } http_downloader::http_downloader() : _curl(nullptr) { // Protect curl_easy_init() it is not considers thread safe std::lock_guard lock(initialize_mutex); _curl = curl_easy_init(); } http_downloader::~http_downloader() { std::lock_guard lock(initialize_mutex); curl_easy_cleanup(_curl); } bool http_downloader::download_to_stream(const std::string& url, std::stringstream &output, user_callback_func_type user_callback_func) { if (!_curl) return false; set_common_options(url); if( curl_easy_setopt(_curl, CURLOPT_WRITEFUNCTION, stream_write_callback) != CURLE_OK || curl_easy_setopt(_curl, CURLOPT_WRITEDATA, &output) != CURLE_OK || curl_easy_setopt(_curl, CURLOPT_SSL_VERIFYPEER ,0L) != CURLE_OK || curl_easy_setopt(_curl, CURLOPT_SSL_VERIFYHOST ,0L) != CURLE_OK ) throw std::invalid_argument( "Setting CURL option failed" ); progress_data progress_record; // Should stay here - "curl_easy_perform" use it if (user_callback_func) { register_progress_call_back(progress_record, user_callback_func); } auto res = curl_easy_perform(_curl); if (CURLE_OK != res) { LOG_ERROR("Download error from URL: " + url + ", error info: " + std::string(curl_easy_strerror(res))); return false; } return true; } bool http_downloader::download_to_bytes_vector(const std::string& url, std::vector &output, user_callback_func_type user_callback_func) { if (!_curl) return false; set_common_options(url); if( curl_easy_setopt(_curl, CURLOPT_WRITEFUNCTION, vector_write_callback) != CURLE_OK || curl_easy_setopt(_curl, CURLOPT_WRITEDATA, &output) != CURLE_OK ) throw std::invalid_argument( "Setting CURL option failed" ); progress_data progress_record; // Should stay here - "curl_easy_perform" use it if (user_callback_func) { register_progress_call_back(progress_record, user_callback_func); } auto res = curl_easy_perform(_curl); if (CURLE_OK != res) { LOG_ERROR("Download error from URL: " + url + ", error info: " + std::string(curl_easy_strerror(res))); return false; } return true; } bool http_downloader::download_to_file(const std::string& url, const std::string &file_name, user_callback_func_type user_callback_func) { if (!_curl) return false; /* open the file */ std::ofstream out_file(file_name, std::ios::out); if (out_file.good()) { set_common_options(url); if( curl_easy_setopt(_curl, CURLOPT_WRITEFUNCTION, file_write_callback) != CURLE_OK || curl_easy_setopt(_curl, CURLOPT_WRITEDATA, &out_file) != CURLE_OK ) throw std::invalid_argument( "Setting CURL option failed" ); progress_data progress_record; // Should stay here - "curl_easy_perform" use it if (user_callback_func) { register_progress_call_back(progress_record, user_callback_func); } auto res = curl_easy_perform(_curl); out_file.close(); if (CURLE_OK != res) { LOG_ERROR("Download error from URL: " + url + ", error info: " + std::string(curl_easy_strerror(res))); return false; } } else { LOG_ERROR("Download error - Cannot open local file: " + file_name); return false; } return true; } void http_downloader::set_common_options(const std::string &url) { if( curl_easy_setopt( _curl, CURLOPT_CONNECTTIMEOUT, CONNECT_TIMEOUT ) != CURLE_OK || // timeout for the connect phase curl_easy_setopt( _curl, CURLOPT_URL, url.c_str() ) != CURLE_OK || // provide the URL to use in the request curl_easy_setopt( _curl, CURLOPT_FOLLOWLOCATION, 1L ) != CURLE_OK || // follow HTTP 3xx redirects curl_easy_setopt( _curl, CURLOPT_NOSIGNAL, 1 ) != CURLE_OK || // skip all signal handling curl_easy_setopt( _curl, CURLOPT_FAILONERROR, 1L ) != CURLE_OK || // request failure on HTTP response >= 400 curl_easy_setopt( _curl, CURLOPT_NOPROGRESS, 1L ) != CURLE_OK ) // switch off the progress meter throw std::invalid_argument( "Setting CURL option failed" ); } void http_downloader::register_progress_call_back(progress_data &progress_record, user_callback_func_type user_callback_func) { progress_record = { 0, user_callback_func, _curl }; if( curl_easy_setopt(_curl, CURLOPT_XFERINFOFUNCTION, progress_callback) != CURLE_OK || curl_easy_setopt(_curl, CURLOPT_XFERINFODATA, &progress_record) != CURLE_OK || curl_easy_setopt(_curl, CURLOPT_NOPROGRESS, 0L) != CURLE_OK ) throw std::invalid_argument( "Setting CURL option failed" ); } #endif } }