Ed Tanous | 2daf672 | 2018-08-23 11:27:23 -0700 | [diff] [blame] | 1 | #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 | |
| 10 | namespace crow |
| 11 | { |
| 12 | namespace obmc_console |
| 13 | { |
| 14 | |
| 15 | static std::unique_ptr<boost::asio::local::stream_protocol::socket> host_socket; |
| 16 | |
| 17 | static std::array<char, 4096> outputBuffer; |
| 18 | static std::string inputBuffer; |
| 19 | |
| 20 | static boost::container::flat_set<crow::websocket::Connection*> sessions; |
| 21 | |
| 22 | static bool doingWrite = false; |
| 23 | |
| 24 | void doWrite() |
| 25 | { |
| 26 | if (doingWrite) |
| 27 | { |
| 28 | BMCWEB_LOG_DEBUG << "Already writing. Bailing out"; |
| 29 | return; |
| 30 | } |
| 31 | |
| 32 | if (inputBuffer.empty()) |
| 33 | { |
| 34 | BMCWEB_LOG_DEBUG << "Outbuffer empty. Bailing out"; |
| 35 | return; |
| 36 | } |
| 37 | |
| 38 | doingWrite = true; |
| 39 | host_socket->async_write_some( |
| 40 | boost::asio::buffer(inputBuffer.data(), inputBuffer.size()), |
| 41 | [](boost::beast::error_code ec, std::size_t bytes_written) { |
| 42 | doingWrite = false; |
| 43 | inputBuffer.erase(0, bytes_written); |
| 44 | |
| 45 | if (ec == boost::asio::error::eof) |
| 46 | { |
| 47 | for (auto session : sessions) |
| 48 | { |
| 49 | session->close("Error in reading to host port"); |
| 50 | } |
| 51 | return; |
| 52 | } |
| 53 | if (ec) |
| 54 | { |
| 55 | BMCWEB_LOG_ERROR << "Error in host serial write " << ec; |
| 56 | return; |
| 57 | } |
| 58 | doWrite(); |
| 59 | }); |
| 60 | } |
| 61 | |
| 62 | void doRead() |
| 63 | { |
| 64 | BMCWEB_LOG_DEBUG << "Reading from socket"; |
| 65 | host_socket->async_read_some( |
| 66 | boost::asio::buffer(outputBuffer.data(), outputBuffer.size()), |
| 67 | [](const boost::system::error_code& ec, std::size_t bytesRead) { |
| 68 | BMCWEB_LOG_DEBUG << "read done. Read " << bytesRead << " bytes"; |
| 69 | if (ec) |
| 70 | { |
| 71 | BMCWEB_LOG_ERROR << "Couldn't read from host serial port: " |
| 72 | << ec; |
| 73 | for (auto session : sessions) |
| 74 | { |
| 75 | session->close("Error in connecting to host port"); |
| 76 | } |
| 77 | return; |
| 78 | } |
Adriana Kobylak | ae29b8c | 2019-04-24 11:19:18 -0500 | [diff] [blame] | 79 | std::string_view payload(outputBuffer.data(), bytesRead); |
Ed Tanous | 2daf672 | 2018-08-23 11:27:23 -0700 | [diff] [blame] | 80 | for (auto session : sessions) |
| 81 | { |
Cheng C Yang | 81bfd11 | 2019-01-03 14:57:03 +0800 | [diff] [blame] | 82 | session->sendBinary(payload); |
Ed Tanous | 2daf672 | 2018-08-23 11:27:23 -0700 | [diff] [blame] | 83 | } |
| 84 | doRead(); |
| 85 | }); |
| 86 | } |
| 87 | |
| 88 | void connectHandler(const boost::system::error_code& ec) |
| 89 | { |
| 90 | if (ec) |
| 91 | { |
| 92 | BMCWEB_LOG_ERROR << "Couldn't connect to host serial port: " << ec; |
| 93 | for (auto session : sessions) |
| 94 | { |
| 95 | session->close("Error in connecting to host port"); |
| 96 | } |
| 97 | return; |
| 98 | } |
| 99 | |
| 100 | doWrite(); |
| 101 | doRead(); |
| 102 | } |
| 103 | |
| 104 | void requestRoutes(CrowApp& app) |
| 105 | { |
| 106 | BMCWEB_ROUTE(app, "/console0") |
| 107 | .websocket() |
| 108 | .onopen([](crow::websocket::Connection& conn) { |
| 109 | BMCWEB_LOG_DEBUG << "Connection " << &conn << " opened"; |
| 110 | |
| 111 | sessions.insert(&conn); |
| 112 | if (host_socket == nullptr) |
| 113 | { |
| 114 | const std::string consoleName("\0obmc-console", 13); |
| 115 | boost::asio::local::stream_protocol::endpoint ep(consoleName); |
| 116 | |
Ed Tanous | 2daf672 | 2018-08-23 11:27:23 -0700 | [diff] [blame] | 117 | host_socket = std::make_unique< |
| 118 | boost::asio::local::stream_protocol::socket>( |
Ed Tanous | ceac6f7 | 2018-12-02 11:58:47 -0800 | [diff] [blame] | 119 | conn.get_io_context()); |
Ed Tanous | 2daf672 | 2018-08-23 11:27:23 -0700 | [diff] [blame] | 120 | host_socket->async_connect(ep, connectHandler); |
| 121 | } |
| 122 | }) |
| 123 | .onclose( |
| 124 | [](crow::websocket::Connection& conn, const std::string& reason) { |
| 125 | sessions.erase(&conn); |
| 126 | if (sessions.empty()) |
| 127 | { |
| 128 | host_socket = nullptr; |
| 129 | inputBuffer.clear(); |
| 130 | inputBuffer.shrink_to_fit(); |
| 131 | } |
| 132 | }) |
| 133 | .onmessage([](crow::websocket::Connection& conn, |
| 134 | const std::string& data, bool is_binary) { |
| 135 | inputBuffer += data; |
| 136 | doWrite(); |
| 137 | }); |
| 138 | } |
| 139 | } // namespace obmc_console |
| 140 | } // namespace crow |