From b2885dd5c5403186536100f5ba9fd3c32731f739 Mon Sep 17 00:00:00 2001 From: Shobhit Adlakha Date: Fri, 3 May 2019 16:02:42 -0400 Subject: WS Connection test and sample WS Server --- .../test/mobile_event_engine_test.cc | 10 +- .../transport_manager/test/CMakeLists.txt | 6 + .../cloud/sample_websocket_server.h | 233 +++++++++++++++++++++ .../test/websocket_connection_test.cc | 129 ++++++++++++ 4 files changed, 373 insertions(+), 5 deletions(-) create mode 100644 src/components/transport_manager/test/include/transport_manager/cloud/sample_websocket_server.h create mode 100644 src/components/transport_manager/test/websocket_connection_test.cc diff --git a/src/components/application_manager/test/mobile_event_engine_test.cc b/src/components/application_manager/test/mobile_event_engine_test.cc index 7e8583d3fd..cad1ff14ff 100644 --- a/src/components/application_manager/test/mobile_event_engine_test.cc +++ b/src/components/application_manager/test/mobile_event_engine_test.cc @@ -32,13 +32,13 @@ #include "gtest/gtest.h" -#include "application_manager/event_engine/event_observer.h" #include "application_manager/event_engine/event.h" #include "application_manager/event_engine/event_dispatcher_impl.h" -#include "application_manager/mock_event_observer.h" +#include "application_manager/event_engine/event_observer.h" +#include "application_manager/message.h" #include "application_manager/mock_event_dispatcher.h" +#include "application_manager/mock_event_observer.h" #include "smart_objects/smart_object.h" -#include "application_manager/message.h" #include "interfaces/MOBILE_API.h" @@ -46,10 +46,10 @@ namespace test { namespace components { namespace event_engine_test { -using application_manager::event_engine::EventDispatcherImpl; using application_manager::event_engine::Event; -using application_manager::event_engine::MobileEvent; +using application_manager::event_engine::EventDispatcherImpl; using application_manager::event_engine::EventObserver; +using application_manager::event_engine::MobileEvent; using testing::_; using ::testing::An; using ::testing::Matcher; diff --git a/src/components/transport_manager/test/CMakeLists.txt b/src/components/transport_manager/test/CMakeLists.txt index 5288d8c697..489b35b796 100644 --- a/src/components/transport_manager/test/CMakeLists.txt +++ b/src/components/transport_manager/test/CMakeLists.txt @@ -46,6 +46,12 @@ set(EXCLUDE_PATHS raw_message_matcher.cc ) +if (NOT BUILD_CLOUD_APP_SUPPORT) + list(APPEND EXCLUDE_PATHS + ${CMAKE_CURRENT_SOURCE_DIR}/websocket_connection_test.cc + ) +endif() + collect_sources(SOURCES "${CMAKE_CURRENT_SOURCE_DIR}" "${EXCLUDE_PATHS}") set(PLATFORM_DEPENDENT_SOURCES) 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 new file mode 100644 index 0000000000..40fbd7dcc1 --- /dev/null +++ b/src/components/transport_manager/test/include/transport_manager/cloud/sample_websocket_server.h @@ -0,0 +1,233 @@ + +/* + * 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. + */ + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace beast = boost::beast; // from +namespace http = beast::http; // from +namespace websocket = beast::websocket; // from +namespace net = boost::asio; // from +using tcp = boost::asio::ip::tcp; // from + +//------------------------------------------------------------------------------ + +// Report a failure +void fail(beast::error_code ec, char const* what) { + std::cerr << what << ": " << ec.message() << "\n"; +} + +// Accepts incoming connections and launches the WSServer +class WSSession : public std::enable_shared_from_this { + private: + // Echoes back all received WebSocket messages + class WSServer : public std::enable_shared_from_this { + public: + // Take ownership of the socket + explicit WSServer(tcp::socket&& socket) + : ws_(std::move(socket)), strand_(ws_.get_executor()) {} + + // Start the asynchronous operation + void run() { + // Accept the websocket handshake + ws_.async_accept( + boost::asio::bind_executor(strand_, + std::bind(&WSServer::on_accept, + shared_from_this(), + std::placeholders::_1))); + } + + void on_accept(beast::error_code ec) { + std::cout << "SERVER: Accepting connection" << std::endl; + if (ec) { + std::cout << "SERVER: Connection accept FAILED" << std::endl; + return fail(ec, "accept"); + } + // Read a message + // do_read(); + } + + // void do_read() { + // // Read a message into our buffer + // ws_.async_read( + // buffer_, + // boost::asio::bind_executor(strand_, + // std::bind(&WSServer::on_read, + // shared_from_this(), + // std::placeholders::_1, + // std::placeholders::_2))); + // } + + // void do_write() { + // ws_.text(ws_.got_text()); + // ws_.async_write(buffer_.data(), + // std::bind(&WSServer::on_write, + // shared_from_this(), + // std::placeholders::_1, + // std::placeholders::_2)); + // } + + // void on_read(beast::error_code ec, std::size_t bytes_transferred) { + // std::cout << "SERVER: receiving data" << std::endl; + + // boost::ignore_unused(bytes_transferred); + + // // This indicates that the WSServer was closed + // if (ec == websocket::error::closed) { + // std::cout << "SERVER: Websocket closed" << std::endl; + // return; + // } + + // if (ec) { + // std::cout << "SERVER: Read failed" << std::endl; + // fail(ec, "read"); + // } + + // // Echo the message + // do_write(); + // } + + // void on_write(beast::error_code ec, std::size_t bytes_transferred) { + // std::cout << "SERVER: Sending data" << std::endl; + + // boost::ignore_unused(bytes_transferred); + + // if (ec) { + // std::cout << "SERVER: Write failed" << std::endl; + // return fail(ec, "write"); + // } + + // // Clear the buffer + // buffer_.consume(buffer_.size()); + + // // Do another read + // do_read(); + // } + + private: + websocket::stream ws_; + beast::flat_buffer buffer_; + boost::asio::strand strand_; + }; + + public: + WSSession(const std::string& address, uint16_t port) + : address_(address), port_(port), acceptor_(ioc_), socket_(ioc_) { + endpoint_ = {boost::asio::ip::make_address(address), port}; + + boost::system::error_code error; + + acceptor_.open(endpoint_.protocol(), error); + if (error) { + std::cerr << "ErrorOpen: " << error.message() << std::endl; + return; + } + acceptor_.set_option(boost::asio::socket_base::reuse_address(true), error); + if (error) { + std::cerr << "ErrorSetOption: " << error.message() << std::endl; + return; + } + acceptor_.bind(endpoint_, error); + if (error) { + std::cerr << "ErrorBind: " << error.message() << std::endl; + return; + } + acceptor_.listen(boost::asio::socket_base::max_listen_connections, error); + if (error) { + std::cerr << "ErrorListen: " << error.message() << std::endl; + return; + } + } + + void run() { + if (acceptor_.is_open()) { + acceptor_.async_accept( + socket_, + std::bind(&WSSession::StartSession, this, std::placeholders::_1)); + ioc_.run(); + } + } + + void stop() { + std::cout << "SERVER: Closing connection" << std::endl; + + try { + ioc_.stop(); + acceptor_.close(); + } catch (...) { + std::cout << "SERVER: Failed to close connection" << std::endl; + } + } + + private: + void StartSession(boost::system::error_code ec) { + if (ec) { + std::cerr << "ErrorMessage: " << ec.message() << std::endl; + ioc_.stop(); + return; + } + + // Make websocket object and start + ws_ = std::make_shared(std::move(socket_)); + ws_->run(); + } + + boost::asio::io_context ioc_; + const std::string& address_; + uint16_t port_; + tcp::acceptor acceptor_; + tcp::socket socket_; + beast::flat_buffer buffer_; + boost::asio::ip::tcp::endpoint endpoint_; + std::shared_ptr ws_; +}; + +#endif // SRC_COMPONENTS_TRANSPORT_MANAGER_TEST_INCLUDE_TRANSPORT_MANAGER_CLOUD_SAMPLE_WEBSOCKET_SERVER_H_ \ No newline at end of file diff --git a/src/components/transport_manager/test/websocket_connection_test.cc b/src/components/transport_manager/test/websocket_connection_test.cc new file mode 100644 index 0000000000..e86bab8c6c --- /dev/null +++ b/src/components/transport_manager/test/websocket_connection_test.cc @@ -0,0 +1,129 @@ +/* + * 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 "gtest/gtest.h" +#include "resumption/last_state_impl.h" +#include "transport_manager/cloud/cloud_websocket_transport_adapter.h" +#include "transport_manager/cloud/sample_websocket_server.h" +#include "transport_manager/cloud/websocket_client_connection.h" +#include "transport_manager/transport_adapter/connection.h" +#include "transport_manager/transport_adapter/transport_adapter_impl.h" + +#include "transport_manager/mock_transport_manager_settings.h" + +namespace test { +namespace components { +namespace transport_manager_test { + +using ::testing::_; +using ::testing::NiceMock; +using ::testing::NotNull; +using ::testing::Return; +using namespace ::transport_manager; +using namespace ::transport_manager::transport_adapter; + +class WebsocketConnectionTest : public ::testing::Test { + public: + void StartServer() { + ws_session = std::make_shared(kHost, kPort); + ws_session->run(); + } + + protected: + WebsocketConnectionTest() + : last_state_("app_storage_folder", "app_info_storage") {} + + ~WebsocketConnectionTest() {} + void SetUp() OVERRIDE { + dev_id = "ws://" + kHost + ":" + std::to_string(kPort) + "/"; + uniq_id = dev_id; + app_handle = 0; + ON_CALL(transport_manager_settings, use_last_state()) + .WillByDefault(Return(true)); + } + + NiceMock transport_manager_settings; + resumption::LastStateImpl last_state_; + std::string dev_id; + std::string uniq_id; + int app_handle; + std::string kHost = "127.0.0.1"; + uint16_t kPort = 8080; + std::shared_ptr ws_session; +}; + +TEST_F(WebsocketConnectionTest, WSConnection) { + transport_manager::transport_adapter::CloudAppProperties properties{ + .endpoint = dev_id, + .certificate = "no cert", + .enabled = true, + .auth_token = "auth_token", + .cloud_transport_type = "WS", + .hybrid_app_preference = "CLOUD"}; + + // Start server + std::thread t1(&WebsocketConnectionTest::StartServer, this); + sleep(1); + + // Start client + CloudWebsocketTransportAdapter cta(last_state_, transport_manager_settings); + cta.SetAppCloudTransportConfig(uniq_id, properties); + + TransportAdapterImpl* ta_cloud = &cta; + ta_cloud->CreateDevice(uniq_id); + + auto connection = cta.FindPendingConnection(dev_id, app_handle); + std::cout << "Pending connection: " << (connection != NULL) << std::endl; + + EXPECT_NE(connection, nullptr); + + std::shared_ptr ws_connection = + std::dynamic_pointer_cast(connection); + + EXPECT_NE(ws_connection.use_count(), 0); + + // 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(); +} + +} // namespace transport_manager_test +} // namespace components +} // namespace test -- cgit v1.2.1