summaryrefslogtreecommitdiff
path: root/src/components/transport_manager/test/sample_websocket_server.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/transport_manager/test/sample_websocket_server.cc')
-rw-r--r--src/components/transport_manager/test/sample_websocket_server.cc189
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