add testing system for timer/handshake handler interleaving
This interleaving is non-deterministic and pre-0.5 can result in state errors when timer handers race with read/write handlers. These tests explicitly and deterministically check all common interleaving for both clients and servers.
This commit is contained in:
1 parent
3c6c757d93
commit
922f1c35bc
2 files changed
+146
-3
No files matched your search
@@ -30,6 +30,10 @@
|
||||
|
||||
#include "connection_tu2.hpp"
|
||||
|
||||
// Include special debugging transport
|
||||
#include <websocketpp/config/minimal_client.hpp>
|
||||
#include "debug/endpoint.hpp"
|
||||
|
||||
// NOTE: these tests currently test against hardcoded output values. I am not
|
||||
// sure how problematic this will be. If issues arise like order of headers the
|
||||
// output should be parsed by http::response and have values checked directly
|
||||
@@ -79,6 +83,54 @@ struct stub_config : public websocketpp::config::core {
|
||||
typedef connection_extension connection_base;
|
||||
};
|
||||
|
||||
struct debug_config_client : public websocketpp::config::core {
|
||||
typedef debug_config_client type;
|
||||
|
||||
typedef core::concurrency_type concurrency_type;
|
||||
|
||||
typedef core::request_type request_type;
|
||||
typedef core::response_type response_type;
|
||||
|
||||
typedef core::message_type message_type;
|
||||
typedef core::con_msg_manager_type con_msg_manager_type;
|
||||
typedef core::endpoint_msg_manager_type endpoint_msg_manager_type;
|
||||
|
||||
typedef core::alog_type alog_type;
|
||||
typedef core::elog_type elog_type;
|
||||
|
||||
typedef websocketpp::random::none::int_generator<uint32_t> rng_type;
|
||||
|
||||
struct transport_config {
|
||||
typedef type::concurrency_type concurrency_type;
|
||||
typedef type::elog_type elog_type;
|
||||
typedef type::alog_type alog_type;
|
||||
typedef type::request_type request_type;
|
||||
typedef type::response_type response_type;
|
||||
|
||||
/// Controls compile time enabling/disabling of thread syncronization
|
||||
/// code Disabling can provide a minor performance improvement to single
|
||||
/// threaded applications
|
||||
static bool const enable_multithreading = true;
|
||||
|
||||
/// Default timer values (in ms)
|
||||
static const long timeout_socket_pre_init = 5000;
|
||||
static const long timeout_proxy = 5000;
|
||||
static const long timeout_socket_post_init = 5000;
|
||||
static const long timeout_connect = 5000;
|
||||
static const long timeout_socket_shutdown = 5000;
|
||||
};
|
||||
|
||||
/// Transport Endpoint Component
|
||||
typedef websocketpp::transport::debug::endpoint<transport_config>
|
||||
transport_type;
|
||||
|
||||
typedef core::endpoint_base endpoint_base;
|
||||
typedef connection_extension connection_base;
|
||||
|
||||
static const websocketpp::log::level elog_level = websocketpp::log::elevel::all;
|
||||
static const websocketpp::log::level alog_level = websocketpp::log::alevel::all;
|
||||
};
|
||||
|
||||
struct connection_setup {
|
||||
connection_setup(bool p_is_server) : c(p_is_server, "", alog, elog, rng) {}
|
||||
|
||||
@@ -89,6 +141,9 @@ struct connection_setup {
|
||||
websocketpp::connection<stub_config> c;
|
||||
};
|
||||
|
||||
typedef websocketpp::client<debug_config_client> debug_client;
|
||||
typedef websocketpp::server<debug_config_client> debug_server;
|
||||
|
||||
/*void echo_func(server* s, websocketpp::connection_hdl hdl, message_ptr msg) {
|
||||
s->send(hdl, msg->get_payload(), msg->get_opcode());
|
||||
}*/
|
||||
@@ -239,3 +294,88 @@ BOOST_AUTO_TEST_CASE( basic_text_message ) {
|
||||
BOOST_CHECK( run_server_test(input) == output);
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE( client_handshake_timeout_race1 ) {
|
||||
debug_client c;
|
||||
|
||||
websocketpp::lib::error_code ec;
|
||||
debug_client::connection_ptr con = c.get_connection("ws://localhost:9002", ec);
|
||||
|
||||
BOOST_CHECK(!ec);
|
||||
|
||||
// This test the case where a handshake times out immediately before the
|
||||
// handler that would have completed it gets invoked. This situation happens
|
||||
// when clients are connecting to overloaded servers and on servers that are
|
||||
// overloaded.
|
||||
c.connect(con);
|
||||
|
||||
con->expire_timer(websocketpp::lib::error_code());
|
||||
// Fullfil the write to simulate the write completing immediately after
|
||||
// timer expires
|
||||
con->fullfil_write();
|
||||
|
||||
BOOST_CHECK_EQUAL(con->get_ec(), make_error_code(websocketpp::error::open_handshake_timeout));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( client_handshake_timeout_race2 ) {
|
||||
debug_client c;
|
||||
|
||||
websocketpp::lib::error_code ec;
|
||||
debug_client::connection_ptr con = c.get_connection("ws://localhost:9002", ec);
|
||||
|
||||
BOOST_CHECK(!ec);
|
||||
|
||||
std::string output = "HTTP/1.1 101 Switching Protocols\r\nConnection: upgrade\r\nSec-WebSocket-Accept: ICX+Yqv66kxgM0FcWaLWlFLwTAI=\r\nServer: foo\r\nUpgrade: websocket\r\n\r\n";
|
||||
|
||||
// This test the case where a handshake times out immediately before the
|
||||
// handler that would have completed it gets invoked. This situation happens
|
||||
// when clients are connecting to overloaded servers and on servers that are
|
||||
// overloaded.
|
||||
c.connect(con);
|
||||
con->fullfil_write();
|
||||
|
||||
con->expire_timer(websocketpp::lib::error_code());
|
||||
// Read valid handshake to simulate receiving the handshake response
|
||||
// immediately after the timer expires
|
||||
con->read_all(output.data(),output.size());
|
||||
|
||||
BOOST_CHECK_EQUAL(con->get_ec(), make_error_code(websocketpp::error::open_handshake_timeout));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( server_handshake_timeout_race1 ) {
|
||||
debug_server s;
|
||||
|
||||
std::string input = "GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: AAAAAAAAAAAAAAAAAAAAAA==\r\n\r\n";
|
||||
|
||||
debug_server::connection_ptr con = s.get_connection();
|
||||
con->start();
|
||||
|
||||
con->expire_timer(websocketpp::lib::error_code());
|
||||
// Read handshake immediately after timer expire
|
||||
con->read_all(input.data(), input.size());
|
||||
|
||||
BOOST_CHECK_EQUAL(con->get_ec(), make_error_code(websocketpp::error::open_handshake_timeout));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( server_handshake_timeout_race2 ) {
|
||||
debug_server s;
|
||||
|
||||
std::string input = "GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: AAAAAAAAAAAAAAAAAAAAAA==\r\n\r\n";
|
||||
|
||||
debug_server::connection_ptr con = s.get_connection();
|
||||
con->start();
|
||||
|
||||
con->read_all(input.data(), input.size());
|
||||
|
||||
con->expire_timer(websocketpp::lib::error_code());
|
||||
// Complete write immediately after timer expire
|
||||
con->fullfil_write();
|
||||
|
||||
BOOST_CHECK_EQUAL(con->get_ec(), make_error_code(websocketpp::error::open_handshake_timeout));
|
||||
}
|
||||
|
||||
|
||||
@@ -273,7 +273,8 @@ void run_dummy_client(std::string port) {
|
||||
}
|
||||
}
|
||||
|
||||
bool on_ping(websocketpp::connection_hdl, std::string) {
|
||||
bool on_ping(server * s, websocketpp::connection_hdl, std::string) {
|
||||
s->get_alog().write(websocketpp::log::alevel::app,"got ping");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -291,7 +292,9 @@ void stop_on_close(server * s, websocketpp::connection_hdl hdl) {
|
||||
template <typename T>
|
||||
void ping_on_open(T * c, std::string payload, websocketpp::connection_hdl hdl) {
|
||||
typename T::connection_ptr con = c->get_con_from_hdl(hdl);
|
||||
con->ping(payload);
|
||||
websocketpp::lib::error_code ec;
|
||||
con->ping(payload,ec);
|
||||
BOOST_CHECK_EQUAL(ec, websocketpp::lib::error_code());
|
||||
}
|
||||
|
||||
void fail_on_pong(websocketpp::connection_hdl, std::string) {
|
||||
@@ -382,7 +385,7 @@ BOOST_AUTO_TEST_CASE( pong_timeout ) {
|
||||
server s;
|
||||
client c;
|
||||
|
||||
s.set_ping_handler(on_ping);
|
||||
s.set_ping_handler(bind(&on_ping, &s,::_1,::_2));
|
||||
s.set_close_handler(bind(&stop_on_close,&s,::_1));
|
||||
|
||||
c.set_fail_handler(bind(&check_ec<client>,&c,
|
||||
|
||||
Reference in new issue
Block a user