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

// 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
}
}