add bind address support to http_connection and replace http_tracker_connection's code with http_connection

This commit is contained in:
Arvid Norberg 2008-01-31 08:24:01 +00:00
parent ee0e2ad51a
commit 3562c3e646
6 changed files with 145 additions and 581 deletions

View File

@ -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;
};
}

View File

@ -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;
};
}

View File

@ -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
{

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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>