diff options
Diffstat (limited to 'whiteboard.cpp')
-rw-r--r-- | whiteboard.cpp | 100 |
1 files changed, 71 insertions, 29 deletions
diff --git a/whiteboard.cpp b/whiteboard.cpp index 6782385..b15ebbe 100644 --- a/whiteboard.cpp +++ b/whiteboard.cpp @@ -93,7 +93,30 @@ std::string make_xml(const std::initializer_list<std::pair<std::string, std::str return oss.str(); } -std::string Whiteboard::handle_request(const std::string& request) +void Whiteboard::notify_other_connections(Whiteboard::connection& c, const std::string& id) +{ + std::for_each(m_registry.begin(id), m_registry.end(id), [&](const Whiteboard::connection& ci) + { + if (c != ci) { + boost::beast::flat_buffer buffer; + boost::beast::ostream(buffer) << make_xml({ + {"type", "getfile"}, + {"data", m_storage->getDocument(id)}, + {"revision", std::to_string(m_storage->getRevision(id)) }, + {"cursorpos", std::to_string(m_storage->getCursorPos(id)) } + }); + std::lock_guard<std::mutex> lock(m_websocket_mutex); + try { + ci->write(buffer.data()); + } catch (const std::exception& ex) { + std::cerr << "Warning: Notify write for " << ci << " not possible, id " << id << std::endl; + m_registry.dump(); + } + } + }); +} + +std::string Whiteboard::handle_request(Whiteboard::connection& c, const std::string& request) { try { std::lock_guard<std::mutex> lock(m_storage_mutex); @@ -109,27 +132,41 @@ std::string Whiteboard::handle_request(const std::string& request) if (command == "modify") { std::string id {xml.get<std::string>("request.id")}; std::string data {xml.get<std::string>("request.data")}; - m_storage->setDocument(id, data); - return make_xml({{"type", "modify"}, {"revision", std::to_string(m_storage->getRevision(id)) }}); + if (m_storage->getDocument(id) != data) { + m_storage->setDocument(id, data); + m_registry.setId(c, id); + notify_other_connections(c, id); + return make_xml({{"type", "modify"}, {"revision", std::to_string(m_storage->getRevision(id)) }}); + } + return {}; + } else if (command == "cursorpos") { + std::string id {xml.get<std::string>("request.id")}; + int pos {xml.get<int>("request.pos")}; + if (m_storage->getCursorPos(id) != pos) { + m_storage->setCursorPos(id, pos); + notify_other_connections(c, id); + } + return {}; } else if (command == "getfile") { std::string id {xml.get<std::string>("request.id")}; - std::string filedata {m_storage->getDocument(id)}; + std::string filedata; + try { + filedata = m_storage->getDocument(id); + } catch (const std::runtime_error&) { + m_storage->setDocument(id, filedata); + } if (filedata.size() > 30000000) throw std::runtime_error("File too big"); - - return make_xml({{"type", "getfile"}, {"data", filedata}, {"revision", std::to_string(m_storage->getRevision(id)) }}); - } else if (command == "checkupdate") { - std::string id {xml.get<std::string>("request.id")}; - int request_revision {xml.get<int>("request.revision")}; - - int revision {m_storage->getRevision(id)}; - if (revision != request_revision) { - return make_xml({{"type", "update"}, {"data", m_storage->getDocument(id)}, {"revision", std::to_string(revision) }}); - } else { - return {}; // no reply - } + m_registry.setId(c, id); + + return make_xml({ + {"type", "getfile"}, + {"data", filedata}, + {"revision", std::to_string(m_storage->getRevision(id)) }, + {"cursorpos", std::to_string(m_storage->getCursorPos(id)) } + }); } else if (command == "newid") { return make_xml({{"type", "newid"}, {"id", m_storage->generate_id()}}); } else if (command == "qrcode") { @@ -146,7 +183,7 @@ std::string Whiteboard::handle_request(const std::string& request) } } catch (const std::exception& ex) { - return "Message handling error: "s + ex.what(); + return make_xml({{"type", "error"}, {"message", "Message handling error: "s + ex.what()}}); } } @@ -154,10 +191,11 @@ void Whiteboard::do_session(boost::asio::ip::tcp::socket socket) { try { // Construct the stream by moving in the socket - boost::beast::websocket::stream<boost::asio::ip::tcp::socket> ws{std::move(socket)}; + std::shared_ptr ws{std::make_shared<boost::beast::websocket::stream<boost::asio::ip::tcp::socket>>(std::move(socket))}; + ConnectionRegistry::RegistryGuard guard(m_registry, ws); // Set a decorator to change the Server of the handshake - ws.set_option(boost::beast::websocket::stream_base::decorator( + ws->set_option(boost::beast::websocket::stream_base::decorator( [](boost::beast::websocket::response_type& res) { res.set(boost::beast::http::field::server, @@ -168,27 +206,31 @@ void Whiteboard::do_session(boost::asio::ip::tcp::socket socket) boost::beast::http::request<boost::beast::http::string_body> req; boost::beast::flat_buffer buffer; - boost::beast::http::read(ws.next_layer(), buffer, parser); + boost::beast::http::read(ws->next_layer(), buffer, parser); req = parser.get(); - ws.accept(req); + ws->accept(req); while (true) { boost::beast::flat_buffer buffer; - ws.read(buffer); + ws->read(buffer); - ws.text(ws.got_text()); + ws->text(ws->got_text()); std::string data(boost::asio::buffers_begin(buffer.data()), boost::asio::buffers_end(buffer.data())); - data = handle_request(data); - buffer.consume(buffer.size()); - boost::beast::ostream(buffer) << data; - if (buffer.data().size() > 0) - ws.write(buffer.data()); + data = handle_request(ws, data); + if (buffer.data().size() > 0) { + buffer.consume(buffer.size()); + } + if (data.size() > 0) { + boost::beast::ostream(buffer) << data; + std::lock_guard<std::mutex> lock(m_websocket_mutex); + ws->write(buffer.data()); + } } } catch (boost::beast::system_error const& se) { // This indicates that the session was closed - if (se.code() != boost::beast::websocket::error::closed) + if (se.code() != boost::beast::websocket::error::closed && se.code() != boost::asio::error::eof) std::cerr << "Boost system_error in session: " << se.code().message() << std::endl; } catch (std::exception const& ex) { std::cerr << "Error in session: " << ex.what() << std::endl; |