diff --git a/include/libtorrent/http_connection.hpp b/include/libtorrent/http_connection.hpp index 4a70a902c..9d32af2e9 100644 --- a/include/libtorrent/http_connection.hpp +++ b/include/libtorrent/http_connection.hpp @@ -115,6 +115,8 @@ private: , asio::error_code const& e); void on_assign_bandwidth(asio::error_code const& e); + void callback(asio::error_code const& e, char const* data = 0, int size = 0); + std::vector m_recvbuffer; tcp::socket m_sock; int m_read_pos; diff --git a/include/libtorrent/http_tracker_connection.hpp b/include/libtorrent/http_tracker_connection.hpp index c0057dfa1..41df4c953 100755 --- a/include/libtorrent/http_tracker_connection.hpp +++ b/include/libtorrent/http_tracker_connection.hpp @@ -91,6 +91,9 @@ namespace libtorrent int content_length() const { return m_content_length; } void reset(); + + std::map const& headers() const { return m_header; } + private: int m_recv_pos; int m_status_code; diff --git a/src/http_connection.cpp b/src/http_connection.cpp index c96bb05dd..a0d8e3fa3 100644 --- a/src/http_connection.cpp +++ b/src/http_connection.cpp @@ -96,9 +96,7 @@ void http_connection::on_connect_timeout() if (m_connection_ticket > -1) m_cc.done(m_connection_ticket); m_connection_ticket = -1; - if (m_bottled && m_called) return; - m_called = true; - m_handler(asio::error::timed_out, m_parser, 0, 0); + callback(asio::error::timed_out); close(); } @@ -112,12 +110,10 @@ void http_connection::on_timeout(boost::weak_ptr p if (e == asio::error::operation_aborted) return; - if (c->m_bottled && c->m_called) return; - if (c->m_last_receive + c->m_timeout < time_now()) { - c->m_called = true; - c->m_handler(asio::error::timed_out, c->m_parser, 0, 0); + c->callback(asio::error::timed_out); + c->close(); return; } @@ -138,9 +134,7 @@ void http_connection::close() if (m_connection_ticket > -1) m_cc.done(m_connection_ticket); m_connection_ticket = -1; - if (m_called) return; - m_called = true; - m_handler(asio::error::operation_aborted, m_parser, 0, 0); + m_handler.clear(); } void http_connection::on_resolve(asio::error_code const& e @@ -148,10 +142,8 @@ void http_connection::on_resolve(asio::error_code const& e { if (e) { + callback(e); close(); - if (m_bottled && m_called) return; - m_called = true; - m_handler(e, m_parser, 0, 0); return; } TORRENT_ASSERT(i != tcp::resolver::iterator()); @@ -187,10 +179,17 @@ void http_connection::on_connect(asio::error_code const& e } */ else { + callback(e); close(); - if (m_bottled && m_called) return; + } +} + +void http_connection::callback(asio::error_code const& e, char const* data, int size) +{ + if (!m_bottled || !m_called) + { m_called = true; - m_handler(e, m_parser, 0, 0); + if (m_handler) m_handler(e, m_parser, data, size); } } @@ -198,10 +197,8 @@ void http_connection::on_write(asio::error_code const& e) { if (e) { + callback(e); close(); - if (m_bottled && m_called) return; - m_called = true; - m_handler(e, m_parser, 0, 0); return; } @@ -236,9 +233,6 @@ void http_connection::on_read(asio::error_code const& e if (e == asio::error::eof) { - close(); - if (m_bottled && m_called) return; - m_called = true; char const* data = 0; std::size_t size = 0; if (m_bottled && m_parser.header_finished()) @@ -246,16 +240,15 @@ void http_connection::on_read(asio::error_code const& e data = m_parser.get_body().begin; size = m_parser.get_body().left(); } - m_handler(e, m_parser, data, size); + callback(e, data, size); + close(); return; } if (e) { + callback(e); close(); - if (m_bottled && m_called) return; - m_called = true; - m_handler(e, m_parser, 0, 0); return; } @@ -273,9 +266,7 @@ void http_connection::on_read(asio::error_code const& e if (url.empty()) { // missing location header - if (m_bottled && m_called) return; - m_called = true; - m_handler(e, m_parser, 0, 0); + callback(e); return; } @@ -297,7 +288,7 @@ void http_connection::on_read(asio::error_code const& e if (!m_bottled && m_parser.header_finished()) { if (m_read_pos > m_parser.body_start()) - m_handler(e, m_parser, &m_recvbuffer[0] + m_parser.body_start() + callback(e, &m_recvbuffer[0] + m_parser.body_start() , m_read_pos - m_parser.body_start()); m_read_pos = 0; m_last_receive = time_now(); @@ -305,15 +296,13 @@ void http_connection::on_read(asio::error_code const& e else if (m_bottled && m_parser.finished()) { m_timer.cancel(); - if (m_bottled && m_called) return; - m_called = true; - m_handler(e, m_parser, m_parser.get_body().begin, m_parser.get_body().left()); + callback(e, m_parser.get_body().begin, m_parser.get_body().left()); } } else { TORRENT_ASSERT(!m_bottled); - m_handler(e, m_parser, &m_recvbuffer[0], m_read_pos); + callback(e, &m_recvbuffer[0], m_read_pos); m_read_pos = 0; m_last_receive = time_now(); } @@ -322,10 +311,8 @@ void http_connection::on_read(asio::error_code const& e m_recvbuffer.resize((std::min)(m_read_pos + 2048, int(max_bottled_buffer))); if (m_read_pos == max_bottled_buffer) { + callback(asio::error::eof); close(); - if (m_bottled && m_called) return; - m_called = true; - m_handler(asio::error::eof, m_parser, 0, 0); return; } int amount_to_read = m_recvbuffer.size() - m_read_pos; @@ -351,8 +338,7 @@ void http_connection::on_assign_bandwidth(asio::error_code const& e) && m_limiter_timer_active) || !m_sock.is_open()) { - if (!m_bottled || !m_called) - m_handler(e, m_parser, 0, 0); + callback(asio::error::eof); return; } m_limiter_timer_active = false; diff --git a/test/Jamfile b/test/Jamfile index 5a22a1f5c..98fb1c094 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -23,6 +23,7 @@ project test-suite libtorrent : + [ run test_http_connection.cpp ] [ run test_buffer.cpp ] [ run test_storage.cpp ] [ run test_piece_picker.cpp ] diff --git a/test/test_http_connection.cpp b/test/test_http_connection.cpp new file mode 100644 index 000000000..dbdeeac32 --- /dev/null +++ b/test/test_http_connection.cpp @@ -0,0 +1,87 @@ +#include "test.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/connection_queue.hpp" +#include "libtorrent/http_connection.hpp" + +using namespace libtorrent; + +io_service ios; +connection_queue cq(ios); + +int connect_handler_called = 0; +int handler_called = 0; +int data_size = 0; +int http_status = 0; +asio::error_code error_code; + +void print_http_header(http_parser const& p) +{ + std::cerr << p.status_code() << " " << p.message() << std::endl; + + for (std::map::const_iterator i + = p.headers().begin(), end(p.headers().end()); i != end; ++i) + { + std::cerr << i->first << ": " << i->second << std::endl; + } + +} + +void http_connect_handler(http_connection& c) +{ + ++connect_handler_called; + TEST_CHECK(c.socket().is_open()); + TEST_CHECK(c.socket().remote_endpoint().address() == address::from_string("127.0.0.1")); +} + +void http_handler(asio::error_code const& ec, http_parser const& parser, char const* data, int size) +{ + ++handler_called; + data_size = size; + error_code = ec; + + if (parser.header_finished()) http_status = parser.status_code(); + print_http_header(parser); + + cq.close(); +} + +void reset_globals() +{ + connect_handler_called = 0; + handler_called = 0; + data_size = 0; + http_status = 0; + error_code = asio::error_code(); +} + +void run_test(char const* url, int size, int status, asio::error_code const& ec) +{ + reset_globals(); + + std::cerr << " ===== TESTING: " << url << " =====" << std::endl; + + boost::shared_ptr h(new http_connection(ios, cq + , &::http_handler, true, &::http_connect_handler)); + h->get(url); + ios.reset(); + ios.run(); + + std::cerr << "connect_handler_called: " << connect_handler_called << std::endl; + std::cerr << "handler_called: " << handler_called << std::endl; + std::cerr << "status: " << http_status << std::endl; + std::cerr << "size: " << data_size << std::endl; + std::cerr << "error_code: " << error_code.message() << std::endl; + TEST_CHECK(connect_handler_called == 1); + TEST_CHECK(handler_called == 1); + TEST_CHECK(data_size == size || size == -1); + TEST_CHECK(error_code == ec); + TEST_CHECK(http_status == status); +} + +int test_main() +{ + run_test("http://127.0.0.1/disk_io.png", 17809, 200, asio::error_code()); + run_test("http://127.0.0.1/non-existing-file", -1, 404, asio::error::eof); + return 0; +} +