diff options
author | Shobhit Adlakha <adlakhashobhit@gmail.com> | 2019-05-03 16:02:42 -0400 |
---|---|---|
committer | Shobhit Adlakha <adlakhashobhit@gmail.com> | 2019-05-03 16:02:42 -0400 |
commit | b2885dd5c5403186536100f5ba9fd3c32731f739 (patch) | |
tree | 3aececc658961a686b5bc61f22fe4c30a65508b6 /src/components/transport_manager | |
parent | 94a46dd9cdbb7b8a281c067f81c188f66cfc6a5d (diff) | |
download | sdl_core-b2885dd5c5403186536100f5ba9fd3c32731f739.tar.gz |
WS Connection test and sample WS Server
Diffstat (limited to 'src/components/transport_manager')
3 files changed, 368 insertions, 0 deletions
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 <unistd.h> +#include <algorithm> +#include <boost/asio/bind_executor.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/placeholders.hpp> +#include <boost/asio/strand.hpp> +#include <boost/beast/core.hpp> +#include <boost/beast/websocket.hpp> +#include <boost/make_shared.hpp> +#include <boost/thread/thread.hpp> +#include <cstdlib> +#include <functional> +#include <iostream> +#include <memory> +#include <string> +#include <thread> +#include <vector> + +namespace beast = boost::beast; // from <boost/beast.hpp> +namespace http = beast::http; // from <boost/beast/http.hpp> +namespace websocket = beast::websocket; // from <boost/beast/websocket.hpp> +namespace net = boost::asio; // from <boost/asio.hpp> +using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp> + +//------------------------------------------------------------------------------ + +// 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<WSSession> { + private: + // Echoes back all received WebSocket messages + class WSServer : public std::enable_shared_from_this<WSServer> { + 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<tcp::socket> ws_; + beast::flat_buffer buffer_; + boost::asio::strand<boost::asio::io_context::executor_type> 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<WSServer>(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<WSServer> 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<WSSession>(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<MockTransportManagerSettings> 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<WSSession> 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<WebsocketClientConnection> ws_connection = + std::dynamic_pointer_cast<WebsocketClientConnection>(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 |