diff --git a/include/libtorrent/http_connection.hpp b/include/libtorrent/http_connection.hpp index faf0194e4..b13b457fa 100644 --- a/include/libtorrent/http_connection.hpp +++ b/include/libtorrent/http_connection.hpp @@ -39,6 +39,7 @@ POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include #include #include "libtorrent/socket.hpp" @@ -91,6 +92,7 @@ struct http_connection : boost::enable_shared_from_this, boost: , m_cc(cc) , m_ssl(false) , m_priority(0) + , m_abort(false) { TORRENT_ASSERT(!m_handler.empty()); } @@ -123,10 +125,10 @@ private: void on_resolve(error_code const& e , tcp::resolver::iterator i); + void queue_connect(); void connect(int ticket, tcp::endpoint target_address); void on_connect_timeout(); - void on_connect(error_code const& e -/* , tcp::resolver::iterator i*/); + void on_connect(error_code const& e); void on_write(error_code const& e); void on_read(error_code const& e, std::size_t bytes_transferred); static void on_timeout(boost::weak_ptr p @@ -160,6 +162,8 @@ private: std::string m_port; std::string m_url; + std::list m_endpoints; + // the current download limit, in bytes per second // 0 is unlimited. int m_rate_limit; @@ -196,6 +200,8 @@ private: // the priority we have in the connection queue. // 0 is normal, 1 is high int m_priority; + + bool m_abort; }; } diff --git a/src/http_connection.cpp b/src/http_connection.cpp index 4bd29a5b0..0351925f6 100644 --- a/src/http_connection.cpp +++ b/src/http_connection.cpp @@ -41,6 +41,7 @@ POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include 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() { + TORRENT_ASSERT(m_connection_ticket >= 0); if (m_connection_ticket > -1) m_cc.done(m_connection_ticket); m_connection_ticket = -1; - callback(asio::error::timed_out); - close(); + if (!m_endpoints.empty()) + { + m_sock.close(); + } + else + { + callback(asio::error::timed_out); + close(); + } } void http_connection::on_timeout(boost::weak_ptr p @@ -210,15 +219,23 @@ void http_connection::on_timeout(boost::weak_ptr p { boost::shared_ptr c = p.lock(); 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 (c->m_last_receive + c->m_timeout < time_now()) { - c->callback(asio::error::timed_out); - c->close(); + if (c->m_connection_ticket > -1 && !c->m_endpoints.empty()) + { + 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; } @@ -236,11 +253,8 @@ void http_connection::close() m_sock.close(ec); m_hostname.clear(); m_port.clear(); - - if (m_connection_ticket > -1) m_cc.done(m_connection_ticket); - m_connection_ticket = -1; - m_handler.clear(); + m_abort = true; } 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()); - // 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); + std::transform(i, tcp::resolver::iterator(), std::back_inserter(m_endpoints) + , boost::bind(&tcp::resolver::iterator::value_type::endpoint, _1)); - if (target != end) - { - target_address = *target; - } - - m_cc.enqueue(bind(&http_connection::connect, shared_from_this(), _1, target_address) + // 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, + // 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()) + > (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()) , m_timeout, m_priority); } @@ -277,28 +296,28 @@ void http_connection::connect(int ticket, tcp::endpoint target_address) { m_connection_ticket = ticket; 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 - /*, tcp::resolver::iterator i*/) +void http_connection::on_connect(error_code const& e) { + TORRENT_ASSERT(m_connection_ticket >= 0); + m_cc.done(m_connection_ticket); + + m_last_receive = time_now(); if (!e) { - m_last_receive = time_now(); if (m_connect_handler) m_connect_handler(*this); async_write(m_sock, asio::buffer(sendbuffer) , 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. m_sock.close(); - m_cc.enqueue(bind(&http_connection::connect, shared_from_this(), _1, *i) - , bind(&http_connection::on_connect_timeout, shared_from_this()) - , m_timeout, m_priority); + queue_connect(); } -*/ else + else { callback(e); close(); diff --git a/test/test_http_connection.cpp b/test/test_http_connection.cpp index f6c9c8a12..ac30bdbe4 100644 --- a/test/test_http_connection.cpp +++ b/test/test_http_connection.cpp @@ -76,7 +76,7 @@ void run_test(std::string const& url, int size, int status, int connected boost::shared_ptr h(new http_connection(ios, cq , &::http_handler, true, &::http_connect_handler)); - h->get(url, seconds(30), 0, &ps); + h->get(url, seconds(5), 0, &ps); ios.reset(); ios.run(); @@ -104,6 +104,9 @@ void run_suite(std::string const& protocol, proxy_settings const& ps) << " proxy **********************\n" << std::endl; typedef boost::optional 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/redirect", 3216, 200, 2, error_code(), ps); run_test(protocol + "://127.0.0.1:8001/infinite_redirect", 0, 301, 6, error_code(), ps);