Set connection timeout when next endpoint is tried (#1952)

This commit is contained in:
Jan Berkel 2017-05-04 23:32:47 +02:00 committed by Arvid Norberg
parent 1ea760ae93
commit fe9f877087
4 changed files with 113 additions and 3 deletions

View File

@ -1,3 +1,4 @@
* fix http connection timeout on multi-homed hosts
* removed depdendency on boost::uintptr_t for better compatibility
* fix memory leak in the disk cache
* fix double free in disk cache

@ -1 +1 @@
Subproject commit 60d786b8fa6ddaacdc98bdf691220660bc194494
Subproject commit 0e8d74baf1f6d9db19857eaa87734faecd530f94

View File

@ -83,6 +83,13 @@ struct sim_config : sim::default_config
return duration_cast<chrono::high_resolution_clock::duration>(chrono::milliseconds(100));
}
if (hostname == "dual-stack.test-hostname.com")
{
result.push_back(address_v4::from_string("10.0.0.2"));
result.push_back(address_v6::from_string("ff::dead:beef"));
return duration_cast<chrono::high_resolution_clock::duration>(chrono::milliseconds(100));
}
return default_config::hostname_lookup(requestor, hostname, result, ec);
}
};
@ -225,8 +232,12 @@ void run_suite(lt::aux::proxy_settings ps)
if (ps.type != settings_pack::socks5
&& ps.type != settings_pack::http)
{
const auto expected_code = ps.type == settings_pack::socks4 ?
boost::system::errc::address_family_not_supported :
boost::system::errc::address_not_available;
run_test(ps, "http://[ff::dead:beef]:8080/test_file", 0, -1
, error_condition(boost::system::errc::address_family_not_supported, generic_category())
, error_condition(expected_code, generic_category())
, {0,1});
}
@ -438,6 +449,102 @@ TORRENT_TEST(http_connection_socks5_proxy_names)
run_suite(ps);
}
// tests the error scenario of a http server listening on two sockets (ipv4/ipv6) which
// both accept the incoming connection but never send anything back. we test that
// both ip addresses get tried in turn and that the connection attempts time out as expected.
TORRENT_TEST(http_connection_timeout_server_stalls)
{
sim_config network_cfg;
sim::simulation sim{network_cfg};
// server has two ip addresses (ipv4/ipv6)
sim::asio::io_service server_ios(sim, address_v4::from_string("10.0.0.2"));
sim::asio::io_service server_ios_ipv6(sim, address_v6::from_string("ff::dead:beef"));
// same for client
sim::asio::io_service client_ios(sim, {
address_v4::from_string("10.0.0.1"),
address_v6::from_string("ff::abad:cafe")
});
lt::resolver resolver(client_ios);
const unsigned short http_port = 8080;
sim::http_server http(server_ios, http_port);
sim::http_server http_ipv6(server_ios_ipv6, http_port);
http.register_stall_handler("/timeout");
http_ipv6.register_stall_handler("/timeout");
char data_buffer[4000];
std::generate(data_buffer, data_buffer + sizeof(data_buffer), &std::rand);
int connect_counter = 0;
int handler_counter = 0;
error_condition timed_out(boost::system::errc::timed_out, boost::system::generic_category());
auto c = test_request(client_ios, resolver
, "http://dual-stack.test-hostname.com:8080/timeout", data_buffer, -1, -1
, timed_out, lt::aux::proxy_settings()
, &connect_counter, &handler_counter);
error_code e;
sim.run(e);
TEST_CHECK(!e);
TEST_EQUAL(2, connect_counter); // both endpoints are connected to
TEST_EQUAL(1, handler_counter); // the handler only gets called once with error_code == timed_out
}
// tests the error scenario of a http server listening on two sockets (ipv4/ipv6) neither of which
// accept incoming connections. we test that both ip addresses get tried in turn and that the
// connection attempts time out as expected.
TORRENT_TEST(http_connection_timeout_server_does_not_accept)
{
sim_config network_cfg;
sim::simulation sim{network_cfg};
// server has two ip addresses (ipv4/ipv6)
sim::asio::io_service server_ios(sim, {
address_v4::from_string("10.0.0.2"),
address_v6::from_string("ff::dead:beef")
});
// same for client
sim::asio::io_service client_ios(sim, {
address_v4::from_string("10.0.0.1"),
address_v6::from_string("ff::abad:cafe")
});
lt::resolver resolver(client_ios);
const unsigned short http_port = 8080;
// listen on two sockets, but don't accept connections
asio::ip::tcp::acceptor server_socket_ipv4(server_ios);
server_socket_ipv4.open(tcp::v4());
server_socket_ipv4.bind(tcp::endpoint(address_v4::any(), http_port));
server_socket_ipv4.listen();
asio::ip::tcp::acceptor server_socket_ipv6(server_ios);
server_socket_ipv6.open(tcp::v6());
server_socket_ipv6.bind(tcp::endpoint(address_v6::any(), http_port));
server_socket_ipv6.listen();
int connect_counter = 0;
int handler_counter = 0;
error_condition timed_out(boost::system::errc::timed_out, boost::system::generic_category());
char data_buffer[4000];
std::generate(data_buffer, data_buffer + sizeof(data_buffer), &std::rand);
auto c = test_request(client_ios, resolver
, "http://dual-stack.test-hostname.com:8080/timeout_server_does_not_accept", data_buffer, -1, -1
, timed_out, lt::aux::proxy_settings()
, &connect_counter, &handler_counter);
error_code e;
sim.run(e);
TEST_CHECK(!e);
TEST_EQUAL(0, connect_counter); // no connection takes place
TEST_EQUAL(1, handler_counter); // the handler only gets called once with error_code == timed_out
}
void test_proxy_failure(lt::settings_pack::proxy_type_t proxy_type)
{
using sim::asio::ip::address_v4;

View File

@ -449,12 +449,14 @@ void http_connection::on_timeout(boost::weak_ptr<http_connection> p
error_code ec;
c->m_sock.close(ec);
if (!c->m_connecting) c->connect();
c->m_last_receive = now;
c->m_start_time = c->m_last_receive;
}
else
{
c->callback(boost::asio::error::timed_out);
return;
}
return;
}
else
{