diff options
Diffstat (limited to 'src/components/transport_manager/test/sample_websocket_server.cc')
-rw-r--r-- | src/components/transport_manager/test/sample_websocket_server.cc | 189 |
1 files changed, 179 insertions, 10 deletions
diff --git a/src/components/transport_manager/test/sample_websocket_server.cc b/src/components/transport_manager/test/sample_websocket_server.cc index 56899e72e2..917184a361 100644 --- a/src/components/transport_manager/test/sample_websocket_server.cc +++ b/src/components/transport_manager/test/sample_websocket_server.cc @@ -1,3 +1,35 @@ +/* + * Copyright (c) 2019, Ford Motor Company + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following + * disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * Neither the name of the Ford Motor Company nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + #include "transport_manager/cloud/sample_websocket_server.h" namespace { @@ -13,20 +45,78 @@ namespace websocket { WSSession::WSServer::WSServer(tcp::socket&& socket) : ws_(std::move(socket)), strand_(ws_.get_executor()) {} +void WSSession::WSServer::AddURLRoute(const std::string& target) { + url_routes_.insert(ParseRouteFromTarget(target)); +} + void WSSession::WSServer::Run() { - // Accept the websocket handshake - ws_.async_accept(boost::asio::bind_executor( - strand_, std::bind(&WSServer::OnAccept, this, std::placeholders::_1))); + req_ = {}; + http::async_read( + ws_.next_layer(), + buffer_, + req_, + std::bind(&WSServer::OnWebsocketHandshake, this, std::placeholders::_1)); +} + +void WSSession::WSServer::OnWebsocketHandshake( + const boost::system::error_code& ec) { + if (ec) { + return Fail("ERROR_HTTP_REQUEST_READ", ec); + } + if (websocket::is_upgrade(req_)) { + // Check target + std::string route = ParseRouteFromTarget(req_.target().to_string()); + if (!CanHandleRoute(route)) { + auto error = boost::system::errc::make_error_code( + boost::system::errc::address_not_available); + ws_.next_layer().close(); + return Fail("ERROR_INVALID_TARGET", error); + } + // Accept the websocket handshake + ws_.async_accept( + req_, + boost::asio::bind_executor( + strand_, + std::bind(&WSServer::OnAccept, this, std::placeholders::_1))); + } } void WSSession::WSServer::OnAccept(beast::error_code ec) { if (ec) { - return Fail("ERROR_CONNECTION_ACCEPT", ec); + return Fail("ERROR_WEBSOCKET_HANDSHAKE", ec); + } +} + +bool WSSession::WSServer::CanHandleRoute(const std::string& route) { + if (url_routes_.find(route) == url_routes_.end()) { + return false; + } + return true; +} + +std::string WSSession::WSServer::ParseRouteFromTarget( + const std::string& target) { + std::string route = target; + // Remove fragment + auto fragment_pos = route.find('#'); + if (fragment_pos != std::string::npos) { + route = route.substr(0, fragment_pos); + } + // Remove query + auto query_pos = route.find('?'); + if (query_pos != std::string::npos) { + route = route.substr(0, query_pos); } + + return route; } WSSession::WSSession(const std::string& address, uint16_t port) - : address_(address), port_(port), acceptor_(ioc_), socket_(ioc_) { + : address_(address) + , port_(port) + , acceptor_(ioc_) + , socket_(ioc_) + , ws_(nullptr) { endpoint_ = {boost::asio::ip::make_address(address), port}; boost::system::error_code error; @@ -76,6 +166,14 @@ void WSSession::Stop() { } } +void WSSession::AddRoute(const std::string& route) { + if (ws_ == nullptr) { + buffered_routes_.push(route); + return; + } + ws_->AddURLRoute(route); +} + void WSSession::on_accept(boost::system::error_code ec) { if (ec) { Fail("ERROR_ON_ACCEPT", ec); @@ -85,12 +183,20 @@ void WSSession::on_accept(boost::system::error_code ec) { // Make websocket object and start ws_ = std::make_shared<WSServer>(std::move(socket_)); + // Load routes stored in buffer + while (!buffered_routes_.empty()) { + ws_->AddURLRoute(buffered_routes_.front()); + buffered_routes_.pop(); + } ws_->Run(); } WSSSession::WSSServer::WSSServer(tcp::socket&& socket, ssl::context& ctx) : wss_(std::move(socket), ctx) {} +void WSSSession::WSSServer::AddURLRoute(const std::string& target) { + url_routes_.insert(ParseRouteFromTarget(target)); +} void WSSSession::WSSServer::Run() { // Perform the SSL handshake wss_.next_layer().async_handshake( @@ -103,9 +209,32 @@ void WSSSession::WSSServer::OnSSLHandshake(beast::error_code ec) { return Fail("ERROR_SSL_HANDSHAKE", ec); } - // Accept the websocket handshake - wss_.async_accept( - std::bind(&WSSServer::OnAccept, this, std::placeholders::_1)); + req_ = {}; + http::async_read( + wss_.next_layer(), + buffer_, + req_, + std::bind(&WSSServer::OnWebsocketHandshake, this, std::placeholders::_1)); +} + +void WSSSession::WSSServer::OnWebsocketHandshake( + const boost::system::error_code& ec) { + if (ec) { + return Fail("ERROR_HTTP_REQUEST_READ", ec); + } + if (websocket::is_upgrade(req_)) { + // Check target + std::string route = ParseRouteFromTarget(req_.target().to_string()); + if (!CanHandleRoute(route)) { + auto error = boost::system::errc::make_error_code( + boost::system::errc::address_not_available); + wss_.next_layer().next_layer().close(); + return Fail("ERROR_INVALID_TARGET", error); + } + // Accept the websocket handshake + wss_.async_accept( + req_, std::bind(&WSSServer::OnAccept, this, std::placeholders::_1)); + } } void WSSSession::WSSServer::OnAccept(beast::error_code ec) { @@ -114,11 +243,38 @@ void WSSSession::WSSServer::OnAccept(beast::error_code ec) { } } +bool WSSSession::WSSServer::CanHandleRoute(const std::string& route) { + if (url_routes_.find(route) == url_routes_.end()) { + return false; + } + return true; +} + +std::string WSSSession::WSSServer::ParseRouteFromTarget( + const std::string& target) { + std::string route = target; + // Remove fragment + auto fragment_pos = route.find('#'); + if (fragment_pos != std::string::npos) { + route = route.substr(0, fragment_pos); + } + // Remove query + auto query_pos = route.find('?'); + if (query_pos != std::string::npos) { + route = route.substr(0, query_pos); + } + + return route; +} + WSSSession::WSSSession(const std::string& address, uint16_t port, const std::string& certificate, const std::string& private_key) - : acceptor_(ioc_), socket_(ioc_), ctx_(ssl::context::sslv23_server) { + : acceptor_(ioc_) + , socket_(ioc_) + , ctx_(ssl::context::sslv23_server) + , wss_(nullptr) { beast::error_code ec; endpoint_ = {boost::asio::ip::make_address(address), port}; @@ -185,6 +341,14 @@ void WSSSession::Stop() { } } +void WSSSession::AddRoute(const std::string& route) { + if (wss_ == nullptr) { + buffered_routes_.push(route); + return; + } + wss_->AddURLRoute(route); +} + void WSSSession::do_accept() { if (acceptor_.is_open()) { acceptor_.async_accept( @@ -202,8 +366,13 @@ void WSSSession::on_accept(boost::system::error_code ec) { } // Create the session and run it wss_ = std::make_shared<WSSServer>(std::move(socket_), ctx_); + // Load routes stored in buffer + while (!buffered_routes_.empty()) { + wss_->AddURLRoute(buffered_routes_.front()); + buffered_routes_.pop(); + } wss_->Run(); } } // namespace websocket -} // namespace sample
\ No newline at end of file +} // namespace sample |