mirror of
https://github.com/rakshasa/rtorrent.git
synced 2026-08-18 08:02:29 +00:00
Moved curl to libtorrent.
This commit is contained in:
@@ -1,122 +0,0 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "core/curl_get.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <curl/curl.h>
|
||||
#include <curl/easy.h>
|
||||
#include <torrent/exceptions.h>
|
||||
|
||||
#include "globals.h"
|
||||
#include "core/curl_stack.h"
|
||||
|
||||
namespace core {
|
||||
|
||||
size_t
|
||||
curl_get_receive_write(void* data, size_t size, size_t nmemb, void* handle) {
|
||||
if (!((CurlGet*)handle)->stream()->write((const char*)data, size * nmemb).fail())
|
||||
return size * nmemb;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
CurlGet::CurlGet(CurlStack* s) :
|
||||
m_stack(s) {
|
||||
|
||||
m_task_timeout.slot() = [this]() { receive_timeout(); };
|
||||
}
|
||||
|
||||
CurlGet::~CurlGet() {
|
||||
close();
|
||||
}
|
||||
|
||||
void
|
||||
CurlGet::start() {
|
||||
if (is_busy())
|
||||
throw torrent::internal_error("Tried to call CurlGet::start on a busy object.");
|
||||
|
||||
if (m_stream == NULL)
|
||||
throw torrent::internal_error("Tried to call CurlGet::start without a valid output stream.");
|
||||
|
||||
if (!m_stack->is_running())
|
||||
return;
|
||||
|
||||
m_handle = curl_easy_init();
|
||||
|
||||
if (m_handle == NULL)
|
||||
throw torrent::internal_error("Call to curl_easy_init() failed.");
|
||||
|
||||
curl_easy_setopt(m_handle, CURLOPT_URL, m_url.c_str());
|
||||
curl_easy_setopt(m_handle, CURLOPT_WRITEFUNCTION, &curl_get_receive_write);
|
||||
curl_easy_setopt(m_handle, CURLOPT_WRITEDATA, this);
|
||||
|
||||
if (m_timeout != 0) {
|
||||
curl_easy_setopt(m_handle, CURLOPT_CONNECTTIMEOUT, (long)60);
|
||||
curl_easy_setopt(m_handle, CURLOPT_TIMEOUT, (long)m_timeout);
|
||||
|
||||
// Normally libcurl should handle the timeout. But sometimes that doesn't
|
||||
// work right so we do a fallback timeout that just aborts the transfer.
|
||||
torrent::this_thread::scheduler()->update_wait_for_ceil_seconds(&m_task_timeout, 5s + 1s*m_timeout);
|
||||
}
|
||||
|
||||
curl_easy_setopt(m_handle, CURLOPT_FORBID_REUSE, (long)1);
|
||||
curl_easy_setopt(m_handle, CURLOPT_NOSIGNAL, (long)1);
|
||||
curl_easy_setopt(m_handle, CURLOPT_FOLLOWLOCATION, (long)1);
|
||||
curl_easy_setopt(m_handle, CURLOPT_MAXREDIRS, (long)5);
|
||||
|
||||
curl_easy_setopt(m_handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER);
|
||||
|
||||
curl_easy_setopt(m_handle, CURLOPT_ENCODING, "");
|
||||
|
||||
m_ipv6 = false;
|
||||
|
||||
m_stack->add_get(this);
|
||||
}
|
||||
|
||||
void
|
||||
CurlGet::close() {
|
||||
torrent::this_thread::scheduler()->erase(&m_task_timeout);
|
||||
|
||||
if (!is_busy())
|
||||
return;
|
||||
|
||||
m_stack->remove_get(this);
|
||||
|
||||
curl_easy_cleanup(m_handle);
|
||||
|
||||
m_handle = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
CurlGet::retry_ipv6() {
|
||||
CURL* nhandle = curl_easy_duphandle(m_handle);
|
||||
|
||||
curl_easy_setopt(nhandle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6);
|
||||
curl_easy_cleanup(m_handle);
|
||||
|
||||
m_handle = nhandle;
|
||||
m_ipv6 = true;
|
||||
}
|
||||
|
||||
void
|
||||
CurlGet::receive_timeout() {
|
||||
return m_stack->transfer_done(m_handle, "Timed out");
|
||||
}
|
||||
|
||||
curl_off_t
|
||||
CurlGet::size_done() {
|
||||
curl_off_t d = 0;
|
||||
curl_easy_getinfo(m_handle, CURLINFO_SIZE_DOWNLOAD_T, &d);
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
curl_off_t
|
||||
CurlGet::size_total() {
|
||||
curl_off_t d = 0;
|
||||
curl_easy_getinfo(m_handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &d);
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
#ifndef RTORRENT_CORE_CURL_GET_H
|
||||
#define RTORRENT_CORE_CURL_GET_H
|
||||
|
||||
#include <iosfwd>
|
||||
#include <string>
|
||||
#include <curl/curl.h>
|
||||
#include <torrent/http.h>
|
||||
#include <torrent/utils/scheduler.h>
|
||||
|
||||
namespace core {
|
||||
|
||||
class CurlStack;
|
||||
|
||||
class CurlGet : public torrent::Http {
|
||||
public:
|
||||
CurlGet(CurlStack* s);
|
||||
virtual ~CurlGet();
|
||||
|
||||
void start();
|
||||
void close();
|
||||
|
||||
bool is_using_ipv6() { return m_ipv6; }
|
||||
void retry_ipv6();
|
||||
|
||||
bool is_busy() const { return m_handle; }
|
||||
bool is_active() const { return m_active; }
|
||||
|
||||
void set_active(bool a) { m_active = a; }
|
||||
|
||||
curl_off_t size_done();
|
||||
curl_off_t size_total();
|
||||
|
||||
CURL* handle() { return m_handle; }
|
||||
|
||||
private:
|
||||
friend class CurlStack;
|
||||
|
||||
CurlGet(const CurlGet&) = delete;
|
||||
void operator = (const CurlGet&) = delete;
|
||||
|
||||
void receive_timeout();
|
||||
|
||||
bool m_active{};
|
||||
bool m_ipv6;
|
||||
|
||||
torrent::utils::SchedulerEntry m_task_timeout;
|
||||
|
||||
CURL* m_handle{};
|
||||
CurlStack* m_stack;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,99 +0,0 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "curl_socket.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <curl/multi.h>
|
||||
#include <torrent/poll.h>
|
||||
#include <torrent/exceptions.h>
|
||||
#include <torrent/utils/thread.h>
|
||||
|
||||
#include "control.h"
|
||||
#include "core/curl_stack.h"
|
||||
|
||||
namespace core {
|
||||
|
||||
int
|
||||
CurlSocket::receive_socket([[maybe_unused]] void* easy_handle, curl_socket_t fd, int what, void* userp, void* socketp) {
|
||||
CurlStack* stack = (CurlStack*)userp;
|
||||
CurlSocket* socket = (CurlSocket*)socketp;
|
||||
|
||||
if (!stack->is_running())
|
||||
return 0;
|
||||
|
||||
if (what == CURL_POLL_REMOVE) {
|
||||
// We also probably need the special code here as we're not
|
||||
// guaranteed that the fd will be closed, afaik.
|
||||
if (socket != NULL)
|
||||
socket->close();
|
||||
|
||||
// TODO: Consider the possibility that we'll need to set the
|
||||
// fd-associated pointer curl holds to NULL.
|
||||
|
||||
delete socket;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (socket == NULL) {
|
||||
socket = stack->new_socket(fd);
|
||||
torrent::this_thread::poll()->open(socket);
|
||||
|
||||
// No interface for libcurl to signal when it's interested in error events.
|
||||
// Assume that hence it must always be interested in them.
|
||||
torrent::this_thread::poll()->insert_error(socket);
|
||||
}
|
||||
|
||||
if (what == CURL_POLL_NONE || what == CURL_POLL_OUT)
|
||||
torrent::this_thread::poll()->remove_read(socket);
|
||||
else
|
||||
torrent::this_thread::poll()->insert_read(socket);
|
||||
|
||||
if (what == CURL_POLL_NONE || what == CURL_POLL_IN)
|
||||
torrent::this_thread::poll()->remove_write(socket);
|
||||
else
|
||||
torrent::this_thread::poll()->insert_write(socket);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
CurlSocket::~CurlSocket() {
|
||||
assert(m_fileDesc == -1 && "CurlSocket::~CurlSocket() m_fileDesc != -1.");
|
||||
}
|
||||
|
||||
void
|
||||
CurlSocket::close() {
|
||||
if (m_fileDesc == -1)
|
||||
throw torrent::internal_error("CurlSocket::close() m_fileDesc == -1.");
|
||||
|
||||
torrent::this_thread::poll()->closed(this);
|
||||
m_fileDesc = -1;
|
||||
}
|
||||
|
||||
void
|
||||
CurlSocket::event_read() {
|
||||
#if (LIBCURL_VERSION_NUM >= 0x071003)
|
||||
return m_stack->receive_action(this, CURL_CSELECT_IN);
|
||||
#else
|
||||
return m_stack->receive_action(this, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
CurlSocket::event_write() {
|
||||
#if (LIBCURL_VERSION_NUM >= 0x071003)
|
||||
return m_stack->receive_action(this, CURL_CSELECT_OUT);
|
||||
#else
|
||||
return m_stack->receive_action(this, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
CurlSocket::event_error() {
|
||||
#if (LIBCURL_VERSION_NUM >= 0x071003)
|
||||
return m_stack->receive_action(this, CURL_CSELECT_ERR);
|
||||
#else
|
||||
return m_stack->receive_action(this, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
#ifndef RTORRENT_CORE_CURL_SOCKET_H
|
||||
#define RTORRENT_CORE_CURL_SOCKET_H
|
||||
|
||||
#include <curl/curl.h>
|
||||
#include <torrent/event.h>
|
||||
|
||||
#include "globals.h"
|
||||
|
||||
namespace core {
|
||||
|
||||
class CurlStack;
|
||||
|
||||
class CurlSocket : public torrent::Event {
|
||||
public:
|
||||
CurlSocket(int fd, CurlStack* stack) : m_stack(stack) { m_fileDesc = fd; }
|
||||
~CurlSocket();
|
||||
|
||||
const char* type_name() const { return "curl"; }
|
||||
|
||||
void close();
|
||||
|
||||
static int receive_socket(void* easy_handle, curl_socket_t fd, int what, void* userp, void* socketp);
|
||||
|
||||
private:
|
||||
CurlSocket(const CurlSocket&);
|
||||
void operator = (const CurlSocket&);
|
||||
|
||||
virtual void event_read();
|
||||
virtual void event_write();
|
||||
virtual void event_error();
|
||||
|
||||
CurlStack* m_stack;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,243 +0,0 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <curl/multi.h>
|
||||
#include <torrent/exceptions.h>
|
||||
|
||||
#include "curl_get.h"
|
||||
#include "curl_socket.h"
|
||||
#include "curl_stack.h"
|
||||
|
||||
namespace core {
|
||||
|
||||
CurlStack::CurlStack() {
|
||||
m_handle = (void*)curl_multi_init();
|
||||
m_task_timeout.slot() = std::bind(&CurlStack::receive_timeout, this);
|
||||
|
||||
#if (LIBCURL_VERSION_NUM >= 0x071000)
|
||||
curl_multi_setopt((CURLM*)m_handle, CURLMOPT_TIMERDATA, this);
|
||||
curl_multi_setopt((CURLM*)m_handle, CURLMOPT_TIMERFUNCTION, &CurlStack::set_timeout);
|
||||
#endif
|
||||
curl_multi_setopt((CURLM*)m_handle, CURLMOPT_SOCKETDATA, this);
|
||||
curl_multi_setopt((CURLM*)m_handle, CURLMOPT_SOCKETFUNCTION, &CurlSocket::receive_socket);
|
||||
}
|
||||
|
||||
CurlStack::~CurlStack() {
|
||||
shutdown();
|
||||
}
|
||||
|
||||
void
|
||||
CurlStack::shutdown() {
|
||||
if (!m_running)
|
||||
return;
|
||||
|
||||
m_running = false;
|
||||
|
||||
while (!empty())
|
||||
front()->close();
|
||||
|
||||
curl_multi_cleanup((CURLM*)m_handle);
|
||||
|
||||
torrent::this_thread::scheduler()->erase(&m_task_timeout);
|
||||
}
|
||||
|
||||
CurlGet*
|
||||
CurlStack::new_object() {
|
||||
return new CurlGet(this);
|
||||
}
|
||||
|
||||
CurlSocket*
|
||||
CurlStack::new_socket(int fd) {
|
||||
if (!m_running)
|
||||
throw torrent::internal_error("CurlStack::new_socket() called when not running.");
|
||||
|
||||
CurlSocket* socket = new CurlSocket(fd, this);
|
||||
curl_multi_assign((CURLM*)m_handle, fd, socket);
|
||||
return socket;
|
||||
}
|
||||
|
||||
void
|
||||
CurlStack::receive_action(CurlSocket* socket, int events) {
|
||||
CURLMcode code;
|
||||
|
||||
do {
|
||||
int count;
|
||||
#if (LIBCURL_VERSION_NUM >= 0x071003)
|
||||
code = curl_multi_socket_action((CURLM*)m_handle,
|
||||
socket != NULL ? socket->file_descriptor() : CURL_SOCKET_TIMEOUT,
|
||||
events,
|
||||
&count);
|
||||
#else
|
||||
code = curl_multi_socket((CURLM*)m_handle,
|
||||
socket != NULL ? socket->file_descriptor() : CURL_SOCKET_TIMEOUT,
|
||||
&count);
|
||||
#endif
|
||||
|
||||
if (code > 0)
|
||||
throw torrent::internal_error("Error calling curl_multi_socket_action.");
|
||||
|
||||
// Socket might be removed when cleaning handles below, future
|
||||
// calls should not use it.
|
||||
socket = NULL;
|
||||
events = 0;
|
||||
|
||||
if ((unsigned int)count != size()) {
|
||||
while (process_done_handle())
|
||||
; // Do nothing.
|
||||
|
||||
if (empty())
|
||||
torrent::this_thread::scheduler()->erase(&m_task_timeout);
|
||||
}
|
||||
|
||||
} while (code == CURLM_CALL_MULTI_PERFORM);
|
||||
}
|
||||
|
||||
bool
|
||||
CurlStack::process_done_handle() {
|
||||
int remaining_msgs = 0;
|
||||
CURLMsg* msg = curl_multi_info_read((CURLM*)m_handle, &remaining_msgs);
|
||||
|
||||
if (msg == NULL)
|
||||
return false;
|
||||
|
||||
if (msg->msg != CURLMSG_DONE)
|
||||
throw torrent::internal_error("CurlStack::receive_action() msg->msg != CURLMSG_DONE.");
|
||||
|
||||
if (msg->data.result == CURLE_COULDNT_RESOLVE_HOST) {
|
||||
iterator itr = std::find_if(begin(), end(), [&msg](CurlGet* get) { return get->handle() == msg->easy_handle; });
|
||||
|
||||
if (itr == end())
|
||||
throw torrent::internal_error("Could not find CurlGet when calling CurlStack::receive_action.");
|
||||
|
||||
if (!(*itr)->is_using_ipv6()) {
|
||||
(*itr)->retry_ipv6();
|
||||
|
||||
if (curl_multi_add_handle((CURLM*)m_handle, (*itr)->handle()) > 0)
|
||||
throw torrent::internal_error("Error calling curl_multi_add_handle.");
|
||||
}
|
||||
|
||||
} else {
|
||||
transfer_done(msg->easy_handle,
|
||||
msg->data.result == CURLE_OK ? NULL : curl_easy_strerror(msg->data.result));
|
||||
}
|
||||
|
||||
return remaining_msgs != 0;
|
||||
}
|
||||
|
||||
void
|
||||
CurlStack::transfer_done(void* handle, const char* msg) {
|
||||
iterator itr = std::find_if(begin(), end(), [&handle](CurlGet* get) { return get->handle() == handle; });
|
||||
|
||||
if (itr == end())
|
||||
throw torrent::internal_error("Could not find CurlGet with the right easy_handle.");
|
||||
|
||||
if (msg == NULL)
|
||||
(*itr)->trigger_done();
|
||||
else
|
||||
(*itr)->trigger_failed(msg);
|
||||
}
|
||||
|
||||
void
|
||||
CurlStack::receive_timeout() {
|
||||
receive_action(NULL, 0);
|
||||
|
||||
if (!empty() && !m_task_timeout.is_scheduled()) {
|
||||
// Sometimes libcurl forgets to reset the timeout. Try to poll the value in that case, or use 10
|
||||
// seconds max.
|
||||
long timeout_ms;
|
||||
curl_multi_timeout((CURLM*)m_handle, &timeout_ms);
|
||||
|
||||
auto timeout = std::max<std::chrono::microseconds>(std::chrono::milliseconds(timeout_ms), 10s);
|
||||
|
||||
torrent::this_thread::scheduler()->wait_for_ceil_seconds(&m_task_timeout, timeout);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CurlStack::add_get(CurlGet* get) {
|
||||
if (!m_user_agent.empty())
|
||||
curl_easy_setopt(get->handle(), CURLOPT_USERAGENT, m_user_agent.c_str());
|
||||
|
||||
if (!m_http_proxy.empty())
|
||||
curl_easy_setopt(get->handle(), CURLOPT_PROXY, m_http_proxy.c_str());
|
||||
|
||||
if (!m_bind_address.empty())
|
||||
curl_easy_setopt(get->handle(), CURLOPT_INTERFACE, m_bind_address.c_str());
|
||||
|
||||
if (!m_http_ca_path.empty())
|
||||
curl_easy_setopt(get->handle(), CURLOPT_CAPATH, m_http_ca_path.c_str());
|
||||
|
||||
if (!m_http_ca_cert.empty())
|
||||
curl_easy_setopt(get->handle(), CURLOPT_CAINFO, m_http_ca_cert.c_str());
|
||||
|
||||
curl_easy_setopt(get->handle(), CURLOPT_SSL_VERIFYHOST, (long)(m_ssl_verify_host ? 2 : 0));
|
||||
curl_easy_setopt(get->handle(), CURLOPT_SSL_VERIFYPEER, (long)(m_ssl_verify_peer ? 1 : 0));
|
||||
curl_easy_setopt(get->handle(), CURLOPT_DNS_CACHE_TIMEOUT, m_dns_timeout);
|
||||
|
||||
base_type::push_back(get);
|
||||
|
||||
if (m_active >= m_max_active)
|
||||
return;
|
||||
|
||||
m_active++;
|
||||
get->set_active(true);
|
||||
|
||||
if (curl_multi_add_handle((CURLM*)m_handle, get->handle()) > 0)
|
||||
throw torrent::internal_error("Error calling curl_multi_add_handle.");
|
||||
|
||||
#if (LIBCURL_VERSION_NUM < 0x071000)
|
||||
receive_timeout();
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
CurlStack::remove_get(CurlGet* get) {
|
||||
iterator itr = std::find(begin(), end(), get);
|
||||
|
||||
if (itr == end())
|
||||
throw torrent::internal_error("Could not find CurlGet when calling CurlStack::remove.");
|
||||
|
||||
base_type::erase(itr);
|
||||
|
||||
// The CurlGet object was never activated, so we just skip this one.
|
||||
if (!get->is_active())
|
||||
return;
|
||||
|
||||
get->set_active(false);
|
||||
|
||||
if (curl_multi_remove_handle((CURLM*)m_handle, get->handle()) > 0)
|
||||
throw torrent::internal_error("Error calling curl_multi_remove_handle.");
|
||||
|
||||
if (m_active == m_max_active &&
|
||||
(itr = std::find_if(begin(), end(), [](CurlGet* get) { return !get->is_active(); })) != end()) {
|
||||
(*itr)->set_active(true);
|
||||
|
||||
if (curl_multi_add_handle((CURLM*)m_handle, (*itr)->handle()) > 0)
|
||||
throw torrent::internal_error("Error calling curl_multi_add_handle.");
|
||||
|
||||
} else {
|
||||
m_active--;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CurlStack::global_init() {
|
||||
curl_global_init(CURL_GLOBAL_ALL);
|
||||
}
|
||||
|
||||
void
|
||||
CurlStack::global_cleanup() {
|
||||
curl_global_cleanup();
|
||||
}
|
||||
|
||||
// TODO: Is this function supposed to set a per-handle timeout, or is
|
||||
// it the shortest timeout amongst all handles?
|
||||
int
|
||||
CurlStack::set_timeout([[maybe_unused]] void* handle, std::chrono::microseconds timeout, void* userp) {
|
||||
CurlStack* stack = (CurlStack*)userp;
|
||||
|
||||
torrent::this_thread::scheduler()->update_wait_for_ceil_seconds(&stack->m_task_timeout, timeout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
#ifndef RTORRENT_CORE_CURL_STACK_H
|
||||
#define RTORRENT_CORE_CURL_STACK_H
|
||||
|
||||
#include <deque>
|
||||
#include <string>
|
||||
#include <torrent/utils/scheduler.h>
|
||||
|
||||
namespace core {
|
||||
|
||||
class CurlGet;
|
||||
class CurlSocket;
|
||||
|
||||
// By using a deque instead of vector we allow for cheaper removal of
|
||||
// the oldest elements, those that will be first in the in the
|
||||
// deque.
|
||||
//
|
||||
// This should fit well with the use-case of a http stack, thus
|
||||
// we get most of the cache locality benefits of a vector with fast
|
||||
// removal of elements.
|
||||
|
||||
class CurlStack : std::deque<CurlGet*> {
|
||||
public:
|
||||
friend class CurlGet;
|
||||
|
||||
typedef std::deque<CurlGet*> base_type;
|
||||
|
||||
using base_type::value_type;
|
||||
using base_type::iterator;
|
||||
using base_type::const_iterator;
|
||||
using base_type::reverse_iterator;
|
||||
using base_type::const_reverse_iterator;
|
||||
|
||||
using base_type::begin;
|
||||
using base_type::end;
|
||||
using base_type::rbegin;
|
||||
using base_type::rend;
|
||||
|
||||
using base_type::back;
|
||||
using base_type::front;
|
||||
|
||||
using base_type::size;
|
||||
using base_type::empty;
|
||||
|
||||
CurlStack();
|
||||
~CurlStack();
|
||||
|
||||
void shutdown();
|
||||
bool is_running() const { return m_running; }
|
||||
|
||||
CurlGet* new_object();
|
||||
CurlSocket* new_socket(int fd);
|
||||
|
||||
unsigned int active() const { return m_active; }
|
||||
unsigned int max_active() const { return m_max_active; }
|
||||
void set_max_active(unsigned int a) { m_max_active = a; }
|
||||
|
||||
const std::string& user_agent() const { return m_user_agent; }
|
||||
const std::string& http_proxy() const { return m_http_proxy; }
|
||||
const std::string& bind_address() const { return m_bind_address; }
|
||||
const std::string& http_capath() const { return m_http_ca_path; }
|
||||
const std::string& http_cacert() const { return m_http_ca_cert; }
|
||||
|
||||
void set_user_agent(const std::string& s) { m_user_agent = s; }
|
||||
void set_http_proxy(const std::string& s) { m_http_proxy = s; }
|
||||
void set_bind_address(const std::string& s) { m_bind_address = s; }
|
||||
void set_http_capath(const std::string& s) { m_http_ca_path = s; }
|
||||
void set_http_cacert(const std::string& s) { m_http_ca_cert = s; }
|
||||
|
||||
bool ssl_verify_host() const { return m_ssl_verify_host; }
|
||||
bool ssl_verify_peer() const { return m_ssl_verify_peer; }
|
||||
void set_ssl_verify_host(bool s) { m_ssl_verify_host = s; }
|
||||
void set_ssl_verify_peer(bool s) { m_ssl_verify_peer = s; }
|
||||
|
||||
long dns_timeout() const { return m_dns_timeout; }
|
||||
void set_dns_timeout(long timeout) { m_dns_timeout = timeout; }
|
||||
|
||||
static void global_init();
|
||||
static void global_cleanup();
|
||||
|
||||
void receive_action(CurlSocket* socket, int type);
|
||||
|
||||
static int set_timeout(void* handle, std::chrono::microseconds timeout, void* userp);
|
||||
|
||||
void transfer_done(void* handle, const char* msg);
|
||||
|
||||
protected:
|
||||
void add_get(CurlGet* get);
|
||||
void remove_get(CurlGet* get);
|
||||
|
||||
private:
|
||||
CurlStack(const CurlStack&) = delete;
|
||||
void operator = (const CurlStack&) = delete;
|
||||
|
||||
void receive_timeout();
|
||||
|
||||
bool process_done_handle();
|
||||
|
||||
void* m_handle;
|
||||
|
||||
bool m_running{true};
|
||||
|
||||
unsigned int m_active{0};
|
||||
unsigned int m_max_active{32};
|
||||
|
||||
torrent::utils::SchedulerEntry m_task_timeout;
|
||||
|
||||
std::string m_user_agent;
|
||||
std::string m_http_proxy;
|
||||
std::string m_bind_address;
|
||||
std::string m_http_ca_path;
|
||||
std::string m_http_ca_cert;
|
||||
|
||||
bool m_ssl_verify_host{true};
|
||||
bool m_ssl_verify_peer{true};
|
||||
long m_dns_timeout{60};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,11 +1,12 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "download_factory.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <rak/path.h>
|
||||
#include <torrent/utils/log.h>
|
||||
#include <torrent/utils/resume.h>
|
||||
@@ -14,18 +15,15 @@
|
||||
#include <torrent/exceptions.h>
|
||||
#include <torrent/rate.h>
|
||||
#include <torrent/data/file_utils.h>
|
||||
#include <torrent/net/http_stack.h>
|
||||
|
||||
#include "rpc/parse_commands.h"
|
||||
|
||||
#include "curl_get.h"
|
||||
#include "control.h"
|
||||
#include "http_queue.h"
|
||||
#include "globals.h"
|
||||
#include "manager.h"
|
||||
|
||||
#include "download.h"
|
||||
#include "download_factory.h"
|
||||
#include "download_store.h"
|
||||
#include "core/download.h"
|
||||
#include "core/download_store.h"
|
||||
#include "core/http_queue.h"
|
||||
#include "core/manager.h"
|
||||
#include "rpc/parse_commands.h"
|
||||
|
||||
namespace core {
|
||||
|
||||
@@ -111,10 +109,11 @@ DownloadFactory::receive_load() {
|
||||
if (is_network_uri(m_uri)) {
|
||||
// Http handling here.
|
||||
m_stream = new std::stringstream;
|
||||
|
||||
HttpQueue::iterator itr = m_manager->http_queue()->insert(m_uri, m_stream);
|
||||
|
||||
(*itr)->signal_done().push_front(std::bind(&DownloadFactory::receive_loaded, this));
|
||||
(*itr)->signal_failed().push_front(std::bind(&DownloadFactory::receive_failed, this, std::placeholders::_1));
|
||||
itr->add_done_slot([this]() { receive_loaded(); });
|
||||
itr->add_failed_slot([this](const std::string& error) { receive_failed(error); });
|
||||
|
||||
m_variables["tied_to_file"] = (int64_t)false;
|
||||
|
||||
|
||||
+12
-25
@@ -1,43 +1,32 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <torrent/http.h>
|
||||
|
||||
#include "http_queue.h"
|
||||
#include "curl_get.h"
|
||||
|
||||
#include <torrent/net/http_get.h>
|
||||
#include <torrent/net/http_stack.h>
|
||||
|
||||
namespace core {
|
||||
|
||||
HttpQueue::iterator
|
||||
HttpQueue::insert(const std::string& url, std::iostream* s) {
|
||||
std::unique_ptr<CurlGet> h(m_slot_factory());
|
||||
auto itr = base_type::insert(end(), torrent::net_thread::http_stack()->create(url, s));
|
||||
|
||||
h->set_url(url);
|
||||
h->set_stream(s);
|
||||
h->set_timeout(5 * 60);
|
||||
itr->add_done_slot([this, itr]() { erase(itr); });
|
||||
itr->add_failed_slot([this, itr](auto) { erase(itr); });
|
||||
|
||||
iterator signal_itr = base_type::insert(end(), h.get());
|
||||
itr->start();
|
||||
|
||||
h->signal_done().push_back(std::bind(&HttpQueue::erase, this, signal_itr));
|
||||
h->signal_failed().push_back(std::bind(&HttpQueue::erase, this, signal_itr));
|
||||
for (auto& slot : m_signal_insert)
|
||||
slot(*itr);
|
||||
|
||||
(*signal_itr)->start();
|
||||
|
||||
h.release();
|
||||
|
||||
for (signal_curl_get::iterator itr = m_signal_insert.begin(), last = m_signal_insert.end(); itr != last; itr++)
|
||||
(*itr)(*signal_itr);
|
||||
|
||||
return signal_itr;
|
||||
return itr;
|
||||
}
|
||||
|
||||
void
|
||||
HttpQueue::erase(iterator signal_itr) {
|
||||
for (signal_curl_get::iterator itr = m_signal_erase.begin(), last = m_signal_erase.end(); itr != last; itr++)
|
||||
(*itr)(*signal_itr);
|
||||
for (const auto& slot : m_signal_erase)
|
||||
slot(*signal_itr);
|
||||
|
||||
delete *signal_itr;
|
||||
base_type::erase(signal_itr);
|
||||
}
|
||||
|
||||
@@ -45,8 +34,6 @@ void
|
||||
HttpQueue::clear() {
|
||||
while (!empty())
|
||||
erase(begin());
|
||||
|
||||
base_type::clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,17 +4,18 @@
|
||||
#include <functional>
|
||||
#include <iosfwd>
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <torrent/net/http_get.h>
|
||||
|
||||
namespace core {
|
||||
|
||||
class CurlGet;
|
||||
// TODO: Remove this.
|
||||
|
||||
class HttpQueue : private std::list<CurlGet*> {
|
||||
class HttpQueue : private std::list<torrent::net::HttpGet> {
|
||||
public:
|
||||
typedef std::list<CurlGet*> base_type;
|
||||
typedef std::function<CurlGet* ()> slot_factory;
|
||||
typedef std::function<void (CurlGet*)> slot_curl_get;
|
||||
typedef std::list<slot_curl_get> signal_curl_get;
|
||||
using base_type = std::list<torrent::net::HttpGet>;
|
||||
using slot_curl_get = std::function<void (torrent::net::HttpGet)>;
|
||||
using signal_curl_get = std::list<slot_curl_get>;
|
||||
|
||||
using base_type::iterator;
|
||||
using base_type::const_iterator;
|
||||
@@ -42,13 +43,10 @@ public:
|
||||
|
||||
void clear();
|
||||
|
||||
void set_slot_factory(slot_factory s) { m_slot_factory = s; }
|
||||
|
||||
signal_curl_get& signal_insert() { return m_signal_insert; }
|
||||
signal_curl_get& signal_erase() { return m_signal_erase; }
|
||||
|
||||
private:
|
||||
slot_factory m_slot_factory;
|
||||
signal_curl_get m_signal_insert;
|
||||
signal_curl_get m_signal_erase;
|
||||
};
|
||||
|
||||
+11
-27
@@ -18,6 +18,7 @@
|
||||
#include <torrent/exceptions.h>
|
||||
#include <torrent/object_stream.h>
|
||||
#include <torrent/throttle.h>
|
||||
#include <torrent/net/http_stack.h>
|
||||
#include <torrent/utils/log.h>
|
||||
|
||||
#include "rpc/parse_commands.h"
|
||||
@@ -26,15 +27,13 @@
|
||||
#include "utils/file_status_cache.h"
|
||||
|
||||
#include "globals.h"
|
||||
#include "curl_get.h"
|
||||
#include "curl_stack.h"
|
||||
#include "control.h"
|
||||
#include "download.h"
|
||||
#include "download_factory.h"
|
||||
#include "download_store.h"
|
||||
#include "http_queue.h"
|
||||
#include "manager.h"
|
||||
#include "view.h"
|
||||
#include "core/download.h"
|
||||
#include "core/download_factory.h"
|
||||
#include "core/download_store.h"
|
||||
#include "core/http_queue.h"
|
||||
#include "core/manager.h"
|
||||
#include "core/view.h"
|
||||
|
||||
namespace core {
|
||||
|
||||
@@ -57,7 +56,6 @@ Manager::Manager() :
|
||||
m_download_list = std::make_unique<DownloadList>();
|
||||
m_file_status_cache = std::make_unique<FileStatusCache>();
|
||||
m_http_queue = std::make_unique<HttpQueue>();
|
||||
m_http_stack = std::make_unique<CurlStack>();
|
||||
|
||||
torrent::Throttle* unthrottled = torrent::Throttle::create_throttle();
|
||||
unthrottled->set_max_rate(0);
|
||||
@@ -131,31 +129,14 @@ Manager::retrieve_throttle_value(const torrent::Object::string_type& name, bool
|
||||
}
|
||||
}
|
||||
|
||||
// Most of this should be possible to move out.
|
||||
void
|
||||
Manager::initialize_second() {
|
||||
torrent::Http::slot_factory() = std::bind(&CurlStack::new_object, m_http_stack.get());
|
||||
m_http_queue->set_slot_factory(std::bind(&CurlStack::new_object, m_http_stack.get()));
|
||||
|
||||
CurlStack::global_init();
|
||||
}
|
||||
|
||||
void
|
||||
Manager::cleanup() {
|
||||
m_http_stack->shutdown();
|
||||
|
||||
// Need to disconnect log signals? Not really since we won't receive
|
||||
// any more.
|
||||
|
||||
m_download_list->clear();
|
||||
|
||||
// When we implement asynchronous DNS lookups, we need to cancel them
|
||||
// here before the torrent::* objects are deleted.
|
||||
|
||||
torrent::cleanup();
|
||||
|
||||
m_http_stack.reset();
|
||||
CurlStack::global_cleanup();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -229,7 +210,10 @@ Manager::set_bind_address(const std::string& addr) {
|
||||
torrent::connection_manager()->set_bind_address(ai->address()->c_sockaddr());
|
||||
}
|
||||
|
||||
m_http_stack->set_bind_address(!ai->address()->is_address_any() ? ai->address()->address_str() : std::string());
|
||||
if (ai->address()->is_address_any())
|
||||
torrent::net_thread::http_stack()->set_bind_address(std::string());
|
||||
else
|
||||
torrent::net_thread::http_stack()->set_bind_address(ai->address()->address_str());
|
||||
|
||||
rak::address_info::free_address_info(ai);
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ class FileStatusCache;
|
||||
|
||||
namespace core {
|
||||
|
||||
class CurlStack;
|
||||
class DownloadStore;
|
||||
class HttpQueue;
|
||||
|
||||
@@ -42,7 +41,6 @@ public:
|
||||
FileStatusCache* file_status_cache() { return m_file_status_cache.get(); }
|
||||
|
||||
HttpQueue* http_queue() { return m_http_queue.get(); }
|
||||
CurlStack* http_stack() { return m_http_stack.get(); }
|
||||
|
||||
View* hashing_view() { return m_hashingView; }
|
||||
void set_hashing_view(View* v);
|
||||
@@ -59,8 +57,6 @@ public:
|
||||
void set_address_throttle(uint32_t begin, uint32_t end, torrent::ThrottlePair throttles);
|
||||
torrent::ThrottlePair get_address_throttle(const sockaddr* addr);
|
||||
|
||||
// Really should find a more descriptive name.
|
||||
void initialize_second();
|
||||
void cleanup();
|
||||
|
||||
void listen_open();
|
||||
@@ -109,7 +105,6 @@ private:
|
||||
std::unique_ptr<DownloadStore> m_download_store;
|
||||
std::unique_ptr<FileStatusCache> m_file_status_cache;
|
||||
std::unique_ptr<HttpQueue> m_http_queue;
|
||||
std::unique_ptr<CurlStack> m_http_stack;
|
||||
|
||||
View* m_hashingView{};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user