Overhaul error handling for failed WebSocket and HTTP connections
This commit is contained in:
1 parent
99f4a66221
commit
ef393f8aef
10 files changed
+306
-57
No files matched your search
@@ -18,6 +18,12 @@ HEAD
|
||||
than binary.
|
||||
- Improvement: Overhaul of handshake state machine. Should make it impossible
|
||||
for exceptions to bubble out of transport methods like `io_service::run`.
|
||||
- Improvement: Overhaul of handshake error reporting. Fail handler error codes
|
||||
will be more detailed and precise. Adds new [fail] and [http] logging channels
|
||||
that log failed websocket connections and successful HTTP connections
|
||||
respectively. A new aggregate channel package, `alevel::access_core`, allows
|
||||
enabling connect, disconnect, fail, and http together. Successful HTTP
|
||||
connections will no longer trigger a fail handler.
|
||||
- Documentation: Add Sending & Receiving Messages step to chapter one of the
|
||||
`utility_client` tutorial. Update `utility_client` example to match.
|
||||
- Cleanup: Removes unused files & STL includes. Adds required STL includes.
|
||||
|
||||
@@ -56,10 +56,13 @@ struct debug_custom : public websocketpp::config::debug_asio {
|
||||
typedef base::endpoint_msg_manager_type endpoint_msg_manager_type;
|
||||
|
||||
/// Custom Logging policies
|
||||
typedef websocketpp::log::syslog<concurrency_type,
|
||||
/*typedef websocketpp::log::syslog<concurrency_type,
|
||||
websocketpp::log::elevel> elog_type;
|
||||
typedef websocketpp::log::syslog<concurrency_type,
|
||||
websocketpp::log::alevel> alog_type;
|
||||
*/
|
||||
typedef base::alog_type alog_type;
|
||||
typedef base::elog_type elog_type;
|
||||
|
||||
typedef base::rng_type rng_type;
|
||||
|
||||
@@ -75,6 +78,8 @@ struct debug_custom : public websocketpp::config::debug_asio {
|
||||
|
||||
typedef websocketpp::transport::asio::endpoint<transport_config>
|
||||
transport_type;
|
||||
|
||||
static const long timeout_open_handshake = 0;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -90,7 +95,7 @@ typedef server::message_ptr message_ptr;
|
||||
|
||||
bool validate(server *, websocketpp::connection_hdl) {
|
||||
//sleep(6);
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void on_http(server* s, websocketpp::connection_hdl hdl) {
|
||||
@@ -105,8 +110,10 @@ void on_http(server* s, websocketpp::connection_hdl hdl) {
|
||||
con->set_status(websocketpp::http::status_code::ok);
|
||||
}
|
||||
|
||||
void on_fail(websocketpp::connection_hdl) {
|
||||
std::cout << "Fail handler" << std::endl;
|
||||
void on_fail(server* s, websocketpp::connection_hdl hdl) {
|
||||
server::connection_ptr con = s->get_con_from_hdl(hdl);
|
||||
|
||||
std::cout << "Fail handler: " << con->get_ec() << " " << con->get_ec().message() << std::endl;
|
||||
}
|
||||
|
||||
void on_close(websocketpp::connection_hdl) {
|
||||
@@ -144,7 +151,7 @@ int main() {
|
||||
echo_server.set_message_handler(bind(&on_message,&echo_server,::_1,::_2));
|
||||
|
||||
echo_server.set_http_handler(bind(&on_http,&echo_server,::_1));
|
||||
echo_server.set_fail_handler(&on_fail);
|
||||
echo_server.set_fail_handler(bind(&on_fail,&echo_server,::_1));
|
||||
echo_server.set_close_handler(&on_close);
|
||||
|
||||
echo_server.set_validate_handler(bind(&validate,&echo_server,::_1));
|
||||
|
||||
@@ -37,8 +37,7 @@ public:
|
||||
telemetry_server() : m_count(0) {
|
||||
// set up access channels to only log interesting things
|
||||
m_endpoint.clear_access_channels(websocketpp::log::alevel::all);
|
||||
m_endpoint.set_access_channels(websocketpp::log::alevel::connect);
|
||||
m_endpoint.set_access_channels(websocketpp::log::alevel::disconnect);
|
||||
m_endpoint.set_access_channels(websocketpp::log::alevel::access_core);
|
||||
m_endpoint.set_access_channels(websocketpp::log::alevel::app);
|
||||
|
||||
// Initialize the Asio transport policy
|
||||
@@ -183,6 +182,7 @@ int main(int argc, char* argv[]) {
|
||||
|
||||
if (argc == 1) {
|
||||
std::cout << "Usage: telemetry_server [documentroot] [port]" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (argc >= 2) {
|
||||
|
||||
@@ -167,6 +167,29 @@ void http_func(server* s, websocketpp::connection_hdl hdl) {
|
||||
con->set_status(websocketpp::http::status_code::ok);
|
||||
}
|
||||
|
||||
void check_on_fail(server* s, websocketpp::lib::error_code ec, bool & called,
|
||||
websocketpp::connection_hdl hdl)
|
||||
{
|
||||
server::connection_ptr con = s->get_con_from_hdl(hdl);
|
||||
|
||||
BOOST_CHECK_EQUAL(ec, con->get_ec());
|
||||
called = true;
|
||||
}
|
||||
|
||||
void on_open_print(server* s, websocketpp::connection_hdl hdl)
|
||||
{
|
||||
server::connection_ptr con = s->get_con_from_hdl(hdl);
|
||||
|
||||
std::cout << con->get_uri() << std::endl;
|
||||
}
|
||||
|
||||
void fail_on_open(websocketpp::connection_hdl hdl) {
|
||||
BOOST_CHECK(false);
|
||||
}
|
||||
void fail_on_http(websocketpp::connection_hdl hdl) {
|
||||
BOOST_CHECK(false);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( connection_extensions ) {
|
||||
connection_setup env(true);
|
||||
|
||||
@@ -270,6 +293,80 @@ BOOST_AUTO_TEST_CASE( set_max_message_size ) {
|
||||
BOOST_CHECK_EQUAL(run_server_test(s,input), output);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( websocket_fail_parse_error ) {
|
||||
std::string input = "asdf\r\n\r\n";
|
||||
|
||||
server s;
|
||||
websocketpp::lib::error_code ec = make_error_code(websocketpp::error::http_parse_error);
|
||||
bool called = false;
|
||||
s.set_fail_handler(bind(&check_on_fail,&s,ec,websocketpp::lib::ref(called),::_1));
|
||||
|
||||
run_server_test(s,input,false);
|
||||
BOOST_CHECK(called);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( websocket_fail_invalid_version ) {
|
||||
std::string input = "GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: foo\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nOrigin: http://www.example.com\r\n\r\n";
|
||||
|
||||
server s;
|
||||
websocketpp::lib::error_code ec = make_error_code(websocketpp::error::invalid_version);
|
||||
bool called = false;
|
||||
s.set_fail_handler(bind(&check_on_fail,&s,ec,websocketpp::lib::ref(called),::_1));
|
||||
|
||||
run_server_test(s,input,false);
|
||||
BOOST_CHECK(called);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( websocket_fail_unsupported_version ) {
|
||||
std::string input = "GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 12\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nOrigin: http://www.example.com\r\n\r\n";
|
||||
|
||||
server s;
|
||||
websocketpp::lib::error_code ec = make_error_code(websocketpp::error::unsupported_version);
|
||||
bool called = false;
|
||||
s.set_fail_handler(bind(&check_on_fail,&s,ec,websocketpp::lib::ref(called),::_1));
|
||||
|
||||
run_server_test(s,input,false);
|
||||
BOOST_CHECK(called);
|
||||
}
|
||||
|
||||
/*BOOST_AUTO_TEST_CASE( websocket_fail_invalid_uri ) {
|
||||
std::string input = "GET http://345.123.123.123/foo HTTP/1.1\r\nHost: www.example.com\r\nConnection: upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nOrigin: http://www.example.com\r\n\r\n";
|
||||
|
||||
server s;
|
||||
websocketpp::lib::error_code ec = make_error_code(websocketpp::error::unsupported_version);
|
||||
bool called = false;
|
||||
s.set_fail_handler(bind(&check_on_fail,&s,ec,websocketpp::lib::ref(called),::_1));
|
||||
s.set_open_handler(bind(&on_open_print,&s,::_1));
|
||||
|
||||
std::cout << run_server_test(s,input,true) << std::endl;
|
||||
BOOST_CHECK(called);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( websocket_fail_invalid_uri_http ) {
|
||||
std::string input = "GET http://345.123.123.123/foo HTTP/1.1\r\nHost: www.example.com\r\nOrigin: http://www.example.com\r\n\r\n";
|
||||
|
||||
server s;
|
||||
websocketpp::lib::error_code ec = make_error_code(websocketpp::error::unsupported_version);
|
||||
bool called = false;
|
||||
s.set_fail_handler(bind(&check_on_fail,&s,ec,websocketpp::lib::ref(called),::_1));
|
||||
s.set_open_handler(bind(&on_open_print,&s,::_1));
|
||||
|
||||
std::cout << run_server_test(s,input,true) << std::endl;
|
||||
BOOST_CHECK(called);
|
||||
}*/
|
||||
|
||||
BOOST_AUTO_TEST_CASE( websocket_fail_upgrade_required ) {
|
||||
std::string input = "GET /foo/bar HTTP/1.1\r\nHost: www.example.com\r\nOrigin: http://www.example.com\r\n\r\n";
|
||||
|
||||
server s;
|
||||
websocketpp::lib::error_code ec = make_error_code(websocketpp::error::upgrade_required);
|
||||
bool called = false;
|
||||
s.set_fail_handler(bind(&check_on_fail,&s,ec,websocketpp::lib::ref(called),::_1));
|
||||
|
||||
run_server_test(s,input,false);
|
||||
BOOST_CHECK(called);
|
||||
}
|
||||
|
||||
// TODO: set max message size in client endpoint test case
|
||||
// TODO: set max message size mid connection test case
|
||||
// TODO: [maybe] set max message size in open handler
|
||||
|
||||
@@ -31,17 +31,22 @@ void echo_func(server* s, websocketpp::connection_hdl hdl, message_ptr msg) {
|
||||
s->send(hdl, msg->get_payload(), msg->get_opcode());
|
||||
}
|
||||
|
||||
std::string run_server_test(std::string input) {
|
||||
std::string run_server_test(std::string input, bool log) {
|
||||
server test_server;
|
||||
return run_server_test(test_server,input);
|
||||
return run_server_test(test_server,input,log);
|
||||
}
|
||||
|
||||
std::string run_server_test(server & s, std::string input) {
|
||||
std::string run_server_test(server & s, std::string input, bool log) {
|
||||
server::connection_ptr con;
|
||||
std::stringstream output;
|
||||
|
||||
s.clear_access_channels(websocketpp::log::alevel::all);
|
||||
s.clear_error_channels(websocketpp::log::elevel::all);
|
||||
if (log) {
|
||||
s.set_access_channels(websocketpp::log::alevel::all);
|
||||
s.set_error_channels(websocketpp::log::elevel::all);
|
||||
} else {
|
||||
s.clear_access_channels(websocketpp::log::alevel::all);
|
||||
s.clear_error_channels(websocketpp::log::elevel::all);
|
||||
}
|
||||
|
||||
s.register_ostream(&output);
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
|
||||
// Test Environment:
|
||||
// server, no TLS, no locks, iostream based transport
|
||||
#include <websocketpp/config/debug.hpp>
|
||||
#include <websocketpp/config/core.hpp>
|
||||
#include <websocketpp/server.hpp>
|
||||
#include <websocketpp/client.hpp>
|
||||
@@ -46,5 +47,5 @@ using websocketpp::lib::placeholders::_2;
|
||||
using websocketpp::lib::bind;
|
||||
|
||||
void echo_func(server* s, websocketpp::connection_hdl hdl, message_ptr msg);
|
||||
std::string run_server_test(std::string input);
|
||||
std::string run_server_test(server & s, std::string input);
|
||||
std::string run_server_test(std::string input, bool log = false);
|
||||
std::string run_server_test(server & s, std::string input, bool log = false);
|
||||
@@ -311,6 +311,7 @@ public:
|
||||
, m_rng(rng)
|
||||
, m_local_close_code(close::status::abnormal_close)
|
||||
, m_remote_close_code(close::status::abnormal_close)
|
||||
, m_is_http(false)
|
||||
, m_was_clean(false)
|
||||
{
|
||||
m_alog.write(log::alevel::devel,"connection constructor");
|
||||
@@ -1245,21 +1246,21 @@ protected:
|
||||
void handle_transport_init(lib::error_code const & ec);
|
||||
|
||||
/// Set m_processor based on information in m_request. Set m_response
|
||||
/// status and return false on error.
|
||||
bool initialize_processor();
|
||||
/// status and return an error code indicating status.
|
||||
lib::error_code initialize_processor();
|
||||
|
||||
/// Perform WebSocket handshake validation of m_request using m_processor.
|
||||
/// set m_response and return false on error.
|
||||
bool process_handshake_request();
|
||||
/// set m_response and return an error code indicating status.
|
||||
lib::error_code process_handshake_request();
|
||||
private:
|
||||
/// Completes m_response, serializes it, and sends it out on the wire.
|
||||
void send_http_response();
|
||||
void send_http_response(lib::error_code const & ec);
|
||||
|
||||
/// Sends an opening WebSocket connect request
|
||||
void send_http_request();
|
||||
|
||||
/// Alternate path for send_http_response in error conditions
|
||||
void send_http_response_error();
|
||||
void send_http_response_error(lib::error_code const & ec);
|
||||
|
||||
/// Process control message
|
||||
/**
|
||||
@@ -1356,6 +1357,12 @@ private:
|
||||
* Includes: error code and message for why it was failed
|
||||
*/
|
||||
void log_fail_result();
|
||||
|
||||
/// Prints information about HTTP connections
|
||||
/**
|
||||
* Includes: TODO
|
||||
*/
|
||||
void log_http_result();
|
||||
|
||||
/// Prints information about an arbitrary error code on the specified channel
|
||||
template <typename error_type>
|
||||
@@ -1499,6 +1506,10 @@ private:
|
||||
|
||||
/// Detailed internal error code
|
||||
lib::error_code m_ec;
|
||||
|
||||
/// A flag that gets set once it is determined that the connection is an
|
||||
/// HTTP connection and not a WebSocket one.
|
||||
bool m_is_http;
|
||||
|
||||
bool m_was_clean;
|
||||
|
||||
|
||||
+27
-1
@@ -124,7 +124,23 @@ enum value {
|
||||
async_accept_not_listening,
|
||||
|
||||
/// The requested operation was canceled
|
||||
operation_canceled
|
||||
operation_canceled,
|
||||
|
||||
/// Connection rejected
|
||||
rejected,
|
||||
|
||||
/// Upgrade Required. This happens if an HTTP request is made to a
|
||||
/// WebSocket++ server that doesn't implement an http handler
|
||||
upgrade_required,
|
||||
|
||||
/// Invalid WebSocket protocol version
|
||||
invalid_version,
|
||||
|
||||
/// Unsupported WebSocket protocol version
|
||||
unsupported_version,
|
||||
|
||||
/// HTTP parse error
|
||||
http_parse_error
|
||||
}; // enum value
|
||||
|
||||
|
||||
@@ -190,6 +206,16 @@ public:
|
||||
return "Async Accept not listening";
|
||||
case error::operation_canceled:
|
||||
return "Operation canceled";
|
||||
case error::rejected:
|
||||
return "Connection rejected";
|
||||
case error::upgrade_required:
|
||||
return "Upgrade required";
|
||||
case error::invalid_version:
|
||||
return "Invalid version";
|
||||
case error::unsupported_version:
|
||||
return "Unsupported version";
|
||||
case error::http_parse_error:
|
||||
return "HTTP parse error";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
@@ -784,7 +784,7 @@ void connection<config>::handle_read_handshake(lib::error_code const & ec,
|
||||
// All HTTP exceptions will result in this request failing and an error
|
||||
// response being returned. No more bytes will be read in this con.
|
||||
m_response.set_status(e.m_error_code,e.m_error_msg);
|
||||
this->send_http_response_error();
|
||||
this->send_http_response_error(error::make_error_code(error::http_parse_error));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -804,8 +804,9 @@ void connection<config>::handle_read_handshake(lib::error_code const & ec,
|
||||
}
|
||||
|
||||
if (m_request.ready()) {
|
||||
if (!this->initialize_processor()) {
|
||||
this->send_http_response_error();
|
||||
lib::error_code processor_ec = this->initialize_processor();
|
||||
if (processor_ec) {
|
||||
this->send_http_response_error(processor_ec);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -822,7 +823,7 @@ void connection<config>::handle_read_handshake(lib::error_code const & ec,
|
||||
// TODO: need more bytes
|
||||
m_alog.write(log::alevel::devel,"short key3 read");
|
||||
m_response.set_status(http::status_code::internal_server_error);
|
||||
this->send_http_response_error();
|
||||
this->send_http_response_error(processor::error::make_error_code(processor::error::short_key3));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -845,8 +846,8 @@ void connection<config>::handle_read_handshake(lib::error_code const & ec,
|
||||
m_internal_state = istate::PROCESS_HTTP_REQUEST;
|
||||
|
||||
// We have the complete request. Process it.
|
||||
this->process_handshake_request();
|
||||
this->send_http_response();
|
||||
lib::error_code handshake_ec = this->process_handshake_request();
|
||||
this->send_http_response(handshake_ec);
|
||||
} else {
|
||||
// read at least 1 more byte
|
||||
transport_con_type::async_read_at_least(
|
||||
@@ -869,7 +870,7 @@ void connection<config>::handle_read_handshake(lib::error_code const & ec,
|
||||
// sure if the hybi00 key3 bytes need to be read). This method sets the correct
|
||||
// state and calls send_http_response
|
||||
template <typename config>
|
||||
void connection<config>::send_http_response_error() {
|
||||
void connection<config>::send_http_response_error(lib::error_code const & ec) {
|
||||
if (m_internal_state != istate::READ_HTTP_REQUEST) {
|
||||
m_alog.write(log::alevel::devel,
|
||||
"send_http_response_error called in invalid state");
|
||||
@@ -879,7 +880,7 @@ void connection<config>::send_http_response_error() {
|
||||
|
||||
m_internal_state = istate::PROCESS_HTTP_REQUEST;
|
||||
|
||||
this->send_http_response();
|
||||
this->send_http_response(ec);
|
||||
}
|
||||
|
||||
// All exit paths for this function need to call send_http_response() or submit
|
||||
@@ -1048,12 +1049,12 @@ void connection<config>::read_frame() {
|
||||
}
|
||||
|
||||
template <typename config>
|
||||
bool connection<config>::initialize_processor() {
|
||||
lib::error_code connection<config>::initialize_processor() {
|
||||
m_alog.write(log::alevel::devel,"initialize_processor");
|
||||
|
||||
// if it isn't a websocket handshake nothing to do.
|
||||
if (!processor::is_websocket_handshake(m_request)) {
|
||||
return true;
|
||||
return lib::error_code();
|
||||
}
|
||||
|
||||
int version = processor::get_websocket_version(m_request);
|
||||
@@ -1061,14 +1062,14 @@ bool connection<config>::initialize_processor() {
|
||||
if (version < 0) {
|
||||
m_alog.write(log::alevel::devel, "BAD REQUEST: can't determine version");
|
||||
m_response.set_status(http::status_code::bad_request);
|
||||
return false;
|
||||
return error::make_error_code(error::invalid_version);
|
||||
}
|
||||
|
||||
m_processor = get_processor(version);
|
||||
|
||||
// if the processor is not null we are done
|
||||
if (m_processor) {
|
||||
return true;
|
||||
return lib::error_code();
|
||||
}
|
||||
|
||||
// We don't have a processor for this version. Return bad request
|
||||
@@ -1086,11 +1087,11 @@ bool connection<config>::initialize_processor() {
|
||||
}
|
||||
|
||||
m_response.replace_header("Sec-WebSocket-Version",ss.str());
|
||||
return false;
|
||||
return error::make_error_code(error::unsupported_version);
|
||||
}
|
||||
|
||||
template <typename config>
|
||||
bool connection<config>::process_handshake_request() {
|
||||
lib::error_code connection<config>::process_handshake_request() {
|
||||
m_alog.write(log::alevel::devel,"process handshake request");
|
||||
|
||||
if (!processor::is_websocket_handshake(m_request)) {
|
||||
@@ -1106,16 +1107,18 @@ bool connection<config>::process_handshake_request() {
|
||||
if (!m_uri->get_valid()) {
|
||||
m_alog.write(log::alevel::devel, "Bad request: failed to parse uri");
|
||||
m_response.set_status(http::status_code::bad_request);
|
||||
return false;
|
||||
return error::make_error_code(error::invalid_uri);
|
||||
}
|
||||
|
||||
if (m_http_handler) {
|
||||
m_is_http = true;
|
||||
m_http_handler(m_connection_hdl);
|
||||
} else {
|
||||
set_status(http::status_code::upgrade_required);
|
||||
return error::make_error_code(error::upgrade_required);
|
||||
}
|
||||
|
||||
return true;
|
||||
return lib::error_code();
|
||||
}
|
||||
|
||||
lib::error_code ec = m_processor->validate_handshake(m_request);
|
||||
@@ -1125,7 +1128,7 @@ bool connection<config>::process_handshake_request() {
|
||||
// Not a valid handshake request
|
||||
m_alog.write(log::alevel::devel, "Bad request " + ec.message());
|
||||
m_response.set_status(http::status_code::bad_request);
|
||||
return false;
|
||||
return ec;
|
||||
}
|
||||
|
||||
// Read extension parameters and set up values necessary for the end user
|
||||
@@ -1138,7 +1141,7 @@ bool connection<config>::process_handshake_request() {
|
||||
// a failed connection attempt.
|
||||
m_alog.write(log::alevel::devel, "Bad request: " + neg_results.first.message());
|
||||
m_response.set_status(http::status_code::bad_request);
|
||||
return false;
|
||||
return neg_results.first;
|
||||
} else {
|
||||
// extension negotiation succeeded, set response header accordingly
|
||||
// we don't send an empty extensions header because it breaks many
|
||||
@@ -1156,7 +1159,7 @@ bool connection<config>::process_handshake_request() {
|
||||
if (!m_uri->get_valid()) {
|
||||
m_alog.write(log::alevel::devel, "Bad request: failed to parse uri");
|
||||
m_response.set_status(http::status_code::bad_request);
|
||||
return false;
|
||||
return error::make_error_code(error::invalid_uri);
|
||||
}
|
||||
|
||||
// extract subprotocols
|
||||
@@ -1181,7 +1184,7 @@ bool connection<config>::process_handshake_request() {
|
||||
m_alog.write(log::alevel::devel, s.str());
|
||||
|
||||
m_response.set_status(http::status_code::internal_server_error);
|
||||
return false;
|
||||
return ec;
|
||||
}
|
||||
} else {
|
||||
// User application has rejected the handshake
|
||||
@@ -1193,19 +1196,22 @@ bool connection<config>::process_handshake_request() {
|
||||
if (m_response.get_status_code() == http::status_code::uninitialized) {
|
||||
m_response.set_status(http::status_code::bad_request);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
return error::make_error_code(error::rejected);
|
||||
}
|
||||
|
||||
return true;
|
||||
return lib::error_code();
|
||||
}
|
||||
|
||||
template <typename config>
|
||||
void connection<config>::send_http_response() {
|
||||
void connection<config>::send_http_response(lib::error_code const & ec) {
|
||||
m_alog.write(log::alevel::devel,"connection send_http_response");
|
||||
|
||||
if (m_response.get_status_code() == http::status_code::uninitialized) {
|
||||
m_response.set_status(http::status_code::internal_server_error);
|
||||
m_ec = error::make_error_code(error::general);
|
||||
} else {
|
||||
m_ec = ec;
|
||||
}
|
||||
|
||||
m_response.set_version("HTTP/1.1");
|
||||
@@ -1290,12 +1296,13 @@ void connection<config>::handle_send_http_response(lib::error_code const & ec) {
|
||||
m_handshake_timer.reset();
|
||||
}
|
||||
|
||||
this->log_open_result();
|
||||
|
||||
if (m_response.get_status_code() != http::status_code::switching_protocols)
|
||||
{
|
||||
if (m_processor) {
|
||||
// this was a websocket connection that ended in an error
|
||||
/*if (m_processor || m_ec == error::http_parse_error ||
|
||||
m_ec == error::invalid_version || m_ec == error::unsupported_version
|
||||
|| m_ec == error::upgrade_required)
|
||||
{*/
|
||||
if (!m_is_http) {
|
||||
std::stringstream s;
|
||||
s << "Handshake ended with HTTP error: "
|
||||
<< m_response.get_status_code();
|
||||
@@ -1303,11 +1310,22 @@ void connection<config>::handle_send_http_response(lib::error_code const & ec) {
|
||||
} else {
|
||||
// if this was not a websocket connection, we have written
|
||||
// the expected response and the connection can be closed.
|
||||
}
|
||||
this->terminate(make_error_code(error::http_connection_ended));
|
||||
|
||||
this->log_http_result();
|
||||
|
||||
if (m_ec) {
|
||||
m_alog.write(log::alevel::devel,
|
||||
"got to writing HTTP results with m_ec set: "+m_ec.message());
|
||||
}
|
||||
m_ec = make_error_code(error::http_connection_ended);
|
||||
}
|
||||
|
||||
this->terminate(m_ec);
|
||||
return;
|
||||
}
|
||||
|
||||
this->log_open_result();
|
||||
|
||||
m_internal_state = istate::PROCESS_CONNECTION;
|
||||
m_state = session::state::open;
|
||||
|
||||
@@ -1586,6 +1604,12 @@ void connection<config>::terminate(lib::error_code const & ec) {
|
||||
if (m_state == session::state::connecting) {
|
||||
m_state = session::state::closed;
|
||||
tstat = failed;
|
||||
|
||||
// Log fail result here before socket is shut down and we can't get
|
||||
// the remote address, etc anymore
|
||||
if (m_ec != error::http_connection_ended) {
|
||||
log_fail_result();
|
||||
}
|
||||
} else if (m_state != session::state::closed) {
|
||||
m_state = session::state::closed;
|
||||
tstat = closed;
|
||||
@@ -1622,10 +1646,11 @@ void connection<config>::handle_terminate(terminate_status tstat,
|
||||
|
||||
// clean shutdown
|
||||
if (tstat == failed) {
|
||||
if (m_fail_handler) {
|
||||
m_fail_handler(m_connection_hdl);
|
||||
if (m_ec != error::http_connection_ended) {
|
||||
if (m_fail_handler) {
|
||||
m_fail_handler(m_connection_hdl);
|
||||
}
|
||||
}
|
||||
log_fail_result();
|
||||
} else if (tstat == closed) {
|
||||
if (m_close_handler) {
|
||||
m_close_handler(m_connection_hdl);
|
||||
@@ -2164,9 +2189,69 @@ void connection<config>::log_close_result()
|
||||
template <typename config>
|
||||
void connection<config>::log_fail_result()
|
||||
{
|
||||
// TODO: include more information about the connection?
|
||||
// should this be filed under connect rather than disconnect?
|
||||
m_alog.write(log::alevel::disconnect,"Failed: "+m_ec.message());
|
||||
std::stringstream s;
|
||||
|
||||
int version = processor::get_websocket_version(m_request);
|
||||
|
||||
// Connection Type
|
||||
s << "WebSocket Connection ";
|
||||
|
||||
// Remote endpoint address & WebSocket version
|
||||
s << transport_con_type::get_remote_endpoint();
|
||||
if (version < 0) {
|
||||
s << " -";
|
||||
} else {
|
||||
s << " v" << version;
|
||||
}
|
||||
|
||||
// User Agent
|
||||
std::string ua = m_request.get_header("User-Agent");
|
||||
if (ua == "") {
|
||||
s << " \"\" ";
|
||||
} else {
|
||||
// check if there are any quotes in the user agent
|
||||
s << " \"" << utility::string_replace_all(ua,"\"","\\\"") << "\" ";
|
||||
}
|
||||
|
||||
// URI
|
||||
s << (m_uri ? m_uri->get_resource() : "-");
|
||||
|
||||
// HTTP Status code
|
||||
s << " " << m_response.get_status_code();
|
||||
|
||||
// WebSocket++ error code & reason
|
||||
s << " " << m_ec << " " << m_ec.message();
|
||||
|
||||
m_alog.write(log::alevel::fail,s.str());
|
||||
}
|
||||
|
||||
template <typename config>
|
||||
void connection<config>::log_http_result() {
|
||||
std::stringstream s;
|
||||
|
||||
if (processor::is_websocket_handshake(m_request)) {
|
||||
m_alog.write(log::alevel::devel,"Call to log_http_result for WebSocket");
|
||||
return;
|
||||
}
|
||||
|
||||
// Connection Type
|
||||
s << (m_request.get_header("host") == "" ? "-" : m_request.get_header("host"))
|
||||
<< " " << transport_con_type::get_remote_endpoint()
|
||||
<< " \"" << m_request.get_method()
|
||||
<< " " << (m_uri ? m_uri->get_resource() : "-")
|
||||
<< " " << m_request.get_version() << "\" " << m_response.get_status_code()
|
||||
<< " " << m_response.get_body().size();
|
||||
|
||||
// User Agent
|
||||
std::string ua = m_request.get_header("User-Agent");
|
||||
if (ua == "") {
|
||||
s << " \"\" ";
|
||||
} else {
|
||||
// check if there are any quotes in the user agent
|
||||
s << " \"" << utility::string_replace_all(ua,"\"","\\\"") << "\" ";
|
||||
}
|
||||
|
||||
m_alog.write(log::alevel::http,s.str());
|
||||
}
|
||||
|
||||
} // namespace websocketpp
|
||||
|
||||
@@ -141,6 +141,13 @@ struct alevel {
|
||||
static level const devel = 0x400;
|
||||
/// Special channel for application specific logs. Not used by the library.
|
||||
static level const app = 0x800;
|
||||
/// Access related to HTTP requests
|
||||
static level const http = 0x1000;
|
||||
/// One line for each failed WebSocket connection with details
|
||||
static level const fail = 0x2000;
|
||||
/// Aggregate package representing the commonly used core access channels
|
||||
/// Connect, Disconnect, Fail, and HTTP
|
||||
static level const access_core = 0x00003003;
|
||||
/// Special aggregate value representing "all levels"
|
||||
static level const all = 0xffffffff;
|
||||
|
||||
@@ -180,6 +187,10 @@ struct alevel {
|
||||
return "devel";
|
||||
case app:
|
||||
return "application";
|
||||
case http:
|
||||
return "http";
|
||||
case fail:
|
||||
return "fail";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user