aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/server/main/session.cpp
diff options
context:
space:
mode:
authorEgor Tensin <Egor.Tensin@gmail.com>2019-12-07 03:36:21 +0300
committerEgor Tensin <Egor.Tensin@gmail.com>2019-12-07 03:36:21 +0300
commit00863566ec4601c65c435b74e575d49546a1c707 (patch)
tree479a0a6e96aba8191c7a65ea9bee2f4d5e3a4aba /server/main/session.cpp
parentadd stress_test.py (diff)
downloadmath-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.cpp114
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();
+}
+
+}