blob: ed7c8cfedd2be932db2281f220a1058bec6e5f19 [file] [log] [blame]
Ed Tanous3eb2f352018-12-20 12:30:45 -08001#pragma once
Ed Tanousc94ad492019-10-10 15:39:33 -07002#include <app.h>
Ed Tanous3eb2f352018-12-20 12:30:45 -08003#include <sys/socket.h>
Ed Tanousc94ad492019-10-10 15:39:33 -07004#include <websocket.h>
Ed Tanous3eb2f352018-12-20 12:30:45 -08005
Iwona Klimaszewskac0a1c8a2019-07-12 18:26:38 +02006#include <async_resp.hpp>
Ed Tanous3eb2f352018-12-20 12:30:45 -08007#include <boost/container/flat_map.hpp>
Ed Tanous3eb2f352018-12-20 12:30:45 -08008#include <webserver_common.hpp>
9
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:
20 explicit KvmSession(crow::websocket::Connection& conn) :
Ed Tanous271584a2019-07-09 16:24:22 -070021 conn(conn), hostSocket(conn.get_io_context()), doingWrite(false)
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(
26 endpoint, [this, &conn](const boost::system::error_code& ec) {
27 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 {
34 conn.close("Error in connecting to KVM port");
35 }
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));
58 BMCWEB_LOG_DEBUG << "conn:" << &conn << ", Commiting " << data.size()
59 << " 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) {
76 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)
84 {
85 conn.close("Error in connecting to KVM port");
86 }
87 return;
88 }
89
90 outputBuffer.commit(bytesRead);
91 std::string_view payload(
92 static_cast<const char*>(outputBuffer.data().data()),
93 bytesRead);
94 BMCWEB_LOG_DEBUG << "conn:" << &conn
95 << ", Sending payload size " << payload.size();
96 conn.sendBinary(payload);
97 outputBuffer.consume(bytesRead);
98
99 doRead();
100 });
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;
119 hostSocket.async_write_some(
120 inputBuffer.data(), [this](const boost::system::error_code& ec,
121 std::size_t bytesWritten) {
122 BMCWEB_LOG_DEBUG << "conn:" << &conn << ", Wrote "
123 << bytesWritten << "bytes";
124 doingWrite = false;
125 inputBuffer.consume(bytesWritten);
126
127 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)
137 {
138 conn.close("Error in reading to host port");
139 }
140 return;
141 }
142
143 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;
149 boost::beast::flat_static_buffer<1024U * 50U> outputBuffer;
150 boost::beast::flat_static_buffer<1024U> inputBuffer;
151 bool doingWrite;
152};
153
154static boost::container::flat_map<crow::websocket::Connection*,
155 std::unique_ptr<KvmSession>>
156 sessions;
Ed Tanous3eb2f352018-12-20 12:30:45 -0800157
158inline void requestRoutes(CrowApp& app)
159{
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700160 sessions.reserve(maxSessions);
161
Ed Tanous3eb2f352018-12-20 12:30:45 -0800162 BMCWEB_ROUTE(app, "/kvm/0")
Ed Tanous8251ffe2019-10-10 14:33:54 -0700163 .requires({"ConfigureComponents", "ConfigureManager"})
Ed Tanous3eb2f352018-12-20 12:30:45 -0800164 .websocket()
Iwona Klimaszewskac0a1c8a2019-07-12 18:26:38 +0200165 .onopen([](crow::websocket::Connection& conn,
166 std::shared_ptr<bmcweb::AsyncResp> asyncResp) {
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 })
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700177 .onclose([](crow::websocket::Connection& conn,
178 const std::string& reason) { sessions.erase(&conn); })
Ed Tanous3eb2f352018-12-20 12:30:45 -0800179 .onmessage([](crow::websocket::Connection& conn,
180 const std::string& data, bool is_binary) {
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700181 if (sessions[&conn])
Ed Tanous3eb2f352018-12-20 12:30:45 -0800182 {
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700183 sessions[&conn]->onMessage(data);
Ed Tanous3eb2f352018-12-20 12:30:45 -0800184 }
Ed Tanous3eb2f352018-12-20 12:30:45 -0800185 });
186}
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700187
Ed Tanous3eb2f352018-12-20 12:30:45 -0800188} // namespace obmc_kvm
189} // namespace crow