diff --git a/src/campaign_server/campaign_server.cpp b/src/campaign_server/campaign_server.cpp index 9e01039eca75..303012048b20 100644 --- a/src/campaign_server/campaign_server.cpp +++ b/src/campaign_server/campaign_server.cpp @@ -231,6 +231,13 @@ void server::handle_new_client(socket_ptr socket) ); } +void server::handle_out_of_band(socket_ptr socket, char data) +{ + socket->cancel(); + socket->send(boost::asio::buffer(&data, 1), boost::asio::socket_base::message_out_of_band); + handle_new_client(socket); +} + void server::handle_request(socket_ptr socket, std::shared_ptr doc) { config data; diff --git a/src/campaign_server/campaign_server.hpp b/src/campaign_server/campaign_server.hpp index 80f8af0e266a..a21aef1c58ed 100644 --- a/src/campaign_server/campaign_server.hpp +++ b/src/campaign_server/campaign_server.hpp @@ -100,6 +100,7 @@ class server : public server_base boost::asio::basic_waitable_timer flush_timer_; void handle_new_client(socket_ptr socket); + void handle_out_of_band(socket_ptr socket, char data); void handle_request(socket_ptr socket, std::shared_ptr doc); #ifndef _WIN32 diff --git a/src/network_asio.cpp b/src/network_asio.cpp index 71bc2d1a7b84..4288431d88aa 100644 --- a/src/network_asio.cpp +++ b/src/network_asio.cpp @@ -36,6 +36,7 @@ connection::connection(const std::string& host, const std::string& service) , resolver_(io_service_) , socket_(io_service_) , done_(false) + , canceled_(false) , write_buf_() , read_buf_() , handshake_response_() @@ -139,11 +140,33 @@ void connection::cancel() if(ec) { WRN_NW << "Failed to cancel network operations: " << ec.message() << std::endl; } + bytes_to_write_ = 0; + bytes_written_ = 0; + bytes_to_read_ = 0; + bytes_read_ = 0; + + // send a message to server out-of-band so it would cancel transfer + socket_.send(boost::asio::buffer(&bytes_read_, 1), boost::asio::socket_base::message_out_of_band); + + // drop data that server sent before canceling + canceled_ = true; + #define BOOST_ASIO_OS_DEF_SO_OOBINLINE SO_OOBINLINE + typedef boost::asio::detail::socket_option::boolean< + BOOST_ASIO_OS_DEF(SOL_SOCKET), BOOST_ASIO_OS_DEF(SO_OOBINLINE)> + out_of_band_inline; + out_of_band_inline option(true); + socket_.set_option(option); + + // TODO: this should be made async but changes to dialogs needed + std::unique_ptr buf { new char[1024] }; + while(canceled_) { + socket_.receive(boost::asio::buffer(buf.get(), 1024), boost::asio::socket_base::message_peek); + if(socket_.at_mark()) { + canceled_ = false; + } + socket_.receive(boost::asio::buffer(buf.get(), 1024)); + } } - bytes_to_write_ = 0; - bytes_written_ = 0; - bytes_to_read_ = 0; - bytes_read_ = 0; } std::size_t connection::is_write_complete(const boost::system::error_code& ec, std::size_t bytes_transferred) diff --git a/src/network_asio.hpp b/src/network_asio.hpp index e3027abe72e0..dcb0ccf8ed9a 100644 --- a/src/network_asio.hpp +++ b/src/network_asio.hpp @@ -131,6 +131,7 @@ class connection socket socket_; bool done_; + bool canceled_; std::unique_ptr write_buf_; std::unique_ptr read_buf_; diff --git a/src/server/server.cpp b/src/server/server.cpp index b01286883b3b..ce436b90f3ed 100644 --- a/src/server/server.cpp +++ b/src/server/server.cpp @@ -514,6 +514,11 @@ void server::handle_new_client(socket_ptr socket) ); } +void server::handle_out_of_band(socket_ptr, char) +{ + +} + void server::handle_version(socket_ptr socket) { async_receive_doc(socket, diff --git a/src/server/server.hpp b/src/server/server.hpp index 719d590bdcf4..00cf49b9e7e7 100644 --- a/src/server/server.hpp +++ b/src/server/server.hpp @@ -37,6 +37,7 @@ class server : public server_base private: void handle_new_client(socket_ptr socket); + void handle_out_of_band(socket_ptr socket, char); void handle_version(socket_ptr socket); void read_version(socket_ptr socket, std::shared_ptr doc); diff --git a/src/server/server_base.cpp b/src/server/server_base.cpp index 4668f3df1709..e8f449a46d63 100644 --- a/src/server/server_base.cpp +++ b/src/server/server_base.cpp @@ -120,6 +120,34 @@ void server_base::handle_handshake(const boost::system::error_code& error, socke [=](const boost::system::error_code& error, size_t) { if(!check_error(error, socket)) this->handle_new_client(socket); } ); + + read_oob_data(socket); +} + +void server_base::read_oob_data(socket_ptr socket) +{ + boost::shared_array buf { new char[1] }; + socket_weak_ptr socket_weak { socket }; + socket->async_receive(boost::asio::buffer(buf.get(), 1), boost::asio::socket_base::message_out_of_band, + [=](const boost::system::error_code& error, std::size_t) + { this->handle_oob_data(error, socket_weak, buf); } + ); +} + +void server_base::handle_oob_data(const boost::system::error_code& error, socket_weak_ptr socket, boost::shared_array buf) +{ + if(socket.expired()) + return; + if(error) { + if(error.value() != boost::system::errc::invalid_argument) { + ERR_SERVER << client_address(socket.lock()) << "\terror reading out-of-band data: " << error.message() << std::endl; + } + return; + } else { + handle_out_of_band(socket.lock(), buf.get()[0]); + } + + read_oob_data(socket.lock()); } #ifndef _WIN32 diff --git a/src/server/server_base.hpp b/src/server/server_base.hpp index 451dd1fc9cdf..d23ea76c7386 100644 --- a/src/server/server_base.hpp +++ b/src/server/server_base.hpp @@ -26,6 +26,7 @@ #include typedef std::shared_ptr socket_ptr; +typedef std::weak_ptr socket_weak_ptr; struct server_shutdown : public game::error { @@ -55,7 +56,11 @@ class server_base void serverside_handshake(socket_ptr socket); void handle_handshake(const boost::system::error_code& error, socket_ptr socket, boost::shared_array buf); + void read_oob_data(socket_ptr socket); + void handle_oob_data(const boost::system::error_code& error, socket_weak_ptr socket, boost::shared_array buf); + virtual void handle_new_client(socket_ptr socket) = 0; + virtual void handle_out_of_band(socket_ptr socket, char) = 0; virtual bool accepting_connections() const { return true; } virtual std::string is_ip_banned(const std::string&) const { return std::string(); }