blob: aa2eaecc3205089f17cb7b3017f682e62e9731f5 [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
25inline void doWrite()
26{
27 if (doingWrite)
28 {
29 BMCWEB_LOG_DEBUG << "Already writing. Bailing out";
30 return;
31 }
32 if (inputBuffer.size() == 0)
33 {
34 BMCWEB_LOG_DEBUG << "inputBuffer empty. Bailing out";
35 return;
36 }
37
38 doingWrite = true;
39 hostSocket->async_write_some(
40 inputBuffer.data(),
41 [](boost::beast::error_code ec, std::size_t bytes_written) {
42 BMCWEB_LOG_DEBUG << "Wrote " << bytes_written << "bytes";
43 doingWrite = false;
44 inputBuffer.consume(bytes_written);
45
46 if (session == nullptr)
47 {
48 return;
49 }
50 if (ec == boost::asio::error::eof)
51 {
52 session->close("KVM socket port closed");
53 return;
54 }
55 if (ec)
56 {
57 session->close("Error in reading to host port");
58 BMCWEB_LOG_ERROR << "Error in KVM socket write " << ec;
59 return;
60 }
61 doWrite();
62 });
63}
64
65inline void doRead();
66
67inline void readDone(const boost::system::error_code& ec, std::size_t bytesRead)
68{
69 outputBuffer.commit(bytesRead);
70 BMCWEB_LOG_DEBUG << "read done. Read " << bytesRead << " bytes";
71 if (ec)
72 {
73 BMCWEB_LOG_ERROR << "Couldn't read from KVM socket port: " << ec;
74 if (session != nullptr)
75 {
76 session->close("Error in connecting to KVM port");
77 }
78 return;
79 }
80 if (session == nullptr)
81 {
82 return;
83 }
84
85 boost::beast::string_view payload(
86 static_cast<const char*>(outputBuffer.data().data()), bytesRead);
87 BMCWEB_LOG_DEBUG << "Sending payload size " << payload.size();
88 session->sendBinary(payload);
89 outputBuffer.consume(bytesRead);
90
91 doRead();
92}
93
94inline void doRead()
95{
96 std::size_t bytes = outputBuffer.capacity() - outputBuffer.size();
97 BMCWEB_LOG_DEBUG << "Reading " << bytes << " from kvm socket";
98 hostSocket->async_read_some(
99 outputBuffer.prepare(outputBuffer.capacity() - outputBuffer.size()),
100 readDone);
101}
102
103inline void connectHandler(const boost::system::error_code& ec)
104{
105 if (ec)
106 {
107 BMCWEB_LOG_ERROR << "Couldn't connect to KVM socket port: " << ec;
108 if (session != nullptr)
109 {
110 session->close("Error in connecting to KVM port");
111 }
112 return;
113 }
114
115 doWrite();
116 doRead();
117}
118
119inline void requestRoutes(CrowApp& app)
120{
121 BMCWEB_ROUTE(app, "/kvm/0")
122 .websocket()
123 .onopen([](crow::websocket::Connection& conn) {
124 BMCWEB_LOG_DEBUG << "Connection " << &conn << " opened";
125
126 if (session != nullptr)
127 {
128 conn.close("User already connected");
129 return;
130 }
131
132 session = &conn;
133 if (hostSocket == nullptr)
134 {
135 boost::asio::ip::tcp::endpoint endpoint(
136 boost::asio::ip::address::from_string("127.0.0.1"), 5900);
137
138 hostSocket = std::make_unique<boost::asio::ip::tcp::socket>(
139 conn.get_io_context());
140 hostSocket->async_connect(endpoint, connectHandler);
141 }
142 })
143 .onclose(
144 [](crow::websocket::Connection& conn, const std::string& reason) {
145 session = nullptr;
146 hostSocket = nullptr;
147 inputBuffer.reset();
148 outputBuffer.reset();
149 })
150 .onmessage([](crow::websocket::Connection& conn,
151 const std::string& data, bool is_binary) {
152 if (data.length() > inputBuffer.capacity())
153 {
154 BMCWEB_LOG_ERROR << "Buffer overrun when writing "
155 << data.length() << " bytes";
156 conn.close("Buffer overrun");
157 return;
158 }
159
160 BMCWEB_LOG_DEBUG << "Read " << data.size()
161 << " bytes from websocket";
162 boost::asio::buffer_copy(inputBuffer.prepare(data.size()),
163 boost::asio::buffer(data));
164 BMCWEB_LOG_DEBUG << "commiting " << data.size()
165 << " bytes from websocket";
166 inputBuffer.commit(data.size());
167
168 BMCWEB_LOG_DEBUG << "inputbuffer size " << inputBuffer.size();
169 doWrite();
170 });
171}
172} // namespace obmc_kvm
173} // namespace crow