From 3be5fd90f27a143b5342cf2720f8e8cf4f9d56a9 Mon Sep 17 00:00:00 2001 From: Reinder Feenstra Date: Wed, 30 Mar 2022 00:15:49 +0200 Subject: [PATCH] ecos: implemented command trottling, to prevent receive buffer overflow in ECoS --- .../protocol/ecos/iohandler/iohandler.cpp | 11 ++++-- .../protocol/ecos/iohandler/iohandler.hpp | 5 ++- .../protocol/ecos/iohandler/tcpiohandler.cpp | 37 +++++++++++++++++-- .../protocol/ecos/iohandler/tcpiohandler.hpp | 6 ++- .../src/hardware/protocol/ecos/messages.cpp | 12 ++++-- .../src/hardware/protocol/ecos/messages.hpp | 1 + 6 files changed, 59 insertions(+), 13 deletions(-) diff --git a/server/src/hardware/protocol/ecos/iohandler/iohandler.cpp b/server/src/hardware/protocol/ecos/iohandler/iohandler.cpp index 57ad7635..94cad124 100644 --- a/server/src/hardware/protocol/ecos/iohandler/iohandler.cpp +++ b/server/src/hardware/protocol/ecos/iohandler/iohandler.cpp @@ -3,7 +3,7 @@ * * This file is part of the traintastic source code. * - * Copyright (C) 2021 Reinder Feenstra + * Copyright (C) 2021-2022 Reinder Feenstra * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -71,9 +71,9 @@ void IOHandler::processRead(size_t bytesTransferred) if(pos != std::string_view::npos) { size_t end = buffer.find('>', pos); - if(end != buffer.size()) + if(end != std::string_view::npos) { - m_kernel.receive(std::string_view{m_readBuffer.data() + m_readPos, end - m_readPos + 1}); + receive(std::string_view{m_readBuffer.data() + m_readPos, end - m_readPos + 1}); m_readPos = end + 1; } else @@ -100,4 +100,9 @@ void IOHandler::processRead(size_t bytesTransferred) } } +void IOHandler::receive(std::string_view message) +{ + m_kernel.receive(message); +} + } diff --git a/server/src/hardware/protocol/ecos/iohandler/iohandler.hpp b/server/src/hardware/protocol/ecos/iohandler/iohandler.hpp index 1fe4d7f4..5f09af57 100644 --- a/server/src/hardware/protocol/ecos/iohandler/iohandler.hpp +++ b/server/src/hardware/protocol/ecos/iohandler/iohandler.hpp @@ -3,7 +3,7 @@ * * This file is part of the traintastic source code. * - * Copyright (C) 2021 Reinder Feenstra + * Copyright (C) 2021-2022 Reinder Feenstra * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -38,12 +38,13 @@ class IOHandler std::array m_readBuffer; size_t m_readBufferOffset; size_t m_readPos; - std::array m_writeBuffer; + std::array m_writeBuffer; size_t m_writeBufferOffset; IOHandler(Kernel& kernel); void processRead(size_t bytesTransferred); + virtual void receive(std::string_view message); virtual void write() = 0; public: diff --git a/server/src/hardware/protocol/ecos/iohandler/tcpiohandler.cpp b/server/src/hardware/protocol/ecos/iohandler/tcpiohandler.cpp index c097aa0b..c4d6c824 100644 --- a/server/src/hardware/protocol/ecos/iohandler/tcpiohandler.cpp +++ b/server/src/hardware/protocol/ecos/iohandler/tcpiohandler.cpp @@ -3,7 +3,7 @@ * * This file is part of the traintastic source code. * - * Copyright (C) 2021 Reinder Feenstra + * Copyright (C) 2021-2022 Reinder Feenstra * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -21,6 +21,7 @@ */ #include "tcpiohandler.hpp" +#include "../messages.hpp" #include "../kernel.hpp" #include "../../../../core/eventloop.hpp" #include "../../../../log/log.hpp" @@ -86,18 +87,48 @@ void TCPIOHandler::read() }); } +void TCPIOHandler::receive(std::string_view message) +{ + IOHandler::receive(message); + if(m_waitingForReply > 0 && isReply(message)) + { + m_waitingForReply--; + if(!m_writing && m_waitingForReply < transferWindow && m_writeBufferOffset > 0) + write(); + } +} + void TCPIOHandler::write() { - m_socket.async_write_some(boost::asio::buffer(m_writeBuffer.data(), m_writeBufferOffset), + assert(!m_writing); + + m_writing = true; + + const char* p = m_writeBuffer.data(); + const char* end = m_writeBuffer.data() + m_writeBufferOffset; + + while(m_waitingForReply < transferWindow && (p = std::find(p, end, '\n')) != end) + { + p++; + m_waitingForReply++; + } + + if(p == m_writeBuffer.data()) + return; + + m_socket.async_write_some(boost::asio::buffer(m_writeBuffer.data(), p - m_writeBuffer.data()), [this](const boost::system::error_code& ec, std::size_t bytesTransferred) { + m_writing = false; + if(!ec) { if(bytesTransferred < m_writeBufferOffset) { m_writeBufferOffset -= bytesTransferred; memmove(m_writeBuffer.data(), m_writeBuffer.data() + bytesTransferred, m_writeBufferOffset); - write(); + if(m_waitingForReply < transferWindow) + write(); } else m_writeBufferOffset = 0; diff --git a/server/src/hardware/protocol/ecos/iohandler/tcpiohandler.hpp b/server/src/hardware/protocol/ecos/iohandler/tcpiohandler.hpp index 024a0466..9022c934 100644 --- a/server/src/hardware/protocol/ecos/iohandler/tcpiohandler.hpp +++ b/server/src/hardware/protocol/ecos/iohandler/tcpiohandler.hpp @@ -3,7 +3,7 @@ * * This file is part of the traintastic source code. * - * Copyright (C) 2021 Reinder Feenstra + * Copyright (C) 2021-2022 Reinder Feenstra * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -31,12 +31,16 @@ namespace ECoS { class TCPIOHandler final : public IOHandler { private: + static constexpr uint32_t transferWindow = 25; boost::asio::ip::tcp::socket m_socket; boost::asio::ip::tcp::endpoint m_endpoint; + bool m_writing = false; + uint32_t m_waitingForReply = 0; void read(); protected: + void receive(std::string_view message) final; void write() final; public: diff --git a/server/src/hardware/protocol/ecos/messages.cpp b/server/src/hardware/protocol/ecos/messages.cpp index 9145a3ac..7c68fae7 100644 --- a/server/src/hardware/protocol/ecos/messages.cpp +++ b/server/src/hardware/protocol/ecos/messages.cpp @@ -28,16 +28,20 @@ namespace ECoS { +static const std::string_view startDelimiterReply = "