blob: 942e9c607e46f0dd217ec7d977a932eb53d07f0c [file] [log] [blame]
Ed Tanous3eb2f352018-12-20 12:30:45 -08001#pragma once
Ed Tanous3ccb3ad2023-01-13 17:40:03 -08002#include "app.hpp"
3#include "async_resp.hpp"
4
Ed Tanous3eb2f352018-12-20 12:30:45 -08005#include <sys/socket.h>
6
7#include <boost/container/flat_map.hpp>
Ed Tanous04e438c2020-10-03 08:06:26 -07008#include <websocket.hpp>
Ed Tanous3eb2f352018-12-20 12:30:45 -08009
10namespace crow
11{
12namespace obmc_kvm
13{
14
Jae Hyun Yooa133b292019-09-16 10:38:47 -070015static constexpr const uint maxSessions = 4;
Ed Tanous3eb2f352018-12-20 12:30:45 -080016
Jae Hyun Yooa133b292019-09-16 10:38:47 -070017class KvmSession
Jae Hyun Yooc68604b2019-02-26 15:46:07 -080018{
Jae Hyun Yooa133b292019-09-16 10:38:47 -070019 public:
Ed Tanous23a21a12020-07-25 04:45:05 +000020 explicit KvmSession(crow::websocket::Connection& connIn) :
Ed Tanousf5b191a2022-02-15 11:30:39 -080021 conn(connIn), hostSocket(conn.getIoContext())
Jae Hyun Yooa133b292019-09-16 10:38:47 -070022 {
23 boost::asio::ip::tcp::endpoint endpoint(
Johnathan Manteyb8c7eb22020-02-19 12:44:19 -080024 boost::asio::ip::make_address("127.0.0.1"), 5900);
Jae Hyun Yooa133b292019-09-16 10:38:47 -070025 hostSocket.async_connect(
Ed Tanous23a21a12020-07-25 04:45:05 +000026 endpoint, [this, &connIn](const boost::system::error_code& ec) {
Jae Hyun Yooa133b292019-09-16 10:38:47 -070027 if (ec)
28 {
29 BMCWEB_LOG_ERROR
30 << "conn:" << &conn
31 << ", Couldn't connect to KVM socket port: " << ec;
32 if (ec != boost::asio::error::operation_aborted)
33 {
Ed Tanous23a21a12020-07-25 04:45:05 +000034 connIn.close("Error in connecting to KVM port");
Jae Hyun Yooa133b292019-09-16 10:38:47 -070035 }
36 return;
37 }
Jae Hyun Yooc68604b2019-02-26 15:46:07 -080038
Jae Hyun Yooa133b292019-09-16 10:38:47 -070039 doRead();
40 });
Jae Hyun Yooc68604b2019-02-26 15:46:07 -080041 }
42
Jae Hyun Yooa133b292019-09-16 10:38:47 -070043 void onMessage(const std::string& data)
Ed Tanous3eb2f352018-12-20 12:30:45 -080044 {
Jae Hyun Yooa133b292019-09-16 10:38:47 -070045 if (data.length() > inputBuffer.capacity())
Ed Tanous3eb2f352018-12-20 12:30:45 -080046 {
Jae Hyun Yooa133b292019-09-16 10:38:47 -070047 BMCWEB_LOG_ERROR << "conn:" << &conn
48 << ", Buffer overrun when writing "
49 << data.length() << " bytes";
50 conn.close("Buffer overrun");
51 return;
Ed Tanous3eb2f352018-12-20 12:30:45 -080052 }
Jae Hyun Yooa133b292019-09-16 10:38:47 -070053
54 BMCWEB_LOG_DEBUG << "conn:" << &conn << ", Read " << data.size()
55 << " bytes from websocket";
56 boost::asio::buffer_copy(inputBuffer.prepare(data.size()),
57 boost::asio::buffer(data));
Gunnar Millscaa3ce32020-07-08 14:46:53 -050058 BMCWEB_LOG_DEBUG << "conn:" << &conn << ", Committing " << data.size()
Jae Hyun Yooa133b292019-09-16 10:38:47 -070059 << " bytes from websocket";
60 inputBuffer.commit(data.size());
61
62 BMCWEB_LOG_DEBUG << "conn:" << &conn << ", inputbuffer size "
63 << inputBuffer.size();
64 doWrite();
Ed Tanous3eb2f352018-12-20 12:30:45 -080065 }
66
Jae Hyun Yooa133b292019-09-16 10:38:47 -070067 protected:
68 void doRead()
Ed Tanous3eb2f352018-12-20 12:30:45 -080069 {
Jae Hyun Yooa133b292019-09-16 10:38:47 -070070 std::size_t bytes = outputBuffer.capacity() - outputBuffer.size();
71 BMCWEB_LOG_DEBUG << "conn:" << &conn << ", Reading " << bytes
72 << " from kvm socket";
73 hostSocket.async_read_some(
74 outputBuffer.prepare(outputBuffer.capacity() - outputBuffer.size()),
75 [this](const boost::system::error_code& ec, std::size_t bytesRead) {
Ed Tanous002d39b2022-05-31 08:59:27 -070076 BMCWEB_LOG_DEBUG << "conn:" << &conn << ", read done. Read "
77 << bytesRead << " bytes";
78 if (ec)
79 {
80 BMCWEB_LOG_ERROR
81 << "conn:" << &conn
82 << ", Couldn't read from KVM socket port: " << ec;
83 if (ec != boost::asio::error::operation_aborted)
Jae Hyun Yooa133b292019-09-16 10:38:47 -070084 {
Ed Tanous002d39b2022-05-31 08:59:27 -070085 conn.close("Error in connecting to KVM port");
Jae Hyun Yooa133b292019-09-16 10:38:47 -070086 }
Ed Tanous002d39b2022-05-31 08:59:27 -070087 return;
88 }
Jae Hyun Yooa133b292019-09-16 10:38:47 -070089
Ed Tanous002d39b2022-05-31 08:59:27 -070090 outputBuffer.commit(bytesRead);
91 std::string_view payload(
92 static_cast<const char*>(outputBuffer.data().data()),
93 bytesRead);
94 BMCWEB_LOG_DEBUG << "conn:" << &conn << ", Sending payload size "
95 << payload.size();
96 conn.sendBinary(payload);
97 outputBuffer.consume(bytesRead);
Jae Hyun Yooa133b292019-09-16 10:38:47 -070098
Ed Tanous002d39b2022-05-31 08:59:27 -070099 doRead();
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700100 });
101 }
102
103 void doWrite()
104 {
105 if (doingWrite)
Ed Tanous3eb2f352018-12-20 12:30:45 -0800106 {
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700107 BMCWEB_LOG_DEBUG << "conn:" << &conn
108 << ", Already writing. Bailing out";
109 return;
Ed Tanous3eb2f352018-12-20 12:30:45 -0800110 }
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700111 if (inputBuffer.size() == 0)
112 {
113 BMCWEB_LOG_DEBUG << "conn:" << &conn
114 << ", inputBuffer empty. Bailing out";
115 return;
116 }
117
118 doingWrite = true;
Ed Tanous002d39b2022-05-31 08:59:27 -0700119 hostSocket.async_write_some(inputBuffer.data(),
120 [this](const boost::system::error_code& ec,
121 std::size_t bytesWritten) {
122 BMCWEB_LOG_DEBUG << "conn:" << &conn << ", Wrote " << bytesWritten
123 << "bytes";
124 doingWrite = false;
125 inputBuffer.consume(bytesWritten);
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700126
Ed Tanous002d39b2022-05-31 08:59:27 -0700127 if (ec == boost::asio::error::eof)
128 {
129 conn.close("KVM socket port closed");
130 return;
131 }
132 if (ec)
133 {
134 BMCWEB_LOG_ERROR << "conn:" << &conn
135 << ", Error in KVM socket write " << ec;
136 if (ec != boost::asio::error::operation_aborted)
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700137 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700138 conn.close("Error in reading to host port");
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700139 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700140 return;
141 }
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700142
Ed Tanous002d39b2022-05-31 08:59:27 -0700143 doWrite();
144 });
Ed Tanous3eb2f352018-12-20 12:30:45 -0800145 }
146
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700147 crow::websocket::Connection& conn;
148 boost::asio::ip::tcp::socket hostSocket;
Ed Tanous6de264c2022-01-06 12:47:59 -0800149 boost::beast::flat_static_buffer<1024UL * 50UL> outputBuffer;
150 boost::beast::flat_static_buffer<1024UL> inputBuffer;
Ed Tanousf5b191a2022-02-15 11:30:39 -0800151 bool doingWrite{false};
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700152};
153
Ed Tanouscf9e4172022-12-21 09:30:16 -0800154using SessionMap = boost::container::flat_map<crow::websocket::Connection*,
155 std::unique_ptr<KvmSession>>;
156// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
157static SessionMap sessions;
Ed Tanous3eb2f352018-12-20 12:30:45 -0800158
Ed Tanous52cc1122020-07-18 13:51:21 -0700159inline void requestRoutes(App& app)
Ed Tanous3eb2f352018-12-20 12:30:45 -0800160{
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700161 sessions.reserve(maxSessions);
162
Ed Tanous3eb2f352018-12-20 12:30:45 -0800163 BMCWEB_ROUTE(app, "/kvm/0")
Ed Tanous432a8902021-06-14 15:28:56 -0700164 .privileges({{"ConfigureComponents", "ConfigureManager"}})
Ed Tanous3eb2f352018-12-20 12:30:45 -0800165 .websocket()
zhanghch0577726382021-10-21 14:07:57 +0800166 .onopen([](crow::websocket::Connection& conn) {
Ed Tanous3eb2f352018-12-20 12:30:45 -0800167 BMCWEB_LOG_DEBUG << "Connection " << &conn << " opened";
168
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700169 if (sessions.size() == maxSessions)
Ed Tanous3eb2f352018-12-20 12:30:45 -0800170 {
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700171 conn.close("Max sessions are already connected");
Ed Tanous3eb2f352018-12-20 12:30:45 -0800172 return;
173 }
174
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700175 sessions[&conn] = std::make_unique<KvmSession>(conn);
Ed Tanous3eb2f352018-12-20 12:30:45 -0800176 })
Ed Tanouscb13a392020-07-25 19:02:03 +0000177 .onclose([](crow::websocket::Connection& conn, const std::string&) {
178 sessions.erase(&conn);
179 })
Ed Tanous3eb2f352018-12-20 12:30:45 -0800180 .onmessage([](crow::websocket::Connection& conn,
Ed Tanouscb13a392020-07-25 19:02:03 +0000181 const std::string& data, bool) {
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700182 if (sessions[&conn])
Ed Tanous3eb2f352018-12-20 12:30:45 -0800183 {
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700184 sessions[&conn]->onMessage(data);
Ed Tanous3eb2f352018-12-20 12:30:45 -0800185 }
Ed Tanous3eb2f352018-12-20 12:30:45 -0800186 });
187}
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700188
Ed Tanous3eb2f352018-12-20 12:30:45 -0800189} // namespace obmc_kvm
190} // namespace crow