diff options
author | Egor Tensin <Egor.Tensin@gmail.com> | 2019-12-07 03:36:21 +0300 |
---|---|---|
committer | Egor Tensin <Egor.Tensin@gmail.com> | 2019-12-07 03:36:21 +0300 |
commit | 00863566ec4601c65c435b74e575d49546a1c707 (patch) | |
tree | 479a0a6e96aba8191c7a65ea9bee2f4d5e3a4aba /server/main/session.cpp | |
parent | add stress_test.py (diff) | |
download | math-server-00863566ec4601c65c435b74e575d49546a1c707.tar.gz math-server-00863566ec4601c65c435b74e575d49546a1c707.zip |
split server into multiple components
In a vague attempt to make header files more readable, split server/
into a number of components.
Also, refactor the unit tests to use the "Data-driven test cases" of
Boost.Test.
Diffstat (limited to 'server/main/session.cpp')
-rw-r--r-- | server/main/session.cpp | 114 |
1 files changed, 114 insertions, 0 deletions
diff --git a/server/main/session.cpp b/server/main/session.cpp new file mode 100644 index 0000000..0ee7f75 --- /dev/null +++ b/server/main/session.cpp @@ -0,0 +1,114 @@ +#include "session.hpp" +#include "session_manager.hpp" + +#include "../common/error.hpp" +#include "../common/log.hpp" +#include "../parser/parser.hpp" + +#include <boost/asio.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/system/error_code.hpp> +#include <boost/system/system_error.hpp> + +#include <cstddef> + +#include <exception> +#include <string> +#include <utility> + +namespace math::server { +namespace { + +std::string reply_to_string(double result) { + return boost::lexical_cast<std::string>(result); +} + +std::string calc_reply(const std::string& input) { + std::string reply; + try { + reply = reply_to_string(Parser{input}.exec()); + } catch (const std::exception& e) { + reply = e.what(); + } + return reply; +} + +} + +Session::Session(SessionManager& mgr, boost::asio::io_context& io_context) + : m_session_mgr{mgr}, m_strand{io_context}, m_socket{io_context} +{ } + +boost::asio::ip::tcp::socket& Session::socket() { + return m_socket; +} + +void Session::start() { + read(); +} + +void Session::stop() { + close(); +} + +void Session::close() { + try { + m_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both); + m_socket.close(); + } catch (const boost::system::system_error& e) { + throw Error{e.what()}; + } +} + +void Session::read() { + const auto self = shared_from_this(); + + // Stop at LF + boost::asio::async_read_until(m_socket, m_buffer, '\n', boost::asio::bind_executor(m_strand, + [this, self] (const boost::system::error_code& ec, std::size_t bytes) { + handle_read(ec, bytes); + })); +} + +void Session::handle_read(const boost::system::error_code& ec, std::size_t bytes) { + if (ec) { + log::error("%1%: %2%", __func__, ec.message()); + m_session_mgr.stop(shared_from_this()); + return; + } + + write(calc_reply(consume_input(bytes))); +} + +std::string Session::consume_input(std::size_t bytes) { + const auto data = boost::asio::buffer_cast<const char*>(m_buffer.data()); + const std::string input{data, bytes - 1}; + m_buffer.consume(bytes); + return input; +} + +void Session::write(std::string output) { + const auto self = shared_from_this(); + + // Include CR (so that Windows' telnet client works) + output += "\r\n"; + + boost::asio::const_buffer buffer{output.c_str(), output.length()}; + + boost::asio::async_write(m_socket, std::move(buffer), boost::asio::bind_executor(m_strand, + [this, self] (const boost::system::error_code& ec, std::size_t bytes) { + handle_write(ec, bytes); + })); +} + +void Session::handle_write(const boost::system::error_code& ec, std::size_t bytes) { + if (ec) { + log::error("%1%: %2%", __func__, ec.message()); + m_session_mgr.stop(shared_from_this()); + return; + } + + read(); +} + +} |