add bind address support to http_connection and replace http_tracker_connection's code with http_connection
This commit is contained in:
parent
ee0e2ad51a
commit
3562c3e646
|
@ -42,10 +42,11 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include <string>
|
||||
|
||||
#include "libtorrent/socket.hpp"
|
||||
#include "libtorrent/http_tracker_connection.hpp"
|
||||
#include "libtorrent/http_parser.hpp"
|
||||
#include "libtorrent/time.hpp"
|
||||
#include "libtorrent/assert.hpp"
|
||||
#include "libtorrent/socket_type.hpp"
|
||||
#include "libtorrent/session_settings.hpp"
|
||||
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
#include "libtorrent/ssl_stream.hpp"
|
||||
|
@ -56,6 +57,7 @@ namespace libtorrent
|
|||
{
|
||||
|
||||
struct http_connection;
|
||||
struct connection_queue;
|
||||
|
||||
typedef boost::function<void(asio::error_code const&
|
||||
, http_parser const&, char const* data, int size)> http_handler;
|
||||
|
@ -101,11 +103,11 @@ struct http_connection : boost::enable_shared_from_this<http_connection>, boost:
|
|||
|
||||
void get(std::string const& url, time_duration timeout = seconds(30)
|
||||
, proxy_settings const* ps = 0, int handle_redirects = 5
|
||||
, std::string const& user_agent = "");
|
||||
, std::string const& user_agent = "", address const& bind_addr = address_v4::any());
|
||||
|
||||
void start(std::string const& hostname, std::string const& port
|
||||
, time_duration timeout, proxy_settings const* ps = 0, bool ssl = false
|
||||
, int handle_redirect = 5);
|
||||
, int handle_redirect = 5, address const& bind_addr = address_v4::any());
|
||||
|
||||
void close();
|
||||
|
||||
|
@ -183,6 +185,10 @@ private:
|
|||
|
||||
// true if the connection is using ssl
|
||||
bool m_ssl;
|
||||
|
||||
// the address to bind to. address_v4::any()
|
||||
// means do not bind
|
||||
address m_bind_addr;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -33,44 +33,31 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#ifndef TORRENT_HTTP_TRACKER_CONNECTION_HPP_INCLUDED
|
||||
#define TORRENT_HTTP_TRACKER_CONNECTION_HPP_INCLUDED
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <ctime>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push, 1)
|
||||
#endif
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/cstdint.hpp>
|
||||
#include <boost/tuple/tuple.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#include "libtorrent/socket.hpp"
|
||||
#include "libtorrent/entry.hpp"
|
||||
#include "libtorrent/session_settings.hpp"
|
||||
#include "libtorrent/peer_id.hpp"
|
||||
#include "libtorrent/peer.hpp"
|
||||
#include "libtorrent/tracker_manager.hpp"
|
||||
#include "libtorrent/config.hpp"
|
||||
#include "libtorrent/buffer.hpp"
|
||||
#include "libtorrent/socket_type.hpp"
|
||||
#include "libtorrent/connection_queue.hpp"
|
||||
#include "libtorrent/http_parser.hpp"
|
||||
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
#include "libtorrent/ssl_stream.hpp"
|
||||
#include "libtorrent/variant_stream.hpp"
|
||||
#endif
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
|
||||
class http_connection;
|
||||
class entry;
|
||||
class http_parser;
|
||||
class connection_queue;
|
||||
class session_settings;
|
||||
|
||||
class TORRENT_EXPORT http_tracker_connection
|
||||
: public tracker_connection
|
||||
{
|
||||
|
@ -99,47 +86,16 @@ namespace libtorrent
|
|||
boost::intrusive_ptr<http_tracker_connection> self()
|
||||
{ return boost::intrusive_ptr<http_tracker_connection>(this); }
|
||||
|
||||
void on_response();
|
||||
|
||||
void init_send_buffer(
|
||||
std::string const& hostname
|
||||
, std::string const& request);
|
||||
void on_response(asio::error_code const& ec, http_parser const& parser
|
||||
, char const* data, int size);
|
||||
|
||||
void name_lookup(asio::error_code const& error, tcp::resolver::iterator i);
|
||||
void connect(int ticket, tcp::endpoint target_address);
|
||||
void connected(asio::error_code const& error);
|
||||
void sent(asio::error_code const& error);
|
||||
void receive(asio::error_code const& error
|
||||
, std::size_t bytes_transferred);
|
||||
virtual void on_timeout() {}
|
||||
|
||||
virtual void on_timeout();
|
||||
|
||||
void parse(const entry& e);
|
||||
void parse(int status_code, const entry& e);
|
||||
bool extract_peer_info(const entry& e, peer_entry& ret);
|
||||
|
||||
tracker_manager& m_man;
|
||||
http_parser m_parser;
|
||||
|
||||
tcp::resolver m_name_lookup;
|
||||
int m_port;
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
variant_stream<socket_type, ssl_stream<socket_type> > m_socket;
|
||||
bool m_ssl;
|
||||
#else
|
||||
socket_type m_socket;
|
||||
#endif
|
||||
int m_recv_pos;
|
||||
std::vector<char> m_buffer;
|
||||
std::string m_send_buffer;
|
||||
|
||||
session_settings const& m_settings;
|
||||
proxy_settings const& m_proxy;
|
||||
std::string m_password;
|
||||
|
||||
bool m_timed_out;
|
||||
|
||||
int m_connection_ticket;
|
||||
connection_queue& m_cc;
|
||||
boost::shared_ptr<http_connection> m_tracker_connection;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -70,8 +70,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include "libtorrent/config.hpp"
|
||||
// parse_url
|
||||
#include "libtorrent/tracker_manager.hpp"
|
||||
// http_parser
|
||||
#include "libtorrent/http_tracker_connection.hpp"
|
||||
#include "libtorrent/http_parser.hpp"
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
|
|
|
@ -34,6 +34,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include "libtorrent/escape_string.hpp"
|
||||
#include "libtorrent/instantiate_connection.hpp"
|
||||
#include "libtorrent/gzip.hpp"
|
||||
#include "libtorrent/tracker_manager.hpp"
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
@ -44,16 +45,12 @@ using boost::bind;
|
|||
|
||||
namespace libtorrent {
|
||||
|
||||
namespace
|
||||
{
|
||||
char to_lower(char c) { return std::tolower(c); }
|
||||
}
|
||||
|
||||
enum { max_bottled_buffer = 1024 * 1024 };
|
||||
|
||||
|
||||
void http_connection::get(std::string const& url, time_duration timeout
|
||||
, proxy_settings const* ps, int handle_redirects, std::string const& user_agent)
|
||||
, proxy_settings const* ps, int handle_redirects, std::string const& user_agent
|
||||
, address const& bind_addr)
|
||||
{
|
||||
std::string protocol;
|
||||
std::string auth;
|
||||
|
@ -106,16 +103,16 @@ void http_connection::get(std::string const& url, time_duration timeout
|
|||
|
||||
sendbuffer = headers.str();
|
||||
start(hostname, boost::lexical_cast<std::string>(port), timeout, ps
|
||||
, ssl, handle_redirects);
|
||||
, ssl, handle_redirects, bind_addr);
|
||||
}
|
||||
|
||||
void http_connection::start(std::string const& hostname, std::string const& port
|
||||
, time_duration timeout, proxy_settings const* ps, bool ssl, int handle_redirects)
|
||||
, time_duration timeout, proxy_settings const* ps, bool ssl, int handle_redirects
|
||||
, address const& bind_addr)
|
||||
{
|
||||
m_redirects = handle_redirects;
|
||||
if (ps) m_proxy = *ps;
|
||||
|
||||
m_ssl = ssl;
|
||||
m_timeout = timeout;
|
||||
asio::error_code ec;
|
||||
m_timer.expires_from_now(m_timeout, ec);
|
||||
|
@ -126,13 +123,22 @@ void http_connection::start(std::string const& hostname, std::string const& port
|
|||
m_recvbuffer.clear();
|
||||
m_read_pos = 0;
|
||||
|
||||
if (m_sock.is_open() && m_hostname == hostname && m_port == port)
|
||||
if (ec)
|
||||
{
|
||||
callback(ec);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_sock.is_open() && m_hostname == hostname && m_port == port
|
||||
&& m_ssl == ssl && m_bind_addr == bind_addr)
|
||||
{
|
||||
asio::async_write(m_sock, asio::buffer(sendbuffer)
|
||||
, bind(&http_connection::on_write, shared_from_this(), _1));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ssl = ssl;
|
||||
m_bind_addr = bind_addr;
|
||||
asio::error_code ec;
|
||||
m_sock.close(ec);
|
||||
|
||||
|
@ -155,6 +161,16 @@ void http_connection::start(std::string const& hostname, std::string const& port
|
|||
bool ret = instantiate_connection(m_resolver.get_io_service(), m_proxy, m_sock);
|
||||
TORRENT_ASSERT(ret);
|
||||
#endif
|
||||
if (m_bind_addr != address_v4::any())
|
||||
{
|
||||
asio::error_code ec;
|
||||
m_sock.bind(tcp::endpoint(m_bind_addr, 0), ec);
|
||||
if (ec)
|
||||
{
|
||||
callback(ec);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
tcp::resolver::query query(hostname, port);
|
||||
m_resolver.async_resolve(query, bind(&http_connection::on_resolve
|
||||
|
@ -212,7 +228,7 @@ void http_connection::close()
|
|||
}
|
||||
|
||||
void http_connection::on_resolve(asio::error_code const& e
|
||||
, tcp::resolver::iterator i)
|
||||
, tcp::resolver::iterator i)
|
||||
{
|
||||
if (e)
|
||||
{
|
||||
|
@ -221,7 +237,22 @@ void http_connection::on_resolve(asio::error_code const& e
|
|||
return;
|
||||
}
|
||||
TORRENT_ASSERT(i != tcp::resolver::iterator());
|
||||
m_cc.enqueue(bind(&http_connection::connect, shared_from_this(), _1, *i)
|
||||
|
||||
// look for an address that has the same kind as the one
|
||||
// we're binding to. To make sure a tracker get our
|
||||
// correct listening address.
|
||||
tcp::resolver::iterator target = i;
|
||||
tcp::resolver::iterator end;
|
||||
tcp::endpoint target_address = *i;
|
||||
for (; target != end && target->endpoint().address().is_v4()
|
||||
!= m_bind_addr.is_v4(); ++target);
|
||||
|
||||
if (target != end)
|
||||
{
|
||||
target_address = *target;
|
||||
}
|
||||
|
||||
m_cc.enqueue(bind(&http_connection::connect, shared_from_this(), _1, target_address)
|
||||
, bind(&http_connection::on_connect_timeout, shared_from_this())
|
||||
, m_timeout);
|
||||
}
|
||||
|
|
|
@ -54,29 +54,15 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
#include "libtorrent/tracker_manager.hpp"
|
||||
#include "libtorrent/http_tracker_connection.hpp"
|
||||
#include "libtorrent/http_connection.hpp"
|
||||
#include "libtorrent/entry.hpp"
|
||||
#include "libtorrent/bencode.hpp"
|
||||
#include "libtorrent/torrent.hpp"
|
||||
#include "libtorrent/io.hpp"
|
||||
#include "libtorrent/instantiate_connection.hpp"
|
||||
|
||||
using namespace libtorrent;
|
||||
using boost::bind;
|
||||
|
||||
namespace
|
||||
{
|
||||
enum
|
||||
{
|
||||
minimum_tracker_response_length = 3,
|
||||
http_buffer_size = 2048
|
||||
};
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
char to_lower(char c) { return std::tolower(c); }
|
||||
}
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
|
||||
|
@ -96,571 +82,155 @@ namespace libtorrent
|
|||
, std::string const& auth)
|
||||
: tracker_connection(man, req, ios, bind_infc, c)
|
||||
, m_man(man)
|
||||
, m_name_lookup(ios)
|
||||
, m_port(port)
|
||||
, m_socket(ios)
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
, m_ssl(protocol == "https")
|
||||
#endif
|
||||
, m_recv_pos(0)
|
||||
, m_buffer(http_buffer_size)
|
||||
, m_settings(stn)
|
||||
, m_proxy(ps)
|
||||
, m_password(auth)
|
||||
, m_timed_out(false)
|
||||
, m_connection_ticket(-1)
|
||||
, m_cc(cc)
|
||||
{
|
||||
m_send_buffer.assign("GET ");
|
||||
// TODO: authentication
|
||||
std::string url = req.url;
|
||||
|
||||
// should we use the proxy?
|
||||
if (m_proxy.type == proxy_settings::http
|
||||
|| m_proxy.type == proxy_settings::http_pw)
|
||||
{
|
||||
m_send_buffer += "http://";
|
||||
m_send_buffer += hostname;
|
||||
if (port != 80)
|
||||
{
|
||||
m_send_buffer += ":";
|
||||
m_send_buffer += boost::lexical_cast<std::string>(port);
|
||||
}
|
||||
}
|
||||
|
||||
if (tracker_req().kind == tracker_request::scrape_request)
|
||||
if (req.kind == tracker_request::scrape_request)
|
||||
{
|
||||
// find and replace "announce" with "scrape"
|
||||
// in request
|
||||
|
||||
std::size_t pos = request.find("announce");
|
||||
std::size_t pos = url.find("announce");
|
||||
if (pos == std::string::npos)
|
||||
{
|
||||
fail(-1, ("scrape is not available on url: '"
|
||||
+ tracker_req().url +"'").c_str());
|
||||
+ req.url +"'").c_str());
|
||||
return;
|
||||
}
|
||||
request.replace(pos, 8, "scrape");
|
||||
url.replace(pos, 8, "scrape");
|
||||
}
|
||||
|
||||
m_send_buffer += request;
|
||||
|
||||
|
||||
// if request-string already contains
|
||||
// some parameters, append an ampersand instead
|
||||
// of a question mark
|
||||
size_t arguments_start = request.find('?');
|
||||
size_t arguments_start = url.find('?');
|
||||
if (arguments_start != std::string::npos)
|
||||
m_send_buffer += "&";
|
||||
url += "&";
|
||||
else
|
||||
m_send_buffer += "?";
|
||||
url += "?";
|
||||
|
||||
if (!url_has_argument(request, "info_hash"))
|
||||
url += "info_hash=";
|
||||
url += escape_string(
|
||||
reinterpret_cast<const char*>(req.info_hash.begin()), 20);
|
||||
|
||||
if (req.kind == tracker_request::announce_request)
|
||||
{
|
||||
m_send_buffer += "info_hash=";
|
||||
m_send_buffer += escape_string(
|
||||
reinterpret_cast<const char*>(req.info_hash.begin()), 20);
|
||||
m_send_buffer += '&';
|
||||
}
|
||||
url += "&peer_id=";
|
||||
url += escape_string(
|
||||
reinterpret_cast<const char*>(req.pid.begin()), 20);
|
||||
|
||||
if (tracker_req().kind == tracker_request::announce_request)
|
||||
{
|
||||
if (!url_has_argument(request, "peer_id"))
|
||||
{
|
||||
m_send_buffer += "peer_id=";
|
||||
m_send_buffer += escape_string(
|
||||
reinterpret_cast<const char*>(req.pid.begin()), 20);
|
||||
m_send_buffer += '&';
|
||||
}
|
||||
url += "&port=";
|
||||
url += boost::lexical_cast<std::string>(req.listen_port);
|
||||
|
||||
if (!url_has_argument(request, "port"))
|
||||
{
|
||||
m_send_buffer += "port=";
|
||||
m_send_buffer += boost::lexical_cast<std::string>(req.listen_port);
|
||||
m_send_buffer += '&';
|
||||
}
|
||||
url += "&uploaded=";
|
||||
url += boost::lexical_cast<std::string>(req.uploaded);
|
||||
|
||||
if (!url_has_argument(request, "uploaded"))
|
||||
{
|
||||
m_send_buffer += "uploaded=";
|
||||
m_send_buffer += boost::lexical_cast<std::string>(req.uploaded);
|
||||
m_send_buffer += '&';
|
||||
}
|
||||
url += "&downloaded=";
|
||||
url += boost::lexical_cast<std::string>(req.downloaded);
|
||||
|
||||
if (!url_has_argument(request, "downloaded"))
|
||||
{
|
||||
m_send_buffer += "downloaded=";
|
||||
m_send_buffer += boost::lexical_cast<std::string>(req.downloaded);
|
||||
m_send_buffer += '&';
|
||||
}
|
||||
|
||||
if (!url_has_argument(request, "left"))
|
||||
{
|
||||
m_send_buffer += "left=";
|
||||
m_send_buffer += boost::lexical_cast<std::string>(req.left);
|
||||
m_send_buffer += '&';
|
||||
}
|
||||
url += "&left=";
|
||||
url += boost::lexical_cast<std::string>(req.left);
|
||||
|
||||
if (req.event != tracker_request::none)
|
||||
{
|
||||
if (!url_has_argument(request, "event"))
|
||||
{
|
||||
const char* event_string[] = {"completed", "started", "stopped"};
|
||||
m_send_buffer += "event=";
|
||||
m_send_buffer += event_string[req.event - 1];
|
||||
m_send_buffer += '&';
|
||||
}
|
||||
}
|
||||
if (!url_has_argument(request, "key"))
|
||||
{
|
||||
m_send_buffer += "key=";
|
||||
std::stringstream key_string;
|
||||
key_string << std::hex << req.key;
|
||||
m_send_buffer += key_string.str();
|
||||
m_send_buffer += '&';
|
||||
const char* event_string[] = {"completed", "started", "stopped"};
|
||||
url += "&event=";
|
||||
url += event_string[req.event - 1];
|
||||
}
|
||||
|
||||
if (!url_has_argument(request, "compact"))
|
||||
url += "&key=";
|
||||
std::stringstream key_string;
|
||||
key_string << std::hex << req.key;
|
||||
url += key_string.str();
|
||||
|
||||
url += "&compact=1";
|
||||
|
||||
url += "&numwant=";
|
||||
url += boost::lexical_cast<std::string>(
|
||||
(std::min)(req.num_want, 999));
|
||||
|
||||
if (stn.announce_ip != address())
|
||||
{
|
||||
m_send_buffer += "compact=1&";
|
||||
}
|
||||
if (!url_has_argument(request, "numwant"))
|
||||
{
|
||||
m_send_buffer += "numwant=";
|
||||
m_send_buffer += boost::lexical_cast<std::string>(
|
||||
(std::min)(req.num_want, 999));
|
||||
m_send_buffer += '&';
|
||||
}
|
||||
if (m_settings.announce_ip != address() && !url_has_argument(request, "ip"))
|
||||
{
|
||||
m_send_buffer += "ip=";
|
||||
m_send_buffer += m_settings.announce_ip.to_string();
|
||||
m_send_buffer += '&';
|
||||
url += "&ip=";
|
||||
url += stn.announce_ip.to_string();
|
||||
}
|
||||
|
||||
#ifndef TORRENT_DISABLE_ENCRYPTION
|
||||
m_send_buffer += "supportcrypto=1&";
|
||||
url += "&supportcrypto=1";
|
||||
#endif
|
||||
|
||||
if (!url_has_argument(request, "ipv6") && !req.ipv6.empty())
|
||||
{
|
||||
m_send_buffer += "ipv6=";
|
||||
m_send_buffer += req.ipv6;
|
||||
m_send_buffer += '&';
|
||||
}
|
||||
url += "&ipv6=";
|
||||
url += req.ipv6;
|
||||
|
||||
// extension that tells the tracker that
|
||||
// we don't need any peer_id's in the response
|
||||
if (!url_has_argument(request, "no_peer_id"))
|
||||
{
|
||||
m_send_buffer += "no_peer_id=1";
|
||||
}
|
||||
else
|
||||
{
|
||||
// remove the trailing '&'
|
||||
m_send_buffer.resize(m_send_buffer.size() - 1);
|
||||
}
|
||||
url += "&no_peer_id=1";
|
||||
}
|
||||
|
||||
m_send_buffer += " HTTP/1.0\r\nAccept-Encoding: gzip\r\n"
|
||||
"User-Agent: ";
|
||||
m_send_buffer += m_settings.user_agent;
|
||||
m_send_buffer += "\r\n"
|
||||
"Host: ";
|
||||
m_send_buffer += hostname;
|
||||
if (port != 80)
|
||||
{
|
||||
m_send_buffer += ':';
|
||||
m_send_buffer += boost::lexical_cast<std::string>(port);
|
||||
}
|
||||
if (m_proxy.type == proxy_settings::http_pw)
|
||||
{
|
||||
m_send_buffer += "\r\nProxy-Authorization: Basic ";
|
||||
m_send_buffer += base64encode(m_proxy.username + ":" + m_proxy.password);
|
||||
}
|
||||
if (!auth.empty())
|
||||
{
|
||||
m_send_buffer += "\r\nAuthorization: Basic ";
|
||||
m_send_buffer += base64encode(auth);
|
||||
}
|
||||
m_send_buffer += "\r\n\r\n";
|
||||
m_tracker_connection.reset(new http_connection(ios, cc
|
||||
, boost::bind(&http_tracker_connection::on_response, self(), _1, _2, _3, _4)));
|
||||
|
||||
m_tracker_connection->get(url, seconds(stn.tracker_completion_timeout)
|
||||
, &ps, 5, stn.user_agent, bind_infc);
|
||||
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
|
||||
boost::shared_ptr<request_callback> cb = requester();
|
||||
if (cb)
|
||||
{
|
||||
cb->debug_log("==> TRACKER_REQUEST [ ih: " + boost::lexical_cast<std::string>(req.info_hash)
|
||||
+ " str: " + m_send_buffer + " ]");
|
||||
cb->debug_log("==> TRACKER_REQUEST [ url: " + url + " ]");
|
||||
}
|
||||
#endif
|
||||
|
||||
tcp::resolver::query q(hostname
|
||||
, boost::lexical_cast<std::string>(m_port));
|
||||
m_name_lookup.async_resolve(q,
|
||||
boost::bind(&http_tracker_connection::name_lookup, self(), _1, _2));
|
||||
set_timeout(req.event == tracker_request::stopped
|
||||
? m_settings.stop_tracker_timeout
|
||||
: m_settings.tracker_completion_timeout
|
||||
, m_settings.tracker_receive_timeout);
|
||||
}
|
||||
|
||||
void http_tracker_connection::on_timeout()
|
||||
{
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
boost::shared_ptr<request_callback> cb = requester();
|
||||
if (cb) cb->debug_log("*** HTTP_TRACKER [ timed out ]");
|
||||
#endif
|
||||
m_timed_out = true;
|
||||
asio::error_code ec;
|
||||
m_socket.close(ec);
|
||||
m_name_lookup.cancel();
|
||||
if (m_connection_ticket > -1) m_cc.done(m_connection_ticket);
|
||||
m_connection_ticket = -1;
|
||||
fail_timeout();
|
||||
}
|
||||
|
||||
void http_tracker_connection::close()
|
||||
{
|
||||
asio::error_code ec;
|
||||
m_socket.close(ec);
|
||||
m_name_lookup.cancel();
|
||||
if (m_connection_ticket > -1) m_cc.done(m_connection_ticket);
|
||||
m_connection_ticket = -1;
|
||||
m_timed_out = true;
|
||||
if (m_tracker_connection)
|
||||
{
|
||||
m_tracker_connection->close();
|
||||
m_tracker_connection.reset();
|
||||
}
|
||||
tracker_connection::close();
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
boost::shared_ptr<request_callback> cb = requester();
|
||||
if (cb) cb->debug_log("*** HTTP_TRACKER [ close: "
|
||||
+ boost::lexical_cast<std::string>(m_man.num_requests()) + " ]");
|
||||
#endif
|
||||
}
|
||||
|
||||
void http_tracker_connection::name_lookup(asio::error_code const& error
|
||||
, tcp::resolver::iterator i)
|
||||
void http_tracker_connection::on_response(asio::error_code const& ec
|
||||
, http_parser const& parser, char const* data, int size)
|
||||
{
|
||||
boost::shared_ptr<request_callback> cb = requester();
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
if (cb) cb->debug_log("*** HTTP_TRACKER [ tracker name lookup handler called ]");
|
||||
#endif
|
||||
if (error == asio::error::operation_aborted) return;
|
||||
if (m_timed_out) return;
|
||||
|
||||
if (error || i == tcp::resolver::iterator())
|
||||
{
|
||||
fail(-1, error.message().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
if (cb) cb->debug_log("*** HTTP_TRACKER [ name lookup successful ]");
|
||||
#endif
|
||||
restart_read_timeout();
|
||||
|
||||
// look for an address that has the same kind as the one
|
||||
// we're listening on. To make sure the tracker get our
|
||||
// correct listening address.
|
||||
tcp::resolver::iterator target = i;
|
||||
tcp::resolver::iterator end;
|
||||
tcp::endpoint target_address = *i;
|
||||
for (; target != end && target->endpoint().address().is_v4()
|
||||
!= bind_interface().is_v4(); ++target);
|
||||
if (target == end)
|
||||
{
|
||||
TORRENT_ASSERT(target_address.address().is_v4() != bind_interface().is_v4());
|
||||
if (cb)
|
||||
{
|
||||
std::string tracker_address_type = target_address.address().is_v4() ? "IPv4" : "IPv6";
|
||||
std::string bind_address_type = bind_interface().is_v4() ? "IPv4" : "IPv6";
|
||||
cb->tracker_warning("the tracker only resolves to an "
|
||||
+ tracker_address_type + " address, and you're listening on an "
|
||||
+ bind_address_type + " socket. This may prevent you from receiving incoming connections.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
target_address = *target;
|
||||
}
|
||||
|
||||
if (cb) cb->m_tracker_address = target_address;
|
||||
asio::io_service& ios = m_name_lookup.io_service();
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
if (m_ssl)
|
||||
{
|
||||
m_socket.instantiate<ssl_stream<socket_type> >(ios);
|
||||
ssl_stream<socket_type>& s = m_socket.get<ssl_stream<socket_type> >();
|
||||
bool ret = instantiate_connection(ios, m_proxy, s.next_layer());
|
||||
TORRENT_ASSERT(ret);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_socket.instantiate<socket_type>(ios);
|
||||
bool ret = instantiate_connection(ios, m_proxy, m_socket.get<socket_type>());
|
||||
TORRENT_ASSERT(ret);
|
||||
}
|
||||
#else
|
||||
bool ret = instantiate_connection(ios, m_proxy, m_socket);
|
||||
TORRENT_ASSERT(ret);
|
||||
#endif
|
||||
|
||||
if (m_proxy.type == proxy_settings::http
|
||||
|| m_proxy.type == proxy_settings::http_pw)
|
||||
{
|
||||
// the tracker connection will talk immediately to
|
||||
// the proxy, without requiring CONNECT support
|
||||
m_socket.get<http_stream>().set_no_connect(true);
|
||||
}
|
||||
|
||||
asio::error_code ec;
|
||||
m_socket.open(target_address.protocol(), ec);
|
||||
if (ec)
|
||||
{
|
||||
fail(-1, ec.message().c_str());
|
||||
return;
|
||||
}
|
||||
m_socket.bind(tcp::endpoint(bind_interface(), 0), ec);
|
||||
if (ec)
|
||||
{
|
||||
fail(-1, ec.message().c_str());
|
||||
return;
|
||||
}
|
||||
m_cc.enqueue(bind(&http_tracker_connection::connect, self(), _1, target_address)
|
||||
, bind(&http_tracker_connection::on_timeout, self())
|
||||
, seconds(m_settings.tracker_receive_timeout));
|
||||
}
|
||||
|
||||
void http_tracker_connection::connect(int ticket, tcp::endpoint target_address)
|
||||
{
|
||||
m_connection_ticket = ticket;
|
||||
m_socket.async_connect(target_address, bind(&http_tracker_connection::connected, self(), _1));
|
||||
}
|
||||
|
||||
void http_tracker_connection::connected(asio::error_code const& error)
|
||||
{
|
||||
if (m_connection_ticket > -1) m_cc.done(m_connection_ticket);
|
||||
m_connection_ticket = -1;
|
||||
if (error == asio::error::operation_aborted) return;
|
||||
if (m_timed_out) return;
|
||||
if (error)
|
||||
{
|
||||
fail(-1, error.message().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
boost::shared_ptr<request_callback> cb = requester();
|
||||
if (cb) cb->debug_log("*** HTTP_TRACKER [ connection successful ]");
|
||||
#endif
|
||||
|
||||
restart_read_timeout();
|
||||
async_write(m_socket, asio::buffer(m_send_buffer.c_str()
|
||||
, m_send_buffer.size()), bind(&http_tracker_connection::sent
|
||||
, self(), _1));
|
||||
}
|
||||
|
||||
void http_tracker_connection::sent(asio::error_code const& error)
|
||||
{
|
||||
if (error == asio::error::operation_aborted) return;
|
||||
if (m_timed_out) return;
|
||||
if (error)
|
||||
{
|
||||
fail(-1, error.message().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
boost::shared_ptr<request_callback> cb = requester();
|
||||
if (cb) cb->debug_log("*** HTTP_TRACKER [ send completed ]");
|
||||
#endif
|
||||
restart_read_timeout();
|
||||
TORRENT_ASSERT(m_buffer.size() - m_recv_pos > 0);
|
||||
m_socket.async_read_some(asio::buffer(&m_buffer[m_recv_pos]
|
||||
, m_buffer.size() - m_recv_pos), bind(&http_tracker_connection::receive
|
||||
, self(), _1, _2));
|
||||
}
|
||||
|
||||
void http_tracker_connection::receive(asio::error_code const& error
|
||||
, std::size_t bytes_transferred)
|
||||
{
|
||||
if (error == asio::error::operation_aborted) return;
|
||||
if (m_timed_out) return;
|
||||
|
||||
if (error)
|
||||
{
|
||||
if (error == asio::error::eof)
|
||||
{
|
||||
on_response();
|
||||
close();
|
||||
return;
|
||||
}
|
||||
|
||||
fail(-1, error.message().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
restart_read_timeout();
|
||||
TORRENT_ASSERT(bytes_transferred > 0);
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
boost::shared_ptr<request_callback> cb = requester();
|
||||
if (cb) cb->debug_log("*** HTTP_TRACKER [ reading: "
|
||||
+ boost::lexical_cast<std::string>(bytes_transferred) + " ]");
|
||||
#endif
|
||||
|
||||
m_recv_pos += bytes_transferred;
|
||||
bool e = false;
|
||||
m_parser.incoming(buffer::const_interval(&m_buffer[0]
|
||||
, &m_buffer[0] + m_recv_pos), e);
|
||||
if (e)
|
||||
{
|
||||
fail(-1, "incorrect http response");
|
||||
}
|
||||
|
||||
// if the receive buffer is full, expand it with http_buffer_size
|
||||
if ((int)m_buffer.size() == m_recv_pos)
|
||||
{
|
||||
if ((int)m_buffer.size() >= m_settings.tracker_maximum_response_length)
|
||||
{
|
||||
fail(200, "too large tracker response");
|
||||
return;
|
||||
}
|
||||
TORRENT_ASSERT(http_buffer_size > 0);
|
||||
if ((int)m_buffer.size() + http_buffer_size
|
||||
> m_settings.tracker_maximum_response_length)
|
||||
m_buffer.resize(m_settings.tracker_maximum_response_length);
|
||||
else
|
||||
m_buffer.resize(m_buffer.size() + http_buffer_size);
|
||||
}
|
||||
|
||||
if (m_parser.header_finished())
|
||||
{
|
||||
int cl = atoi(m_parser.header("content-length").c_str());
|
||||
if (cl > m_settings.tracker_maximum_response_length)
|
||||
{
|
||||
fail(-1, "content-length is greater than maximum response length");
|
||||
return;
|
||||
}
|
||||
|
||||
if (cl > 0 && cl < minimum_tracker_response_length && m_parser.status_code() == 200)
|
||||
{
|
||||
fail(-1, "content-length is smaller than minimum response length");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_parser.finished())
|
||||
{
|
||||
on_response();
|
||||
close();
|
||||
return;
|
||||
}
|
||||
|
||||
TORRENT_ASSERT(m_buffer.size() - m_recv_pos > 0);
|
||||
m_socket.async_read_some(asio::buffer(&m_buffer[m_recv_pos]
|
||||
, m_buffer.size() - m_recv_pos), bind(&http_tracker_connection::receive
|
||||
, self(), _1, _2));
|
||||
}
|
||||
|
||||
void http_tracker_connection::on_response()
|
||||
{
|
||||
if (!m_parser.header_finished())
|
||||
if (!parser.header_finished())
|
||||
{
|
||||
fail(-1, "premature end of file");
|
||||
return;
|
||||
}
|
||||
|
||||
std::string location = m_parser.header("location");
|
||||
|
||||
boost::shared_ptr<request_callback> cb = requester();
|
||||
if (parser.status_code() != 200)
|
||||
{
|
||||
fail(parser.status_code(), parser.message().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (ec && ec != asio::error::eof)
|
||||
{
|
||||
fail(parser.status_code(), ec.message().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_parser.status_code() >= 300 && m_parser.status_code() < 400)
|
||||
{
|
||||
if (location.empty())
|
||||
{
|
||||
std::string error_str = "got redirection response (";
|
||||
error_str += boost::lexical_cast<std::string>(m_parser.status_code());
|
||||
error_str += ") without 'Location' header";
|
||||
fail(-1, error_str.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// if the protocol isn't specified, assume http
|
||||
if (location.compare(0, 7, "http://") != 0
|
||||
&& location.compare(0, 6, "udp://") != 0)
|
||||
{
|
||||
location.insert(0, "http://");
|
||||
}
|
||||
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
if (cb) cb->debug_log("*** HTTP_TRACKER [ redirecting to: " + location + "]");
|
||||
#endif
|
||||
if (cb) cb->tracker_warning("Redirecting to \"" + location + "\"");
|
||||
tracker_request req = tracker_req();
|
||||
|
||||
req.url = location;
|
||||
|
||||
m_man.queue_request(m_name_lookup.get_io_service(), m_cc, req
|
||||
, m_password, bind_interface(), m_requester);
|
||||
close();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_parser.status_code() != 200)
|
||||
{
|
||||
fail(m_parser.status_code(), m_parser.message().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
buffer::const_interval buf(&m_buffer[0] + m_parser.body_start(), &m_buffer[0] + m_recv_pos);
|
||||
|
||||
std::string content_encoding = m_parser.header("content-encoding");
|
||||
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
if (cb) cb->debug_log("*** HTTP_TRACKER [ content-encoding: " + content_encoding + "]");
|
||||
#endif
|
||||
|
||||
if (content_encoding == "gzip" || content_encoding == "x-gzip")
|
||||
{
|
||||
if (!cb)
|
||||
{
|
||||
close();
|
||||
return;
|
||||
}
|
||||
std::vector<char> buffer;
|
||||
std::string error;
|
||||
if (inflate_gzip(&m_buffer[0] + m_parser.body_start(), m_buffer.size(), buffer
|
||||
, m_settings.tracker_maximum_response_length, error))
|
||||
{
|
||||
cb->tracker_request_error(tracker_req(), 200, error);
|
||||
close();
|
||||
return;
|
||||
}
|
||||
m_buffer.swap(buffer);
|
||||
buf.begin = &m_buffer[0];
|
||||
buf.end = &m_buffer[0] + m_buffer.size();
|
||||
}
|
||||
else if (!content_encoding.empty())
|
||||
{
|
||||
std::string error_str = "unknown content encoding in response: \"";
|
||||
error_str += content_encoding;
|
||||
error_str += "\"";
|
||||
fail(-1, error_str.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// handle tracker response
|
||||
entry e = bdecode(buf.begin, buf.end);
|
||||
entry e = bdecode(data, data + size);
|
||||
|
||||
if (e.type() != entry::undefined_t)
|
||||
{
|
||||
parse(e);
|
||||
parse(parser.status_code(), e);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string error_str("invalid bencoding of tracker response: \"");
|
||||
for (char const* i = buf.begin, *end(buf.end); i != end; ++i)
|
||||
for (char const* i = data, *end(data + size); i != end; ++i)
|
||||
{
|
||||
if (std::isprint(*i)) error_str += *i;
|
||||
else error_str += "0x" + boost::lexical_cast<std::string>((unsigned int)*i) + " ";
|
||||
}
|
||||
error_str += "\"";
|
||||
fail(m_parser.status_code(), error_str.c_str());
|
||||
fail(parser.status_code(), error_str.c_str());
|
||||
}
|
||||
close();
|
||||
}
|
||||
|
@ -710,7 +280,7 @@ namespace libtorrent
|
|||
return true;
|
||||
}
|
||||
|
||||
void http_tracker_connection::parse(entry const& e)
|
||||
void http_tracker_connection::parse(int status_code, entry const& e)
|
||||
{
|
||||
boost::shared_ptr<request_callback> cb = requester();
|
||||
if (!cb) return;
|
||||
|
@ -719,7 +289,7 @@ namespace libtorrent
|
|||
entry const* failure = e.find_key("failure reason");
|
||||
if (failure && failure->type() == entry::string_t)
|
||||
{
|
||||
fail(m_parser.status_code(), failure->string().c_str());
|
||||
fail(status_code, failure->string().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,8 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include "libtorrent/lsd.hpp"
|
||||
#include "libtorrent/io.hpp"
|
||||
#include "libtorrent/http_tracker_connection.hpp"
|
||||
#include "libtorrent/buffer.hpp"
|
||||
#include "libtorrent/http_parser.hpp"
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/ref.hpp>
|
||||
|
|
Loading…
Reference in New Issue