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
2 months ago
|
// 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
|
||
|
}
|
||
|
}
|