summaryrefslogtreecommitdiff
path: root/include/kvm_websocket.hpp
diff options
context:
space:
mode:
authorJae Hyun Yoo <jae.hyun.yoo@linux.intel.com>2019-09-16 20:38:47 +0300
committerEd Tanous <ed.tanous@intel.com>2019-09-24 00:10:27 +0300
commita133b2918eec1f25492d14bbd665d8e2aaef3088 (patch)
treedefe30ddc16305514b463e7f01d96cdf0a9bd60e /include/kvm_websocket.hpp
parent58eb238f94fd2ba3e8e51eaac6113a097b926f18 (diff)
downloadbmcweb-a133b2918eec1f25492d14bbd665d8e2aaef3088.tar.xz
Add concurrent KVM sessions support
Currently, bmcweb allows just one KVM session. To make it support multiple concurrent KVM sessions, this commit adds KVM session class implementation. It limits the number of sessions to 4 to secure the connection quality. Tested: Up to 4 concurrent KVM sessions worked. Change-Id: I37342ba2e30f840131d6287b4249a298aa17a1f1 Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
Diffstat (limited to 'include/kvm_websocket.hpp')
-rw-r--r--include/kvm_websocket.hpp266
1 files changed, 135 insertions, 131 deletions
diff --git a/include/kvm_websocket.hpp b/include/kvm_websocket.hpp
index ae4d899860..db42ab8de4 100644
--- a/include/kvm_websocket.hpp
+++ b/include/kvm_websocket.hpp
@@ -4,7 +4,6 @@
#include <sys/socket.h>
#include <boost/container/flat_map.hpp>
-#include <boost/container/flat_set.hpp>
#include <webserver_common.hpp>
namespace crow
@@ -12,171 +11,176 @@ namespace crow
namespace obmc_kvm
{
-static std::unique_ptr<boost::asio::ip::tcp::socket> hostSocket;
+static constexpr const uint maxSessions = 4;
-// TODO(ed) validate that these buffer sizes are sane
-static boost::beast::flat_static_buffer<1024U * 50U> outputBuffer;
-static boost::beast::flat_static_buffer<1024U> inputBuffer;
-
-static crow::websocket::Connection* session = nullptr;
-
-static bool doingWrite = false;
-
-inline void doWrite();
-
-inline void WriteDone(const boost::system::error_code& ec,
- std::size_t bytesWritten)
-{
- BMCWEB_LOG_DEBUG << "Wrote " << bytesWritten << "bytes";
- doingWrite = false;
- inputBuffer.consume(bytesWritten);
-
- if (session == nullptr)
- {
- return;
- }
- if (ec == boost::asio::error::eof)
- {
- session->close("KVM socket port closed");
- return;
- }
- if (ec)
- {
- session->close("Error in reading to host port");
- BMCWEB_LOG_ERROR << "Error in KVM socket write " << ec;
- return;
- }
-
- doWrite();
-}
-
-inline void doWrite()
+class KvmSession
{
- if (doingWrite)
- {
- BMCWEB_LOG_DEBUG << "Already writing. Bailing out";
- return;
- }
- if (inputBuffer.size() == 0)
+ public:
+ explicit KvmSession(crow::websocket::Connection& conn) :
+ conn(conn), doingWrite(false), hostSocket(conn.get_io_context())
{
- BMCWEB_LOG_DEBUG << "inputBuffer empty. Bailing out";
- return;
+ boost::asio::ip::tcp::endpoint endpoint(
+ boost::asio::ip::make_address("::1"), 5900);
+ hostSocket.async_connect(
+ endpoint, [this, &conn](const boost::system::error_code& ec) {
+ if (ec)
+ {
+ BMCWEB_LOG_ERROR
+ << "conn:" << &conn
+ << ", Couldn't connect to KVM socket port: " << ec;
+ if (ec != boost::asio::error::operation_aborted)
+ {
+ conn.close("Error in connecting to KVM port");
+ }
+ return;
+ }
+
+ doRead();
+ });
}
- doingWrite = true;
- hostSocket->async_write_some(inputBuffer.data(), WriteDone);
-}
-
-inline void doRead();
-
-inline void readDone(const boost::system::error_code& ec, std::size_t bytesRead)
-{
- BMCWEB_LOG_DEBUG << "read done. Read " << bytesRead << " bytes";
- if (ec)
+ void onMessage(const std::string& data)
{
- BMCWEB_LOG_ERROR << "Couldn't read from KVM socket port: " << ec;
- if (session != nullptr)
+ if (data.length() > inputBuffer.capacity())
{
- session->close("Error in connecting to KVM port");
+ BMCWEB_LOG_ERROR << "conn:" << &conn
+ << ", Buffer overrun when writing "
+ << data.length() << " bytes";
+ conn.close("Buffer overrun");
+ return;
}
- return;
+
+ BMCWEB_LOG_DEBUG << "conn:" << &conn << ", Read " << data.size()
+ << " bytes from websocket";
+ boost::asio::buffer_copy(inputBuffer.prepare(data.size()),
+ boost::asio::buffer(data));
+ BMCWEB_LOG_DEBUG << "conn:" << &conn << ", Commiting " << data.size()
+ << " bytes from websocket";
+ inputBuffer.commit(data.size());
+
+ BMCWEB_LOG_DEBUG << "conn:" << &conn << ", inputbuffer size "
+ << inputBuffer.size();
+ doWrite();
}
- if (session == nullptr)
+
+ protected:
+ void doRead()
{
- return;
+ std::size_t bytes = outputBuffer.capacity() - outputBuffer.size();
+ BMCWEB_LOG_DEBUG << "conn:" << &conn << ", Reading " << bytes
+ << " from kvm socket";
+ hostSocket.async_read_some(
+ outputBuffer.prepare(outputBuffer.capacity() - outputBuffer.size()),
+ [this](const boost::system::error_code& ec, std::size_t bytesRead) {
+ BMCWEB_LOG_DEBUG << "conn:" << &conn << ", read done. Read "
+ << bytesRead << " bytes";
+ if (ec)
+ {
+ BMCWEB_LOG_ERROR
+ << "conn:" << &conn
+ << ", Couldn't read from KVM socket port: " << ec;
+ if (ec != boost::asio::error::operation_aborted)
+ {
+ conn.close("Error in connecting to KVM port");
+ }
+ return;
+ }
+
+ outputBuffer.commit(bytesRead);
+ std::string_view payload(
+ static_cast<const char*>(outputBuffer.data().data()),
+ bytesRead);
+ BMCWEB_LOG_DEBUG << "conn:" << &conn
+ << ", Sending payload size " << payload.size();
+ conn.sendBinary(payload);
+ outputBuffer.consume(bytesRead);
+
+ doRead();
+ });
}
- outputBuffer.commit(bytesRead);
- std::string_view payload(
- static_cast<const char*>(outputBuffer.data().data()), bytesRead);
- BMCWEB_LOG_DEBUG << "Sending payload size " << payload.size();
- session->sendBinary(payload);
- outputBuffer.consume(bytesRead);
-
- doRead();
-}
-
-inline void doRead()
-{
- std::size_t bytes = outputBuffer.capacity() - outputBuffer.size();
- BMCWEB_LOG_DEBUG << "Reading " << bytes << " from kvm socket";
- hostSocket->async_read_some(
- outputBuffer.prepare(outputBuffer.capacity() - outputBuffer.size()),
- readDone);
-}
-
-inline void connectHandler(const boost::system::error_code& ec)
-{
- if (ec)
+ void doWrite()
{
- BMCWEB_LOG_ERROR << "Couldn't connect to KVM socket port: " << ec;
- if (session != nullptr)
+ if (doingWrite)
{
- session->close("Error in connecting to KVM port");
+ BMCWEB_LOG_DEBUG << "conn:" << &conn
+ << ", Already writing. Bailing out";
+ return;
}
- return;
+ if (inputBuffer.size() == 0)
+ {
+ BMCWEB_LOG_DEBUG << "conn:" << &conn
+ << ", inputBuffer empty. Bailing out";
+ return;
+ }
+
+ doingWrite = true;
+ hostSocket.async_write_some(
+ inputBuffer.data(), [this](const boost::system::error_code& ec,
+ std::size_t bytesWritten) {
+ BMCWEB_LOG_DEBUG << "conn:" << &conn << ", Wrote "
+ << bytesWritten << "bytes";
+ doingWrite = false;
+ inputBuffer.consume(bytesWritten);
+
+ if (ec == boost::asio::error::eof)
+ {
+ conn.close("KVM socket port closed");
+ return;
+ }
+ if (ec)
+ {
+ BMCWEB_LOG_ERROR << "conn:" << &conn
+ << ", Error in KVM socket write " << ec;
+ if (ec != boost::asio::error::operation_aborted)
+ {
+ conn.close("Error in reading to host port");
+ }
+ return;
+ }
+
+ doWrite();
+ });
}
- doRead();
-}
+ crow::websocket::Connection& conn;
+ boost::asio::ip::tcp::socket hostSocket;
+ boost::beast::flat_static_buffer<1024U * 50U> outputBuffer;
+ boost::beast::flat_static_buffer<1024U> inputBuffer;
+ bool doingWrite;
+};
+
+static boost::container::flat_map<crow::websocket::Connection*,
+ std::unique_ptr<KvmSession>>
+ sessions;
inline void requestRoutes(CrowApp& app)
{
+ sessions.reserve(maxSessions);
+
BMCWEB_ROUTE(app, "/kvm/0")
.websocket()
.onopen([](crow::websocket::Connection& conn) {
BMCWEB_LOG_DEBUG << "Connection " << &conn << " opened";
- if (session != nullptr)
+ if (sessions.size() == maxSessions)
{
- conn.close("User already connected");
+ conn.close("Max sessions are already connected");
return;
}
- session = &conn;
- if (hostSocket == nullptr)
- {
- boost::asio::ip::tcp::endpoint endpoint(
- boost::asio::ip::make_address("127.0.0.1"), 5900);
-
- hostSocket = std::make_unique<boost::asio::ip::tcp::socket>(
- conn.get_io_context());
- hostSocket->async_connect(endpoint, connectHandler);
- }
+ sessions[&conn] = std::make_unique<KvmSession>(conn);
})
- .onclose(
- [](crow::websocket::Connection& conn, const std::string& reason) {
- session = nullptr;
- hostSocket = nullptr;
-#if BOOST_VERSION >= 107000
- inputBuffer.clear();
- outputBuffer.clear();
-#else
- inputBuffer.reset();
- outputBuffer.reset();
-#endif
- })
+ .onclose([](crow::websocket::Connection& conn,
+ const std::string& reason) { sessions.erase(&conn); })
.onmessage([](crow::websocket::Connection& conn,
const std::string& data, bool is_binary) {
- if (data.length() > inputBuffer.capacity())
+ if (sessions[&conn])
{
- BMCWEB_LOG_ERROR << "Buffer overrun when writing "
- << data.length() << " bytes";
- conn.close("Buffer overrun");
- return;
+ sessions[&conn]->onMessage(data);
}
-
- BMCWEB_LOG_DEBUG << "Read " << data.size()
- << " bytes from websocket";
- boost::asio::buffer_copy(inputBuffer.prepare(data.size()),
- boost::asio::buffer(data));
- BMCWEB_LOG_DEBUG << "commiting " << data.size()
- << " bytes from websocket";
- inputBuffer.commit(data.size());
-
- BMCWEB_LOG_DEBUG << "inputbuffer size " << inputBuffer.size();
- doWrite();
});
}
+
} // namespace obmc_kvm
} // namespace crow