blob: ae4d899860a4cd8265abf73309cb03bdbf312804 [file] [log] [blame]
#pragma once
#include <crow/app.h>
#include <crow/websocket.h>
#include <sys/socket.h>
#include <boost/container/flat_map.hpp>
#include <boost/container/flat_set.hpp>
#include <webserver_common.hpp>
namespace crow
{
namespace obmc_kvm
{
static std::unique_ptr<boost::asio::ip::tcp::socket> hostSocket;
// 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()
{
if (doingWrite)
{
BMCWEB_LOG_DEBUG << "Already writing. Bailing out";
return;
}
if (inputBuffer.size() == 0)
{
BMCWEB_LOG_DEBUG << "inputBuffer empty. Bailing out";
return;
}
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)
{
BMCWEB_LOG_ERROR << "Couldn't read from KVM socket port: " << ec;
if (session != nullptr)
{
session->close("Error in connecting to KVM port");
}
return;
}
if (session == nullptr)
{
return;
}
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)
{
BMCWEB_LOG_ERROR << "Couldn't connect to KVM socket port: " << ec;
if (session != nullptr)
{
session->close("Error in connecting to KVM port");
}
return;
}
doRead();
}
inline void requestRoutes(CrowApp& app)
{
BMCWEB_ROUTE(app, "/kvm/0")
.websocket()
.onopen([](crow::websocket::Connection& conn) {
BMCWEB_LOG_DEBUG << "Connection " << &conn << " opened";
if (session != nullptr)
{
conn.close("User 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);
}
})
.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
})
.onmessage([](crow::websocket::Connection& conn,
const std::string& data, bool is_binary) {
if (data.length() > inputBuffer.capacity())
{
BMCWEB_LOG_ERROR << "Buffer overrun when writing "
<< data.length() << " bytes";
conn.close("Buffer overrun");
return;
}
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