blob: ae4d899860a4cd8265abf73309cb03bdbf312804 [file] [log] [blame]
Ed Tanous3eb2f352018-12-20 12:30:45 -08001#pragma once
2#include <crow/app.h>
3#include <crow/websocket.h>
4#include <sys/socket.h>
5
6#include <boost/container/flat_map.hpp>
7#include <boost/container/flat_set.hpp>
8#include <webserver_common.hpp>
9
10namespace crow
11{
12namespace obmc_kvm
13{
14
15static std::unique_ptr<boost::asio::ip::tcp::socket> hostSocket;
16
17// TODO(ed) validate that these buffer sizes are sane
18static boost::beast::flat_static_buffer<1024U * 50U> outputBuffer;
19static boost::beast::flat_static_buffer<1024U> inputBuffer;
20
21static crow::websocket::Connection* session = nullptr;
22
23static bool doingWrite = false;
24
Jae Hyun Yooc68604b2019-02-26 15:46:07 -080025inline void doWrite();
26
27inline void WriteDone(const boost::system::error_code& ec,
28 std::size_t bytesWritten)
29{
30 BMCWEB_LOG_DEBUG << "Wrote " << bytesWritten << "bytes";
31 doingWrite = false;
32 inputBuffer.consume(bytesWritten);
33
34 if (session == nullptr)
35 {
36 return;
37 }
38 if (ec == boost::asio::error::eof)
39 {
40 session->close("KVM socket port closed");
41 return;
42 }
43 if (ec)
44 {
45 session->close("Error in reading to host port");
46 BMCWEB_LOG_ERROR << "Error in KVM socket write " << ec;
47 return;
48 }
49
50 doWrite();
51}
52
Ed Tanous3eb2f352018-12-20 12:30:45 -080053inline void doWrite()
54{
55 if (doingWrite)
56 {
57 BMCWEB_LOG_DEBUG << "Already writing. Bailing out";
58 return;
59 }
60 if (inputBuffer.size() == 0)
61 {
62 BMCWEB_LOG_DEBUG << "inputBuffer empty. Bailing out";
63 return;
64 }
65
66 doingWrite = true;
Jae Hyun Yooc68604b2019-02-26 15:46:07 -080067 hostSocket->async_write_some(inputBuffer.data(), WriteDone);
Ed Tanous3eb2f352018-12-20 12:30:45 -080068}
69
70inline void doRead();
71
72inline void readDone(const boost::system::error_code& ec, std::size_t bytesRead)
73{
Ed Tanous3eb2f352018-12-20 12:30:45 -080074 BMCWEB_LOG_DEBUG << "read done. Read " << bytesRead << " bytes";
75 if (ec)
76 {
77 BMCWEB_LOG_ERROR << "Couldn't read from KVM socket port: " << ec;
78 if (session != nullptr)
79 {
80 session->close("Error in connecting to KVM port");
81 }
82 return;
83 }
84 if (session == nullptr)
85 {
86 return;
87 }
88
Jae Hyun Yooc68604b2019-02-26 15:46:07 -080089 outputBuffer.commit(bytesRead);
Adriana Kobylakae29b8c2019-04-24 11:19:18 -050090 std::string_view payload(
Ed Tanous3eb2f352018-12-20 12:30:45 -080091 static_cast<const char*>(outputBuffer.data().data()), bytesRead);
92 BMCWEB_LOG_DEBUG << "Sending payload size " << payload.size();
93 session->sendBinary(payload);
94 outputBuffer.consume(bytesRead);
95
96 doRead();
97}
98
99inline void doRead()
100{
101 std::size_t bytes = outputBuffer.capacity() - outputBuffer.size();
102 BMCWEB_LOG_DEBUG << "Reading " << bytes << " from kvm socket";
103 hostSocket->async_read_some(
104 outputBuffer.prepare(outputBuffer.capacity() - outputBuffer.size()),
105 readDone);
106}
107
108inline void connectHandler(const boost::system::error_code& ec)
109{
110 if (ec)
111 {
112 BMCWEB_LOG_ERROR << "Couldn't connect to KVM socket port: " << ec;
113 if (session != nullptr)
114 {
115 session->close("Error in connecting to KVM port");
116 }
117 return;
118 }
119
Ed Tanous3eb2f352018-12-20 12:30:45 -0800120 doRead();
121}
122
123inline void requestRoutes(CrowApp& app)
124{
125 BMCWEB_ROUTE(app, "/kvm/0")
126 .websocket()
127 .onopen([](crow::websocket::Connection& conn) {
128 BMCWEB_LOG_DEBUG << "Connection " << &conn << " opened";
129
130 if (session != nullptr)
131 {
132 conn.close("User already connected");
133 return;
134 }
135
136 session = &conn;
137 if (hostSocket == nullptr)
138 {
139 boost::asio::ip::tcp::endpoint endpoint(
Ed Tanouse278c182019-03-13 16:23:37 -0700140 boost::asio::ip::make_address("127.0.0.1"), 5900);
Ed Tanous3eb2f352018-12-20 12:30:45 -0800141
142 hostSocket = std::make_unique<boost::asio::ip::tcp::socket>(
143 conn.get_io_context());
144 hostSocket->async_connect(endpoint, connectHandler);
145 }
146 })
147 .onclose(
148 [](crow::websocket::Connection& conn, const std::string& reason) {
149 session = nullptr;
150 hostSocket = nullptr;
Ed Tanouse278c182019-03-13 16:23:37 -0700151#if BOOST_VERSION >= 107000
152 inputBuffer.clear();
153 outputBuffer.clear();
154#else
Ed Tanous3eb2f352018-12-20 12:30:45 -0800155 inputBuffer.reset();
156 outputBuffer.reset();
Ed Tanouse278c182019-03-13 16:23:37 -0700157#endif
Ed Tanous3eb2f352018-12-20 12:30:45 -0800158 })
159 .onmessage([](crow::websocket::Connection& conn,
160 const std::string& data, bool is_binary) {
161 if (data.length() > inputBuffer.capacity())
162 {
163 BMCWEB_LOG_ERROR << "Buffer overrun when writing "
164 << data.length() << " bytes";
165 conn.close("Buffer overrun");
166 return;
167 }
168
169 BMCWEB_LOG_DEBUG << "Read " << data.size()
170 << " bytes from websocket";
171 boost::asio::buffer_copy(inputBuffer.prepare(data.size()),
172 boost::asio::buffer(data));
173 BMCWEB_LOG_DEBUG << "commiting " << data.size()
174 << " bytes from websocket";
175 inputBuffer.commit(data.size());
176
177 BMCWEB_LOG_DEBUG << "inputbuffer size " << inputBuffer.size();
178 doWrite();
179 });
180}
181} // namespace obmc_kvm
182} // namespace crow