You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							233 lines
						
					
					
						
							9.2 KiB
						
					
					
				
			
		
		
	
	
							233 lines
						
					
					
						
							9.2 KiB
						
					
					
				| // License: Apache 2.0. See LICENSE file in root directory.
 | |
| // Copyright(c) 2020 Intel Corporation. All Rights Reserved.
 | |
| 
 | |
| 
 | |
| 
 | |
| #ifdef CHECK_FOR_UPDATES
 | |
| #include <sstream>
 | |
| #include <iostream>
 | |
| #include <thread>
 | |
| #include <atomic>
 | |
| #include <mutex>
 | |
| #include <fstream>
 | |
| #include <curl/curl.h>
 | |
| #include <curl/easy.h>
 | |
| #endif // CHECK_FOR_UPDATES
 | |
| 
 | |
| #include "http-downloader.h"
 | |
| #include <rsutils/easylogging/easyloggingpp.h>
 | |
| 
 | |
| 
 | |
| 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<uint8_t> &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<uint8_t*>(input_stream));
 | |
| 
 | |
|             if (input_stream && output_vec)
 | |
|             {
 | |
|                 int total_size((int)(size * nmemb));
 | |
|                 while (total_size > 0)
 | |
|                 {
 | |
|                     static_cast<std::vector<uint8_t> *>(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<std::ofstream*> (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<progress_data *>(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<uint64_t>(dlnow),
 | |
|                         static_cast<uint64_t>(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<std::mutex> lock(initialize_mutex);
 | |
|             _curl = curl_easy_init();
 | |
|         }
 | |
| 
 | |
|         http_downloader::~http_downloader()
 | |
|         {
 | |
|             std::lock_guard<std::mutex> 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<uint8_t> &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
 | |
|     }
 | |
| }
 |