diff options
author | JackLivio <jack@livio.io> | 2019-06-04 14:06:53 -0400 |
---|---|---|
committer | JackLivio <jack@livio.io> | 2019-06-04 14:06:53 -0400 |
commit | cb2947598f5b356ec8c102bcb8c74ccd92ece905 (patch) | |
tree | 2a5b3c79852bbaab8a16548016ac0ddbcd26f486 /src/components/transport_manager | |
parent | 1e82d45d2b07ab3697cf1985f980ba0e2860f5e3 (diff) | |
parent | 2026979753640570e5a6bfe239106a90f7c6ffdc (diff) | |
download | sdl_core-cb2947598f5b356ec8c102bcb8c74ccd92ece905.tar.gz |
Merge remote-tracking branch 'origin/develop' into feature/add_media_service_data_image
Diffstat (limited to 'src/components/transport_manager')
8 files changed, 636 insertions, 56 deletions
diff --git a/src/components/transport_manager/include/transport_manager/cloud/cloud_device.h b/src/components/transport_manager/include/transport_manager/cloud/cloud_device.h index 47a82e7921..9c25be2a3e 100644 --- a/src/components/transport_manager/include/transport_manager/cloud/cloud_device.h +++ b/src/components/transport_manager/include/transport_manager/cloud/cloud_device.h @@ -43,22 +43,33 @@ namespace transport_manager { namespace transport_adapter { +struct CloudAppEndpoint { + std::string host; + std::string port; + std::string path; + std::string query; + std::string fragment; +}; + class CloudDevice : public Device { public: CloudDevice(std::string& host, std::string& port, std::string& name); + CloudDevice(CloudAppEndpoint endpoint, std::string& name); + virtual const std::string& GetHost() const; virtual const std::string& GetPort() const; + virtual const std::string GetTarget() const; + protected: virtual bool IsSameAs(const Device* other_device) const; virtual ApplicationList GetApplicationList() const; private: - const std::string host_; - const std::string port_; + const CloudAppEndpoint endpoint_; }; } // namespace transport_adapter diff --git a/src/components/transport_manager/src/cloud/cloud_device.cc b/src/components/transport_manager/src/cloud/cloud_device.cc index 35510f4cf8..c1ad186ded 100644 --- a/src/components/transport_manager/src/cloud/cloud_device.cc +++ b/src/components/transport_manager/src/cloud/cloud_device.cc @@ -42,7 +42,15 @@ CREATE_LOGGERPTR_GLOBAL(logger_, "TransportManager") CloudDevice::CloudDevice(std::string& host, std::string& port, std::string& name) - : Device(name, std::string(name)), host_(host), port_(port) {} + : Device(name, std::string(name)) + , endpoint_(CloudAppEndpoint{.host = host, + .port = port, + .path = "/", + .query = "", + .fragment = ""}) {} + +CloudDevice::CloudDevice(CloudAppEndpoint endpoint, std::string& name) + : Device(name, std::string(name)), endpoint_(endpoint) {} bool CloudDevice::IsSameAs(const Device* other) const { LOG4CXX_TRACE(logger_, "enter. device: " << other); @@ -54,12 +62,18 @@ bool CloudDevice::IsSameAs(const Device* other) const { return false; } - if (host_ != other_cloud_device->GetHost()) { + if (GetHost() != other_cloud_device->GetHost()) { + return false; + } + + if (GetPort() != other_cloud_device->GetPort()) { return false; } - if (port_ != other_cloud_device->GetPort()) { + + if (GetTarget() != other_cloud_device->GetTarget()) { return false; } + return true; } @@ -68,11 +82,15 @@ ApplicationList CloudDevice::GetApplicationList() const { } const std::string& CloudDevice::GetHost() const { - return host_; + return endpoint_.host; } const std::string& CloudDevice::GetPort() const { - return port_; + return endpoint_.port; +} + +const std::string CloudDevice::GetTarget() const { + return endpoint_.path + endpoint_.query + endpoint_.fragment; } } // namespace transport_adapter diff --git a/src/components/transport_manager/src/cloud/cloud_websocket_transport_adapter.cc b/src/components/transport_manager/src/cloud/cloud_websocket_transport_adapter.cc index e141a65ec0..622531b120 100644 --- a/src/components/transport_manager/src/cloud/cloud_websocket_transport_adapter.cc +++ b/src/components/transport_manager/src/cloud/cloud_websocket_transport_adapter.cc @@ -80,15 +80,20 @@ void CloudWebsocketTransportAdapter::CreateDevice(const std::string& uid) { return; } - // Port after second colon in valid endpoint string - std::size_t pos_port = uid.find(":"); - pos_port = uid.find(":", pos_port + 1); + std::string protocol_pattern = "(wss?)"; + std::string host_pattern = + "(([^?#%\\\\/@:\\s]{1,})\\:?([^?#%\\\\/@\\s]*)\\@?([^?#%\\\\/\\s]*))"; + std::string port_pattern = "(\\d{2,5})"; + // Optional parameters + std::string path_pattern = "((\\/[^\\/#?\\s]+)*)?\\/?"; + std::string query_pattern = "(\\?[^=&#\\s]*=?[^#\\s]*&?)?"; + std::string fragment_pattern = "(#[^\\s]*)?"; // Extract host and port from endpoint string - boost::regex group_pattern( - "(wss?:\\/\\/)([A-Z\\d\\.-]{2,}\\.?([A-Z]{2,})?)(:)(\\d{2,5})(\\/" - "[A-Z\\d\\.-]+)*\\/?", - boost::regex::icase); + boost::regex group_pattern(protocol_pattern + ":\\/\\/" + host_pattern + ":" + + port_pattern + path_pattern + query_pattern + + fragment_pattern, + boost::regex::icase); boost::smatch results; std::string str = uid; @@ -97,20 +102,30 @@ void CloudWebsocketTransportAdapter::CreateDevice(const std::string& uid) { return; } - std::string host = results[2]; - std::string port = results[5]; + LOG4CXX_DEBUG(logger_, "#Results: " << results.size()); + std::string results_str; + for (size_t i = 0; i < results.size(); i++) { + results_str += " R[" + std::to_string(i) + "]:"; + results_str += + (results[i].length() != 0) ? results[i] : std::string("<EMPTY>"); + } + LOG4CXX_DEBUG(logger_, "Results: " << results_str); - LOG4CXX_DEBUG(logger_, - "Results: " << results[0] << " " << results[1] << " " - << results[2] << " " << results[3] << " " - << results[4] << " " << results[5] << " "); std::string device_id = uid; - LOG4CXX_DEBUG( - logger_, - "Creating Cloud Device For Host: " << host << " and Port: " << port); + CloudAppEndpoint endpoint{.host = results[2], + .port = results[6], + .path = results[7] + "/", + .query = results[9], + .fragment = results[10]}; + + LOG4CXX_DEBUG(logger_, + "Creating Cloud Device For Host: " + << endpoint.host << " at Port: " << endpoint.port + << " with Target: " + << (endpoint.path + endpoint.query + endpoint.fragment)); - auto cloud_device = std::make_shared<CloudDevice>(host, port, device_id); + auto cloud_device = std::make_shared<CloudDevice>(endpoint, device_id); DeviceVector devices{cloud_device}; diff --git a/src/components/transport_manager/src/cloud/websocket_client_connection.cc b/src/components/transport_manager/src/cloud/websocket_client_connection.cc index a93a27ba40..ec2fb0bcfb 100644 --- a/src/components/transport_manager/src/cloud/websocket_client_connection.cc +++ b/src/components/transport_manager/src/cloud/websocket_client_connection.cc @@ -160,11 +160,11 @@ TransportAdapter::Error WebsocketClientConnection::Start() { // Perform websocket handshake if (cloud_properties.cloud_transport_type == "WS") { - ws_.handshake(host, "/", ec); + ws_.handshake(host, cloud_device->GetTarget(), ec); } #ifdef ENABLE_SECURITY else if (cloud_properties.cloud_transport_type == "WSS") { - wss_.handshake(host, "/", ec); + wss_.handshake(host, cloud_device->GetTarget(), ec); } #endif // ENABLE_SECURITY if (ec) { diff --git a/src/components/transport_manager/test/include/transport_manager/cloud/sample_websocket_server.h b/src/components/transport_manager/test/include/transport_manager/cloud/sample_websocket_server.h index a60aefc7b0..a8b801c1fe 100644 --- a/src/components/transport_manager/test/include/transport_manager/cloud/sample_websocket_server.h +++ b/src/components/transport_manager/test/include/transport_manager/cloud/sample_websocket_server.h @@ -34,8 +34,6 @@ #ifndef SRC_COMPONENTS_TRANSPORT_MANAGER_TEST_INCLUDE_TRANSPORT_MANAGER_CLOUD_SAMPLE_WEBSOCKET_SERVER_H_ #define SRC_COMPONENTS_TRANSPORT_MANAGER_TEST_INCLUDE_TRANSPORT_MANAGER_CLOUD_SAMPLE_WEBSOCKET_SERVER_H_ -#include <unistd.h> -#include <algorithm> #include <boost/asio/bind_executor.hpp> #include <boost/asio/ip/tcp.hpp> #include <boost/asio/placeholders.hpp> @@ -46,13 +44,11 @@ #include <boost/beast/websocket/ssl.hpp> #include <boost/make_shared.hpp> #include <boost/thread/thread.hpp> -#include <cstdlib> -#include <functional> #include <iostream> -#include <memory> +#include <queue> +#include <set> #include <string> #include <thread> -#include <vector> namespace sample { namespace websocket { @@ -70,19 +66,31 @@ class WSSession { class WSServer { public: explicit WSServer(tcp::socket&& socket); + void AddURLRoute(const std::string& route); + // Start the asynchronous operation void Run(); - void OnAccept(beast::error_code ec); private: + void OnWebsocketHandshake(const boost::system::error_code& ec); + void OnAccept(beast::error_code ec); + // Check if route can be handled by the server + bool CanHandleRoute(const std::string& route); + std::string ParseRouteFromTarget(const std::string& target); + websocket::stream<tcp::socket> ws_; beast::flat_buffer buffer_; boost::asio::strand<boost::asio::io_context::executor_type> strand_; + http::request<http::string_body> req_; + std::set<std::string> url_routes_; }; public: WSSession(const std::string& address, uint16_t port); + // Start Accepting incoming connections void Run(); void Stop(); + // Add route endpoint which can be handled the server + void AddRoute(const std::string& route); private: void on_accept(boost::system::error_code ec); @@ -94,6 +102,7 @@ class WSSession { beast::flat_buffer buffer_; boost::asio::ip::tcp::endpoint endpoint_; std::shared_ptr<WSServer> ws_; + std::queue<std::string> buffered_routes_; }; // Accepts incoming connections and launches the sessions @@ -103,14 +112,23 @@ class WSSSession { public: // Take ownership of the socket WSSServer(tcp::socket&& socket, ssl::context& ctx); + void AddURLRoute(const std::string& route); // Start the asynchronous operation void Run(); + + private: void OnSSLHandshake(beast::error_code ec); + void OnWebsocketHandshake(const boost::system::error_code& ec); void OnAccept(beast::error_code ec); - private: + // Check if route can be handled by the server + bool CanHandleRoute(const std::string& route); + std::string ParseRouteFromTarget(const std::string& target); + websocket::stream<ssl::stream<tcp::socket> > wss_; beast::flat_buffer buffer_; + http::request<http::string_body> req_; + std::set<std::string> url_routes_; }; public: @@ -121,6 +139,8 @@ class WSSSession { // Start accepting incoming connections void Run(); void Stop(); + // Add route endpoint which can be handled the server + void AddRoute(const std::string& route); private: void do_accept(); @@ -133,9 +153,10 @@ class WSSSession { ssl::context ctx_; tcp::endpoint endpoint_; std::shared_ptr<WSSServer> wss_; + std::queue<std::string> buffered_routes_; }; } // namespace websocket } // namespace sample -#endif // SRC_COMPONENTS_TRANSPORT_MANAGER_TEST_INCLUDE_TRANSPORT_MANAGER_CLOUD_SAMPLE_WEBSOCKET_SERVER_H_
\ No newline at end of file +#endif // SRC_COMPONENTS_TRANSPORT_MANAGER_TEST_INCLUDE_TRANSPORT_MANAGER_CLOUD_SAMPLE_WEBSOCKET_SERVER_H_ 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 diff --git a/src/components/transport_manager/test/transport_adapter_test.cc b/src/components/transport_manager/test/transport_adapter_test.cc index 3cef78493d..56386db201 100644 --- a/src/components/transport_manager/test/transport_adapter_test.cc +++ b/src/components/transport_manager/test/transport_adapter_test.cc @@ -42,6 +42,8 @@ #include "utils/test_async_waiter.h" #include "protocol/raw_message.h" +#include "transport_manager/cloud/cloud_device.h" +#include "transport_manager/cloud/cloud_websocket_transport_adapter.h" #include "transport_manager/transport_adapter/connection.h" #include "transport_manager/transport_adapter/transport_adapter_controller.h" #include "transport_manager/transport_adapter/transport_adapter_impl.h" @@ -84,6 +86,139 @@ class TransportAdapterTest : public ::testing::Test { std::string dev_id; std::string uniq_id; int app_handle; + +#if defined(CLOUD_APP_WEBSOCKET_TRANSPORT_SUPPORT) + struct TestEndpoint { + std::string test_string; + std::string host; + std::string port; + std::string target; + }; + + CloudAppProperties test_cloud_properties_{.endpoint = "", + .certificate = "no cert", + .enabled = true, + .auth_token = "no auth token", + .cloud_transport_type = "WS", + .hybrid_app_preference = "CLOUD"}; + std::vector<std::string> kWebsocketProtocols{"ws://", "wss://"}; + + std::vector<TestEndpoint> kValidTestEndpoints{ + // Host and port + TestEndpoint{"localhost:40/", "localhost", "40", "/"}, + TestEndpoint{"[::1]:40", "[::1]", "40", "/"}, + TestEndpoint{"username:password@localhost.com:80", + "username:password@localhost.com", + "80", + "/"}, + // With path + TestEndpoint{ + "localhost:440/file.html", "localhost", "440", "/file.html/"}, + TestEndpoint{"101.180.1.213:23234/folder/img_index(1)/file.html", + "101.180.1.213", + "23234", + "/folder/img_index(1)/file.html/"}, + TestEndpoint{"[2600:3c00::f03c:91ff:fe73:2b08]:31333/folder/img_index(1)/" + "file.html", + "[2600:3c00::f03c:91ff:fe73:2b08]", + "31333", + "/folder/img_index(1)/file.html/"}, + // With query and/or fragment + TestEndpoint{ + "username@localhost:22/folder/img_index(1)/" + "file.html?eventId=2345&eventName='some%20event'&eventSuccess=true", + "username@localhost", + "22", + "/folder/img_index(1)/file.html/" + "?eventId=2345&eventName='some%20event'&eventSuccess=" + "true"}, + TestEndpoint{"username@localhost.com:80/folder/img_index(1)/" + "file.html?eventId=2345&eventName='some%20event'&" + "eventSuccess=true#section1", + "username@localhost.com", + "80", + "/folder/img_index(1)/file.html/" + "?eventId=2345&eventName='some%20event'&eventSuccess=true#" + "section1"}, + TestEndpoint{ + "localhost:443/" + "?eventId=2345&eventName='some%20event'&eventSuccess=true#section1", + "localhost", + "443", + "/?eventId=2345&eventName='some%20event'&eventSuccess=true#" + "section1"}, + TestEndpoint{"a1-b2.com:443/folder/img_index(1)/file.html#section1", + "a1-b2.com", + "443", + "/folder/img_index(1)/file.html/#section1"}, + TestEndpoint{"a1-b2.com:23#section1", "a1-b2.com", "23", "/#section1"}}; + + std::vector<TestEndpoint> kInvalidTestEndpoints{ + // Invalid hostname + TestEndpoint{"/localhost:80", "localhost", "80", "/"}, + TestEndpoint{"local?host:80", "local?host", "80", "/"}, + TestEndpoint{"local#host:80", "local#host", "80", "/"}, + TestEndpoint{"local\%host:80", "local\%host", "80", "/"}, + TestEndpoint{"local\\host:80", "local\\host", "80", "/"}, + TestEndpoint{"local/host:80", "local/host", "80", "/"}, + TestEndpoint{"local host:80", "local host", "80", "/"}, + TestEndpoint{"local\thost:80", "local\thost", "80", "/"}, + TestEndpoint{":80#section1", "", "80", "/#section1"}, + // Invalid port + TestEndpoint{"username:password@localhost.com", + "username:password@localhost.com", + "", + "/"}, + TestEndpoint{"username:password@localhost.com:5", + "username:password@localhost.com", + "5", + "/"}, + TestEndpoint{"201.123.213:2h32/", "201.123.213", "2h32", "/"}}; + std::vector<TestEndpoint> kIncorrectTestEndpoints{ + // Incorrect port number + TestEndpoint{"201.123.1.213:232454/folder/img_index(1)/file.html", + "201.123.1.213", + "232454", + "/folder/img_index(1)/file.html/"}, + // Incorrect path + TestEndpoint{"201.123.1.213:232//folder/img_index(1)/file.html", + "201.123.1.213", + "232", + "//folder/img_index(1)/file.html/"}, + TestEndpoint{"201.123.1.213:232/folder/img_index(1)//file.html", + "201.123.1.213", + "232", + "/folder/img_index(1)//file.html/"}, + TestEndpoint{"201.123.1.213:232/folder/img index(1)//file.html", + "201.123.1.213", + "232", + "/folder/img index(1)//file.html/"}, + TestEndpoint{"201.123.1.213:232/folder/img\tindex(1)//file.html", + "201.123.1.213", + "232", + "/folder/img\tindex(1)//file.html/"}, + // Incorrect query + TestEndpoint{"username@localhost:443/?eventId=2345&eventName='some " + "event'&eventSuccess=true", + "username@localhost", + "443", + "?eventId=2345&eventName='some event'&eventSuccess=true"}, + TestEndpoint{"username@localhost:443/" + "?eventId=2345&eventName='some\tevent'&eventSuccess=true", + "username@localhost", + "443", + "?eventId=2345&eventName='some\tevent'&eventSuccess=true"}, + // Incorrect fragment + TestEndpoint{"a1(b2).com:80/folder/img_index(1)/file.html#section 1", + "a1(b2).com", + "80", + "/folder/img_index(1)/file.html#section 1"}, + TestEndpoint{"a1(b2).com:80/folder/img_index(1)/file.html#section\t1", + "a1(b2).com", + "80", + "/folder/img_index(1)/file.html#section\t1"}}; + +#endif // CLOUD_APP_WEBSOCKET_TRANSPORT_SUPPORT }; TEST_F(TransportAdapterTest, Init) { @@ -609,6 +744,83 @@ TEST_F(TransportAdapterTest, EXPECT_CALL(*serverMock, Terminate()); } + +TEST_F(TransportAdapterTest, WebsocketEndpointParsing_SUCCESS) { + std::shared_ptr<CloudWebsocketTransportAdapter> cta = + std::make_shared<CloudWebsocketTransportAdapter>( + last_state_, transport_manager_settings); + + for (auto protocol : kWebsocketProtocols) { + for (auto endpoint : kValidTestEndpoints) { + test_cloud_properties_.endpoint = protocol + endpoint.test_string; + cta->SetAppCloudTransportConfig("cloud app", test_cloud_properties_); + + auto ta = std::dynamic_pointer_cast<TransportAdapter>(cta); + ASSERT_NE(ta.use_count(), 0); + + ta->CreateDevice(test_cloud_properties_.endpoint); + + auto device = cta->FindDevice(test_cloud_properties_.endpoint); + ASSERT_NE(device.use_count(), 0); + + std::shared_ptr<CloudDevice> cloud_device = + std::dynamic_pointer_cast<CloudDevice>(device); + ASSERT_NE(cloud_device.use_count(), 0); + + EXPECT_EQ(cloud_device->GetHost(), endpoint.host); + EXPECT_EQ(cloud_device->GetPort(), endpoint.port); + EXPECT_EQ(cloud_device->GetTarget(), endpoint.target); + } + } +} +TEST_F(TransportAdapterTest, WebsocketEndpointParsing_INVALID) { + std::shared_ptr<CloudWebsocketTransportAdapter> cta = + std::make_shared<CloudWebsocketTransportAdapter>( + last_state_, transport_manager_settings); + + for (auto protocol : kWebsocketProtocols) { + for (auto endpoint : kInvalidTestEndpoints) { + test_cloud_properties_.endpoint = protocol + endpoint.test_string; + cta->SetAppCloudTransportConfig("cloud app", test_cloud_properties_); + + auto ta = std::dynamic_pointer_cast<TransportAdapter>(cta); + ASSERT_NE(ta.use_count(), 0); + + ta->CreateDevice(test_cloud_properties_.endpoint); + + auto device = cta->FindDevice(test_cloud_properties_.endpoint); + EXPECT_EQ(device.use_count(), 0); + } + } +} +TEST_F(TransportAdapterTest, WebsocketEndpointParsing_INCORRECT) { + std::shared_ptr<CloudWebsocketTransportAdapter> cta = + std::make_shared<CloudWebsocketTransportAdapter>( + last_state_, transport_manager_settings); + + for (auto protocol : kWebsocketProtocols) { + for (auto endpoint : kIncorrectTestEndpoints) { + test_cloud_properties_.endpoint = protocol + endpoint.test_string; + cta->SetAppCloudTransportConfig("cloud app", test_cloud_properties_); + + auto ta = std::dynamic_pointer_cast<TransportAdapter>(cta); + ASSERT_NE(ta.use_count(), 0); + + ta->CreateDevice(test_cloud_properties_.endpoint); + + auto device = cta->FindDevice(test_cloud_properties_.endpoint); + ASSERT_NE(device.use_count(), 0); + + std::shared_ptr<CloudDevice> cloud_device = + std::dynamic_pointer_cast<CloudDevice>(device); + ASSERT_NE(cloud_device.use_count(), 0); + + EXPECT_FALSE(cloud_device->GetHost() == endpoint.host && + cloud_device->GetPort() == endpoint.port && + cloud_device->GetTarget() == endpoint.target); + } + } +} #endif // CLOUD_APP_WEBSOCKET_TRANSPORT_SUPPORT TEST_F(TransportAdapterTest, DisconnectDevice_DeviceAddedConnectionCreated) { diff --git a/src/components/transport_manager/test/websocket_connection_test.cc b/src/components/transport_manager/test/websocket_connection_test.cc index 9f9793765a..db3bc598b1 100644 --- a/src/components/transport_manager/test/websocket_connection_test.cc +++ b/src/components/transport_manager/test/websocket_connection_test.cc @@ -84,14 +84,16 @@ class WebsocketConnectionTest : public ::testing::Test { ASSERT_NE(client_out.connection.use_count(), 0); } - void StartWSServer() { + void StartWSServer(std::string path) { ws_session = std::make_shared<websocket::WSSession>(kHost, kPort); + ws_session->AddRoute(path); ws_session->Run(); } - void StartWSSServer() { + void StartWSSServer(std::string path) { wss_session = std::make_shared<websocket::WSSSession>( kHost, kPort, kCertificate, kPrivateKey); + wss_session->AddRoute(path); wss_session->Run(); } @@ -115,6 +117,10 @@ class WebsocketConnectionTest : public ::testing::Test { WebsocketClient ws_client; std::string kHost = "127.0.0.1"; uint16_t kPort = 8080; + std::string kPath = "/folder/file.html/"; + std::string kQuery = "?eventId=2345&eventName='Test'&expectedResult=true"; + std::string kFragment = "#section_1"; + // Sample certificate for localhost std::string kCertificate = "-----BEGIN " @@ -262,7 +268,39 @@ class WebsocketConnectionTest : public ::testing::Test { TEST_F(WebsocketConnectionTest, WSConnection_SUCCESS) { transport_manager::transport_adapter::CloudAppProperties properties{ - .endpoint = "ws://" + kHost + ":" + std::to_string(kPort) + "/", + .endpoint = "ws://" + kHost + ":" + std::to_string(kPort), + .certificate = "no cert", + .enabled = true, + .auth_token = "auth_token", + .cloud_transport_type = "WS", + .hybrid_app_preference = "CLOUD"}; + + // Start server + std::thread t1(&WebsocketConnectionTest::StartWSServer, this, "/"); + usleep(5000); + + // Start client + InitWebsocketClient(properties, ws_client); + std::shared_ptr<WebsocketClientConnection> ws_connection = + ws_client.connection; + + // Check websocket connection + TransportAdapter::Error ret_code = ws_connection->Start(); + EXPECT_EQ(TransportAdapter::OK, ret_code); + + // Stop client + ret_code = ws_connection->Disconnect(); + EXPECT_EQ(TransportAdapter::OK, ret_code); + + // Stop server thread + ws_session->Stop(); + t1.join(); +} + +TEST_F(WebsocketConnectionTest, WSConnection_SUCCESS_ValidTarget) { + transport_manager::transport_adapter::CloudAppProperties properties{ + .endpoint = "ws://" + kHost + ":" + std::to_string(kPort) + kPath + + kQuery + kFragment, .certificate = "no cert", .enabled = true, .auth_token = "auth_token", @@ -270,8 +308,8 @@ TEST_F(WebsocketConnectionTest, WSConnection_SUCCESS) { .hybrid_app_preference = "CLOUD"}; // Start server - std::thread t1(&WebsocketConnectionTest::StartWSServer, this); - sleep(1); + std::thread t1(&WebsocketConnectionTest::StartWSServer, this, kPath); + usleep(5000); // Start client InitWebsocketClient(properties, ws_client); @@ -291,9 +329,41 @@ TEST_F(WebsocketConnectionTest, WSConnection_SUCCESS) { t1.join(); } +TEST_F(WebsocketConnectionTest, WSConnection_FAILURE_InvalidTarget) { + transport_manager::transport_adapter::CloudAppProperties properties{ + .endpoint = "ws://" + kHost + ":" + std::to_string(kPort) + kPath + + kQuery + kFragment, + .certificate = "no cert", + .enabled = true, + .auth_token = "auth_token", + .cloud_transport_type = "WS", + .hybrid_app_preference = "CLOUD"}; + + // Start server + std::thread t1(&WebsocketConnectionTest::StartWSServer, this, "/"); + usleep(5000); + + // Start client + InitWebsocketClient(properties, ws_client); + std::shared_ptr<WebsocketClientConnection> ws_connection = + ws_client.connection; + + // Check websocket connection + TransportAdapter::Error ret_code = ws_connection->Start(); + EXPECT_EQ(TransportAdapter::FAIL, ret_code); + + // Stop client + ret_code = ws_connection->Disconnect(); + EXPECT_EQ(TransportAdapter::OK, ret_code); + + // Stop server thread + ws_session->Stop(); + t1.join(); +} + TEST_F(WebsocketConnectionTest, WSSConnection_SUCCESS) { transport_manager::transport_adapter::CloudAppProperties properties{ - .endpoint = "wss://" + kHost + ":" + std::to_string(kPort) + "/", + .endpoint = "wss://" + kHost + ":" + std::to_string(kPort), .certificate = kCACertificate, .enabled = true, .auth_token = "auth_token", @@ -301,8 +371,8 @@ TEST_F(WebsocketConnectionTest, WSSConnection_SUCCESS) { .hybrid_app_preference = "CLOUD"}; // Start server - std::thread t1(&WebsocketConnectionTest::StartWSSServer, this); - sleep(1); + std::thread t1(&WebsocketConnectionTest::StartWSSServer, this, "/"); + usleep(5000); // Start client InitWebsocketClient(properties, ws_client); @@ -322,9 +392,73 @@ TEST_F(WebsocketConnectionTest, WSSConnection_SUCCESS) { t1.join(); } +TEST_F(WebsocketConnectionTest, WSSConnection_SUCCESS_ValidTarget) { + transport_manager::transport_adapter::CloudAppProperties properties{ + .endpoint = "wss://" + kHost + ":" + std::to_string(kPort) + kPath, + .certificate = kCACertificate, + .enabled = true, + .auth_token = "auth_token", + .cloud_transport_type = "WSS", + .hybrid_app_preference = "CLOUD"}; + + // Start server + std::thread t1(&WebsocketConnectionTest::StartWSSServer, + this, + (kPath + kQuery + kFragment)); + usleep(5000); + + // Start client + InitWebsocketClient(properties, ws_client); + std::shared_ptr<WebsocketClientConnection> wss_connection = + ws_client.connection; + + // Check websocket connection + TransportAdapter::Error ret_code = wss_connection->Start(); + EXPECT_EQ(TransportAdapter::OK, ret_code); + + // Stop client + ret_code = wss_connection->Disconnect(); + EXPECT_EQ(TransportAdapter::OK, ret_code); + + // Stop server thread + wss_session->Stop(); + t1.join(); +} + +TEST_F(WebsocketConnectionTest, WSSConnection_FAILURE_InvalidTarget) { + transport_manager::transport_adapter::CloudAppProperties properties{ + .endpoint = "wss://" + kHost + ":" + std::to_string(kPort), + .certificate = kCACertificate, + .enabled = true, + .auth_token = "auth_token", + .cloud_transport_type = "WSS", + .hybrid_app_preference = "CLOUD"}; + + // Start server + std::thread t1(&WebsocketConnectionTest::StartWSSServer, this, kPath); + usleep(5000); + + // Start client + InitWebsocketClient(properties, ws_client); + std::shared_ptr<WebsocketClientConnection> wss_connection = + ws_client.connection; + + // Check websocket connection + TransportAdapter::Error ret_code = wss_connection->Start(); + EXPECT_EQ(TransportAdapter::FAIL, ret_code); + + // Stop client + ret_code = wss_connection->Disconnect(); + EXPECT_EQ(TransportAdapter::OK, ret_code); + + // Stop server thread + wss_session->Stop(); + t1.join(); +} + TEST_F(WebsocketConnectionTest, WSSConnection_FAILURE_IncorrectCert) { transport_manager::transport_adapter::CloudAppProperties properties{ - .endpoint = "wss://" + kHost + ":" + std::to_string(kPort) + "/", + .endpoint = "wss://" + kHost + ":" + std::to_string(kPort), .certificate = kIncorrectCertificate, .enabled = true, .auth_token = "auth_token", @@ -332,8 +466,8 @@ TEST_F(WebsocketConnectionTest, WSSConnection_FAILURE_IncorrectCert) { .hybrid_app_preference = "CLOUD"}; // Start server - std::thread t1(&WebsocketConnectionTest::StartWSSServer, this); - sleep(1); + std::thread t1(&WebsocketConnectionTest::StartWSSServer, this, "/"); + usleep(5000); // Start client InitWebsocketClient(properties, ws_client); |