From 81302c8fed67eee26af55d10f37508c5c12e4770 Mon Sep 17 00:00:00 2001 From: Arvid Norberg Date: Sat, 12 Jan 2008 01:41:56 +0000 Subject: [PATCH] fixed http_connection redirection support and added test (still requires lighttp) --- include/libtorrent/http_connection.hpp | 11 ++--- src/http_connection.cpp | 66 ++++++++++++++------------ test/setup_transfer.cpp | 6 ++- test/test_http_connection.cpp | 2 + 4 files changed, 46 insertions(+), 39 deletions(-) diff --git a/include/libtorrent/http_connection.hpp b/include/libtorrent/http_connection.hpp index 9d32af2e9..b65b303ae 100644 --- a/include/libtorrent/http_connection.hpp +++ b/include/libtorrent/http_connection.hpp @@ -78,7 +78,7 @@ struct http_connection : boost::enable_shared_from_this, boost: , m_download_quota(0) , m_limiter_timer_active(false) , m_limiter_timer(ios) - , m_redirect(true) + , m_redirects(5) , m_connection_ticket(-1) , m_cc(cc) { @@ -93,10 +93,10 @@ struct http_connection : boost::enable_shared_from_this, boost: std::string sendbuffer; void get(std::string const& url, time_duration timeout = seconds(30) - , bool handle_redirect = true); + , int handle_redirects = 5); void start(std::string const& hostname, std::string const& port - , time_duration timeout, bool handle_redirect = true); + , time_duration timeout, int handle_redirect = 5); void close(); tcp::socket const& socket() const { return m_sock; } @@ -153,9 +153,8 @@ private: // as all the quota was used. deadline_timer m_limiter_timer; - // if set to true, the connection should handle - // HTTP redirects. - bool m_redirect; + // the number of redirects to follow (in sequence) + int m_redirects; int m_connection_ticket; connection_queue& m_cc; diff --git a/src/http_connection.cpp b/src/http_connection.cpp index 9643bf449..e02c6370b 100644 --- a/src/http_connection.cpp +++ b/src/http_connection.cpp @@ -46,9 +46,8 @@ namespace libtorrent enum { max_bottled_buffer = 1024 * 1024 }; void http_connection::get(std::string const& url, time_duration timeout - , bool handle_redirect) + , int handle_redirects) { - m_redirect = handle_redirect; std::string protocol; std::string auth; std::string hostname; @@ -63,22 +62,24 @@ void http_connection::get(std::string const& url, time_duration timeout headers << "Authorization: Basic " << base64encode(auth) << "\r\n"; headers << "\r\n"; sendbuffer = headers.str(); - start(hostname, boost::lexical_cast(port), timeout); + start(hostname, boost::lexical_cast(port), timeout, handle_redirects); } void http_connection::start(std::string const& hostname, std::string const& port - , time_duration timeout, bool handle_redirect) + , time_duration timeout, int handle_redirects) { - m_redirect = handle_redirect; + m_redirects = handle_redirects; m_timeout = timeout; asio::error_code ec; m_timer.expires_from_now(m_timeout, ec); m_timer.async_wait(bind(&http_connection::on_timeout , boost::weak_ptr(shared_from_this()), _1)); m_called = false; + m_parser.reset(); + m_recvbuffer.clear(); + m_read_pos = 0; if (m_sock.is_open() && m_hostname == hostname && m_port == port) { - m_parser.reset(); asio::async_write(m_sock, asio::buffer(sendbuffer) , bind(&http_connection::on_write, shared_from_this(), _1)); } @@ -238,6 +239,7 @@ void http_connection::on_read(asio::error_code const& e if (e == asio::error::eof) { + TORRENT_ASSERT(bytes_transferred == 0); char const* data = 0; std::size_t size = 0; if (m_bottled && m_parser.header_finished()) @@ -252,6 +254,7 @@ void http_connection::on_read(asio::error_code const& e if (e) { + TORRENT_ASSERT(bytes_transferred == 0); callback(e); close(); return; @@ -260,31 +263,6 @@ void http_connection::on_read(asio::error_code const& e m_read_pos += bytes_transferred; TORRENT_ASSERT(m_read_pos <= int(m_recvbuffer.size())); - // having a nonempty path means we should handle redirects - if (m_redirect && m_parser.header_finished()) - { - int code = m_parser.status_code(); - if (code >= 300 && code < 400) - { - // attempt a redirect - std::string const& url = m_parser.header("location"); - if (url.empty()) - { - // missing location header - callback(e); - return; - } - - m_limiter_timer_active = false; - close(); - - get(url, m_timeout); - return; - } - - m_redirect = false; - } - if (m_bottled || !m_parser.header_finished()) { libtorrent::buffer::const_interval rcv_buf(&m_recvbuffer[0] @@ -298,6 +276,32 @@ void http_connection::on_read(asio::error_code const& e callback(ec, 0, 0); return; } + + // having a nonempty path means we should handle redirects + if (m_redirects && m_parser.header_finished()) + { + int code = m_parser.status_code(); + + if (code >= 300 && code < 400) + { + // attempt a redirect + std::string const& url = m_parser.header("location"); + if (url.empty()) + { + // missing location header + callback(e); + return; + } + + asio::error_code ec; + m_sock.close(ec); + get(url, m_timeout, m_redirects - 1); + return; + } + + m_redirects = 0; + } + if (!m_bottled && m_parser.header_finished()) { if (m_read_pos > m_parser.body_start()) diff --git a/test/setup_transfer.cpp b/test/setup_transfer.cpp index 35362ea32..47fc0ef9a 100644 --- a/test/setup_transfer.cpp +++ b/test/setup_transfer.cpp @@ -71,11 +71,13 @@ void start_web_server(int port) { stop_web_server(port); std::ofstream f("./lighty_config"); - f << "server.modules = (\"mod_access\")\n" + f << "server.modules = (\"mod_access\", \"mod_redirect\")\n" "server.document-root = \"" << boost::filesystem::initial_path().string() << "\"\n" "server.range-requests = \"enable\"\n" "server.port = " << port << "\n" - "server.pid-file = \"./lighty" << port << ".pid\"\n"; + "server.pid-file = \"./lighty" << port << ".pid\"\n" + "url.redirect = (\"^/redirect$\" => \"http://127.0.0.1:" << port << "/test_file\", " + "\"^/infinite_redirect$\" => \"http://127.0.0.1:" << port << "/infinite_redirect\")"; f.close(); system("lighttpd -f lighty_config &"); diff --git a/test/test_http_connection.cpp b/test/test_http_connection.cpp index 9bee8c112..19b5dc529 100644 --- a/test/test_http_connection.cpp +++ b/test/test_http_connection.cpp @@ -97,6 +97,8 @@ int test_main() std::srand(std::time(0)); std::generate(data_buffer, data_buffer + sizeof(data_buffer), &std::rand); std::ofstream("test_file").write(data_buffer, 3216); + run_test("http://127.0.0.1:8001/redirect", 3216, 200, 2, asio::error_code()); + run_test("http://127.0.0.1:8001/infinite_redirect", 0, 301, 6, asio::error_code()); run_test("http://127.0.0.1:8001/test_file", 3216, 200, 1, asio::error_code()); run_test("http://127.0.0.1:8001/non-existing-file", -1, 404, 1, err()); run_test("http://non-existent-domain.se/non-existing-file", -1, -1, 0, err(asio::error::host_not_found));