32#include <boost/filesystem.hpp>
33#include <boost/thread/thread.hpp>
38#undef ELECTRONEUM_DEFAULT_LOG_CATEGORY
39#define ELECTRONEUM_DEFAULT_LOG_CATEGORY "net.dl"
46 const std::string
uri;
47 std::function<void(
const std::string&,
const std::string&,
bool)>
result_cb;
48 std::function<bool(
const std::string&,
const std::string&,
size_t, ssize_t)>
progress_cb;
62 static std::atomic<unsigned int> thread_id(0);
68 stopped_setter(
const download_async_handle &control): control(control) {}
69 ~stopped_setter() { control->stopped =
true; }
71 } stopped_setter(control);
75 boost::unique_lock<boost::mutex> lock(control->mutex);
76 std::ios_base::openmode mode = std::ios_base::out | std::ios_base::binary;
80 MINFO(
"Resuming downloading " << control->uri <<
" to " << control->path <<
" from " << existing_size);
81 mode |= std::ios_base::app;
85 MINFO(
"Downloading " << control->uri <<
" to " << control->path);
86 mode |= std::ios_base::trunc;
89 f.open(control->path, mode);
91 MERROR(
"Failed to open file " << control->path);
92 control->result_cb(control->path, control->uri, control->success);
98 download_client(download_async_handle control, std::ofstream &f,
uint64_t offset = 0):
99 control(control), f(f), content_length(-1), total(0), offset(offset) {}
100 virtual ~download_client() { f.close(); }
101 virtual bool on_header(
const epee::net_utils::http::http_response_info &headers)
104 MDEBUG(
"Header: " << kv.first <<
": " << kv.second);
108 MINFO(
"Content-Length: " << length);
109 content_length = length;
110 boost::filesystem::path path(control->path);
111 boost::filesystem::space_info si = boost::filesystem::space(path);
112 if (si.available < (
size_t)content_length)
114 const uint64_t avail = (si.available + 1023) / 1024, needed = (content_length + 1023) / 1024;
115 MERROR(
"Not enough space to download " << needed <<
" kB to " << path <<
" (" << avail <<
" kB available)");
122 bool got_range =
false;
123 const std::string prefix =
"bytes=" + std::to_string(offset) +
"-";
126 if (kv.first ==
"Content-Range" && strncmp(kv.second.c_str(), prefix.c_str(), prefix.size()))
134 MWARNING(
"We did not get the requested range, downloading from start");
136 f.open(control->path, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
141 virtual bool handle_target_data(std::string &piece_of_transfer)
145 boost::lock_guard<boost::mutex> lock(control->mutex);
148 f << piece_of_transfer;
149 total += piece_of_transfer.size();
150 if (control->progress_cb && !control->progress_cb(control->path, control->uri, total, content_length))
154 catch (
const std::exception &e)
156 MERROR(
"Error writing data: " << e.what());
163 ssize_t content_length;
166 } client(control, f, existing_size);
167 epee::net_utils::http::url_content u_c;
170 MERROR(
"Failed to parse URL " << control->uri);
171 control->result_cb(control->path, control->uri, control->success);
174 if (u_c.
host.empty())
176 MERROR(
"Failed to determine address from URL " << control->uri);
177 control->result_cb(control->path, control->uri, control->success);
185 MDEBUG(
"Connecting to " << u_c.
host <<
":" << port);
186 client.set_server(u_c.
host, std::to_string(port), boost::none, ssl);
187 if (!client.connect(std::chrono::seconds(30)))
189 boost::lock_guard<boost::mutex> lock(control->mutex);
190 MERROR(
"Failed to connect to " << control->uri);
191 control->result_cb(control->path, control->uri, control->success);
195 const epee::net_utils::http::http_response_info *
info = NULL;
197 if (existing_size > 0)
199 const std::string range =
"bytes=" + std::to_string(existing_size) +
"-";
200 MDEBUG(
"Asking for range: " << range);
201 fields.push_back(std::make_pair(
"Range", range));
203 if (!client.invoke_get(u_c.
uri, std::chrono::seconds(30),
"", &
info, fields))
205 boost::lock_guard<boost::mutex> lock(control->mutex);
206 MERROR(
"Failed to connect to " << control->uri);
208 control->result_cb(control->path, control->uri, control->success);
213 boost::lock_guard<boost::mutex> lock(control->mutex);
214 MDEBUG(
"Download cancelled");
216 control->result_cb(control->path, control->uri, control->success);
221 boost::lock_guard<boost::mutex> lock(control->mutex);
222 MERROR(
"Failed invoking GET command to " << control->uri <<
", no status info returned");
224 control->result_cb(control->path, control->uri, control->success);
227 MDEBUG(
"response code: " <<
info->m_response_code);
228 MDEBUG(
"response length: " <<
info->m_header_info.m_content_length);
229 MDEBUG(
"response comment: " <<
info->m_response_comment);
231 for (
const auto &f:
info->m_additional_fields)
232 MDEBUG(
"additional field: " << f.first <<
": " << f.second);
233 if (
info->m_response_code != 200 &&
info->m_response_code != 206)
235 boost::lock_guard<boost::mutex> lock(control->mutex);
236 MERROR(
"Status code " <<
info->m_response_code);
238 control->result_cb(control->path, control->uri, control->success);
243 MDEBUG(
"Download complete");
245 control->success =
true;
246 control->result_cb(control->path, control->uri, control->success);
249 catch (
const std::exception &e)
251 MERROR(
"Exception in download thread: " << e.what());
254 boost::lock_guard<boost::mutex> lock(control->mutex);
255 control->result_cb(control->path, control->uri, control->success);
258 bool download(
const std::string &path,
const std::string &url, std::function<
bool(
const std::string&,
const std::string&,
size_t, ssize_t)> cb)
260 bool success =
false;
266 download_async_handle download_async(
const std::string &path,
const std::string &url, std::function<
void(
const std::string&,
const std::string&,
bool)> result, std::function<
bool(
const std::string&,
const std::string&,
size_t, ssize_t)> progress)
268 download_async_handle control = std::make_shared<download_thread_control>(path, url, result, progress);
269 control->
thread = boost::thread([control](){ download_thread(control); });
276 boost::lock_guard<boost::mutex> lock(control->
mutex);
283 boost::lock_guard<boost::mutex> lock(control->
mutex);
291 boost::lock_guard<boost::mutex> lock(control->
mutex);
303 boost::lock_guard<boost::mutex> lock(control->
mutex);
306 control->
stop =
true;
#define CHECK_AND_ASSERT_MES(expr, fail_ret_val, message)
#define MLOG_SET_THREAD_NAME(x)
bool get_file_size(const std::string &path_to_file, uint64_t &size)
http_simple_client_template< blocked_mode_client > http_simple_client
std::list< std::pair< std::string, std::string > > fields_list
bool parse_url(const std::string url_str, http::url_content &content)
unsigned __int64 uint64_t
http_header_info m_header_info