http_connection now supports connecting to all IPs a hostname resolves to, as fallbacks

This commit is contained in:
Arvid Norberg 2008-05-18 22:14:55 +00:00
parent 282f30c7e7
commit c5d61667b3
3 changed files with 64 additions and 36 deletions

View File

@ -39,6 +39,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include <boost/enable_shared_from_this.hpp> #include <boost/enable_shared_from_this.hpp>
#include <boost/noncopyable.hpp> #include <boost/noncopyable.hpp>
#include <vector> #include <vector>
#include <list>
#include <string> #include <string>
#include "libtorrent/socket.hpp" #include "libtorrent/socket.hpp"
@ -91,6 +92,7 @@ struct http_connection : boost::enable_shared_from_this<http_connection>, boost:
, m_cc(cc) , m_cc(cc)
, m_ssl(false) , m_ssl(false)
, m_priority(0) , m_priority(0)
, m_abort(false)
{ {
TORRENT_ASSERT(!m_handler.empty()); TORRENT_ASSERT(!m_handler.empty());
} }
@ -123,10 +125,10 @@ private:
void on_resolve(error_code const& e void on_resolve(error_code const& e
, tcp::resolver::iterator i); , tcp::resolver::iterator i);
void queue_connect();
void connect(int ticket, tcp::endpoint target_address); void connect(int ticket, tcp::endpoint target_address);
void on_connect_timeout(); void on_connect_timeout();
void on_connect(error_code const& e void on_connect(error_code const& e);
/* , tcp::resolver::iterator i*/);
void on_write(error_code const& e); void on_write(error_code const& e);
void on_read(error_code const& e, std::size_t bytes_transferred); void on_read(error_code const& e, std::size_t bytes_transferred);
static void on_timeout(boost::weak_ptr<http_connection> p static void on_timeout(boost::weak_ptr<http_connection> p
@ -160,6 +162,8 @@ private:
std::string m_port; std::string m_port;
std::string m_url; std::string m_url;
std::list<tcp::endpoint> m_endpoints;
// the current download limit, in bytes per second // the current download limit, in bytes per second
// 0 is unlimited. // 0 is unlimited.
int m_rate_limit; int m_rate_limit;
@ -196,6 +200,8 @@ private:
// the priority we have in the connection queue. // the priority we have in the connection queue.
// 0 is normal, 1 is high // 0 is normal, 1 is high
int m_priority; int m_priority;
bool m_abort;
}; };
} }

View File

