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" 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);
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);
99 control(control), f(f), content_length(-1), total(0), offset(offset) {}
100 virtual ~download_client() { f.close(); }
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;
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);
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);
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);
197 if (existing_size > 0)
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);
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);
277 return control->stopped;
283 boost::lock_guard<boost::mutex> lock(control->mutex);
284 return !control->success;
291 boost::lock_guard<boost::mutex> lock(control->mutex);
292 if (control->stopped)
295 control->thread.join();
303 boost::lock_guard<boost::mutex> lock(control->mutex);
304 if (control->stopped)
306 control->stop =
true;
308 control->thread.join();
std::list< std::pair< std::string, std::string > > fields_list
http_header_info m_header_info
#define CHECK_AND_ASSERT_MES(expr, fail_ret_val, message)
unsigned __int64 uint64_t
boost::endian::big_uint16_t port
expect< void > success() noexcept
bool parse_url(const std::string url_str, http::url_content &content)
#define MLOG_SET_THREAD_NAME(x)
std::string to_string(t_connection_type type)
bool get_file_size(const std::string &path_to_file, uint64_t &size)