@ -41,6 +41,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include <boost/bind.hpp> #include <boost/bind.hpp>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <string> #include <string>
#include <algorithm>
using boost::bind; using boost::bind;
@ -198,11 +199,19 @@ void http_connection::start(std::string const& hostname, std::string const& port
void http_connection::on_connect_timeout() void http_connection::on_connect_timeout()
{ {
TORRENT_ASSERT(m_connection_ticket >= 0);
if (m_connection_ticket > -1) m_cc.done(m_connection_ticket); if (m_connection_ticket > -1) m_cc.done(m_connection_ticket);
m_connection_ticket = -1; m_connection_ticket = -1;
callback(asio::error::timed_out); if (!m_endpoints.empty())
close(); {
m_sock.close();
}
else
{
callback(asio::error::timed_out);
close();
}
} }
void http_connection::on_timeout(boost::weak_ptr<http_connection> p void http_connection::on_timeout(boost::weak_ptr<http_connection> p
@ -210,15 +219,23 @@ void http_connection::on_timeout(boost::weak_ptr<http_connection> p
{ {
boost::shared_ptr<http_connection> c = p.lock(); boost::shared_ptr<http_connection> c = p.lock();
if (!c) return; if (!c) return;
if (c->m_connection_ticket > -1) c->m_cc.done(c->m_connection_ticket);
c->m_connection_ticket = -1;
if (e == asio::error::operation_aborted) return; if (e == asio::error::operation_aborted) return;
if (c->m_last_receive + c->m_timeout < time_now()) if (c->m_last_receive + c->m_timeout < time_now())
{ {
c->callback(asio::error::timed_out); if (c->m_connection_ticket > -1 && !c->m_endpoints.empty())
c->close(); {
c->m_sock.close();
error_code ec;
c->m_timer.expires_at(c->m_last_receive + c->m_timeout, ec);
c->m_timer.async_wait(bind(&http_connection::on_timeout, p, _1));
}
else
{
c->callback(asio::error::timed_out);
c->close();
}
return; return;
} }
@ -236,11 +253,8 @@ void http_connection::close()
m_sock.close(ec); m_sock.close(ec);
m_hostname.clear(); m_hostname.clear();
m_port.clear(); m_port.clear();
if (m_connection_ticket > -1) m_cc.done(m_connection_ticket);
m_connection_ticket = -1;
m_handler.clear(); m_handler.clear();
m_abort = true;
} }
void http_connection::on_resolve(error_code const& e void http_connection::on_resolve(error_code const& e
@ -254,21 +268,26 @@ void http_connection::on_resolve(error_code const& e
} }
TORRENT_ASSERT(i != tcp::resolver::iterator()); TORRENT_ASSERT(i != tcp::resolver::iterator());
// look for an address that has the same kind as the one std::transform(i, tcp::resolver::iterator(), std::back_inserter(m_endpoints)
// we're binding to. To make sure a tracker get our , boost::bind(&tcp::resolver::iterator::value_type::endpoint, _1));
// 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) // sort the endpoints so that the ones with the same IP version as our
{ // bound listen socket are first. So that when contacting a tracker,
target_address = *target; // we'll talk to it from the same IP that we're listening on
} m_endpoints.sort(
(bind(&address::is_v4, bind(&tcp::endpoint::address, _1)) == m_bind_addr.is_v4())
m_cc.enqueue(bind(&http_connection::connect, shared_from_this(), _1, target_address) > (bind(&address::is_v4, bind(&tcp::endpoint::address, _2)) == m_bind_addr.is_v4()));
queue_connect();
}
void http_connection::queue_connect()
{
TORRENT_ASSERT(!m_endpoints.empty());
tcp::endpoint target = m_endpoints.front();
m_endpoints.pop_front();
m_cc.enqueue(bind(&http_connection::connect, shared_from_this(), _1, target)
, bind(&http_connection::on_connect_timeout, shared_from_this()) , bind(&http_connection::on_connect_timeout, shared_from_this())
, m_timeout, m_priority); , m_timeout, m_priority);
} }
@ -277,28 +296,28 @@ void http_connection::connect(int ticket, tcp::endpoint target_address)
{ {
m_connection_ticket = ticket; m_connection_ticket = ticket;
m_sock.async_connect(target_address, boost::bind(&http_connection::on_connect m_sock.async_connect(target_address, boost::bind(&http_connection::on_connect
, shared_from_this(), _1/*, ++i*/)); , shared_from_this(), _1));
} }
void http_connection::on_connect(error_code const& e void http_connection::on_connect(error_code const& e)
/*, tcp::resolver::iterator i*/)
{ {
TORRENT_ASSERT(m_connection_ticket >= 0);
m_cc.done(m_connection_ticket);
m_last_receive = time_now();
if (!e) if (!e)
{ {
m_last_receive = time_now();
if (m_connect_handler) m_connect_handler(*this); if (m_connect_handler) m_connect_handler(*this);
async_write(m_sock, asio::buffer(sendbuffer) async_write(m_sock, asio::buffer(sendbuffer)
, bind(&http_connection::on_write, shared_from_this(), _1)); , bind(&http_connection::on_write, shared_from_this(), _1));
} }
/* else if (i != tcp::resolver::iterator()) else if (!m_endpoints.empty() && !m_abort)
{ {
// The connection failed. Try the next endpoint in the list. // The connection failed. Try the next endpoint in the list.
m_sock.close(); m_sock.close();
m_cc.enqueue(bind(&http_connection::connect, shared_from_this(), _1, *i) queue_connect();
, bind(&http_connection::on_connect_timeout, shared_from_this())
, m_timeout, m_priority);
} }
*/ else else
{ {
callback(e); callback(e);
close(); close();

View File

@ -76,7 +76,7 @@ void run_test(std::string const& url, int size, int status, int connected
boost::shared_ptr<http_connection> h(new http_connection(ios, cq boost::shared_ptr<http_connection> h(new http_connection(ios, cq
, &::http_handler, true, &::http_connect_handler)); , &::http_handler, true, &::http_connect_handler));
h->get(url, seconds(30), 0, &ps); h->get(url, seconds(5), 0, &ps);
ios.reset(); ios.reset();
ios.run(); ios.run();
@ -104,6 +104,9 @@ void run_suite(std::string const& protocol, proxy_settings const& ps)
<< " proxy **********************\n" << std::endl; << " proxy **********************\n" << std::endl;
typedef boost::optional<error_code> err; typedef boost::optional<error_code> err;
// this requires the hosts file to be modified
// run_test(protocol + "://test.dns.ts:8001/test_file", 3216, 200, 1, error_code(), ps);
run_test(protocol + "://127.0.0.1:8001/relative/redirect", 3216, 200, 2, error_code(), ps); run_test(protocol + "://127.0.0.1:8001/relative/redirect", 3216, 200, 2, error_code(), ps);
run_test(protocol + "://127.0.0.1:8001/redirect", 3216, 200, 2, error_code(), ps); run_test(protocol + "://127.0.0.1:8001/redirect", 3216, 200, 2, error_code(), ps);
run_test(protocol + "://127.0.0.1:8001/infinite_redirect", 0, 301, 6, error_code(), ps); run_test(protocol + "://127.0.0.1:8001/infinite_redirect", 0, 301, 6, error_code(), ps);