diff options
author | Alexander Kutsan <akutsan@luxoft.com> | 2020-01-28 00:09:52 +0300 |
---|---|---|
committer | Andriy Byzhynar (GitHub) <AByzhynar@luxoft.com> | 2020-02-05 21:38:00 +0200 |
commit | 20b121130939ad6f00fe3535ef60e60b132cb54e (patch) | |
tree | b2fde4932a79319106aecd0787b086b799702d0a | |
parent | 731af6e613becacf539672cd0d58cdde7aa5bbbd (diff) | |
download | sdl_core-20b121130939ad6f00fe3535ef60e60b132cb54e.tar.gz |
Add Server WebSocket transport
51 files changed, 3313 insertions, 596 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 351bfb4ab6..5e156c283c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,6 +44,7 @@ option(BUILD_SHARED_LIBS "Build all libraries as shared (if ON) or static (if OF option(BUILD_BT_SUPPORT "Bluetooth support" ON) option(BUILD_USB_SUPPORT "libusb support" ON) option(BUILD_CLOUD_APP_SUPPORT "Cloud App Transport Support" ON) +option(BUILD_WEBSOCKET_SERVER_SUPPORT "Web Engine App Transport Support" ON) option(BUILD_BACKTRACE_SUPPORT "backtrace support" ON) option(BUILD_TESTS "Possibility to build and run tests" OFF) option(TELEMETRY_MONITOR "Enable profiling time test util" ON) @@ -208,6 +209,13 @@ get_property(cValue CACHE ENABLE_HMI_PTU_DECRYPTION PROPERTY VALUE) file(APPEND "${build_config_path}" "//${cHelpString}\n") file(APPEND "${build_config_path}" "ENABLE_HMI_PTU_DECRYPTION:${cType}=${cValue}\n") +get_property(cHelpString CACHE BUILD_WEBSOCKET_SERVER_SUPPORT PROPERTY HELPSTRING) +get_property(cType CACHE BUILD_WEBSOCKET_SERVER_SUPPORT PROPERTY TYPE) +get_property(cValue CACHE BUILD_WEBSOCKET_SERVER_SUPPORT PROPERTY VALUE) +file(APPEND "${build_config_path}" "//${cHelpString}\n") +file(APPEND "${build_config_path}" "BUILD_WEBSOCKET_SERVER_SUPPORT:${cType}=${cValue}\n") + + add_custom_target(pasa-tarball COMMAND ${CMAKE_SOURCE_DIR}/tools/Utils/export-customer-specific.sh ${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} pasa COMMAND tar -cz -C /tmp/PASA -f ${CMAKE_BINARY_DIR}/pasa.tar.gz . @@ -322,6 +330,11 @@ if (BUILD_CLOUD_APP_SUPPORT) message(STATUS "Cloud app websocket support enabled") endif() +if (BUILD_WEBSOCKET_SERVER_SUPPORT) + add_definitions(-DWEBSOCKET_SERVER_TRANSPORT_SUPPORT) + message(STATUS "Web engine app websocket support enabled") +endif() + if (BUILD_BACKTRACE_SUPPORT) add_definitions(-DBACKTRACE_SUPPORT) message(STATUS "Backtrace support enabled") diff --git a/src/components/config_profile/include/config_profile/profile.h b/src/components/config_profile/include/config_profile/profile.h index 1862fbd7e6..897f7a69ef 100644 --- a/src/components/config_profile/include/config_profile/profile.h +++ b/src/components/config_profile/include/config_profile/profile.h @@ -413,6 +413,7 @@ class Profile : public protocol_handler::ProtocolHandlerSettings, const std::string& transport_manager_tcp_adapter_network_interface() const OVERRIDE; +#ifdef WEBSOCKET_SERVER_TRANSPORT_SUPPORT /** * @brief Returns websocket server address */ @@ -422,6 +423,29 @@ class Profile : public protocol_handler::ProtocolHandlerSettings, * @brief Returns port for websocket server */ uint16_t websocket_server_port() const OVERRIDE; +#ifdef ENABLE_SECURITY + /** + * @brief Returns ws server certificate path to pem file + */ + const std::string& ws_server_cert_path() const OVERRIDE; + + /** + * @brief Returns ws server CA certificate path to pem file + */ + const std::string& ws_server_ca_cert_path() const OVERRIDE; + + /** + * @brief Returns ws server key path to pem file + */ + const std::string& ws_server_key_path() const OVERRIDE; + + /** + * @brief Returns bool flag indicating whether WSS settings were setup + * correctly + */ + const bool is_wss_settings_setup() const OVERRIDE; +#endif // ENABLE_SECURITY +#endif // WEBSOCKET_SERVER_TRANSPORT_SUPPORT /** * @brief Returns retry timeout for cloud app connections @@ -508,21 +532,6 @@ class Profile : public protocol_handler::ProtocolHandlerSettings, const std::string& ca_cert_path() const; /** - * @brief Returns ws server certificate path to pem file - */ - const std::string& ws_server_cert_path() const OVERRIDE; - - /** - * @brief Returns ws server CA certificate path to pem file - */ - const std::string& ws_server_ca_cert_path() const OVERRIDE; - - /** - * @brief Returns ws server key path to pem file - */ - const std::string& ws_server_key_path() const OVERRIDE; - - /** * @brief Returns ciphers */ const std::string& ciphers_list() const; @@ -1016,8 +1025,16 @@ class Profile : public protocol_handler::ProtocolHandlerSettings, std::string system_files_path_; uint16_t transport_manager_tcp_adapter_port_; std::string transport_manager_tcp_adapter_network_interface_; +#ifdef WEBSOCKET_SERVER_TRANSPORT_SUPPORT std::string websocket_server_address_; uint16_t websocket_server_port_; +#ifdef ENABLE_SECURITY + std::string ws_server_cert_path_; + std::string ws_server_ca_cert_path_; + std::string ws_server_key_path_; + bool is_wss_settings_setup_; +#endif // ENABLE_SECURITY +#endif // WEBSOCKET_SERVER_TRANSPORT_SUPPORT uint32_t cloud_app_retry_timeout_; uint16_t cloud_app_max_retry_attempts_; std::vector<uint8_t> bluetooth_uuid_; @@ -1041,11 +1058,8 @@ class Profile : public protocol_handler::ProtocolHandlerSettings, #ifdef ENABLE_SECURITY std::string cert_path_; std::string ca_cert_path_; - std::string ws_server_cert_path_; - std::string ws_server_ca_cert_path_; std::string ssl_mode_; std::string key_path_; - std::string ws_server_key_path_; std::string ciphers_list_; bool verify_peer_; uint32_t update_before_hours_; diff --git a/src/components/config_profile/src/profile.cc b/src/components/config_profile/src/profile.cc index a058654f85..09802f39fa 100644 --- a/src/components/config_profile/src/profile.cc +++ b/src/components/config_profile/src/profile.cc @@ -136,11 +136,8 @@ const char* kAudioStreamFileKey = "AudioStreamFile"; const char* kSecurityProtocolKey = "Protocol"; const char* kSecurityCertificatePathKey = "CertificatePath"; const char* kSecurityCACertificatePathKey = "CACertificatePath"; -const char* kWSServerCertificatePathKey = "WSServerCertificatePath"; -const char* kWSServerCACertificaePathKey = "WSServerCACertificatePath"; const char* kSecuritySSLModeKey = "SSLMode"; const char* kSecurityKeyPathKey = "KeyPath"; -const char* kWSServerKeyPathKey = "WSServerKeyPath"; const char* kSecurityCipherListKey = "CipherList"; const char* kSecurityVerifyPeerKey = "VerifyPeer"; const char* kBeforeUpdateHours = "UpdateBeforeHours"; @@ -160,10 +157,15 @@ const char* kMaxSupportedProtocolVersionKey = "MaxSupportedProtocolVersion"; const char* kUseLastStateKey = "UseLastState"; const char* kTCPAdapterPortKey = "TCPAdapterPort"; const char* kTCPAdapterNetworkInterfaceKey = "TCPAdapterNetworkInterface"; - +#ifdef WEBSOCKET_SERVER_TRANSPORT_SUPPORT const char* kWebSocketServerAddressKey = "WebSocketServerAddress"; const char* kWebSocketServerPortKey = "WebSocketServerPort"; - +#ifdef ENABLE_SECURITY +const char* kWSServerCertificatePathKey = "WSServerCertificatePath"; +const char* kWSServerCACertificaePathKey = "WSServerCACertificatePath"; +const char* kWSServerKeyPathKey = "WSServerKeyPath"; +#endif // ENABLE_SECURITY +#endif // WEBSOCKET_SERVER_TRANSPORT_SUPPORT const char* kCloudAppRetryTimeoutKey = "CloudAppRetryTimeout"; const char* kCloudAppMaxRetryAttemptsKey = "CloudAppMaxRetryAttempts"; const char* kServerPortKey = "ServerPort"; @@ -504,8 +506,10 @@ Profile::Profile() , supported_diag_modes_() , system_files_path_(kDefaultSystemFilesPath) , transport_manager_tcp_adapter_port_(kDefautTransportManagerTCPPort) +#ifdef WEBSOCKET_SERVER_TRANSPORT_SUPPORT , websocket_server_address_(kDefaultWebsocketServerAddress) , websocket_server_port_(kDefaultWebSocketServerPort) +#endif , cloud_app_retry_timeout_(kDefaultCloudAppRetryTimeout) , cloud_app_max_retry_attempts_(kDefaultCloudAppMaxRetryAttempts) , tts_delimiter_(kDefaultTtsDelimiter) @@ -842,6 +846,7 @@ const std::string& Profile::transport_manager_tcp_adapter_network_interface() return transport_manager_tcp_adapter_network_interface_; } +#ifdef WEBSOCKET_SERVER_TRANSPORT_SUPPORT const std::string& Profile::websocket_server_address() const { return websocket_server_address_; } @@ -849,6 +854,24 @@ const std::string& Profile::websocket_server_address() const { uint16_t Profile::websocket_server_port() const { return websocket_server_port_; } +#ifdef ENABLE_SECURITY +const std::string& Profile::ws_server_cert_path() const { + return ws_server_cert_path_; +} + +const std::string& Profile::ws_server_key_path() const { + return ws_server_key_path_; +} + +const std::string& Profile::ws_server_ca_cert_path() const { + return ws_server_ca_cert_path_; +} + +const bool Profile::is_wss_settings_setup() const { + return is_wss_settings_setup_; +} +#endif // ENABLE_SECURITY +#endif // WEBSOCKET_SERVER_TRANSPORT_SUPPORT uint32_t Profile::cloud_app_retry_timeout() const { return cloud_app_retry_timeout_; @@ -1059,18 +1082,6 @@ const std::string& Profile::ca_cert_path() const { return ca_cert_path_; } -const std::string& Profile::ws_server_cert_path() const { - return ws_server_cert_path_; -} - -const std::string& Profile::ws_server_key_path() const { - return ws_server_key_path_; -} - -const std::string& Profile::ws_server_ca_cert_path() const { - return ws_server_ca_cert_path_; -} - const std::string& Profile::ssl_mode() const { return ssl_mode_; } @@ -1899,7 +1910,7 @@ void Profile::UpdateValues() { LOG_UPDATED_VALUE(transport_manager_tcp_adapter_network_interface_, kTCPAdapterNetworkInterfaceKey, kTransportManagerSection); - +#ifdef WEBSOCKET_SERVER_TRANSPORT_SUPPORT // Websocket server address ReadStringValue(&websocket_server_address_, kDefaultWebsocketServerAddress, @@ -1921,30 +1932,36 @@ void Profile::UpdateValues() { kTransportManagerSection); #ifdef ENABLE_SECURITY - ReadStringValue(&ws_server_cert_path_, - "", - kTransportManagerSection, - kWSServerCertificatePathKey); + const bool is_ws_server_cert_setup = + ReadStringValue(&ws_server_cert_path_, + "", + kTransportManagerSection, + kWSServerCertificatePathKey); LOG_UPDATED_VALUE(ws_server_cert_path_, kWSServerCertificatePathKey, kTransportManagerSection); - ReadStringValue( + const bool is_ws_server_key_setup = ReadStringValue( &ws_server_key_path_, "", kTransportManagerSection, kWSServerKeyPathKey); LOG_UPDATED_VALUE( ws_server_key_path_, kWSServerKeyPathKey, kTransportManagerSection); - ReadStringValue(&ws_server_ca_cert_path_, - "", - kTransportManagerSection, - kWSServerCACertificaePathKey); + const bool is_ws_ca_cert_setup = + ReadStringValue(&ws_server_ca_cert_path_, + "", + kTransportManagerSection, + kWSServerCACertificaePathKey); LOG_UPDATED_VALUE(ws_server_ca_cert_path_, kWSServerCACertificaePathKey, kTransportManagerSection); + + is_wss_settings_setup_ = + is_ws_server_cert_setup && is_ws_server_key_setup && is_ws_ca_cert_setup; #endif // ENABLE_SECURITY +#endif // WEBSOCKET_SERVER_TRANSPORT_SUPPORT ReadUIntValue(&cloud_app_retry_timeout_, kDefaultCloudAppRetryTimeout, diff --git a/src/components/connection_handler/include/connection_handler/connection_handler_impl.h b/src/components/connection_handler/include/connection_handler/connection_handler_impl.h index b7b791a5c1..bffd1188f8 100644 --- a/src/components/connection_handler/include/connection_handler/connection_handler_impl.h +++ b/src/components/connection_handler/include/connection_handler/connection_handler_impl.h @@ -606,6 +606,10 @@ class ConnectionHandlerImpl const transport_manager::ConnectionUID secondary_connection_handle) OVERRIDE; + void CreateWebEngineDevice(const std::string& vin_code) OVERRIDE; + + const transport_manager::DeviceInfo& GetWebEngineDeviceInfo() const OVERRIDE; + private: /** * \brief Disconnect application. @@ -646,7 +650,7 @@ class ConnectionHandlerImpl * \brief List of devices */ DeviceMap device_list_; - + mutable sync_primitives::RWLock device_list_lock_; /** * @brief session/connection map */ @@ -685,6 +689,8 @@ class ConnectionHandlerImpl */ Connection* ending_connection_; + std::string vin_code_; + #ifdef BUILD_TESTS // Methods for test usage public: diff --git a/src/components/connection_handler/src/connection_handler_impl.cc b/src/components/connection_handler/src/connection_handler_impl.cc index 4a51d00558..4b50e189e6 100644 --- a/src/components/connection_handler/src/connection_handler_impl.cc +++ b/src/components/connection_handler/src/connection_handler_impl.cc @@ -75,7 +75,8 @@ ConnectionHandlerImpl::ConnectionHandlerImpl( , connection_list_deleter_(&connection_list_) , start_service_context_map_lock_() , start_service_context_map_() - , ending_connection_(NULL) {} + , ending_connection_(NULL) + , vin_code_("") {} ConnectionHandlerImpl::~ConnectionHandlerImpl() { LOG4CXX_AUTO_TRACE(logger_); @@ -120,6 +121,7 @@ void ConnectionHandlerImpl::OnDeviceListUpdated( const std::vector<transport_manager::DeviceInfo>&) { LOG4CXX_AUTO_TRACE(logger_); sync_primitives::AutoReadLock read_lock(connection_handler_observer_lock_); + sync_primitives::AutoReadLock lock(device_list_lock_); if (connection_handler_observer_) { connection_handler_observer_->OnDeviceListUpdated(device_list_); } @@ -152,14 +154,21 @@ void ConnectionHandlerImpl::OnDeviceAdded( device_info.mac_address(), device_info.connection_type()); - auto result = device_list_.insert(std::make_pair(handle, device)); + { + sync_primitives::AutoWriteLock write_lock(device_list_lock_); + auto result = device_list_.insert(std::make_pair(handle, device)); - if (!result.second) { - LOG4CXX_ERROR(logger_, - "Device with handle " << handle - << " is known already. " - "Information won't be updated."); - return; + if (!result.second) { + LOG4CXX_ERROR(logger_, + "Device with handle " << handle + << " is known already. " + "Information won't be updated."); + return; + } + } + if (device_info.name() == + transport_manager::webengine_constants::kWebEngineDeviceName) { + connection_handler_observer_->OnWebEngineDeviceCreated(); } } @@ -189,7 +198,10 @@ void ConnectionHandlerImpl::OnDeviceRemoved( } sync_primitives::AutoReadLock read_lock(connection_handler_observer_lock_); - device_list_.erase(device_info.device_handle()); + { + sync_primitives::AutoWriteLock lock(device_list_lock_); + device_list_.erase(device_info.device_handle()); + } if (connection_handler_observer_) { connection_handler_observer_->RemoveDevice(device_info.device_handle()); } @@ -219,6 +231,8 @@ struct DeviceFinder { void ConnectionHandlerImpl::OnDeviceSwitchingStart( const std::string& device_uid_from, const std::string& device_uid_to) { + sync_primitives::AutoReadLock lock(device_list_lock_); + auto device_from = std::find_if(device_list_.begin(), device_list_.end(), @@ -260,10 +274,14 @@ void ConnectionHandlerImpl::OnConnectionPending( << device_info.name() << " " << device_info.mac_address() << " " << device_info.connection_type()); - DeviceMap::iterator it = device_list_.find(device_info.device_handle()); - if (device_list_.end() == it) { - LOG4CXX_ERROR(logger_, "Unknown device!"); - return; + { + sync_primitives::AutoReadLock lock(device_list_lock_); + auto it = device_list_.find(device_info.device_handle()); + + if (device_list_.end() == it) { + LOG4CXX_ERROR(logger_, "Unknown device!"); + return; + } } LOG4CXX_DEBUG(logger_, "Add Pending Connection #" << connection_id << " to the list."); @@ -310,11 +328,15 @@ void ConnectionHandlerImpl::OnConnectionEstablished( << device_info.device_handle() << " " << device_info.name() << " " << device_info.mac_address() << " " << device_info.connection_type()); - DeviceMap::iterator it = device_list_.find(device_info.device_handle()); - if (device_list_.end() == it) { - LOG4CXX_ERROR(logger_, "Unknown device!"); - return; + { + sync_primitives::AutoReadLock lock(device_list_lock_); + auto it = device_list_.find(device_info.device_handle()); + if (device_list_.end() == it) { + LOG4CXX_ERROR(logger_, "Unknown device!"); + return; + } } + LOG4CXX_DEBUG(logger_, "Add Connection #" << connection_id << " to the list."); sync_primitives::AutoWriteLock lock(connection_list_lock_); @@ -413,8 +435,9 @@ void ConnectionHandlerImpl::OnSessionStartedCallback( // In case this is a Session running on a Secondary Transport, we need to // find the Sessions's primary transport. In this case, "connection_handle" - // reflects the secondary transport, which we need for the various callbacks, - // so they can send appropriate Ack or NAK messages on the correct transport. + // reflects the secondary transport, which we need for the various + // callbacks, so they can send appropriate Ack or NAK messages on the + // correct transport. transport_manager::ConnectionUID primary_connection_handle = connection_handle; SessionTransports st = GetSessionTransports(session_id); @@ -533,7 +556,8 @@ void ConnectionHandlerImpl::NotifyServiceStartedResult( start_service_context_map_.erase(it); } - // We need the context's primary connection so we can manage its services list + // We need the context's primary connection so we can manage its services + // list Connection* connection = NULL; { sync_primitives::AutoReadLock lock(connection_list_lock_); @@ -646,8 +670,9 @@ uint32_t ConnectionHandlerImpl::OnSessionEndedCallback( // In case this is a Session running on a Secondary Transport, we need to // find the Sessions's primary transport. In this case, "connection_handle" - // reflects the secondary transport, which we need for the various callbacks, - // so they can send appropriate Ack or NAK messages on the correct transport. + // reflects the secondary transport, which we need for the various + // callbacks, so they can send appropriate Ack or NAK messages on the + // correct transport. transport_manager::ConnectionUID primary_connection_handle = connection_handle; if (session_id != 0) { @@ -841,6 +866,15 @@ void ConnectionHandlerImpl::OnSecondaryTransportEnded( } } +void ConnectionHandlerImpl::CreateWebEngineDevice(const std::string& vin_code) { + transport_manager_.CreateWebEngineDevice(vin_code); +} + +const transport_manager::DeviceInfo& +ConnectionHandlerImpl::GetWebEngineDeviceInfo() const { + return transport_manager_.GetWebEngineDeviceInfo(); +} + const std::string ConnectionHandlerImpl::TransportTypeProfileStringFromConnHandle( transport_manager::ConnectionUID connection_handle) const { @@ -861,11 +895,14 @@ const std::string ConnectionHandlerImpl::TransportTypeProfileStringFromDeviceHandle( DeviceHandle device_handle) const { std::string connection_type; - DeviceMap::const_iterator it = device_list_.find(device_handle); - if (device_list_.end() == it) { - LOG4CXX_ERROR(logger_, "Device not found!"); - } else { - connection_type = it->second.connection_type(); + { + sync_primitives::AutoReadLock lock(device_list_lock_); + auto it = device_list_.find(device_handle); + if (device_list_.end() == it) { + LOG4CXX_ERROR(logger_, "Device not found!"); + } else { + connection_type = it->second.connection_type(); + } } // Caution: this should be in sync with devicesType map in @@ -1067,9 +1104,10 @@ SessionTransports ConnectionHandlerImpl::SetSecondaryTransportID( st = it->second; // The only time we overwrite an existing entry in the map is if the new - // secondary transport ID is kDisabledSecondary, which effectively DISABLES - // the secondary transport feature for the session, or if the new secondary - // transport ID is 0, which means a secondary transport has shut down + // secondary transport ID is kDisabledSecondary, which effectively + // DISABLES the secondary transport feature for the session, or if the new + // secondary transport ID is 0, which means a secondary transport has shut + // down if (st.secondary_transport != 0 && secondary_transport_id != kDisabledSecondary && secondary_transport_id != 0) { @@ -1167,6 +1205,7 @@ struct CompareMAC { bool ConnectionHandlerImpl::GetDeviceID(const std::string& mac_address, DeviceHandle* device_handle) { DCHECK_OR_RETURN(device_handle, false); + sync_primitives::AutoReadLock lock(device_list_lock_); DeviceMap::const_iterator it = std::find_if( device_list_.begin(), device_list_.end(), CompareMAC(mac_address)); if (it != device_list_.end()) { @@ -1185,7 +1224,10 @@ int32_t ConnectionHandlerImpl::GetDataOnDeviceID( LOG4CXX_AUTO_TRACE(logger_); int32_t result = -1; - DeviceMap::const_iterator it = device_list_.find(device_handle); + + sync_primitives::AutoReadLock lock(device_list_lock_); + auto it = device_list_.find(device_handle); + if (device_list_.end() == it) { LOG4CXX_ERROR(logger_, "Device not found for handle " << device_handle); return result; @@ -1231,6 +1273,7 @@ void ConnectionHandlerImpl::GetConnectedDevicesMAC( std::vector<std::string>& device_macs) const { DeviceMap::const_iterator first = device_list_.begin(); DeviceMap::const_iterator last = device_list_.end(); + sync_primitives::AutoReadLock lock(device_list_lock_); while (first != last) { device_macs.push_back((*first).second.mac_address()); @@ -1328,6 +1371,7 @@ void ConnectionHandlerImpl::StartDevicesDiscovery() { transport_manager_.SearchDevices(); sync_primitives::AutoReadLock read_lock(connection_handler_observer_lock_); + sync_primitives::AutoReadLock lock(device_list_lock_); if (connection_handler_observer_) { connection_handler_observer_->OnDeviceListUpdated(device_list_); } @@ -1336,6 +1380,7 @@ void ConnectionHandlerImpl::StartDevicesDiscovery() { void ConnectionHandlerImpl::ConnectToDevice( connection_handler::DeviceHandle device_handle) { connection_handler::DeviceMap::const_iterator it_in; + sync_primitives::AutoReadLock lock(device_list_lock_); it_in = device_list_.find(device_handle); if (device_list_.end() != it_in) { LOG4CXX_INFO(logger_, "Connecting to device with handle " << device_handle); @@ -1357,6 +1402,7 @@ transport_manager::ConnectionStatus ConnectionHandlerImpl::GetConnectionStatus( void ConnectionHandlerImpl::RunAppOnDevice(const std::string& device_mac, const std::string& bundle_id) const { + sync_primitives::AutoReadLock lock(device_list_lock_); for (DeviceMap::const_iterator i = device_list_.begin(); i != device_list_.end(); ++i) { @@ -1370,6 +1416,7 @@ void ConnectionHandlerImpl::RunAppOnDevice(const std::string& device_mac, } void ConnectionHandlerImpl::ConnectToAllDevices() { + sync_primitives::AutoReadLock lock(device_list_lock_); for (DeviceMap::iterator i = device_list_.begin(); i != device_list_.end(); ++i) { connection_handler::DeviceHandle device_handle = i->first; diff --git a/src/components/include/connection_handler/connection_handler.h b/src/components/include/connection_handler/connection_handler.h index eb92336b4f..4c3aa69ea7 100644 --- a/src/components/include/connection_handler/connection_handler.h +++ b/src/components/include/connection_handler/connection_handler.h @@ -312,6 +312,21 @@ class ConnectionHandler { const transport_manager::ConnectionUID primary_connection_handle, const transport_manager::ConnectionUID secondary_connection_handle) = 0; + /** + * @brief Called when VIN code is received, creates + * WebSocketDevice for WebEngine and add it to the device list + * @param vin_code VIN code received from HMI + */ + virtual void CreateWebEngineDevice(const std::string& vin_code) = 0; + + /** + * @brief GetWebEngineDeviceInfo + * @return device info for WebEngine device + */ + + virtual const transport_manager::DeviceInfo& GetWebEngineDeviceInfo() + const = 0; + protected: /** * \brief Destructor diff --git a/src/components/include/connection_handler/connection_handler_observer.h b/src/components/include/connection_handler/connection_handler_observer.h index 48e4263959..7d6664a009 100644 --- a/src/components/include/connection_handler/connection_handler_observer.h +++ b/src/components/include/connection_handler/connection_handler_observer.h @@ -172,6 +172,11 @@ class ConnectionHandlerObserver { const transport_manager::ConnectionUID connection_id, const transport_manager::DeviceInfo& device_info) = 0; + /** + *@brief Called when webengine device added + */ + virtual void OnWebEngineDeviceCreated() = 0; + protected: /** * \brief Destructor diff --git a/src/components/include/test/connection_handler/mock_connection_handler.h b/src/components/include/test/connection_handler/mock_connection_handler.h index 41c2f04ebb..9642621147 100644 --- a/src/components/include/test/connection_handler/mock_connection_handler.h +++ b/src/components/include/test/connection_handler/mock_connection_handler.h @@ -133,6 +133,9 @@ class MockConnectionHandler : public connection_handler::ConnectionHandler { OnSecondaryTransportEnded, void(const transport_manager::ConnectionUID primary_connection_handle, const transport_manager::ConnectionUID secondary_connection_handle)); + + MOCK_METHOD1(CreateWebEngineDevice, void(const std::string& vin_code)); + MOCK_CONST_METHOD0(GetWebEngineDeviceInfo, transport_manager::DeviceInfo&()); }; } // namespace connection_handler_test diff --git a/src/components/include/test/connection_handler/mock_connection_handler_observer.h b/src/components/include/test/connection_handler/mock_connection_handler_observer.h index 8f7ec90550..61877daa23 100644 --- a/src/components/include/test/connection_handler/mock_connection_handler_observer.h +++ b/src/components/include/test/connection_handler/mock_connection_handler_observer.h @@ -46,6 +46,7 @@ class MockConnectionHandlerObserver MOCK_METHOD1(OnDeviceListUpdated, void(const connection_handler::DeviceMap& device_list)); MOCK_METHOD0(OnFindNewApplicationsRequest, void()); + MOCK_METHOD0(OnWebEngineDeviceCreated, void()); MOCK_METHOD1(RemoveDevice, void(const connection_handler::DeviceHandle& device_handle)); MOCK_METHOD4(OnServiceStartedCallback, diff --git a/src/components/include/test/transport_manager/mock_transport_manager.h b/src/components/include/test/transport_manager/mock_transport_manager.h index 884bde50bf..5681bba875 100644 --- a/src/components/include/test/transport_manager/mock_transport_manager.h +++ b/src/components/include/test/transport_manager/mock_transport_manager.h @@ -86,6 +86,8 @@ class MockTransportManager : public ::transport_manager::TransportManager, MOCK_METHOD1(SetTelemetryObserver, void(transport_manager::TMTelemetryObserver* observer)); + MOCK_METHOD1(CreateWebEngineDevice, void(const std::string& vin_code)); + MOCK_CONST_METHOD0(GetWebEngineDeviceInfo, transport_manager::DeviceInfo&()); }; } // namespace transport_manager_test diff --git a/src/components/include/test/transport_manager/mock_transport_manager_settings.h b/src/components/include/test/transport_manager/mock_transport_manager_settings.h index 63bca9de9c..692ad50b58 100644 --- a/src/components/include/test/transport_manager/mock_transport_manager_settings.h +++ b/src/components/include/test/transport_manager/mock_transport_manager_settings.h @@ -77,6 +77,7 @@ class MockTransportManagerSettings MOCK_CONST_METHOD0(ws_server_cert_path, const std::string&()); MOCK_CONST_METHOD0(ws_server_key_path, const std::string&()); MOCK_CONST_METHOD0(ws_server_ca_cert_path, const std::string&()); + MOCK_CONST_METHOD0(is_wss_settings_setup, const bool()); }; } // namespace transport_manager_test diff --git a/src/components/include/test/transport_manager/transport_adapter/mock_transport_adapter.h b/src/components/include/test/transport_manager/transport_adapter/mock_transport_adapter.h index a07365f8c0..d4ce0a39c8 100644 --- a/src/components/include/test/transport_manager/transport_adapter/mock_transport_adapter.h +++ b/src/components/include/test/transport_manager/transport_adapter/mock_transport_adapter.h @@ -108,6 +108,10 @@ class MockTransportAdapter transport_manager::transport_adapter::TransportConfig()); MOCK_METHOD1(CreateDevice, void(const std::string& uid)); + MOCK_METHOD1(AddDevice, + transport_manager::transport_adapter::DeviceSptr( + transport_manager::transport_adapter::DeviceSptr device)); + #ifdef TELEMETRY_MONITOR MOCK_METHOD0(GetTelemetryObserver, ::transport_manager::TMTelemetryObserver*()); diff --git a/src/components/include/transport_manager/common.h b/src/components/include/transport_manager/common.h index 08f52ae1d0..f8b0cabe2e 100644 --- a/src/components/include/transport_manager/common.h +++ b/src/components/include/transport_manager/common.h @@ -69,6 +69,11 @@ enum { enum ConnectionStatus { INVALID = -1, PENDING, RETRY, CONNECTED, CLOSING }; +namespace webengine_constants { +const std::string kWebEngineConnectionType("WEBENGINE_WEBSOCKET"); +const std::string kWebEngineDeviceName("Web Engine"); +} // namespace webengine_constants + /** * @brief Type definition for variable that hold handle of device. */ diff --git a/src/components/include/transport_manager/transport_adapter/transport_adapter.h b/src/components/include/transport_manager/transport_adapter/transport_adapter.h index 8b46be253b..e9494b4ced 100644 --- a/src/components/include/transport_manager/transport_adapter/transport_adapter.h +++ b/src/components/include/transport_manager/transport_adapter/transport_adapter.h @@ -68,6 +68,7 @@ enum DeviceType { IOS_USB_HOST_MODE, IOS_USB_DEVICE_MODE, IOS_CARPLAY_WIRELESS, // running on iAP over Carplay wireless transport + WEBENGINE_WEBSOCKET, UNKNOWN }; @@ -225,6 +226,16 @@ class TransportAdapter { const DeviceUID& device_handle) const = 0; /** + * @brief Add device to the container(map), if container doesn't hold it yet. + * in TransportAdapter is used only to add a WebEngine device + * + * @param device Smart pointer to the device. + * + * @return Smart pointer to the device. + */ + virtual DeviceSptr AddDevice(DeviceSptr device) = 0; + + /** * @brief RunAppOnDevice allows to run specific application on the certain *device. * diff --git a/src/components/include/transport_manager/transport_manager.h b/src/components/include/transport_manager/transport_manager.h index ce92bfd9cb..e5f79edfc5 100644 --- a/src/components/include/transport_manager/transport_manager.h +++ b/src/components/include/transport_manager/transport_manager.h @@ -215,6 +215,19 @@ class TransportManager { */ virtual int PerformActionOnClients( const TransportAction required_action) const = 0; + + /** + * @brief Called when VIN code is received, creates + * WebSocketDevice for WebEngine and add it to the device list + * @param vin_code VIN code received from HMI + */ + virtual void CreateWebEngineDevice(const std::string& vin_code) = 0; + + /** + * @brief GetWebEngineDeviceInfo + * @return device info for WebEngine device + */ + virtual const DeviceInfo& GetWebEngineDeviceInfo() const = 0; }; } // namespace transport_manager #endif // SRC_COMPONENTS_INCLUDE_TRANSPORT_MANAGER_TRANSPORT_MANAGER_H_ diff --git a/src/components/include/transport_manager/transport_manager_settings.h b/src/components/include/transport_manager/transport_manager_settings.h index ae2ad45246..ce41da2e2b 100644 --- a/src/components/include/transport_manager/transport_manager_settings.h +++ b/src/components/include/transport_manager/transport_manager_settings.h @@ -69,7 +69,7 @@ class TransportManagerSettings : public TransportManagerMMESettings { */ virtual const std::string& transport_manager_tcp_adapter_network_interface() const = 0; - +#ifdef WEBSOCKET_SERVER_TRANSPORT_SUPPORT /** *@brief Returns websocket server address */ @@ -80,6 +80,13 @@ class TransportManagerSettings : public TransportManagerMMESettings { */ virtual uint16_t websocket_server_port() const = 0; +#ifdef ENABLE_SECURITY + virtual const std::string& ws_server_cert_path() const = 0; + virtual const std::string& ws_server_key_path() const = 0; + virtual const std::string& ws_server_ca_cert_path() const = 0; + virtual const bool is_wss_settings_setup() const = 0; +#endif // ENABLE_SECURITY +#endif // WEBSOCKET_SERVER_TRANSPORT_SUPPORT /** * @brief Returns retry timeout for cloud app connections */ @@ -98,11 +105,6 @@ class TransportManagerSettings : public TransportManagerMMESettings { virtual const std::string& aoa_filter_version() const = 0; virtual const std::string& aoa_filter_uri() const = 0; virtual const std::string& aoa_filter_serial_number() const = 0; -#ifdef ENABLE_SECURITY - virtual const std::string& ws_server_cert_path() const = 0; - virtual const std::string& ws_server_key_path() const = 0; - virtual const std::string& ws_server_ca_cert_path() const = 0; -#endif // ENABLE_SECURITY }; } // namespace transport_manager #endif // SRC_COMPONENTS_INCLUDE_TRANSPORT_MANAGER_TRANSPORT_MANAGER_SETTINGS_H_ diff --git a/src/components/transport_manager/CMakeLists.txt b/src/components/transport_manager/CMakeLists.txt index 11986ee9fa..3ed845a06f 100644 --- a/src/components/transport_manager/CMakeLists.txt +++ b/src/components/transport_manager/CMakeLists.txt @@ -106,6 +106,18 @@ else() ) endif() +if(NOT BUILD_WEBSOCKET_SERVER_SUPPORT) + list(APPEND EXCLUDE_PATHS + ${CMAKE_CURRENT_SOURCE_DIR}/include/transport_manager/websocket + ${CMAKE_CURRENT_SOURCE_DIR}/src/websocket + ) +elseif(NOT ENABLE_SECURITY) + list(APPEND EXCLUDE_PATHS + ${CMAKE_CURRENT_SOURCE_DIR}/include/transport_manager/websocket/websocket_secure_session.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/websocket/websocket_secure_session.cc + ) +endif() + if(NOT BUILD_TESTS) list (APPEND EXCLUDE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/include/iap2_emulation/iap2_transport_adapter.h diff --git a/src/components/transport_manager/include/transport_manager/transport_adapter/transport_adapter_controller.h b/src/components/transport_manager/include/transport_manager/transport_adapter/transport_adapter_controller.h index 72a67a3087..92a3a2c2f4 100644 --- a/src/components/transport_manager/include/transport_manager/transport_adapter/transport_adapter_controller.h +++ b/src/components/transport_manager/include/transport_manager/transport_adapter/transport_adapter_controller.h @@ -225,6 +225,8 @@ class TransportAdapterController { * @param new_config The new configuration of the transport */ virtual void TransportConfigUpdated(const TransportConfig& new_config) = 0; + + virtual DeviceSptr GetWebEngineDevice() const = 0; }; } // namespace transport_adapter diff --git a/src/components/transport_manager/include/transport_manager/transport_adapter/transport_adapter_impl.h b/src/components/transport_manager/include/transport_manager/transport_adapter/transport_adapter_impl.h index 5f54cf8376..99077b69f4 100644 --- a/src/components/transport_manager/include/transport_manager/transport_adapter/transport_adapter_impl.h +++ b/src/components/transport_manager/include/transport_manager/transport_adapter/transport_adapter_impl.h @@ -276,13 +276,6 @@ class TransportAdapterImpl : public TransportAdapter, */ void SearchDeviceFailed(const SearchDeviceError& error) OVERRIDE; - /** - * @brief Add device to the container(map), if container doesn't hold it yet. - * - * @param device Smart pointer to the device. - * - * @return Smart pointer to the device. - */ DeviceSptr AddDevice(DeviceSptr device) OVERRIDE; /** @@ -510,6 +503,12 @@ class TransportAdapterImpl : public TransportAdapter, TMTelemetryObserver* GetTelemetryObserver() OVERRIDE; #endif // TELEMETRY_MONITOR + /** + * @brief GetWebEngineDevice + * @return shared pointer to WebEngine device + */ + DeviceSptr GetWebEngineDevice() const OVERRIDE; + protected: /** * @brief Store adapter state where applicable diff --git a/src/components/transport_manager/include/transport_manager/transport_manager_impl.h b/src/components/transport_manager/include/transport_manager/transport_manager_impl.h index 5e97a5567e..3cfe7c5dfe 100644 --- a/src/components/transport_manager/include/transport_manager/transport_manager_impl.h +++ b/src/components/transport_manager/include/transport_manager/transport_manager_impl.h @@ -259,6 +259,10 @@ class TransportManagerImpl int PerformActionOnClients( const TransportAction required_action) const OVERRIDE; + void CreateWebEngineDevice(const std::string& vin_code) OVERRIDE; + + const DeviceInfo& GetWebEngineDeviceInfo() const OVERRIDE; + /** * @brief OnDeviceListUpdated updates device list and sends appropriate * notifications to listeners in case of something is changed @@ -418,6 +422,8 @@ class TransportManagerImpl sync_primitives::Lock events_processing_lock_; sync_primitives::ConditionalVariable events_processing_cond_var_; + DeviceInfo web_engine_device_info_; + /** * @brief Adds new incoming connection to connections list * @param c New connection diff --git a/src/components/transport_manager/include/transport_manager/websocket/websocket_connection.h b/src/components/transport_manager/include/transport_manager/websocket/websocket_connection.h new file mode 100644 index 0000000000..d811ca261b --- /dev/null +++ b/src/components/transport_manager/include/transport_manager/websocket/websocket_connection.h @@ -0,0 +1,124 @@ +/* +Copyright (c) 2020 Livio, Inc. +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 SmartDeviceLink Consortium, Inc. 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_INCLUDE_TRANSPORT_MANAGER_WEBSOCKET_WEBSOCKET_CONNECTION_H_ +#define SRC_COMPONENTS_TRANSPORT_MANAGER_INCLUDE_TRANSPORT_MANAGER_WEBSOCKET_WEBSOCKET_CONNECTION_H_ + +#include "transport_manager/transport_adapter/connection.h" +#include "utils/message_queue.h" +#include "utils/threads/thread.h" + +#ifdef ENABLE_SECURITY +#include "transport_manager/websocket/websocket_secure_session.h" +#else +#include "transport_manager/websocket/websocket_session.h" +#endif // ENABLE_SECURITY + +namespace transport_manager { +namespace transport_adapter { + +using ::utils::MessageQueue; + +typedef ::protocol_handler::RawMessagePtr Message; +typedef std::queue<Message> AsyncQueue; + +class TransportAdapterController; + +template <typename Session = WebSocketSession<> > +class WebSocketConnection + : public std::enable_shared_from_this<WebSocketConnection<Session> >, + public Connection { + public: + WebSocketConnection(const DeviceUID& device_uid, + const ApplicationHandle& app_handle, + boost::asio::ip::tcp::socket socket, + TransportAdapterController* controller); + +#ifdef ENABLE_SECURITY + WebSocketConnection(const DeviceUID& device_uid, + const ApplicationHandle& app_handle, + boost::asio::ip::tcp::socket socket, + ssl::context& ctx, + TransportAdapterController* controller); +#endif // ENABLE_SECURITY + + ~WebSocketConnection(); + + TransportAdapter::Error Disconnect() OVERRIDE; + + TransportAdapter::Error SendData( + protocol_handler::RawMessagePtr message) OVERRIDE; + + void DataReceive(protocol_handler::RawMessagePtr frame); + void Run(); + bool IsShuttingDown(); + + protected: + void Shutdown(); + void OnError(); + + private: + const DeviceUID device_uid_; + const ApplicationHandle app_handle_; + std::shared_ptr<Session> session_; + TransportAdapterController* controller_; + + std::atomic_bool shutdown_; + + MessageQueue<Message, AsyncQueue> message_queue_; + + class LoopThreadDelegate : public threads::ThreadDelegate { + public: + LoopThreadDelegate(MessageQueue<Message, AsyncQueue>* message_queue, + DataWriteCallback data_write, + OnIOErrorCallback on_io_error); + + virtual void threadMain() OVERRIDE; + virtual void exitThreadMain() OVERRIDE; + + void OnWrite(); + + void SetShutdown(); + + private: + void DrainQueue(); + MessageQueue<Message, AsyncQueue>& message_queue_; + DataWriteCallback data_write_; + OnIOErrorCallback on_io_error_; + }; + + LoopThreadDelegate* thread_delegate_; + threads::Thread* thread_; +}; + +} // namespace transport_adapter +} // namespace transport_manager + +#endif // SRC_COMPONENTS_TRANSPORT_MANAGER_INCLUDE_TRANSPORT_MANAGER_WEBSOCKET_WEBSOCKET_CONNECTION_H_ diff --git a/src/components/transport_manager/include/transport_manager/websocket/websocket_device.h b/src/components/transport_manager/include/transport_manager/websocket/websocket_device.h new file mode 100644 index 0000000000..455498c8fb --- /dev/null +++ b/src/components/transport_manager/include/transport_manager/websocket/websocket_device.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2020, Livio + * 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. + */ + +/** + * \file websocket_device.h + * \brief WebSocketDevice class header file. + */ + +#ifndef SRC_COMPONENTS_TRANSPORT_MANAGER_INCLUDE_TRANSPORT_MANAGER_WEBSOCKET_WEBSOCKET_DEVICE_H_ +#define SRC_COMPONENTS_TRANSPORT_MANAGER_INCLUDE_TRANSPORT_MANAGER_WEBSOCKET_WEBSOCKET_DEVICE_H_ + +#include <boost/beast/websocket.hpp> +#include "transport_manager/transport_adapter/device.h" + +namespace transport_manager { +namespace transport_adapter { + +using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp> +using protocol_type = boost::asio::basic_stream_socket<tcp>::protocol_type; + +class WebSocketDevice : public Device { + public: + WebSocketDevice(const std::string& name, const DeviceUID& unique_device_id); + + virtual const std::string& GetHost() const; + virtual const std::string& GetPort() const; + virtual const std::string GetTarget() const; + virtual void AddApplication(const ApplicationHandle& app_handle); + + protected: + bool IsSameAs(const Device* other_device) const OVERRIDE; + ApplicationList GetApplicationList() const OVERRIDE; + + private: + std::string host_; + std::string port_; + bool is_secure_connect_; + protocol_type protocol_; + ApplicationList app_list_; +}; + +} // namespace transport_adapter +} // namespace transport_manager + +#endif // SRC_COMPONENTS_TRANSPORT_MANAGER_INCLUDE_TRANSPORT_MANAGER_WEBSOCKET_WEBSOCKET_DEVICE_H_ diff --git a/src/components/transport_manager/include/transport_manager/websocket/websocket_listener.h b/src/components/transport_manager/include/transport_manager/websocket/websocket_listener.h new file mode 100644 index 0000000000..a81190a736 --- /dev/null +++ b/src/components/transport_manager/include/transport_manager/websocket/websocket_listener.h @@ -0,0 +1,118 @@ +/* + * \file websocket_listener.h + * \brief WebSocketListener class header file. + * + * Copyright (c) 2020 + * 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_INCLUDE_TRANSPORT_MANAGER_WEBSOCKET_WEBSOCKET_LISTENER_H_ +#define SRC_COMPONENTS_TRANSPORT_MANAGER_INCLUDE_TRANSPORT_MANAGER_WEBSOCKET_WEBSOCKET_LISTENER_H_ + +#include <boost/asio/thread_pool.hpp> +#include "transport_manager/transport_adapter/client_connection_listener.h" +#include "transport_manager/transport_manager_settings.h" +#include "transport_manager/websocket/websocket_connection.h" + +namespace transport_manager { +namespace transport_adapter { + +class TransportAdapterController; + +/** + * @brief Class responsible for communication over websockets. + */ +class WebSocketListener : public ClientConnectionListener { + public: + /** + * @brief Constructor. + * @param controller Pointer to the device adapter controller. + * @param number of threads for listen incoming connections + */ + WebSocketListener(TransportAdapterController* controller, + const TransportManagerSettings& settings, + const int num_threads = 1); + + /** + * @brief Destructor. + */ + ~WebSocketListener(); + + TransportAdapter::Error Init() OVERRIDE; + void Terminate() OVERRIDE; + bool IsInitialised() const OVERRIDE { + return true; + } + TransportAdapter::Error StartListening() OVERRIDE; + TransportAdapter::Error StopListening() OVERRIDE { + return TransportAdapter::OK; + } + TransportAdapter::Error SuspendListening() OVERRIDE { + return TransportAdapter::OK; + } + TransportAdapter::Error ResumeListening() OVERRIDE { + return TransportAdapter::OK; + } + + protected: +#ifdef ENABLE_SECURITY + TransportAdapter::Error AddCertificateAuthority(); +#endif + bool Run(); + bool WaitForConnection(); + void StartSession(boost::system::error_code ec); + void Shutdown(); + + template <typename Connection> + void ProcessConnection(std::shared_ptr<Connection> connection, + const DeviceSptr, + const ApplicationHandle); + + private: + TransportAdapterController* controller_; + boost::asio::io_context ioc_; +#ifdef ENABLE_SECURITY + ssl::context ctx_; + bool start_secure_; +#endif // ENABLE_SECURITY + tcp::acceptor acceptor_; + tcp::socket socket_; + std::unique_ptr<boost::asio::thread_pool> io_pool_; + const int num_threads_; + std::atomic_bool shutdown_; + std::vector<std::shared_ptr<Connection> > connection_list_; + sync_primitives::Lock connection_list_lock; + const TransportManagerSettings& settings_; +}; + +} // namespace transport_adapter +} // namespace transport_manager + +#endif // SRC_COMPONENTS_TRANSPORT_MANAGER_INCLUDE_TRANSPORT_MANAGER_WEBSOCKET_WEBSOCKET_LISTENER_H_ diff --git a/src/components/transport_manager/include/transport_manager/websocket/websocket_secure_session.h b/src/components/transport_manager/include/transport_manager/websocket/websocket_secure_session.h new file mode 100644 index 0000000000..1ec8dbcf29 --- /dev/null +++ b/src/components/transport_manager/include/transport_manager/websocket/websocket_secure_session.h @@ -0,0 +1,63 @@ +/* +Copyright (c) 2020 Livio, Inc. +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 SmartDeviceLink Consortium, Inc. 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_INCLUDE_TRANSPORT_MANAGER_WEBSOCKET_WEBSOCKET_SECURE_SESSION_H_ +#define SRC_COMPONENTS_TRANSPORT_MANAGER_INCLUDE_TRANSPORT_MANAGER_WEBSOCKET_WEBSOCKET_SECURE_SESSION_H_ + +#include "transport_manager/websocket/websocket_session.h" + +namespace transport_manager { +namespace transport_adapter { + +CREATE_LOGGERPTR_GLOBAL(wss_logger_, "WebSocketSecureSession") + +template <typename ExecutorType = ssl::stream<tcp::socket&> > +class WebSocketSecureSession : public WebSocketSession<ExecutorType> { + public: + WebSocketSecureSession(tcp::socket, + ssl::context& ctx, + DataReceiveCallback data_receive, + OnIOErrorCallback on_errror); + + void AsyncAccept() OVERRIDE; + virtual void AsyncHandshake(boost::system::error_code ec); + + std::shared_ptr<WebSocketSecureSession<ExecutorType> > shared_from_this() { + auto base_ptr = + static_cast<WebSocketSession<ExecutorType>*>(this)->shared_from_this(); + return std::static_pointer_cast<WebSocketSecureSession<ExecutorType> >( + base_ptr); + } +}; + +} // namespace transport_adapter +} // namespace transport_manager + +#endif // SRC_COMPONENTS_TRANSPORT_MANAGER_INCLUDE_TRANSPORT_MANAGER_WEBSOCKET_WEBSOCKET_SECURE_SESSION_H_ diff --git a/src/components/transport_manager/include/transport_manager/websocket/websocket_session.h b/src/components/transport_manager/include/transport_manager/websocket/websocket_session.h new file mode 100644 index 0000000000..7383136e24 --- /dev/null +++ b/src/components/transport_manager/include/transport_manager/websocket/websocket_session.h @@ -0,0 +1,107 @@ +/* +Copyright (c) 2020 Livio, Inc. +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 SmartDeviceLink Consortium, Inc. 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_INCLUDE_TRANSPORT_MANAGER_WEBSOCKET_WEBSOCKET_SESSION_H_ +#define SRC_COMPONENTS_TRANSPORT_MANAGER_INCLUDE_TRANSPORT_MANAGER_WEBSOCKET_WEBSOCKET_SESSION_H_ + +#include <boost/asio/bind_executor.hpp> +#include <boost/asio/strand.hpp> +#include <boost/beast/core.hpp> +#include <boost/beast/websocket.hpp> +#include "protocol/raw_message.h" +#include "transport_manager/transport_adapter/transport_adapter.h" +#include "utils/logger.h" + +#ifdef ENABLE_SECURITY +#include <boost/beast/websocket/ssl.hpp> +#endif // ENABLE_SECURITY + +namespace transport_manager { +namespace transport_adapter { + +using DataReceiveCallback = + std::function<void(protocol_handler::RawMessagePtr)>; +using DataWriteCallback = std::function<TransportAdapter::Error( + protocol_handler::RawMessagePtr message)>; +using OnIOErrorCallback = std::function<void()>; + +using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp> +namespace websocket = + boost::beast::websocket; // from <boost/beast/websocket.hpp> +#ifdef ENABLE_SECURITY +namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp> +#endif // ENABLE_SECURITY + +CREATE_LOGGERPTR_GLOBAL(ws_logger_, "WebSocketSession") + +class TransportAdapterController; + +template <typename ExecutorType = tcp::socket&> +class WebSocketSession + : public std::enable_shared_from_this<WebSocketSession<ExecutorType> > { + public: + WebSocketSession(boost::asio::ip::tcp::socket socket, + DataReceiveCallback data_receive, + OnIOErrorCallback on_error); + +#ifdef ENABLE_SECURITY + WebSocketSession(boost::asio::ip::tcp::socket socket, + ssl::context& ctx, + DataReceiveCallback data_receive, + OnIOErrorCallback on_error); +#endif // ENABLE_SECURITY + + virtual ~WebSocketSession(); + + virtual void AsyncAccept(); + + virtual void AsyncRead(boost::system::error_code ec); + + virtual TransportAdapter::Error WriteDown( + ::protocol_handler::RawMessagePtr message); + + virtual void Read(boost::system::error_code ec, + std::size_t bytes_transferred); + + virtual bool Shutdown(); + + protected: + tcp::socket socket_; + websocket::stream<ExecutorType> ws_; + boost::asio::strand<boost::asio::io_context::executor_type> strand_; + boost::beast::flat_buffer buffer_; + DataReceiveCallback data_receive_; + OnIOErrorCallback on_io_error_; +}; + +} // namespace transport_adapter +} // namespace transport_manager + +#endif // SRC_COMPONENTS_TRANSPORT_MANAGER_INCLUDE_TRANSPORT_MANAGER_WEBSOCKET_WEBSOCKET_SESSION_H_ diff --git a/src/components/transport_manager/include/transport_manager/websocket/websocket_transport_adapter.h b/src/components/transport_manager/include/transport_manager/websocket/websocket_transport_adapter.h new file mode 100644 index 0000000000..ce64c277b5 --- /dev/null +++ b/src/components/transport_manager/include/transport_manager/websocket/websocket_transport_adapter.h @@ -0,0 +1,115 @@ +/* + * \file websocket_transport_adapter.h + * \brief WebSocketTransportAdapter class header file. + * + * Copyright (c) 2020, 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_INCLUDE_TRANSPORT_MANAGER_WEBSOCKET_WEBSOCKET_TRANSPORT_ADAPTER_H_ +#define SRC_COMPONENTS_TRANSPORT_MANAGER_INCLUDE_TRANSPORT_MANAGER_WEBSOCKET_WEBSOCKET_TRANSPORT_ADAPTER_H_ + +#include "transport_manager/transport_adapter/transport_adapter_impl.h" + +namespace transport_manager { +namespace transport_adapter { + +/** + * @brief Transport adapter that uses WebSocket transport. + */ +class WebSocketTransportAdapter : public TransportAdapterImpl { + public: + /** + * @brief Constructor. + */ + explicit WebSocketTransportAdapter(resumption::LastState& last_state, + const TransportManagerSettings& settings); + + /** + * @brief Destructor. + */ + virtual ~WebSocketTransportAdapter(); + + /** + * @brief Websocket transport adapter + * specific Init() method + * @note Perform additional actions required by WS transport adapter + * then calls basic class Init() + */ + TransportAdapter::Error Init() OVERRIDE; + + /** + * @brief Adds webengine device to internal storage + * of related WebSocket transport adapter + * @param device webengine device to add + * @note webengine device storage required + * to be used in Low Voltage conditions + */ + DeviceSptr AddDevice(DeviceSptr device) OVERRIDE; + + /** + * @brief Notification that transport's configuration is updated + * + * @param new_config The new configuration of the transport + */ + void TransportConfigUpdated(const TransportConfig& new_config) OVERRIDE; + + /** + * @brief Returns the transport's configuration information + */ + virtual TransportConfig GetTransportConfiguration() const OVERRIDE; + + protected: + /** + * @brief Return type of device. + * + * @return String with device type. + */ + DeviceType GetDeviceType() const OVERRIDE; + + private: + /** + * @brief Keeps transport specific configuration + * + * TCP transport uses following information: + * - "enabled": whether the transport is currently enabled or not. Value can + * be "true" or "false". + * - "tcp_ip_address": string representation of IP address (either IPv4 or + * IPv6) + * - "tcp_port": string representation of TCP port number (e.g. "12345") + */ + TransportConfig transport_config_; + DeviceSptr webengine_device_; +}; + +} // namespace transport_adapter +} // namespace transport_manager + +#endif // SRC_COMPONENTS_TRANSPORT_MANAGER_INCLUDE_TRANSPORT_MANAGER_WEBSOCKET_WEBSOCKET_TRANSPORT_ADAPTER_H_ diff --git a/src/components/transport_manager/src/transport_adapter/transport_adapter_impl.cc b/src/components/transport_manager/src/transport_adapter/transport_adapter_impl.cc index 78a9840401..f8e9c8a15f 100644 --- a/src/components/transport_manager/src/transport_adapter/transport_adapter_impl.cc +++ b/src/components/transport_manager/src/transport_adapter/transport_adapter_impl.cc @@ -40,6 +40,9 @@ #include "transport_manager/transport_adapter/server_connection_factory.h" #include "transport_manager/transport_adapter/transport_adapter_impl.h" #include "transport_manager/transport_adapter/transport_adapter_listener.h" +#ifdef WEBSOCKET_SERVER_TRANSPORT_SUPPORT +#include "transport_manager/websocket/websocket_device.h" +#endif namespace transport_manager { namespace transport_adapter { @@ -62,8 +65,9 @@ DeviceTypes devicesType = { std::string("USB_IOS_DEVICE_MODE")), std::make_pair(DeviceType::IOS_CARPLAY_WIRELESS, std::string("CARPLAY_WIRELESS_IOS")), - std::make_pair(DeviceType::CLOUD_WEBSOCKET, - std::string("CLOUD_WEBSOCKET"))}; + std::make_pair(DeviceType::CLOUD_WEBSOCKET, std::string("CLOUD_WEBSOCKET")), + std::make_pair(DeviceType::WEBENGINE_WEBSOCKET, + std::string("WEBENGINE_WEBSOCKET"))}; } TransportAdapterImpl::TransportAdapterImpl( @@ -373,6 +377,10 @@ TransportAdapter::Error TransportAdapterImpl::DisconnectDevice( Error error = OK; DeviceSptr device = FindDevice(device_id); + if (!device) { + LOG4CXX_WARN(logger_, "Device with id: " << device_id << " Not found"); + return BAD_PARAM; + } ConnectionStatusUpdated(device, ConnectionStatus::CLOSING); std::vector<ConnectionInfo> to_disconnect; @@ -488,6 +496,32 @@ DeviceList TransportAdapterImpl::GetDeviceList() const { return devices; } +DeviceSptr TransportAdapterImpl::GetWebEngineDevice() const { +#ifndef WEBSOCKET_SERVER_TRANSPORT_SUPPORT + LOG4CXX_TRACE(logger_, + "Web engine support is disabled. Device does not exist"); + return DeviceSptr(); +#else + LOG4CXX_AUTO_TRACE(logger_); + sync_primitives::AutoLock locker(devices_mutex_); + + auto web_engine_device = + std::find_if(devices_.begin(), + devices_.end(), + [](const std::pair<DeviceUID, DeviceSptr> device) { + return webengine_constants::kWebEngineDeviceName == + device.second->name(); + }); + + if (devices_.end() != web_engine_device) { + return web_engine_device->second; + } + + LOG4CXX_ERROR(logger_, "WebEngine device not found!"); + return std::make_shared<transport_adapter::WebSocketDevice>("", ""); +#endif +} + DeviceSptr TransportAdapterImpl::AddDevice(DeviceSptr device) { LOG4CXX_TRACE(logger_, "enter. device: " << device); DeviceSptr existing_device; diff --git a/src/components/transport_manager/src/transport_manager_default.cc b/src/components/transport_manager/src/transport_manager_default.cc index 0d58e24b5c..7955ca3655 100644 --- a/src/components/transport_manager/src/transport_manager_default.cc +++ b/src/components/transport_manager/src/transport_manager_default.cc @@ -48,6 +48,10 @@ #include "transport_manager/cloud/cloud_websocket_transport_adapter.h" #endif // CLOUD_APP_WEBSOCKET_TRANSPORT_SUPPORT +#ifdef WEBSOCKET_SERVER_TRANSPORT_SUPPORT +#include "transport_manager/websocket/websocket_transport_adapter.h" +#endif + #if defined(BUILD_TESTS) #include "transport_manager/iap2_emulation/iap2_transport_adapter.h" #endif // BUILD_TEST @@ -139,6 +143,18 @@ int TransportManagerDefault::Init(resumption::LastState& last_state) { AddTransportAdapter(ta_cloud); #endif // CLOUD_APP_WEBSOCKET_TRANSPORT_SUPPORT +#ifdef WEBSOCKET_SERVER_TRANSPORT_SUPPORT + auto ta_websocket = new transport_adapter::WebSocketTransportAdapter( + last_state, get_settings()); +#ifdef TELEMETRY_MONITOR + if (metric_observer_) { + ta_websocket->SetTelemetryObserver(metric_observer_); + } +#endif // TELEMETRY_MONITOR + AddTransportAdapter(ta_websocket); + ta_websocket = NULL; +#endif // WEBSOCKET_SERVER_TRANSPORT_SUPPORT + #if defined BUILD_TESTS const uint16_t iap2_bt_emu_port = 23456; transport_adapter::IAP2BluetoothEmulationTransportAdapter* diff --git a/src/components/transport_manager/src/transport_manager_impl.cc b/src/components/transport_manager/src/transport_manager_impl.cc index 07dd35bc2f..90972ed10d 100644 --- a/src/components/transport_manager/src/transport_manager_impl.cc +++ b/src/components/transport_manager/src/transport_manager_impl.cc @@ -54,6 +54,9 @@ #include "transport_manager/transport_adapter/transport_adapter_event.h" #include "transport_manager/transport_manager_listener.h" #include "transport_manager/transport_manager_listener_empty.h" +#ifdef WEBSOCKET_SERVER_TRANSPORT_SUPPORT +#include "transport_manager/websocket/websocket_device.h" +#endif #include "utils/timer_task_impl.h" using ::transport_manager::transport_adapter::TransportAdapter; @@ -104,7 +107,11 @@ TransportManagerImpl::TransportManagerImpl( this, &TransportManagerImpl::ReconnectionTimeout)) , events_processing_is_active_(true) , events_processing_lock_() - , events_processing_cond_var_() { + , events_processing_cond_var_() + , web_engine_device_info_(0, + "", + webengine_constants::kWebEngineDeviceName, + webengine_constants::kWebEngineConnectionType) { LOG4CXX_TRACE(logger_, "TransportManager has created"); } @@ -645,6 +652,53 @@ int TransportManagerImpl::PerformActionOnClients( return E_SUCCESS; } +void TransportManagerImpl::CreateWebEngineDevice(const std::string& vin_code) { +#ifndef WEBSOCKET_SERVER_TRANSPORT_SUPPORT + LOG4CXX_TRACE(logger_, "Web engine support is disabled. Exiting function"); +#else + LOG4CXX_AUTO_TRACE(logger_); + auto web_socket_ta = std::find_if( + transport_adapters_.begin(), + transport_adapters_.end(), + [](const TransportAdapter* ta) { + return transport_adapter::DeviceType::WEBENGINE_WEBSOCKET == + ta->GetDeviceType(); + }); + + if (transport_adapters_.end() == web_socket_ta) { + LOG4CXX_WARN(logger_, + "WebSocketTransportAdapter not found." + "Not able to create WebEngineDevice"); + return; + } + + DeviceHandle device_handle = converter_.UidToHandle( + vin_code, webengine_constants::kWebEngineConnectionType); + + web_engine_device_info_ = + DeviceInfo(device_handle, + vin_code, + webengine_constants::kWebEngineDeviceName, + webengine_constants::kWebEngineConnectionType); + + auto ws_device = std::make_shared<transport_adapter::WebSocketDevice>( + web_engine_device_info_.name(), web_engine_device_info_.mac_address()); + + ws_device->set_keep_on_disconnect(true); + + RaiseEvent(&TransportManagerListener::OnDeviceAdded, web_engine_device_info_); + device_list_.push_back( + std::make_pair(*web_socket_ta, web_engine_device_info_)); + (*web_socket_ta)->AddDevice(ws_device); + +#endif +} + +const DeviceInfo& TransportManagerImpl::GetWebEngineDeviceInfo() const { + LOG4CXX_AUTO_TRACE(logger_); + return web_engine_device_info_; +} + void TransportManagerImpl::UpdateDeviceList(TransportAdapter* ta) { LOG4CXX_TRACE(logger_, "enter. TransportAdapter: " << ta); std::set<DeviceInfo> old_devices; diff --git a/src/components/transport_manager/src/websocket/websocket_connection.cc b/src/components/transport_manager/src/websocket/websocket_connection.cc new file mode 100644 index 0000000000..b467493a13 --- /dev/null +++ b/src/components/transport_manager/src/websocket/websocket_connection.cc @@ -0,0 +1,218 @@ +/* +Copyright (c) 2020 Livio, Inc. +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 SmartDeviceLink Consortium, Inc. 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/websocket/websocket_connection.h" +#include <unistd.h> +#include "transport_manager/transport_adapter/transport_adapter_controller.h" + +namespace transport_manager { +namespace transport_adapter { + +CREATE_LOGGERPTR_GLOBAL(wsc_logger_, "WebSocketConnection") + +using namespace boost::beast::websocket; + +template <> +WebSocketConnection<WebSocketSession<> >::WebSocketConnection( + const DeviceUID& device_uid, + const ApplicationHandle& app_handle, + boost::asio::ip::tcp::socket socket, + TransportAdapterController* controller) + : device_uid_(device_uid) + , app_handle_(app_handle) + , session_(new WebSocketSession<>( + std::move(socket), + std::bind( + &WebSocketConnection::DataReceive, this, std::placeholders::_1), + std::bind(&WebSocketConnection::OnError, this))) + , controller_(controller) + , shutdown_(false) + , thread_delegate_(new LoopThreadDelegate( + &message_queue_, + std::bind(&WebSocketSession<>::WriteDown, + session_.get(), + std::placeholders::_1), + std::bind(&WebSocketConnection::OnError, this))) + , thread_(threads::CreateThread("WS Async Send", thread_delegate_)) { + thread_->start(threads::ThreadOptions()); +} + +#ifdef ENABLE_SECURITY +template <> +WebSocketConnection<WebSocketSecureSession<> >::WebSocketConnection( + const DeviceUID& device_uid, + const ApplicationHandle& app_handle, + boost::asio::ip::tcp::socket socket, + ssl::context& ctx, + TransportAdapterController* controller) + : device_uid_(device_uid) + , app_handle_(app_handle) + , session_(new WebSocketSecureSession<>( + std::move(socket), + ctx, + std::bind( + &WebSocketConnection::DataReceive, this, std::placeholders::_1), + std::bind(&WebSocketConnection::OnError, this))) + , controller_(controller) + , shutdown_(false) + , thread_delegate_(new LoopThreadDelegate( + &message_queue_, + std::bind(&WebSocketSecureSession<>::WriteDown, + session_.get(), + std::placeholders::_1), + std::bind(&WebSocketConnection::OnError, this))) + , thread_(threads::CreateThread("WS Async Send", thread_delegate_)) { + thread_->start(threads::ThreadOptions()); +} +template class WebSocketConnection<WebSocketSecureSession<> >; +#endif // ENABLE_SECURITY + +template <typename Session> +WebSocketConnection<Session>::~WebSocketConnection() { + if (!IsShuttingDown()) { + Shutdown(); + } +} + +template <typename Session> +void WebSocketConnection<Session>::OnError() { + LOG4CXX_AUTO_TRACE(wsc_logger_); + + controller_->ConnectionAborted( + device_uid_, app_handle_, CommunicationError()); + + session_->Shutdown(); +} + +template <typename Session> +TransportAdapter::Error WebSocketConnection<Session>::Disconnect() { + LOG4CXX_AUTO_TRACE(wsc_logger_); + if (!IsShuttingDown()) { + Shutdown(); + controller_->DisconnectDone(device_uid_, app_handle_); + return TransportAdapter::OK; + } + return TransportAdapter::BAD_STATE; +} + +template <typename Session> +TransportAdapter::Error WebSocketConnection<Session>::SendData( + ::protocol_handler::RawMessagePtr message) { + if (IsShuttingDown()) { + return TransportAdapter::BAD_STATE; + } + + message_queue_.push(message); + + return TransportAdapter::OK; +} + +template <typename Session> +void WebSocketConnection<Session>::DataReceive( + protocol_handler::RawMessagePtr frame) { + controller_->DataReceiveDone(device_uid_, app_handle_, frame); +} + +template <typename Session> +void WebSocketConnection<Session>::Run() { + LOG4CXX_AUTO_TRACE(wsc_logger_); + session_->AsyncAccept(); +} + +template <typename Session> +void WebSocketConnection<Session>::Shutdown() { + LOG4CXX_AUTO_TRACE(wsc_logger_); + shutdown_ = true; + if (thread_delegate_) { + session_->Shutdown(); + thread_delegate_->SetShutdown(); + thread_->join(); + delete thread_delegate_; + thread_delegate_ = nullptr; + threads::DeleteThread(thread_); + thread_ = nullptr; + } +} + +template <typename Session> +bool WebSocketConnection<Session>::IsShuttingDown() { + return shutdown_; +} + +template <typename Session> +WebSocketConnection<Session>::LoopThreadDelegate::LoopThreadDelegate( + MessageQueue<Message, AsyncQueue>* message_queue, + DataWriteCallback data_write, + OnIOErrorCallback on_io_error) + : message_queue_(*message_queue) + , data_write_(data_write) + , on_io_error_(on_io_error) {} + +template <typename Session> +void WebSocketConnection<Session>::LoopThreadDelegate::threadMain() { + while (!message_queue_.IsShuttingDown()) { + DrainQueue(); + message_queue_.wait(); + } + DrainQueue(); +} + +template <typename Session> +void WebSocketConnection<Session>::LoopThreadDelegate::exitThreadMain() { + if (!message_queue_.IsShuttingDown()) { + message_queue_.Shutdown(); + } +} + +template <typename Session> +void WebSocketConnection<Session>::LoopThreadDelegate::DrainQueue() { + Message message_ptr; + while (!message_queue_.IsShuttingDown() && message_queue_.pop(message_ptr)) { + auto res = data_write_(message_ptr); + if (TransportAdapter::FAIL == res) { + LOG4CXX_WARN(wsc_logger_, + "Writing to websocket stream failed. Will now close " + "websocket connection."); + on_io_error_(); + } + } +} + +template <typename Session> +void WebSocketConnection<Session>::LoopThreadDelegate::SetShutdown() { + if (!message_queue_.IsShuttingDown()) { + message_queue_.Shutdown(); + } +} + +template class WebSocketConnection<WebSocketSession<> >; + +} // namespace transport_adapter +} // namespace transport_manager diff --git a/src/components/transport_manager/src/websocket/websocket_device.cc b/src/components/transport_manager/src/websocket/websocket_device.cc new file mode 100644 index 0000000000..2a4ab4fe03 --- /dev/null +++ b/src/components/transport_manager/src/websocket/websocket_device.cc @@ -0,0 +1,94 @@ +/* + * + * Copyright (c) 2020, Livio + * 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/websocket/websocket_device.h" + +#include "utils/logger.h" + +namespace transport_manager { +namespace transport_adapter { +CREATE_LOGGERPTR_GLOBAL(logger_, "TransportManager") + +WebSocketDevice::WebSocketDevice(const std::string& name, + const DeviceUID& unique_device_id) + : Device(name, unique_device_id) + , is_secure_connect_(false) + , protocol_(boost::asio::ip::tcp::v4()) {} + +bool WebSocketDevice::IsSameAs(const Device* other) const { + LOG4CXX_TRACE(logger_, "enter. device: " << other); + + const WebSocketDevice* other_websocket_device = + dynamic_cast<const WebSocketDevice*>(other); + + if (!other_websocket_device) { + return false; + } + + if (GetHost() != other_websocket_device->GetHost()) { + return false; + } + + if (GetPort() != other_websocket_device->GetPort()) { + return false; + } + + if (GetTarget() != other_websocket_device->GetTarget()) { + return false; + } + + return true; +} + +ApplicationList WebSocketDevice::GetApplicationList() const { + return ApplicationList{0}; +} + +const std::string& WebSocketDevice::GetHost() const { + return host_; +} + +const std::string& WebSocketDevice::GetPort() const { + return port_; +} + +const std::string WebSocketDevice::GetTarget() const { + return host_ + port_ + name(); +} + +void WebSocketDevice::AddApplication(const ApplicationHandle& app_handle) { + app_list_.push_back(app_handle); +} + +} // namespace transport_adapter +} // namespace transport_manager diff --git a/src/components/transport_manager/src/websocket/websocket_listener.cc b/src/components/transport_manager/src/websocket/websocket_listener.cc new file mode 100644 index 0000000000..1086607cb3 --- /dev/null +++ b/src/components/transport_manager/src/websocket/websocket_listener.cc @@ -0,0 +1,299 @@ +#include "transport_manager/websocket/websocket_listener.h" +#include "transport_manager/transport_adapter/transport_adapter_controller.h" +#include "transport_manager/websocket/websocket_device.h" +#include "utils/file_system.h" + +namespace transport_manager { +namespace transport_adapter { +CREATE_LOGGERPTR_GLOBAL(logger_, "WebSocketListener") + +WebSocketListener::WebSocketListener(TransportAdapterController* controller, + const TransportManagerSettings& settings, + const int num_threads) + : controller_(controller) + , ioc_(num_threads) +#ifdef ENABLE_SECURITY + , ctx_(ssl::context::sslv23) + , start_secure_(false) +#endif // ENABLE_SECURITY + , acceptor_(ioc_) + , socket_(ioc_) + , io_pool_(new boost::asio::thread_pool(num_threads)) + , num_threads_(num_threads) + , shutdown_(false) + , settings_(settings) { +} + +WebSocketListener::~WebSocketListener() { + Terminate(); +} + +TransportAdapter::Error WebSocketListener::Init() { + LOG4CXX_AUTO_TRACE(logger_); + const auto old_shutdown_value = shutdown_.exchange(false); + if (old_shutdown_value) { + ioc_.restart(); + io_pool_.reset(new boost::asio::thread_pool(num_threads_)); + } + return StartListening(); +} + +void WebSocketListener::Terminate() { + LOG4CXX_AUTO_TRACE(logger_); + Shutdown(); +} + +TransportAdapter::Error WebSocketListener::StartListening() { + LOG4CXX_AUTO_TRACE(logger_); + if (acceptor_.is_open()) { + return TransportAdapter::OK; + } + +#ifdef ENABLE_SECURITY + auto const ta_error = AddCertificateAuthority(); + if (TransportAdapter::OK != ta_error) { + return ta_error; + } +#endif + + auto const address = + boost::asio::ip::make_address(settings_.websocket_server_address()); + tcp::endpoint endpoint = {address, settings_.websocket_server_port()}; + + // Open the acceptor + boost::system::error_code ec; + acceptor_.open(endpoint.protocol(), ec); + if (ec) { + auto str_err = "ErrorOpen: " + ec.message(); + LOG4CXX_ERROR(logger_, + str_err << " host/port: " << endpoint.address().to_string() + << "/" << endpoint.port()); + return TransportAdapter::FAIL; + } + + acceptor_.set_option(boost::asio::socket_base::reuse_address(true), ec); + if (ec) { + std::string str_err = "ErrorSetOption: " + ec.message(); + LOG4CXX_ERROR(logger_, + str_err << " host/port: " << endpoint.address().to_string() + << "/" << endpoint.port()); + return TransportAdapter::FAIL; + } + + // Bind to the server address + acceptor_.bind(endpoint, ec); + if (ec) { + std::string str_err = "ErrorBind: " + ec.message(); + LOG4CXX_ERROR(logger_, + str_err << " host/port: " << endpoint.address().to_string() + << "/" << endpoint.port()); + return TransportAdapter::FAIL; + } + + // Start listening for connections + acceptor_.listen(boost::asio::socket_base::max_listen_connections, ec); + if (ec) { + std::string str_err = "ErrorListen: " + ec.message(); + LOG4CXX_ERROR(logger_, + str_err << " host/port: " << endpoint.address().to_string() + << "/" << endpoint.port()); + return TransportAdapter::FAIL; + } + + if (false == Run()) { + return TransportAdapter::FAIL; + } + + return TransportAdapter::OK; +} + +#ifdef ENABLE_SECURITY +TransportAdapter::Error WebSocketListener::AddCertificateAuthority() { + LOG4CXX_AUTO_TRACE(logger_); + + const auto cert_path = settings_.ws_server_cert_path(); + LOG4CXX_DEBUG(logger_, "Path to certificate : " << cert_path); + const auto key_path = settings_.ws_server_key_path(); + LOG4CXX_DEBUG(logger_, "Path to key : " << key_path); + const auto ca_cert_path = settings_.ws_server_ca_cert_path(); + LOG4CXX_DEBUG(logger_, "Path to ca cert : " << ca_cert_path); + start_secure_ = settings_.is_wss_settings_setup(); + + if (start_secure_ && (!file_system::FileExists(cert_path) || + !file_system::FileExists(key_path) || + !file_system::FileExists(ca_cert_path))) { + LOG4CXX_ERROR(logger_, "Certificate or key file not found"); + return TransportAdapter::FAIL; + } + + if (!start_secure_) { + auto check_config = [](const std::string& config, + const std::string config_name) { + bool start_unsecure = config.empty(); + if (!start_unsecure) { + LOG4CXX_ERROR(logger_, + "Configuration for secure WS is incomplete. " + << config_name + << " config is " + "present, meanwhile others may be missing. Please " + "check INI file"); + } + return start_unsecure; + }; + if (!check_config(cert_path, "Server cert") || + !check_config(key_path, "Server key") || + !check_config(ca_cert_path, "CA cert")) { + return TransportAdapter::FAIL; + } + } else { + LOG4CXX_INFO(logger_, "WebSocket server will start secure connection"); + ctx_.add_verify_path(cert_path); + ctx_.set_options(boost::asio::ssl::context::default_workarounds); + using context = boost::asio::ssl::context_base; + ctx_.set_verify_mode(ssl::verify_peer | ssl::verify_fail_if_no_peer_cert); + boost::system::error_code sec_ec; + ctx_.use_certificate_chain_file(cert_path, sec_ec); + ctx_.load_verify_file(ca_cert_path); + if (sec_ec) { + LOG4CXX_ERROR( + logger_, + "Loading WS server certificate failed: " << sec_ec.message()); + return TransportAdapter::FAIL; + } + sec_ec.clear(); + ctx_.use_private_key_file(key_path, context::pem, sec_ec); + if (sec_ec) { + LOG4CXX_ERROR(logger_, + "Loading WS server key failed: " << sec_ec.message()); + return TransportAdapter::FAIL; + } + } + + return TransportAdapter::OK; +} +#endif // ENABLE_SECURITY + +bool WebSocketListener::Run() { + LOG4CXX_AUTO_TRACE(logger_); + const bool is_connection_open = WaitForConnection(); + if (is_connection_open) { + boost::asio::post(*io_pool_.get(), [&]() { ioc_.run(); }); + } else { + LOG4CXX_ERROR(logger_, "Connection is shutdown or acceptor isn't open"); + } + + return is_connection_open; +} + +bool WebSocketListener::WaitForConnection() { + LOG4CXX_AUTO_TRACE(logger_); + if (!shutdown_ && acceptor_.is_open()) { + acceptor_.async_accept( + socket_, + std::bind( + &WebSocketListener::StartSession, this, std::placeholders::_1)); + return true; + } + return false; +} + +template <> +void WebSocketListener::ProcessConnection( + std::shared_ptr<WebSocketConnection<WebSocketSession<> > > connection, + const DeviceSptr device, + const ApplicationHandle app_handle) { + LOG4CXX_AUTO_TRACE(logger_); + controller_->ConnectionCreated( + connection, device->unique_device_id(), app_handle); + + controller_->ConnectDone(device->unique_device_id(), app_handle); + + connection->Run(); + + connection_list_lock.Acquire(); + connection_list_.push_back(connection); + connection_list_lock.Release(); + + WaitForConnection(); +} + +#ifdef ENABLE_SECURITY +template <> +void WebSocketListener::ProcessConnection( + std::shared_ptr<WebSocketConnection<WebSocketSecureSession<> > > connection, + const DeviceSptr device, + const ApplicationHandle app_handle) { + LOG4CXX_AUTO_TRACE(logger_); + controller_->ConnectionCreated( + connection, device->unique_device_id(), app_handle); + + controller_->ConnectDone(device->unique_device_id(), app_handle); + + connection->Run(); + + connection_list_lock.Acquire(); + connection_list_.push_back(connection); + connection_list_lock.Release(); + + WaitForConnection(); +} +#endif // ENABLE_SECURITY + +void WebSocketListener::StartSession(boost::system::error_code ec) { + LOG4CXX_AUTO_TRACE(logger_); + if (ec) { + std::string str_err = "ErrorAccept: " + ec.message(); + LOG4CXX_ERROR(logger_, str_err); + return; + } + + if (shutdown_) { + return; + } + + const ApplicationHandle app_handle = socket_.native_handle(); + + std::shared_ptr<WebSocketDevice> device = + std::static_pointer_cast<WebSocketDevice>( + controller_->GetWebEngineDevice()); + + LOG4CXX_INFO(logger_, "Connected client: " << app_handle); + +#ifdef ENABLE_SECURITY + if (start_secure_) { + auto connection = + std::make_shared<WebSocketConnection<WebSocketSecureSession<> > >( + device->unique_device_id(), + app_handle, + std::move(socket_), + ctx_, + controller_); + ProcessConnection(connection, device, app_handle); + return; + } +#endif // ENABLE_SECURITY + + auto connection = std::make_shared<WebSocketConnection<WebSocketSession<> > >( + device->unique_device_id(), app_handle, std::move(socket_), controller_); + ProcessConnection(connection, device, app_handle); +} + +void WebSocketListener::Shutdown() { + LOG4CXX_AUTO_TRACE(logger_); + if (false == shutdown_.exchange(true)) { + ioc_.stop(); + socket_.close(); + boost::system::error_code ec; + acceptor_.close(ec); + + if (ec) { + LOG4CXX_ERROR(logger_, "Acceptor closed with error: " << ec); + } + + io_pool_->stop(); + io_pool_->join(); + } +} + +} // namespace transport_adapter +} // namespace transport_manager diff --git a/src/components/transport_manager/src/websocket/websocket_secure_session.cc b/src/components/transport_manager/src/websocket/websocket_secure_session.cc new file mode 100644 index 0000000000..0d85fb4935 --- /dev/null +++ b/src/components/transport_manager/src/websocket/websocket_secure_session.cc @@ -0,0 +1,79 @@ +/* +Copyright (c) 2020 Livio, Inc. +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 SmartDeviceLink Consortium, Inc. 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/websocket/websocket_secure_session.h" +#include <unistd.h> +#include "transport_manager/transport_adapter/transport_adapter_controller.h" + +namespace transport_manager { +namespace transport_adapter { + +using namespace boost::beast::websocket; + +template <typename ExecutorType> +WebSocketSecureSession<ExecutorType>::WebSocketSecureSession( + tcp::socket socket, + ssl::context& ctx, + DataReceiveCallback data_receive, + OnIOErrorCallback on_error) + : WebSocketSession<ExecutorType>( + std::move(socket), ctx, data_receive, on_error) {} + +template <typename ExecutorType> +void WebSocketSecureSession<ExecutorType>::AsyncAccept() { + LOG4CXX_AUTO_TRACE(ws_logger_); + // Perform the SSL handshake + WebSocketSecureSession<ExecutorType>::ws_.next_layer().async_handshake( + ssl::stream_base::server, + boost::asio::bind_executor( + WebSocketSecureSession<ExecutorType>::strand_, + std::bind(&WebSocketSecureSession::AsyncHandshake, + this->shared_from_this(), + std::placeholders::_1))); +} + +template <typename ExecutorType> +void WebSocketSecureSession<ExecutorType>::AsyncHandshake( + boost::system::error_code ec) { + LOG4CXX_AUTO_TRACE(ws_logger_); + if (ec) { + auto str_err = "ErrorMessage: " + ec.message(); + LOG4CXX_ERROR(ws_logger_, str_err); + WebSocketSession<ExecutorType>::on_io_error_(); + return; + } + + WebSocketSession<ExecutorType>::AsyncAccept(); +} + +template class WebSocketSecureSession<ssl::stream<tcp::socket&> >; + +} // namespace transport_adapter +} // namespace transport_manager diff --git a/src/components/transport_manager/src/websocket/websocket_session.cc b/src/components/transport_manager/src/websocket/websocket_session.cc new file mode 100644 index 0000000000..df9d2d76a0 --- /dev/null +++ b/src/components/transport_manager/src/websocket/websocket_session.cc @@ -0,0 +1,162 @@ +/* +Copyright (c) 2020 Livio, Inc. +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 SmartDeviceLink Consortium, Inc. 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/websocket/websocket_session.h" +#include <unistd.h> +#include "transport_manager/transport_adapter/transport_adapter_controller.h" + +namespace transport_manager { +namespace transport_adapter { + +using namespace boost::beast::websocket; + +template <> +WebSocketSession<tcp::socket&>::WebSocketSession( + boost::asio::ip::tcp::socket socket, + DataReceiveCallback data_receive, + OnIOErrorCallback on_error) + : socket_(std::move(socket)) + , ws_(socket_) + , strand_(ws_.get_executor()) + , data_receive_(data_receive) + , on_io_error_(on_error) { + ws_.binary(true); +} + +#ifdef ENABLE_SECURITY +template <> +WebSocketSession<ssl::stream<tcp::socket&> >::WebSocketSession( + boost::asio::ip::tcp::socket socket, + ssl::context& ctx, + DataReceiveCallback data_receive, + OnIOErrorCallback on_error) + : socket_(std::move(socket)) + , ws_(socket_, ctx) + , strand_(ws_.get_executor()) + , data_receive_(data_receive) + , on_io_error_(on_error) { + ws_.binary(true); +} +template class WebSocketSession<ssl::stream<tcp::socket&> >; +#endif // ENABLE_SECURITY + +template <typename ExecutorType> +WebSocketSession<ExecutorType>::~WebSocketSession() {} + +template <typename ExecutorType> +void WebSocketSession<ExecutorType>::AsyncAccept() { + LOG4CXX_AUTO_TRACE(ws_logger_); + ws_.async_accept( + boost::asio::bind_executor(strand_, + std::bind(&WebSocketSession::AsyncRead, + this->shared_from_this(), + std::placeholders::_1))); +} + +template <typename ExecutorType> +void WebSocketSession<ExecutorType>::AsyncRead(boost::system::error_code ec) { + LOG4CXX_AUTO_TRACE(ws_logger_); + if (ec) { + auto str_err = "ErrorMessage: " + ec.message(); + LOG4CXX_ERROR(ws_logger_, str_err); + return; + } + + ws_.async_read(buffer_, + boost::asio::bind_executor(strand_, + std::bind(&WebSocketSession::Read, + this->shared_from_this(), + std::placeholders::_1, + std::placeholders::_2))); +} + +template <typename ExecutorType> +TransportAdapter::Error WebSocketSession<ExecutorType>::WriteDown( + ::protocol_handler::RawMessagePtr message) { + boost::system::error_code ec; + ws_.write(boost::asio::buffer(message->data(), message->data_size()), ec); + + if (ec) { + LOG4CXX_ERROR(ws_logger_, "A system error has occurred: " << ec.message()); + return TransportAdapter::FAIL; + } + + return TransportAdapter::OK; +} + +template <typename ExecutorType> +void WebSocketSession<ExecutorType>::Read(boost::system::error_code ec, + std::size_t bytes_transferred) { + LOG4CXX_AUTO_TRACE(ws_logger_); + boost::ignore_unused(bytes_transferred); + if (ec) { + LOG4CXX_ERROR(ws_logger_, ec.message()); + buffer_.consume(buffer_.size()); + on_io_error_(); + return; + } + + auto size = buffer_.size(); + const auto data = boost::asio::buffer_cast<const uint8_t*>( + boost::beast::buffers_front(buffer_.data())); + + LOG4CXX_DEBUG(ws_logger_, + "Msg: " << boost::beast::buffers_to_string(buffer_.data()) + << " Size: " << size;); + + ::protocol_handler::RawMessagePtr frame( + new protocol_handler::RawMessage(0, 0, data, size, false)); + + data_receive_(frame); + + buffer_.consume(buffer_.size()); + AsyncRead(ec); +} + +template <typename ExecutorType> +bool WebSocketSession<ExecutorType>::Shutdown() { + LOG4CXX_AUTO_TRACE(ws_logger_); + boost::system::error_code ec; + if (socket_.is_open()) { + socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); + socket_.close(); + } + buffer_.consume(buffer_.size()); + if (ec) { + LOG4CXX_ERROR(ws_logger_, ec.message()); + return false; + } + return true; +} + +template class WebSocketSession<tcp::socket&>; + +} // namespace transport_adapter +} // namespace transport_manager diff --git a/src/components/transport_manager/src/websocket/websocket_transport_adapter.cc b/src/components/transport_manager/src/websocket/websocket_transport_adapter.cc new file mode 100644 index 0000000000..eff7f13441 --- /dev/null +++ b/src/components/transport_manager/src/websocket/websocket_transport_adapter.cc @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2020, 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/websocket/websocket_transport_adapter.h" + +#include <errno.h> +#include <memory.h> +#include <signal.h> +#include <stdio.h> + +#include <cstdlib> +#include <sstream> + +#include "transport_manager/websocket/websocket_listener.h" +#include "utils/logger.h" +#include "utils/threads/thread_delegate.h" + +namespace transport_manager { +namespace transport_adapter { + +CREATE_LOGGERPTR_GLOBAL(logger_, "WebSocketTransportAdapter") + +WebSocketTransportAdapter::WebSocketTransportAdapter( + resumption::LastState& last_state, const TransportManagerSettings& settings) + : TransportAdapterImpl(nullptr, + nullptr, + new WebSocketListener(this, settings), + last_state, + settings) {} + +WebSocketTransportAdapter::~WebSocketTransportAdapter() {} + +void WebSocketTransportAdapter::TransportConfigUpdated( + const TransportConfig& new_config) { + LOG4CXX_AUTO_TRACE(logger_); + + transport_config_ = new_config; + + // call the method of parent class to trigger OnTransportConfigUpdated() for + // the listeners + TransportAdapterImpl::TransportConfigUpdated(new_config); +} + +TransportConfig WebSocketTransportAdapter::GetTransportConfiguration() const { + LOG4CXX_AUTO_TRACE(logger_); + return transport_config_; +} + +DeviceType WebSocketTransportAdapter::GetDeviceType() const { + return WEBENGINE_WEBSOCKET; +} + +DeviceSptr WebSocketTransportAdapter::AddDevice(DeviceSptr device) { + LOG4CXX_AUTO_TRACE(logger_); + webengine_device_ = device; + return TransportAdapterImpl::AddDevice(webengine_device_); +} + +TransportAdapter::Error WebSocketTransportAdapter::Init() { + LOG4CXX_AUTO_TRACE(logger_); + if (webengine_device_) { + AddDevice(webengine_device_); + } + return TransportAdapterImpl::Init(); +} + +} // namespace transport_adapter +} // namespace transport_manager diff --git a/src/components/transport_manager/test/CMakeLists.txt b/src/components/transport_manager/test/CMakeLists.txt index f6e1212b36..76680b2fad 100644 --- a/src/components/transport_manager/test/CMakeLists.txt +++ b/src/components/transport_manager/test/CMakeLists.txt @@ -48,11 +48,18 @@ set(EXCLUDE_PATHS if (NOT BUILD_CLOUD_APP_SUPPORT) list(APPEND EXCLUDE_PATHS - ${CMAKE_CURRENT_SOURCE_DIR}/websocket_connection_test.cc ${CMAKE_CURRENT_SOURCE_DIR}/sample_websocket_server.cc ) endif() +if (NOT BUILD_WEBSOCKET_SERVER_SUPPORT) + list(APPEND EXCLUDE_PATHS + ${CMAKE_CURRENT_SOURCE_DIR}/websocket_client_connection_test.cc + ${CMAKE_CURRENT_SOURCE_DIR}/websocket_connection_test.cc + ${CMAKE_CURRENT_SOURCE_DIR}/websocket_server_listener_test.cc + ) +endif() + collect_sources(SOURCES "${CMAKE_CURRENT_SOURCE_DIR}" "${EXCLUDE_PATHS}") set(PLATFORM_DEPENDENT_SOURCES) @@ -90,3 +97,4 @@ endif() create_test("transport_manager_test" "${SOURCES}" "${LIBRARIES}") file(COPY smartDeviceLink_test.ini DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) +file(COPY "test_certs/" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/test_certs) diff --git a/src/components/transport_manager/test/include/transport_manager/transport_adapter/mock_transport_adapter_controller.h b/src/components/transport_manager/test/include/transport_manager/transport_adapter/mock_transport_adapter_controller.h index 1de5eac702..7ddb84001c 100644 --- a/src/components/transport_manager/test/include/transport_manager/transport_adapter/mock_transport_adapter_controller.h +++ b/src/components/transport_manager/test/include/transport_manager/transport_adapter/mock_transport_adapter_controller.h @@ -45,56 +45,66 @@ using namespace ::transport_manager::transport_adapter; class MockTransportAdapterController : public TransportAdapterController { public: MOCK_METHOD1(AddDevice, DeviceSptr(DeviceSptr device)); - MOCK_METHOD1(SearchDeviceDone, void(DeviceVector device)); + MOCK_METHOD1(SearchDeviceDone, void(const DeviceVector& device)); MOCK_METHOD1(ApplicationListUpdated, - ApplicationListUpdated(const DeviceUID& device_handle)); + void(const transport_manager::DeviceUID& device_handle)); MOCK_METHOD0(FindNewApplicationsRequest, void()); - MOCK_METHOD1(SearchDeviceFailed, void(const SearchDeviceError& error)); - MOCK_CONST_METHOD1(FindDevice, DeviceSptr(const DeviceUID& device_handle)); - MOCK_CONST_METHOD3(FindDevice, - void(ConnectionSPtr connection, - const DeviceUID& device_handle, - const ApplicationHandle& app_handle)); - MOCK_CONST_METHOD2(FindPendingConnection, - ConnectionSPtr(const DeviceUID& device_handle, - const ApplicationHandle& app_handle)); + MOCK_METHOD1(SearchDeviceFailed, + void(const transport_manager::SearchDeviceError& error)); + MOCK_CONST_METHOD1( + FindDevice, + DeviceSptr(const transport_manager::DeviceUID& device_handle)); + MOCK_CONST_METHOD2( + FindPendingConnection, + ConnectionSPtr(const transport_manager::DeviceUID& device_handle, + const transport_manager::ApplicationHandle& app_handle)); + MOCK_METHOD3(ConnectionCreated, + void(ConnectionSPtr connection, + const transport_manager::DeviceUID& device_handle, + const transport_manager::ApplicationHandle& app_handle)); + MOCK_METHOD2(ConnectPending, + void(const transport_manager::DeviceUID& device_handle, + const transport_manager::ApplicationHandle& app_handle)); MOCK_METHOD2(ConnectDone, - void(const DeviceUID& device_handle, - const ApplicationHandle& app_handle)); + void(const transport_manager::DeviceUID& device_handle, + const transport_manager::ApplicationHandle& app_handle)); MOCK_METHOD3(ConnectFailed, - void(const DeviceUID& device_handle, - const ApplicationHandle& app_handle, - const ConnectError& error)); + void(const transport_manager::DeviceUID& device_handle, + const transport_manager::ApplicationHandle& app_handle, + const transport_manager::ConnectError& error)); MOCK_METHOD2(ConnectionFinished, - void(const DeviceUID& device_handle, - const ApplicationHandle& app_handle)); + void(const transport_manager::DeviceUID& device_handle, + const transport_manager::ApplicationHandle& app_handle)); MOCK_METHOD3(ConnectionAborted, - void(const DeviceUID& device_handle, - const ApplicationHandle& app_handle, - const CommunicationError& error)); + void(const transport_manager::DeviceUID& device_handle, + const transport_manager::ApplicationHandle& app_handle, + const transport_manager::CommunicationError& error)); MOCK_METHOD2(DeviceDisconnected, - void(const DeviceUID& device_handle, - const DisconnectDeviceError& error)); + void(const transport_manager::DeviceUID& device_handle, + const transport_manager::DisconnectDeviceError& error)); MOCK_METHOD2(DisconnectDone, - void(const DeviceUID& device_handle, - const ApplicationHandle& app_handle)); + void(const transport_manager::DeviceUID& device_handle, + const transport_manager::ApplicationHandle& app_handle)); MOCK_METHOD3(DataReceiveDone, - void(const DeviceUID& device_handle, - const ApplicationHandle& app_handle, + void(const transport_manager::DeviceUID& device_handle, + const transport_manager::ApplicationHandle& app_handle, ::protocol_handler::RawMessagePtr message)); MOCK_METHOD3(DataReceiveFailed, - void(const DeviceUID& device_handle, - const ApplicationHandle& app_handle, - const DataReceiveError& error)); + void(const transport_manager::DeviceUID& device_handle, + const transport_manager::ApplicationHandle& app_handle, + const transport_manager::DataReceiveError& error)); MOCK_METHOD3(DataSendDone, - void(const DeviceUID& device_handle, - const ApplicationHandle& app_handle, + void(const transport_manager::DeviceUID& device_handle, + const transport_manager::ApplicationHandle& app_handle, ::protocol_handler::RawMessagePtr message)); - MOCK_METHOD3(DataReceiveFailed, - void(const DeviceUID& device_handle, - const ApplicationHandle& app_handle, + + MOCK_METHOD4(DataSendFailed, + void(const transport_manager::DeviceUID& device_handle, + const transport_manager::ApplicationHandle& app_handle, ::protocol_handler::RawMessagePtr message, - const DataSendError& error)); + const transport_manager::DataSendError&)); + MOCK_METHOD1(TransportConfigUpdated, void(const TransportConfig& new_config)); + MOCK_CONST_METHOD0(GetWebEngineDevice, DeviceSptr()); }; } // namespace transport_manager_test diff --git a/src/components/transport_manager/test/include/transport_manager/websocket_server/websocket_sample_client.h b/src/components/transport_manager/test/include/transport_manager/websocket_server/websocket_sample_client.h new file mode 100644 index 0000000000..138502e02c --- /dev/null +++ b/src/components/transport_manager/test/include/transport_manager/websocket_server/websocket_sample_client.h @@ -0,0 +1,117 @@ +/* + * \file websocket_listener.h + * \brief WebSocketListener class header file. + * + * Copyright (c) 2020 + * 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_WEBSOCKET_SERVER_WEBSOCKET_SAMPLE_CLIENT_ +#define SRC_COMPONENTS_TRANSPORT_MANAGER_TEST_INCLUDE_TRANSPORT_MANAGER_WEBSOCKET_SERVER_WEBSOCKET_SAMPLE_CLIENT_ + +#include <boost/asio/bind_executor.hpp> +#include <boost/asio/connect.hpp> +#include <boost/asio/ssl/stream.hpp> +#include <boost/asio/strand.hpp> +#include <boost/asio/thread_pool.hpp> +#include <boost/beast/core.hpp> +#include <boost/beast/websocket.hpp> +#include <boost/beast/websocket/ssl.hpp> +#include <boost/beast/websocket/stream.hpp> +#include <cstdlib> +#include <functional> +#include <iostream> +#include <memory> +#include <string> + +namespace transport_manager { +namespace transport_adapter { + +namespace beast = boost::beast; +namespace http = beast::http; +namespace websocket = beast::websocket; +namespace asio = boost::asio; +namespace ssl = boost::asio::ssl; +using tcp = boost::asio::ip::tcp; + +using WS = websocket::stream<tcp::socket>; +using WSS = websocket::stream<ssl::stream<tcp::socket> >; + +struct SecurityParams { + std::string ca_cert_; + std::string client_cert_; + std::string client_key_; +}; + +template <typename Stream = WS> +class WSSampleClient + : public std::enable_shared_from_this<WSSampleClient<Stream> > { + public: + WSSampleClient(const std::string& host, const std::string& port); + WSSampleClient(const std::string& host, + const std::string& port, + const SecurityParams& params); + ~WSSampleClient() {} + + /** + * @brief Inside a Run(), functions are invoked from the boost (connection, + * handshake, message) which are blocking calls + * @return true if Run() did without errors + **/ + bool Run(); + + void OnRead(beast::error_code ec, std::size_t bytes_transferred); + + bool Connect(tcp::resolver::results_type& results); + + bool Handshake(const std::string& host, const std::string& target); + + void OnHandshakeTimeout(); + + bool IsHandshakeSuccessful() const; + + void Stop(); + + private: + asio::io_context ioc_; + tcp::resolver resolver_; + ssl::context ctx_; + std::unique_ptr<Stream> ws_; + boost::asio::thread_pool io_pool_; + beast::flat_buffer buffer_; + std::string host_; + std::string port_; + std::atomic_bool handshake_successful_; +}; + +} // namespace transport_adapter +} // namespace transport_manager + +#endif // SRC_COMPONENTS_TRANSPORT_MANAGER_TEST_INCLUDE_TRANSPORT_MANAGER_WEBSOCKET_SERVER_WEBSOCKET_SAMPLE_CLIENT_ diff --git a/src/components/transport_manager/test/tcp_client_listener_test.cc b/src/components/transport_manager/test/tcp_client_listener_test.cc index d524b3eb6a..dbd7799b62 100644 --- a/src/components/transport_manager/test/tcp_client_listener_test.cc +++ b/src/components/transport_manager/test/tcp_client_listener_test.cc @@ -42,6 +42,7 @@ #include "transport_manager/tcp/tcp_client_listener.h" #include "transport_manager/transport_adapter/mock_device.h" #include "transport_manager/transport_adapter/mock_transport_adapter.h" +#include "transport_manager/transport_adapter/mock_transport_adapter_controller.h" #include "transport_manager/transport_adapter/transport_adapter_controller.h" #include "utils/test_async_waiter.h" @@ -62,67 +63,6 @@ const long kThreadStartWaitMsec = 10; const uint32_t kConnectionCreatedTimeoutMsec = 200; } // namespace -class MockTransportAdapterController : public TransportAdapterController { - public: - MOCK_METHOD1(AddDevice, DeviceSptr(DeviceSptr device)); - MOCK_METHOD0(AckDevices, void()); - MOCK_METHOD1(SearchDeviceDone, void(const DeviceVector& devices)); - MOCK_METHOD1(SearchDeviceFailed, void(const SearchDeviceError& error)); - MOCK_CONST_METHOD1(FindDevice, DeviceSptr(const DeviceUID& device_handle)); - MOCK_CONST_METHOD2(FindPendingConnection, - ConnectionSPtr(const DeviceUID& device_handle, - const ApplicationHandle& app_handle)); - MOCK_METHOD3(ConnectionCreated, - void(ConnectionSPtr connection, - const DeviceUID& device_handle, - const ApplicationHandle& app_handle)); - MOCK_METHOD2(ConnectPending, - void(const DeviceUID& device_handle, - const ApplicationHandle& app_handle)); - MOCK_METHOD2(ConnectDone, - void(const DeviceUID& device_handle, - const ApplicationHandle& app_handle)); - MOCK_METHOD3(ConnectFailed, - void(const DeviceUID& device_handle, - const ApplicationHandle& app_handle, - const ConnectError& error)); - MOCK_METHOD2(ConnectionFinished, - void(const DeviceUID& device_handle, - const ApplicationHandle& app_handle)); - MOCK_METHOD3(ConnectionAborted, - void(const DeviceUID& device_handle, - const ApplicationHandle& app_handle, - const CommunicationError& error)); - MOCK_METHOD2(DisconnectDone, - void(const DeviceUID& device_handle, - const ApplicationHandle& app_handle)); - MOCK_METHOD3(DataReceiveDone, - void(const DeviceUID& device_handle, - const ApplicationHandle& app_handle, - const ::protocol_handler::RawMessagePtr message)); - MOCK_METHOD3(DataReceiveFailed, - void(const DeviceUID& device_handle, - const ApplicationHandle& app_handle, - const DataReceiveError& error)); - MOCK_METHOD3(DataSendDone, - void(const DeviceUID& device_handle, - const ApplicationHandle& app_handle, - const ::protocol_handler::RawMessagePtr message)); - MOCK_METHOD4(DataSendFailed, - void(const DeviceUID& device_handle, - const ApplicationHandle& app_handle, - const ::protocol_handler::RawMessagePtr message, - const DataSendError& error)); - MOCK_METHOD0(FindNewApplicationsRequest, void()); - MOCK_METHOD1(ApplicationListUpdated, void(const DeviceUID& device_handle)); - MOCK_METHOD2(DeviceDisconnected, - void(const DeviceUID& device_handle, - const DisconnectDeviceError& error)); - MOCK_METHOD1(TransportConfigUpdated, - void(const transport_manager::transport_adapter::TransportConfig& - new_config)); -}; - class MockNetworkInterfaceListener : public NetworkInterfaceListener { public: MOCK_METHOD0(Init, bool()); diff --git a/src/components/transport_manager/test/test_certs/ca-cert.pem b/src/components/transport_manager/test/test_certs/ca-cert.pem new file mode 100644 index 0000000000..e28684043c --- /dev/null +++ b/src/components/transport_manager/test/test_certs/ca-cert.pem @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIIDyzCCArOgAwIBAgIJAM6Tk4KJmUgsMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNV +BAYTAlVBMQ8wDQYDVQQIDAZPZGVzc2ExDzANBgNVBAcMBk9kZXNzYTEPMA0GA1UE +CgwGTHV4b2Z0MQ0wCwYDVQQLDARGVENOMQ8wDQYDVQQDDAZMdXgtQ0ExGjAYBgkq +hkiG9w0BCQEWC2NhQGZ0Y24uY29tMB4XDTIwMDExMDE0MzA1OFoXDTIyMTAwNjE0 +MzA1OFowfDELMAkGA1UEBhMCVUExDzANBgNVBAgMBk9kZXNzYTEPMA0GA1UEBwwG +T2Rlc3NhMQ8wDQYDVQQKDAZMdXhvZnQxDTALBgNVBAsMBEZUQ04xDzANBgNVBAMM +Bkx1eC1DQTEaMBgGCSqGSIb3DQEJARYLY2FAZnRjbi5jb20wggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQDWEz7yGIAEs6w/7CdMjkZ5J0O9IghL0f9wZVFO +ficeREJglClInPrD7BwG7MG1tydPULbrf1rXgxi1NdDY+lsJLFmkxrGVlgJUJl55 +cGpWGliTUepfPz/6CgIabRw2fEMx/eIUlcE+WjY+f4uowVyRYjmNj7IydlQ5UjcL +wWhjg1QMcjgmDzh8Jdx8I+JHYuOP9CtEEfFZy5DjVPFDSlTYhhnNclfw+4NkOYcs +hp+EcMBr6egfxpG2dZbdCJtGw6QqHGG7kqqtLr+9wM5VFhuvebus5waM1G18dIME +SgZmDdgvHO3bbylR+DRmAjJVn4DaDW6uszK9MSPsk53idOUXAgMBAAGjUDBOMB0G +A1UdDgQWBBSSRwc4sGpz6V1kb0H371ZqhDuQDzAfBgNVHSMEGDAWgBSSRwc4sGpz +6V1kb0H371ZqhDuQDzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCb +ZYtM2nrokFL1D34bhozrLu0MxWwDF+gQrUsRr45s63Y5Pv7BVvuS6gF2MubMXskw +mVeCerFw2vQHJKqe7leTy69hwIydPxPQWWno7MamwBDm3VQThr+b18rEpcjbmBMm +p50usYzU9nxEEbIaiSbxfuZNvInLNmvMhKnKO/CIazJnYin9TGdOj9vZnh0UkWF3 +780mMBisycfxG+VwPXQZz5OzWWFB1uMiYrRVdwU6Y5umc2Oce7+ykWy+fXeefMhb +lLJXHZK584qY/krmW0Ec6ZWSbiWcLW5SjGh756n05gBGLDBwijHnfEHNaqn+KlnZ +qqIAImNTA9F+DlMQ7BV3 +-----END CERTIFICATE----- diff --git a/src/components/transport_manager/test/test_certs/client-cert.pem b/src/components/transport_manager/test/test_certs/client-cert.pem new file mode 100644 index 0000000000..47e85876c8 --- /dev/null +++ b/src/components/transport_manager/test/test_certs/client-cert.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDeDCCAmACAQIwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVUExDzANBgNV +BAgMBk9kZXNzYTEPMA0GA1UEBwwGT2Rlc3NhMQ8wDQYDVQQKDAZMdXhvZnQxDTAL +BgNVBAsMBEZUQ04xDzANBgNVBAMMBkx1eC1DQTEaMBgGCSqGSIb3DQEJARYLY2FA +ZnRjbi5jb20wHhcNMjAwMTEwMTQzODQxWhcNMjIxMDA2MTQzODQxWjCBhzELMAkG +A1UEBhMCVUExDTALBgNVBAgMBEt5aXYxDTALBgNVBAcMBEt5aXYxDzANBgNVBAoM +Bkx1eG9mdDEVMBMGA1UECwwMQXBwc0VueXdoZXJlMRIwEAYDVQQDDAlsb2NhbGhv +c3QxHjAcBgkqhkiG9w0BCQEWD2NsaWVudEBmdGNuLmNvbTCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBALV4qI/gRvVip3u5JtT+n+7j7gUsUVW5DtwHruIy +drzNvQbG1Ukd3EXvU69HTG4BtoDRubqlSe/sjvO5Ypmg/UvpzV36IbjrA46s98uR +T6fKpJU/Zl2zwAHH++iWpFo3mqIDmu7i0KVBieKaGpz+Ft0zh7wHAztS7b6Mjns4 +QynpjDO+iaLIaHqBjc1hLn8dIBXNolOtLu8F8CL7RLRpWP2I2Fk2k0+Q5YKajbil +gptA53Uu55wCBVLTOfUYzTarGwS00+7txLY06g2x20FHD8UQxfCK7kSAeZwSNkbt +SUhXc9OWUvT1uggb2/wBHJN3fwj7y6pvzUJy7p09212hw7UCAwEAATANBgkqhkiG +9w0BAQsFAAOCAQEAjfASZwfJMTPKk45XVbvuNqdlbiI20SNV7pQQ/FqTBKbFmh4g +ndNCvECmBEUH5YdZegiGaONQlsQujmtIkguu3HnA0+2pO2SncmK6D1DLzJv1IFDC +25tTStA6806hWcTK31sxEbi5/aPdy7FMmsRfyhRr/yew0TqlWCVOfJRwgDSc3NKH +/AXgDBrqHzSBegnWe9v3xL8NxehFp41dJG2fyUab03cHzmNtR9v7/NrBglSdK9VS +AU4BCmjmvYlbvmvhZai23y+uLqzlWZ9OtK3qhEWkg6QHor11iBvxBQFeYKp1ZjMl +sQuTxyBLmXOZ/u3hkqLcKvasx9W4DmmPjG2T8Q== +-----END CERTIFICATE----- diff --git a/src/components/transport_manager/test/test_certs/client-key.pem b/src/components/transport_manager/test/test_certs/client-key.pem new file mode 100644 index 0000000000..b286854bd5 --- /dev/null +++ b/src/components/transport_manager/test/test_certs/client-key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC1eKiP4Eb1Yqd7 +uSbU/p/u4+4FLFFVuQ7cB67iMna8zb0GxtVJHdxF71OvR0xuAbaA0bm6pUnv7I7z +uWKZoP1L6c1d+iG46wOOrPfLkU+nyqSVP2Zds8ABx/volqRaN5qiA5ru4tClQYni +mhqc/hbdM4e8BwM7Uu2+jI57OEMp6YwzvomiyGh6gY3NYS5/HSAVzaJTrS7vBfAi ++0S0aVj9iNhZNpNPkOWCmo24pYKbQOd1LuecAgVS0zn1GM02qxsEtNPu7cS2NOoN +sdtBRw/FEMXwiu5EgHmcEjZG7UlIV3PTllL09boIG9v8ARyTd38I+8uqb81Ccu6d +PdtdocO1AgMBAAECggEALiPCf+pfQE7YFJ4L4IXo9h1fzFLrTydhPtJ5RavdAr4w +vINbgV6lPebO2TcAmMu4smIgnfMerHDyG8fb6QHExUNp4uYRIuomGmWiD1Ef9qKQ +XB4lkdd9Dzbgts9udD3FBEJ0Zx6mPA5A16uk7puwBofukAEccj3wks08ANpaQVJh +LoYQdVY9Q2QHUucED9uO2hOv66bDM5aunk0x4Q/b0gIOAnJyqk0xXnmnU76FbkgI +bU3YQ73ZEJKTFLF8B2JaE9REASmHyLQChYqGA9SPdObVhgxJ8gz5AWp5yHgMmYjf +Dp66tCsyGnmfgTlG4Wi13HInxLpRWDWT2JWIy9cUlQKBgQDshRq/rTj4tuVW09hX +bWlAEV2IDn7JOWEZqPagnxew0atqow44VORW+OoUZ5760aOVjvmXeyGXpWCJk5ch +qsFRL9rLRzIy9oC6C/chkJQAprcaOBOSXvj3HnFHVChjbIBENf4dvtsYZHOW42h2 +br0kszv1bbBzVIyTTY0OvGsBUwKBgQDEauIuC6RiDN+qFAf4/lHkUx7wG6DPhvDm +EhpkiVEHYVjH5vm132/cc12y9CvsFS4MJPK4KQR9P+HFhEu/uH3uLa1vvKY+69iU +dZ1bfe4UrEm/bwBepDSbqlQk58WC6NyJ6fwMq0BYJVvx593znEbU2wLVUTm8H7l2 +yzyxQwXd1wKBgBkYvo/cJ5FshsVB0VDlkSd1MEGBmD5t0jnQzeqZNwBSHyg/iQC9 +MUVxQBVOMXZXzE3QT/ec3yGiMK4odP7jiYO92i97rH3v3hTftCdhmfK/veoQTTNY +1H4UQtzYtzhliO6z8/TgDYt3DTTTiIAYnAVK52/RZcm3DPuMXQ1VPN11AoGBAIN5 +eASSTmpDa8OQvPVyZqaK7P6Tv8Sp8r5OB9ScBd0G0EKe3S9cbKgHoQSUZIIWe0gt +wzp6WkLsa9emgn3GpKS1do6AnFcpz0MwpzACz0aPPJ4jUwAGsiAwlzpM2eySqmy2 +brycNOnLuAvoxKy4QsFgCDl5sUe3hJF74RhWYKrpAoGAUrJMF5IaAAsv367nD8CX +CfDsDlez54H/sZD7iKj8LhyuspAQBU3vQ3xKfaKRi4px8bgnty3stOMZxEC7I6BC +jHH1hQK8l9nqRhOjPvoViUM6aOpabCkVsOZjWKD+OCz3X9+MmlGkMuNd22GRdwlb +Emb3yEoFr5vDbez0IQNxnLs= +-----END PRIVATE KEY----- diff --git a/src/components/transport_manager/test/test_certs/invalid_cert.pem b/src/components/transport_manager/test/test_certs/invalid_cert.pem new file mode 100644 index 0000000000..70d124c2b7 --- /dev/null +++ b/src/components/transport_manager/test/test_certs/invalid_cert.pem @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDlDCCAnwCCQDIe7AwFpWRlDANBgkqhkiG9w0BAQUFADCBizELMAkGA1UEBhMC +UlUxEzARBgNVBAgMClNvbWUtU3RhdGUxGTAXBgNVBAcMEFNhaW50LVBldGVyc2J1 +cmcxDzANBgNVBAoMBkx1eG9mdDEXMBUGA1UEAwwORG1pdHJ5IENobWVyZXYxIjAg +BgkqhkiG9w0BCQEWE2RjaG1lcmV2QGx1eG9mdC5jb20wHhcNMTQwMjI1MDkxODUz +WhcNMTYxMTIyMDkxODUzWjCBizELMAkGA1UEBhMCUlUxEzARBgNVBAgMClNvbWUt +U3RhdGUxGTAXBgNVBAcMEFNhaW50LVBldGVyc2J1cmcxDzANBgNVBAoMBkx1eG9m +dDEXMBUGA1UEAwwORG1pdHJ5IENobWVyZXYxIjAgBgkqhkiG9w0BCQEWE2RjaG1l +cmV2QGx1eG9mdC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCn +Mu9wNmXKaKOnefSv5iT8G2ESLjq+eYlxys/XAnDfkmnlgGYAcPno+XMhRj/lNV/c +3A0L/R4631GFJA8vaM8m9Bn47FrPP4AXIHEQh9acA4qXiLfhhA8+9PPt4xVkjQYj +bmexBLqDvRgT3MJwwFecUn/UABBlVZRCspn+6DkjiodbgmBOqyi1p0ng8BFeUbEH ++fLQVILCX3pjnMiP2bBtvq/7njgZT2luVtAAcOdRwRTuZT0YbgaXrHYsOa6VYDl1 +I0uOcdD8qENBXtBnykEqH+jZtKu6Rej1DsGOYWqz3AAaGiR1GJauNBxh+4v+i/eB +0aCIA8T8qUqyuVVg48S/AgMBAAEwDQYJKoZIhvcNAQEFBQADggEBACliraOJYijK +yS+Sl6S6pFRqdF/evPdYF6zDJlM3P+/9qHoEy751vbBTzRkVbC/azyiZLwQMuyED +6oCpkI7MqnrRip1ZelGx9K7ChaHOpX/QRN+3eqiDhzvMTGd2nPJf9np4xi8SJpGP +UUROYI5fToIY5MaOKuOIR2a6c8xIuLWMG1XKJxXrRetLJZDgBqQPkuqaZIjYCY+q +HQRjNUFNX4Mc453tKd90gFLGI3fxs1fJDIRSGfKJsj0qc+amSz4Sgiz4QUBcUQKd +hJxUpStYhliZGZchEopLsShtIGfKKFaaPCIOTpVAwSr1oIDm9lpkdxeuQfedKT5f +ZZmkez2pAF8= +-----END CERTIFICATE----- diff --git a/src/components/transport_manager/test/test_certs/invalid_key.pem b/src/components/transport_manager/test/test_certs/invalid_key.pem new file mode 100644 index 0000000000..d2aacc1638 --- /dev/null +++ b/src/components/transport_manager/test/test_certs/invalid_key.pem @@ -0,0 +1,28 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEApzLvcDZlymijp3n0r+Yk/BthEi46vnmJccrP1wJw35Jp5YBm +AHD56PlzIUY/5TVf3NwNC/0eOt9RhSQPL2jPJvQZ+Oxazz+AFyBxEIfWnAOKl4i3 +4YQPPvTz7eMVZI0GI25nsQS6g70YE9zCcMBXnFJ/1AAQZVWUQrKZ/ug5I4qHW4Jg +TqsotadJ4PARXlGxB/ny0FSCwl96Y5zIj9mwbb6v+544GU9pblbQAHDnUcEU7mU9 +GG4Gl6x2LDmulWA5dSNLjnHQ/KhDQV7QZ8pBKh/o2bSrukXo9Q7BjmFqs9wAGhok +dRiWrjQcYfuL/ov3gdGgiAPE/KlKsrlVYOPEvwIDAQABAoIBAQCAjkNXzhuZ87bR +UI34qUYKqaqLZgw45A3v9naz5OaQoGzXz0+eSz98CECjdvYt8EoS8Qb/DtGthoOR +kVYzp6yPUOSfZmu0Kij8ny8P/MHgF0D6nl50ASwPxhu/7vhF5cCwgXUswGwAWuYm +b3j5ZIp4YV5zzNDOeWyTk+uf+UHltqFD7Ae4M9z58r17/OWhva5mtusTuuEYjzC6 +AE/fsOC0gLNSM4+SfclfCkHpH+GikzNMSQ2H0hlXllPmR73BoC6N6aoY5hQWBLV7 +LxtYbJqx7TAqRyypBQekjJe36roRetXtzy3i6V/y69045td5kk70cVjmFhl79475 +82rnRLHBAoGBANgq4axr5OotTUmPkGd0afoaWSRPJfiTTdNeMkqTzM6zIcVLSKhB +78ERwdDD9FOu+Bgivg4DlpmH7ArWn8QNDtdkhmPfKYfTqX6qH7AK4cybvYICMlct +EdW4TvKm/ZB3mrVOP9JVPjdyFMp+Je6N+qp1w+ui9mxX8pWnrC/+DfTvAoGBAMYC +GFjnw/O9hjF2Mb00qUarmM+reJZMXv/pVik+cm0eAiYvgGvKbAYkIXwdb7rLBw9k +baJmxP0PrAoXy5TpPdfROqPwrRCyReKymKkEZeTpONgD0s8MbX167ovZu1OQVKQo +IyJeUzWa0kpglnbL2lLVu49x8jWHDJdYhmkDNE0xAoGAR4ux07qGMoe5693rYoJi +TRgJZv4XSDWg7ZNgu9Q9VjBtvfoT2zSvoMw6xNkGdegUTxC4rLS9VKVrF48/o8ja +n6my3T1QZpdEoxq1kDOZ1nm5eF03wii1nXH6F0/z3qvndZingPsbs4g7n2WvMkyl +qWN+6++s9eEJ9kRftia1AdsCgYAUnU05nE97RcT9y0dcYmopMF5FaJ2yUBsn23wb +6SNylsg0f4eIMVfTv9k4mbvzH4YJpTQAz2A81G/d0SJhy3Kj0GWhgcIS1eyOsHdS +SWHuVhWT77n30lxnzu+c4bst9P3K5V7bCiTxlL/F/I5NqeV98ECJq5xC1F+MNiww +LKQ6UQKBgC2zL59Vf8QnRkRN0gOUfs3ejrLcxFRzTXvcKqcHtbaqzCs3qSNC6UvV +wjBazEwQCo1wnM81X8uT5fLhnjXebWtnYexQo5P38PiaqTQDgrbAdhP5P8NwRCXM +G3SNEz0XeL27jmWjf0VJdwD0LuHXYhcwAWq4alhJ024rjgVHwOze +-----END RSA PRIVATE KEY----- + diff --git a/src/components/transport_manager/test/test_certs/server-cert.pem b/src/components/transport_manager/test/test_certs/server-cert.pem new file mode 100644 index 0000000000..d2b1cf0ec8 --- /dev/null +++ b/src/components/transport_manager/test/test_certs/server-cert.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDdDCCAlwCAQEwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVUExDzANBgNV +BAgMBk9kZXNzYTEPMA0GA1UEBwwGT2Rlc3NhMQ8wDQYDVQQKDAZMdXhvZnQxDTAL +BgNVBAsMBEZUQ04xDzANBgNVBAMMBkx1eC1DQTEaMBgGCSqGSIb3DQEJARYLY2FA +ZnRjbi5jb20wHhcNMjAwMTEwMTQzMzM2WhcNMjIxMDA2MTQzMzM2WjCBgzELMAkG +A1UEBhMCVUExDzANBgNVBAgMBk9kZXNzYTEPMA0GA1UEBwwGT2Rlc3NhMQ8wDQYD +VQQKDAZMdXhvZnQxDTALBgNVBAsMBEZUQ04xEjAQBgNVBAMMCWxvY2FsaG9zdDEe +MBwGCSqGSIb3DQEJARYPc2VydmVyQGZ0Y24uY29tMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEApnW3zyWadWiD1eMPszMM0Hyzm3Zd6mr21LMtQMwqw10p +PxMpZLo0rgCkJevVRxO/y4J+TcE9LtralzzsWK+DD/i2Gf8CfAOapcDETJ8b68jM +rUMwmN3tdiEBqimHBKEKIgDOiJt2Y08Jw2AWR41LuyjtD+IWSWo1kqJF3rxpsfz0 +SfTQWkvHVXg1c0qbfsp2i82Nvt5HzvDdk0jzX+GNHSmkUECcE0GIhK8GHxAFYugk +siRU/tgY/wzP9iUkj7UbPWb5k+d8Z3sqUFpVAa4dXhIzx5L0l5peXvhunYqr7Vk+ +cfBAHIQZKJa7coBBahA7gjBylz+BbIOadGYYoYZVBQIDAQABMA0GCSqGSIb3DQEB +CwUAA4IBAQDNUMIv6X9scvVN8II/PbvvQzWAxi0qzDejnEF579PA9MCNt6JY20lj +JTscUN5lWNuLGJtkUuscBMBYe21ePtGeS855Q6csoUe6m0fnY+ybKVYIKk+SL5Hx +1vurBIsHOyX6097e8VIzWyxcWW1074oTYLpYfEWr0vECrGodoXGtPdEeyB0+QdbI +H0Pcngqu5yLoWxoWwuAj94YG7eX3sJv6PXOW71i4yMmT8ToYNXFwqTK/xq/pl6H2 +KH150zDNOaE2Z5+u21Elau+3qWPWQ6C9KpxhmJ/iDftRe+hgMISSygYK0nwk0zk4 +rmNODAeuTvsrh9bNsYQfjERsh0VYaG24 +-----END CERTIFICATE----- diff --git a/src/components/transport_manager/test/test_certs/server-key.pem b/src/components/transport_manager/test/test_certs/server-key.pem new file mode 100644 index 0000000000..6ff13eaf28 --- /dev/null +++ b/src/components/transport_manager/test/test_certs/server-key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCmdbfPJZp1aIPV +4w+zMwzQfLObdl3qavbUsy1AzCrDXSk/EylkujSuAKQl69VHE7/Lgn5NwT0u2tqX +POxYr4MP+LYZ/wJ8A5qlwMRMnxvryMytQzCY3e12IQGqKYcEoQoiAM6Im3ZjTwnD +YBZHjUu7KO0P4hZJajWSokXevGmx/PRJ9NBaS8dVeDVzSpt+ynaLzY2+3kfO8N2T +SPNf4Y0dKaRQQJwTQYiErwYfEAVi6CSyJFT+2Bj/DM/2JSSPtRs9ZvmT53xneypQ +WlUBrh1eEjPHkvSXml5e+G6diqvtWT5x8EAchBkolrtygEFqEDuCMHKXP4Fsg5p0 +ZhihhlUFAgMBAAECggEBAJ35UfuxACk0iwlNd/TlGeAyuHFtoCt8X8v0T5oTKPJH +U4GcucfyP1RzH1Utvza5M2f768n2/g2dfZ3SH6r6xjM+IfQB42W2NengS2s8BM97 +vWMhRNmOpHFbWa0XxB9MhcVHZrqWb4BH3kggxIQbQCfa60ALnIMH3NfQUObVgKl8 +khPU3fWx5zH2fbLNiLbImr5U5ViNoOKw592cmAEocIgIE33SDf2LHuuByikb38/r +98bAp6IdNhRb7qY/Jllq3fu06fLubGQusVT01vu3sp//G2gCzFwp0O/qX2M1JBPS +JYKKGXrNxeaQNzzD6cEcSkADpZvIG/CmmnR9u/FjIXECgYEA2palcAJKXPWsnwLb +QtDd7MQ6sG3qvkCaiiDsn5BAGkxn1rZ3NRGMxBx4Fb9oiIl6SBpQ/YdMkd7tMkLX +MEmbPyOcHPnsxcCqeFGsiTejpx1OyPSUAtxtpxGc/D63wCPhHGO3xNdQ2FiYb5K3 +jG8cslE39gH+/9XSN8kS2gZZ4d8CgYEAwvMUbXSBYx01I+jjqsjSI9afk3w9VwKM +vuW9MgUBYLP5ryadNqt2cuHydT5KJiZ7YUQSLNztZ++g5WR7yamWpLzoGFmrSyWb +304xA9jdLRJHMsBM0V32abdfTV5+EW+24309UmcaUtsQoUTxXOSqwDaHF4Mq1zLg +jwb2phYlzZsCgYEAtz5q2gdRh7R8TaD7ZnvqTz4BZT3/+BX4d6s6MlmfI2zB8AFu +1ZIsy4qCMNkRLMTzOda15pOx4OddOTFHbDeIadnUWYY6s1zci5kMZsu56bJsBZLj +MbLQSao+TEfXir+JS19dAyrtnzBGOeJo9NWA3QuxOg5aUuZRIGrz3spMN0kCgYB3 +ZLnpAwZO9k9aS8JLESypqEMY52kFxdj+/OKvJKOgXvkWzPZRyhcD6t878McmsEC1 +5COheDipg/etJaouan+JKuyWJSykHEdnLpMUQRfMB7q1GVKykvJb8mMaljltYlbG +4ifRNLXJcsKvkfKkKqNsjriTrNBq9YzT67bZJw1F6wKBgGlEd9O+qWY4dSPKN60N +khG1Splz+eBbbsqcISeFGZepEc4HaEIcYgIHTh8nw5ycYxh8A1UBdaBZmU9UHdfl +j9M2u0htKZ27ntVVNZJCLeSgufaPUDIfvnK4o5q630NGhKJmVcYD3WeggIrPfca3 +fP8WaHq9fx5k4YZokD2VHOJb +-----END PRIVATE KEY----- diff --git a/src/components/transport_manager/test/transport_manager_default_test.cc b/src/components/transport_manager/test/transport_manager_default_test.cc index 8a7f95c74b..4b3bd6929f 100644 --- a/src/components/transport_manager/test/transport_manager_default_test.cc +++ b/src/components/transport_manager/test/transport_manager_default_test.cc @@ -64,6 +64,12 @@ const std::string kTransportManager = "TransportManager"; const std::string kTcpAdapter = "TcpAdapter"; const std::string kBluetoothAdapter = "BluetoothAdapter"; const std::string kDevices = "devices"; +const uint16_t kPort = 12345u; +const std::string kAddress = "127.0.0.1"; +const std::string kServerCertPath = "server_certificate.crt"; +const std::string kServerCACertPath = "ca-certificate.crt"; +const std::string kWSServerKeyPathKey = "WSServerKeyPath"; +const std::string kWSServerCACertPath = "WSServerCACertificatePath"; std::vector<uint8_t> kBTUUID = {0x93, 0x6D, 0xA0, @@ -173,7 +179,7 @@ void TestTransportManagerDefault::ExpectationsSettings_TM( // Arrange TM Settings expectations Json::Value tcp_device; tcp_device[kDeviceName] = unique_tcp_dev_name_; - tcp_device[kDeviceAddress] = "127.0.0.1"; + tcp_device[kDeviceAddress] = kAddress; tcp_device[kDeviceApplications][0][kApplicationPort] = "1"; Json::Value bluetooth_device; @@ -185,6 +191,16 @@ void TestTransportManagerDefault::ExpectationsSettings_TM( custom_dictionary_[kTransportManager][kTcpAdapter][kDevices][0] = tcp_device; custom_dictionary_[kTransportManager][kBluetoothAdapter][kDevices][0] = bluetooth_device; + ON_CALL(transport_manager_settings_, websocket_server_port()) + .WillByDefault(Return(kPort)); + ON_CALL(transport_manager_settings_, websocket_server_address()) + .WillByDefault(ReturnRef(kAddress)); + ON_CALL(transport_manager_settings_, ws_server_cert_path()) + .WillByDefault(ReturnRef(kServerCertPath)); + ON_CALL(transport_manager_settings_, ws_server_key_path()) + .WillByDefault(ReturnRef(kWSServerKeyPathKey)); + ON_CALL(transport_manager_settings_, ws_server_ca_cert_path()) + .WillByDefault(ReturnRef(kWSServerCACertPath)); ON_CALL(mock_last_state_, get_dictionary()) .WillByDefault(ReturnRef(custom_dictionary_)); diff --git a/src/components/transport_manager/test/websocket_client_connection_test.cc b/src/components/transport_manager/test/websocket_client_connection_test.cc new file mode 100644 index 0000000000..9b0e94f8c3 --- /dev/null +++ b/src/components/transport_manager/test/websocket_client_connection_test.cc @@ -0,0 +1,477 @@ +/* + * Copyright (c) 2020, 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/websocket_client_connection.h" +#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/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::Return; +using namespace ::transport_manager; +using namespace ::transport_manager::transport_adapter; +namespace websocket = sample::websocket; + +class WebsocketConnectionTest : public ::testing::Test { + public: + struct WebsocketClient { + std::shared_ptr<CloudWebsocketTransportAdapter> adapter; + std::shared_ptr<WebsocketClientConnection> connection; + }; + + void InitWebsocketClient( + const transport_manager::transport_adapter::CloudAppProperties& + properties, + WebsocketClient& client_out) { + uniq_id = dev_id = properties.endpoint; + + client_out = + WebsocketClient{std::make_shared<CloudWebsocketTransportAdapter>( + last_state_, transport_manager_settings), + nullptr}; + client_out.adapter->SetAppCloudTransportConfig(uniq_id, properties); + + TransportAdapterImpl* ta_cloud = + dynamic_cast<TransportAdapterImpl*>(client_out.adapter.get()); + ta_cloud->CreateDevice(uniq_id); + + auto connection = client_out.adapter->FindPendingConnection(dev_id, 0); + + ASSERT_NE(connection, nullptr); + + client_out.connection = + std::dynamic_pointer_cast<WebsocketClientConnection>(connection); + + ASSERT_NE(client_out.connection.use_count(), 0); + } + + void StartWSServer(std::string path) { + ws_session = std::make_shared<websocket::WSSession>(kHost, kPort); + ws_session->AddRoute(path); + ws_session->Run(); + } + + void StartWSSServer(std::string path) { + wss_session = std::make_shared<websocket::WSSSession>( + kHost, kPort, kCertificate, kPrivateKey); + wss_session->AddRoute(path); + wss_session->Run(); + } + + protected: + WebsocketConnectionTest() + : last_state_("app_storage_folder", "app_info_storage") {} + + ~WebsocketConnectionTest() {} + + void SetUp() OVERRIDE { + 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; + std::shared_ptr<websocket::WSSession> ws_session; + std::shared_ptr<websocket::WSSSession> wss_session; + 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 " + "CERTIFICATE-----\nMIIDqTCCApECCQC/" + "5LlQ+" + "GLgqTANBgkqhkiG9w0BAQsFADAXMRUwEwYDVQQDDAwx\nMjcuMC4wLjEgQ0EwIBcNMTkwNTA" + "2MTkzMjM2WhgPMjExOTA0MTIxOTMyMzZaMBQx\nEjAQBgNVBAMMCTEyNy4wLjAuMTCCAiIwD" + "QYJKoZIhvcNAQEBBQADggIPADCCAgoC\nggIBALbeIoAHFaFeNqbMnhtAO5QTgRd0X9qxpB0" + "d4M0dEeog2l/+inNA/eu+CCvm\nJj2I8+6MWH2TUrl/" + "2xfhHjzsMrtISCdpNcjaNzCnvi8ZcspFi3evknvs3+uo2/" + "wn\nyNZ04jp0oQ0k1cZ6WBpLTYV7WgmueemiWEiAfw+YE+wtRg+" + "0H7rksrbpeNPnxQHX\nBDDkWqwvfkD15Sd0XFQkW27K72/" + "et2uKuAcJHCIUbsA4iZyJw4Uu4eusy7W5kddX\nThE7Y1WqTXyA4j/" + "ZCYigXmsHbWrikPLVXbORhiMF4/60u8RDs0jI8xtgRQhLR9Vp\n3xys7/" + "5tHZX00s6x6OVy8aMSZIIVS0eWoVN8bGd9B4+fDMOcNT0YODeQA10w/" + "85P\nEiZDQ8AxneQkh8H3qjD2+" + "G9oHZaTk0zHTyaKRg3xGP3N9C96onaJX4Rm6nNvApO8\nU7lQ+xHkLwjKCQke39W+" + "r3FHwwQvUDeoJBXf6iVkIMFoUPAHNZqKf9Db6KKIEp8i\nDgIhbBxiB4MSn36Zly4SOMojyM" + "ZFri+" + "HzMbuHFlm8e8QRWGmM4UM2rHIpl4OJolg\nejesDqO8YZR5mZXV0FJRiPgLo2Q4OTtb3tEHJ" + "ZLmlT+" + "f42bge4ZCZmGPrkNfr68Y\nDEnJ6z4ksOVkMefOp2SNYLYYGPYyiKwa9qVkH9Obkect1omNA" + "gMBAAEwDQYJKoZI\nhvcNAQELBQADggEBABFJQtOTftrzbLBA5Vm6aPkbUyxhcaOpz+d+" + "Ljd6pIs4H+" + "Eb\nXkoHhmay4stZkDc2HVSKESZloI3Ylup8z3aRJjfOexJqHlYzk2vraYryWm8odgID\n5V" + "B0Zle8ofpHTJw1LCOXHkzKt1G6vdQpuq/" + "4OKpmggaIqqEC1bfOYZt5t6vIx3OF\nxjPz91NTR9gZ4lKrho1+sfS+" + "jbSjKkqVAhE4yTpKLPHRshQnBFEhvATXNvdZGftF\n+tXxqKsBZ9aX0/" + "YmPFIXFcjdjSSiuq1F1DjiqIZo88qfa9jlTg6VJdayyQ/" + "cu41C\nBucY8YTF0Ui8ccIS55h2UTzPy5/4PbrwI4P+Zgo=\n-----END " + "CERTIFICATE-----\n"; + // Sample private key + std::string kPrivateKey = + "-----BEGIN RSA PRIVATE " + "KEY-----\nMIIJKAIBAAKCAgEAtt4igAcVoV42psyeG0A7lBOBF3Rf2rGkHR3gzR0R6iDaX/" + "6K\nc0D9674IK+YmPYjz7oxYfZNSuX/" + "bF+EePOwyu0hIJ2k1yNo3MKe+LxlyykWLd6+S\ne+zf66jb/" + "CfI1nTiOnShDSTVxnpYGktNhXtaCa556aJYSIB/" + "D5gT7C1GD7QfuuSy\ntul40+fFAdcEMORarC9+" + "QPXlJ3RcVCRbbsrvb963a4q4BwkcIhRuwDiJnInDhS7h\n66zLtbmR11dOETtjVapNfIDiP9" + "kJiKBeawdtauKQ8tVds5GGIwXj/rS7xEOzSMjz\nG2BFCEtH1WnfHKzv/" + "m0dlfTSzrHo5XLxoxJkghVLR5ahU3xsZ30Hj58Mw5w1PRg4\nN5ADXTD/" + "zk8SJkNDwDGd5CSHwfeqMPb4b2gdlpOTTMdPJopGDfEY/" + "c30L3qidolf\nhGbqc28Ck7xTuVD7EeQvCMoJCR7f1b6vcUfDBC9QN6gkFd/" + "qJWQgwWhQ8Ac1mop/" + "\n0NvooogSnyIOAiFsHGIHgxKffpmXLhI4yiPIxkWuL4fMxu4cWWbx7xBFYaYzhQza\nscim" + "Xg4miWB6N6wOo7xhlHmZldXQUlGI+AujZDg5O1ve0QclkuaVP5/" + "jZuB7hkJm\nYY+uQ1+" + "vrxgMScnrPiSw5WQx586nZI1gthgY9jKIrBr2pWQf05uR5y3WiY0CAwEA\nAQKCAgBGSDGyS" + "wbBMliG2vWZO6KqUqS2wv9kKgoNNsKDkryj42SKqGXFziDJTgwN\n8zKXS9+Uu1P3T3vn13/" + "5OYhJme4VlL5Gh2UogNXdWVr69yjrHLdxlIUUJAIbrJZ/" + "\n3zqNUfbwyIptZs7SrYrW8EInHzWHqwsoBEEx/" + "FDZSXW+u9fFiVD4n5UgP7M0nktV\nXbI6qElBDC/V/" + "6vG8i3aGO8bMdu8fzi3mGUKLzIk1v2J2zDofPosYcxqq8rPWTb4\nMJHMhaqz7fRB+" + "bb7GwtS+2/Oathe0B0td1u//Bo1s7ng1s2jrPFm8/" + "SbfPCLM4O0\nPjCF8OF8Q6uvSp0K283LAdZk+liuDztc/" + "Ed8mcnCZQhBp86mJQi0Jj3Mje7prOAY\nXojMroYj7r2igCJvcwGb1y6zZWSj3jXuHze3bLy" + "fv7pCD+hkiZR7mZhQpOhQRZaU\ntdFGc+" + "DuykxKPqVjAPy7iVQXYnMjpo36SGIWoZLuepQto3GvU6czyOwhK0/" + "Mwbwd\nZpYpLH3L9IetY8GcPefmUR5wQUlUTrpxgGElIzkkWW8zmUWBXwrGbAtN1HJWpJdN" + "\neVshKod2fo03IQMPywSdENCJVeSrgRMuwPyFaOM+" + "CVrBJwD66K9YWn4cVRUIZsTq\nAXhQ8DzF+WCOZshhMUbCJX+KpcOFI8nxOrPp+" + "J1s1YpLLvdmcQKCAQEA7bwvOiCD\nHvaqpYg1jJak6l/" + "iY3QIOOpFyjfYrQXS0BNRmmxK8Lzevi/" + "NPTqu146QKDaIGvzu\n+" + "bXnuV1LmZqnOm5J6Kdx0Mk4Fb88akRtS9gOdzU7WWMYIGkeF1oih0ZrhHuIey6e\nCeLSmJh" + "UDaTIWcCirALRHcLWUS6BTGGuE+up5QG7djIW/" + "LD17lhGE6fXPlbYXb7l\nPbYwL1Yr0urIp9Un+zrExw+77TTGK7T37T1ZJv46qnro0/" + "HK8XxZ+" + "JNc18F763O4\nPmwu8KWrx4qnyPYAuB1rbVntK6UxYks9jvk93XecCUP6HHp0I0tV/" + "kBNd2+" + "18zux\n033xFEVKrUksmwKCAQEAxOrPGyBVFp29V4gQsMQdmafkDAgCLREgjQTOamFsYL5+" + "\nZWq+6VhT/" + "B650sIoRdX0N8timnSLBfjeqaBG5VVhhyKZhP5hulfRjALhdbGBOYvf\n0gLSffImwWdYQfx" + "jtCSO+" + "XCLVdAJGOOWeBVVKzH18ZvCFQwUr3Yrp7KmKWKAqgf7\n6rYovWh8T5LLYS0NzXCEwf9nJ0N" + "JMOy7I9B7EtF8Cs6tK3aaHVqDz7zufwosS7gI\n3aI51Qh4a5D1p95+" + "YU09beWjWGYnPiCKk4D47zaeOe7OQINaWugExlelHtJh9unO\nnhOFXhziav2Kxq1CICAuXl" + "Vx3A+gn/cU3niNHz2A9wKCAQEAws+aw78ws4bef5cG\nipZHveek1GqY8krHtdXdsKs8/" + "VVXYXusTWn3/VGelbYo4GrqpolJLxRloCr4IGXb\nNZwNvUvzNLtCAR1i4C89irdX+Paro/" + "PzFmSluKlrByfNc5y5Lm8sgATLbL56ZKEu\n/58wrpu0sc/" + "9HK40gYHiYn0I8ToElqy8uTaCr78zSIT9p826DFOOKgPsRo2tHp02\nfDf5Bc8eXDjkV1sFX" + "HQKkHZTVA0ZqWJbIKhncoaJDyofcBsR0ZuzuFWzfTOZo4mf\nInz00TEFldpF1e4C8+" + "kCdtHBOA/2Ki2Bp/YUVpHh6aoqZZa75Euehhs8tVpW242M\njEOSUQKCAQAM64sjMH/kt/" + "zQXXEa6AM5LbbcwznBUzpbhlE00aeWwWjxpotYLB92\nj12J4otZ6avYbVPO5o6omaeiYY3F" + "RlDb2P1RqI8o9tIc6aN5YWglKnRJBz5gXR8F\n2Y4E5lZ0X2GyJBxASSIPq/" + "8Xae7ooqKMc7fMQbqpuIssuaAFXx0qCtQQllsd8lkV\nr4AApEAflp5fTC6seNG4kA/" + "HTcqFdZE59E2QaHu8KVA0tSTA2R4G6dBLGnXI8IFW\nLXCwzvxjzfmV2FdbWXiBrwjonLG4o" + "FDJZE3MFdI73LVTfjSrTQp4dObFoGpDvols\nk64jUwLfsLzaG6kY0z2qwT9xSV+" + "ZCSQJAoIBADsSBeyELc5KnmOhT0Xue2o0bkAS\n8KcGWdAoQlaLCIs3vtdlC7DXDXO1l8FkT" + "Qrd+GwP3eWDq6ymtO4vkJ3m4cZ1PEsv\n6Qet7qtkVhUfa+" + "FYKtDzSMRuSaFPW3Ro1PesFa1H3y+sw5HL36vhRaSK+T4Wy5k2\nf7EwNZIf/" + "ZZeA+" + "sEtqj33obOBIiuU9htAjN81Kz4a0Xlh6jc4q6iwYKL8nZ76JYV\n8hXWIz6OXxLXE158+" + "QtJSZaRCvdr6L5UtWfMPKSMqgfhXfTYViPDleQCkJ132mIS\nH28UoET0Y5wI8M6pMkWpSqW" + "WcKPFGwyLInvHdxgnTAsutowkldA7qFwoRz4=\n-----END RSA PRIVATE KEY-----\n"; + // Sample CA certificate(used to sign the server certificate) + std::string kCACertificate = + "-----BEGIN " + "CERTIFICATE-----" + "\nMIIDBjCCAe6gAwIBAgIJAPyCrKRDl3SWMA0GCSqGSIb3DQEBCwUAMBcxFTATBgNV\nBAMM" + "DDEyNy4wLjAuMSBDQTAgFw0xOTA1MDYxOTMyMzZaGA8yMTE5MDQxMjE5MzIz\nNlowFzEVMB" + "MGA1UEAwwMMTI3LjAuMC4xIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC\nAQ8AMIIBCgKCAQEA" + "xrNHoY5+" + "JCgnyvEdG2dvvOaZ3sg6uhuF5Ssb5snyo9ixrxO5\nZkDGWwUqFO9PiYuO5ovNeq9LZtPG6s" + "K8zsCS062ChZX/7tZHndf4MKCUDzv/" + "LPHe\nnROoPi9n2FAiOPctY5hpgpIJDPI5Ofx0el2KPeFGeUO/" + "W3yqnfol1ZqzZ2h3uErR\noHkgT2Ja4K+5gnPkr/" + "RluJIu3AmWYw4eKi8i3+" + "PoThGmCFvoGfcWvRoctgnOHYHE\n4bDRirXL9nGkZ5FMzOVDeoIAEAShOqJwL08VcY+Pg/" + "qFQjzRrTmiKgm6QsHNnm0q\nzg70XaD88VJimiGYZOuJHNZpX8o0W+1Ls/" + "NbawIDAQABo1MwUTAdBgNVHQ4EFgQU\nkW3hgFWYMpVUkq9AlzGlI3awTGEwHwYDVR0jBBgw" + "FoAUkW3hgFWYMpVUkq9AlzGl\nI3awTGEwDwYDVR0TAQH/BAUwAwEB/" + "zANBgkqhkiG9w0BAQsFAAOCAQEAZeMkILiG\neEcELWb8cktP3LzvS47O8hys9+" + "6TFmftuO7kjDBd9YH2v8iQ7qCcUvMJ7bqL5RP1\nQssKfNOHZtw/" + "MMcKE6E3nl4yQSKc8ymivvYSVu5SYtQCedcgfRLb5zvVxXw8JmCp\nGNS3/" + "OlIYZAamh76GxkKSaV3tv0XZB6n3rUVQZShncFbMpyJRW0XWxReGWrhXv4s\nNxMeC1r07EE" + "WIDecv8KKf1F8uT4UF48HnC0VBpXiOyDGvn35NiKp+" + "Q5k7QV6jdCS\ngPRcnZhs6jiU0jnV8C9A1A+" + "3pXSSPrAed7tvECOgHCfS10CLsLWsLuSjc93BE5Vt\nav7kmxSwrdvQ2A==\n-----END " + "CERTIFICATE-----\n"; + std::string kIncorrectCertificate = + "-----BEGIN " + "CERTIFICATE-----\nMIIC/" + "jCCAeagAwIBAgIJAIZjLucUID1mMA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV\nBAMMCTEyNy4" + "wLjAuMTAeFw0xOTA1MDYxNDA1MzdaFw0yOjA1MDZxNDA1MzdaMBQx\nEjAQBgNVBAMMCTEyN" + "y4wLjAuMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\nggEBALfE5Qhc2mHTIux30l7" + "eHFjFLdzYeXE8vcaXKodalCG8EkRxojDOfUv+y2DV\nJzHAMiGxMFAEcSz71k+" + "jFZwhNG8PLY0b36ChnUsrGkOSJWq3OKUFrg8KxO9At9dL\nJsa+" + "R0N0D1bMoPYdpCi3m0b0q2ITHe56qKuTLTrIPia+" + "qXGEVD7EoEhU9tnwlcwE\npsUURMXCn2+FhHyqN9wjFkldmu4k8U3OJOK4385L+" + "4RJoIV8dsYawAMAf+" + "WuxyWq\niPQTPxr8q33ZZm6z0XrgsgxHYCCsryx8L9Ub9Zwu0mie7eL63rYdr86gULvnF1bY" + "\ncOunKFc71rBYFalbD6YYsre733kCAwEAAaNTMFEwHQYDVR0OBBYEFKW9ByUNG84Z\nYiSc" + "hUrB7KV9FinZMB8GA1UdIwQYMBaAFKW9ByUNG84ZYiSchUrB7KV9FinZMA8G\nA1UdEwEB/" + "wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAHYROS1EL2qnrwmAsR6c\nqo8yrk8qzLKt4os" + "41nv7QMUQFIO+QLPE8SbH4fK1YOMMUJ4ppARQBDaIIR3UEhp1\nZHT/" + "wGjK9qxcuQ1EXLyHOY0rxS5q57dYGxOyQo4v6IoLVMZ1ij+RJGPYI/" + "2fDXs0\nbDldeJyC1voHmG6lqTN5nLG7Y3j9j6rtSqJyRz5muaecQNiFPQOM2OTp0rC4VeAF" + "\ndejmTmLhUFVuHMzLF+" + "bpzsN76GnnQquJy2jexzFoWgbEfFVLKuyhTHQAalRb4ccq\nXCIx1hecNDYRY3Sc2Gzv5qxk" + "kWF8zqltT/0d5tx0JwN3k5nP4SlaEldFvD6BELxy\nVkU=\n-----END " + "CERTIFICATE-----\n"; +}; + +TEST_F(WebsocketConnectionTest, WSConnection_SUCCESS) { + transport_manager::transport_adapter::CloudAppProperties properties{ + .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 + StartWSServer("/"); + + // 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(); +} + +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", + .cloud_transport_type = "WS", + .hybrid_app_preference = "CLOUD"}; + + // Start server + StartWSServer(kPath); + + // 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(); +} + +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 + StartWSServer("/"); + + // 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(); +} + +TEST_F(WebsocketConnectionTest, WSSConnection_SUCCESS) { + 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 + StartWSSServer("/"); + + // 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(); +} + +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 + StartWSSServer((kPath + kQuery + kFragment)); + + // 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(); +} + +#ifdef ENABLE_SECURITY +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 + StartWSSServer(kPath); + + // 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(); +} + +TEST_F(WebsocketConnectionTest, WSSConnection_FAILURE_IncorrectCert) { + transport_manager::transport_adapter::CloudAppProperties properties{ + .endpoint = "wss://" + kHost + ":" + std::to_string(kPort), + .certificate = kIncorrectCertificate, + .enabled = true, + .auth_token = "auth_token", + .cloud_transport_type = "WSS", + .hybrid_app_preference = "CLOUD"}; + + // Start server + StartWSSServer("/"); + + // 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(); +} +#endif // ENABLE_SECURITY +} // namespace transport_manager_test +} // namespace components +} // namespace test diff --git a/src/components/transport_manager/test/websocket_connection_test.cc b/src/components/transport_manager/test/websocket_connection_test.cc index 6f6baa82fd..664c4a5155 100644 --- a/src/components/transport_manager/test/websocket_connection_test.cc +++ b/src/components/transport_manager/test/websocket_connection_test.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Ford Motor Company + * Copyright (c) 2020, Ford Motor Company * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,14 +31,26 @@ */ #include "gtest/gtest.h" + +#include <memory> +#include <string> + #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/websocket/websocket_connection.h" +#include "transport_manager/websocket/websocket_session.h" #include "transport_manager/mock_transport_manager_settings.h" +#include "transport_manager/transport_adapter/mock_transport_adapter_controller.h" + +namespace { +const std::string kHost = "127.0.0.1"; +const uint16_t kPort = 8080; +const std::string kPath = "/folder/file.html/"; +const std::uint32_t kConnectionKey = 1u; +const std::uint32_t kProtocolVersion = 5u; +const std::string kDeviceUid_ = "deviceUID"; +const transport_manager::ApplicationHandle kAppHandle = 12345u; +} // namespace namespace test { namespace components { @@ -47,431 +59,112 @@ namespace transport_manager_test { using ::testing::_; using ::testing::NiceMock; using ::testing::Return; + +using protocol_handler::RawMessagePtr; +using protocol_handler::ServiceType; + using namespace ::transport_manager; using namespace ::transport_manager::transport_adapter; -namespace websocket = sample::websocket; -class WebsocketConnectionTest : public ::testing::Test { +class WebsocketNotSecureSessionConnectionTest : public testing::Test { public: - struct WebsocketClient { - std::shared_ptr<CloudWebsocketTransportAdapter> adapter; - std::shared_ptr<WebsocketClientConnection> connection; - }; + WebsocketNotSecureSessionConnectionTest() + : websocket_connection_( + std::make_shared<WebSocketConnection<WebSocketSession<> > >( + kDeviceUid_, + kAppHandle, + boost::asio::ip::tcp::socket(i_co_), + &mock_transport_adapter_ctrl_)) - void InitWebsocketClient( - const transport_manager::transport_adapter::CloudAppProperties& - properties, - WebsocketClient& client_out) { - uniq_id = dev_id = properties.endpoint; + , last_state_("test_app_folder", "test_app_info_storage") {} - client_out = - WebsocketClient{std::make_shared<CloudWebsocketTransportAdapter>( - last_state_, transport_manager_settings), - nullptr}; - client_out.adapter->SetAppCloudTransportConfig(uniq_id, properties); - - TransportAdapterImpl* ta_cloud = - dynamic_cast<TransportAdapterImpl*>(client_out.adapter.get()); - ta_cloud->CreateDevice(uniq_id); + void SetUp() OVERRIDE { + ON_CALL(mock_transport_manager_settings_, use_last_state()) + .WillByDefault(Return(true)); + } - auto connection = client_out.adapter->FindPendingConnection(dev_id, 0); + RawMessagePtr CreateDefaultRawMessage() { + const uint32_t data_sending_size = 3u; + unsigned char data_sending[data_sending_size] = {0x20, 0x07, 0x01}; + RawMessagePtr raw_message_ptr( + new ::protocol_handler::RawMessage(kConnectionKey, + kProtocolVersion, + data_sending, + data_sending_size, + false, + ServiceType::kAudio)); + return raw_message_ptr; + } - ASSERT_NE(connection, nullptr); + protected: + boost::asio::io_context i_co_; + std::shared_ptr<WebSocketConnection<WebSocketSession<> > > + websocket_connection_; + NiceMock<MockTransportAdapterController> mock_transport_adapter_ctrl_; + NiceMock<MockTransportManagerSettings> mock_transport_manager_settings_; + resumption::LastStateImpl last_state_; +}; - client_out.connection = - std::dynamic_pointer_cast<WebsocketClientConnection>(connection); +TEST_F(WebsocketNotSecureSessionConnectionTest, Disconnect_SUCCESS) { + websocket_connection_->Run(); + EXPECT_CALL(mock_transport_adapter_ctrl_, + DisconnectDone(kDeviceUid_, kAppHandle)) + .Times(1); - ASSERT_NE(client_out.connection.use_count(), 0); - } + auto error = websocket_connection_->Disconnect(); - void StartWSServer(std::string path) { - ws_session = std::make_shared<websocket::WSSession>(kHost, kPort); - ws_session->AddRoute(path); - ws_session->Run(); - } + ASSERT_EQ(TransportAdapter::Error::OK, error); +} - void StartWSSServer(std::string path) { - wss_session = std::make_shared<websocket::WSSSession>( - kHost, kPort, kCertificate, kPrivateKey); - wss_session->AddRoute(path); - wss_session->Run(); - } +TEST_F(WebsocketNotSecureSessionConnectionTest, SecondDisconnect_UNSUCCESS) { + websocket_connection_->Run(); + EXPECT_CALL(mock_transport_adapter_ctrl_, + DisconnectDone(kDeviceUid_, kAppHandle)) + .Times(1); - protected: - WebsocketConnectionTest() - : last_state_("app_storage_folder", "app_info_storage") {} + auto result_error = websocket_connection_->Disconnect(); - ~WebsocketConnectionTest() {} + ASSERT_EQ(TransportAdapter::Error::OK, result_error); - void SetUp() OVERRIDE { - ON_CALL(transport_manager_settings, use_last_state()) - .WillByDefault(Return(true)); - } + EXPECT_CALL(mock_transport_adapter_ctrl_, + DisconnectDone(kDeviceUid_, kAppHandle)) + .Times(0); - NiceMock<MockTransportManagerSettings> transport_manager_settings; - resumption::LastStateImpl last_state_; - std::string dev_id; - std::string uniq_id; - std::shared_ptr<websocket::WSSession> ws_session; - std::shared_ptr<websocket::WSSSession> wss_session; - 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 " - "CERTIFICATE-----\nMIIDqTCCApECCQC/" - "5LlQ+" - "GLgqTANBgkqhkiG9w0BAQsFADAXMRUwEwYDVQQDDAwx\nMjcuMC4wLjEgQ0EwIBcNMTkwNTA" - "2MTkzMjM2WhgPMjExOTA0MTIxOTMyMzZaMBQx\nEjAQBgNVBAMMCTEyNy4wLjAuMTCCAiIwD" - "QYJKoZIhvcNAQEBBQADggIPADCCAgoC\nggIBALbeIoAHFaFeNqbMnhtAO5QTgRd0X9qxpB0" - "d4M0dEeog2l/+inNA/eu+CCvm\nJj2I8+6MWH2TUrl/" - "2xfhHjzsMrtISCdpNcjaNzCnvi8ZcspFi3evknvs3+uo2/" - "wn\nyNZ04jp0oQ0k1cZ6WBpLTYV7WgmueemiWEiAfw+YE+wtRg+" - "0H7rksrbpeNPnxQHX\nBDDkWqwvfkD15Sd0XFQkW27K72/" - "et2uKuAcJHCIUbsA4iZyJw4Uu4eusy7W5kddX\nThE7Y1WqTXyA4j/" - "ZCYigXmsHbWrikPLVXbORhiMF4/60u8RDs0jI8xtgRQhLR9Vp\n3xys7/" - "5tHZX00s6x6OVy8aMSZIIVS0eWoVN8bGd9B4+fDMOcNT0YODeQA10w/" - "85P\nEiZDQ8AxneQkh8H3qjD2+" - "G9oHZaTk0zHTyaKRg3xGP3N9C96onaJX4Rm6nNvApO8\nU7lQ+xHkLwjKCQke39W+" - "r3FHwwQvUDeoJBXf6iVkIMFoUPAHNZqKf9Db6KKIEp8i\nDgIhbBxiB4MSn36Zly4SOMojyM" - "ZFri+" - "HzMbuHFlm8e8QRWGmM4UM2rHIpl4OJolg\nejesDqO8YZR5mZXV0FJRiPgLo2Q4OTtb3tEHJ" - "ZLmlT+" - "f42bge4ZCZmGPrkNfr68Y\nDEnJ6z4ksOVkMefOp2SNYLYYGPYyiKwa9qVkH9Obkect1omNA" - "gMBAAEwDQYJKoZI\nhvcNAQELBQADggEBABFJQtOTftrzbLBA5Vm6aPkbUyxhcaOpz+d+" - "Ljd6pIs4H+" - "Eb\nXkoHhmay4stZkDc2HVSKESZloI3Ylup8z3aRJjfOexJqHlYzk2vraYryWm8odgID\n5V" - "B0Zle8ofpHTJw1LCOXHkzKt1G6vdQpuq/" - "4OKpmggaIqqEC1bfOYZt5t6vIx3OF\nxjPz91NTR9gZ4lKrho1+sfS+" - "jbSjKkqVAhE4yTpKLPHRshQnBFEhvATXNvdZGftF\n+tXxqKsBZ9aX0/" - "YmPFIXFcjdjSSiuq1F1DjiqIZo88qfa9jlTg6VJdayyQ/" - "cu41C\nBucY8YTF0Ui8ccIS55h2UTzPy5/4PbrwI4P+Zgo=\n-----END " - "CERTIFICATE-----\n"; - // Sample private key - std::string kPrivateKey = - "-----BEGIN RSA PRIVATE " - "KEY-----\nMIIJKAIBAAKCAgEAtt4igAcVoV42psyeG0A7lBOBF3Rf2rGkHR3gzR0R6iDaX/" - "6K\nc0D9674IK+YmPYjz7oxYfZNSuX/" - "bF+EePOwyu0hIJ2k1yNo3MKe+LxlyykWLd6+S\ne+zf66jb/" - "CfI1nTiOnShDSTVxnpYGktNhXtaCa556aJYSIB/" - "D5gT7C1GD7QfuuSy\ntul40+fFAdcEMORarC9+" - "QPXlJ3RcVCRbbsrvb963a4q4BwkcIhRuwDiJnInDhS7h\n66zLtbmR11dOETtjVapNfIDiP9" - "kJiKBeawdtauKQ8tVds5GGIwXj/rS7xEOzSMjz\nG2BFCEtH1WnfHKzv/" - "m0dlfTSzrHo5XLxoxJkghVLR5ahU3xsZ30Hj58Mw5w1PRg4\nN5ADXTD/" - "zk8SJkNDwDGd5CSHwfeqMPb4b2gdlpOTTMdPJopGDfEY/" - "c30L3qidolf\nhGbqc28Ck7xTuVD7EeQvCMoJCR7f1b6vcUfDBC9QN6gkFd/" - "qJWQgwWhQ8Ac1mop/" - "\n0NvooogSnyIOAiFsHGIHgxKffpmXLhI4yiPIxkWuL4fMxu4cWWbx7xBFYaYzhQza\nscim" - "Xg4miWB6N6wOo7xhlHmZldXQUlGI+AujZDg5O1ve0QclkuaVP5/" - "jZuB7hkJm\nYY+uQ1+" - "vrxgMScnrPiSw5WQx586nZI1gthgY9jKIrBr2pWQf05uR5y3WiY0CAwEA\nAQKCAgBGSDGyS" - "wbBMliG2vWZO6KqUqS2wv9kKgoNNsKDkryj42SKqGXFziDJTgwN\n8zKXS9+Uu1P3T3vn13/" - "5OYhJme4VlL5Gh2UogNXdWVr69yjrHLdxlIUUJAIbrJZ/" - "\n3zqNUfbwyIptZs7SrYrW8EInHzWHqwsoBEEx/" - "FDZSXW+u9fFiVD4n5UgP7M0nktV\nXbI6qElBDC/V/" - "6vG8i3aGO8bMdu8fzi3mGUKLzIk1v2J2zDofPosYcxqq8rPWTb4\nMJHMhaqz7fRB+" - "bb7GwtS+2/Oathe0B0td1u//Bo1s7ng1s2jrPFm8/" - "SbfPCLM4O0\nPjCF8OF8Q6uvSp0K283LAdZk+liuDztc/" - "Ed8mcnCZQhBp86mJQi0Jj3Mje7prOAY\nXojMroYj7r2igCJvcwGb1y6zZWSj3jXuHze3bLy" - "fv7pCD+hkiZR7mZhQpOhQRZaU\ntdFGc+" - "DuykxKPqVjAPy7iVQXYnMjpo36SGIWoZLuepQto3GvU6czyOwhK0/" - "Mwbwd\nZpYpLH3L9IetY8GcPefmUR5wQUlUTrpxgGElIzkkWW8zmUWBXwrGbAtN1HJWpJdN" - "\neVshKod2fo03IQMPywSdENCJVeSrgRMuwPyFaOM+" - "CVrBJwD66K9YWn4cVRUIZsTq\nAXhQ8DzF+WCOZshhMUbCJX+KpcOFI8nxOrPp+" - "J1s1YpLLvdmcQKCAQEA7bwvOiCD\nHvaqpYg1jJak6l/" - "iY3QIOOpFyjfYrQXS0BNRmmxK8Lzevi/" - "NPTqu146QKDaIGvzu\n+" - "bXnuV1LmZqnOm5J6Kdx0Mk4Fb88akRtS9gOdzU7WWMYIGkeF1oih0ZrhHuIey6e\nCeLSmJh" - "UDaTIWcCirALRHcLWUS6BTGGuE+up5QG7djIW/" - "LD17lhGE6fXPlbYXb7l\nPbYwL1Yr0urIp9Un+zrExw+77TTGK7T37T1ZJv46qnro0/" - "HK8XxZ+" - "JNc18F763O4\nPmwu8KWrx4qnyPYAuB1rbVntK6UxYks9jvk93XecCUP6HHp0I0tV/" - "kBNd2+" - "18zux\n033xFEVKrUksmwKCAQEAxOrPGyBVFp29V4gQsMQdmafkDAgCLREgjQTOamFsYL5+" - "\nZWq+6VhT/" - "B650sIoRdX0N8timnSLBfjeqaBG5VVhhyKZhP5hulfRjALhdbGBOYvf\n0gLSffImwWdYQfx" - "jtCSO+" - "XCLVdAJGOOWeBVVKzH18ZvCFQwUr3Yrp7KmKWKAqgf7\n6rYovWh8T5LLYS0NzXCEwf9nJ0N" - "JMOy7I9B7EtF8Cs6tK3aaHVqDz7zufwosS7gI\n3aI51Qh4a5D1p95+" - "YU09beWjWGYnPiCKk4D47zaeOe7OQINaWugExlelHtJh9unO\nnhOFXhziav2Kxq1CICAuXl" - "Vx3A+gn/cU3niNHz2A9wKCAQEAws+aw78ws4bef5cG\nipZHveek1GqY8krHtdXdsKs8/" - "VVXYXusTWn3/VGelbYo4GrqpolJLxRloCr4IGXb\nNZwNvUvzNLtCAR1i4C89irdX+Paro/" - "PzFmSluKlrByfNc5y5Lm8sgATLbL56ZKEu\n/58wrpu0sc/" - "9HK40gYHiYn0I8ToElqy8uTaCr78zSIT9p826DFOOKgPsRo2tHp02\nfDf5Bc8eXDjkV1sFX" - "HQKkHZTVA0ZqWJbIKhncoaJDyofcBsR0ZuzuFWzfTOZo4mf\nInz00TEFldpF1e4C8+" - "kCdtHBOA/2Ki2Bp/YUVpHh6aoqZZa75Euehhs8tVpW242M\njEOSUQKCAQAM64sjMH/kt/" - "zQXXEa6AM5LbbcwznBUzpbhlE00aeWwWjxpotYLB92\nj12J4otZ6avYbVPO5o6omaeiYY3F" - "RlDb2P1RqI8o9tIc6aN5YWglKnRJBz5gXR8F\n2Y4E5lZ0X2GyJBxASSIPq/" - "8Xae7ooqKMc7fMQbqpuIssuaAFXx0qCtQQllsd8lkV\nr4AApEAflp5fTC6seNG4kA/" - "HTcqFdZE59E2QaHu8KVA0tSTA2R4G6dBLGnXI8IFW\nLXCwzvxjzfmV2FdbWXiBrwjonLG4o" - "FDJZE3MFdI73LVTfjSrTQp4dObFoGpDvols\nk64jUwLfsLzaG6kY0z2qwT9xSV+" - "ZCSQJAoIBADsSBeyELc5KnmOhT0Xue2o0bkAS\n8KcGWdAoQlaLCIs3vtdlC7DXDXO1l8FkT" - "Qrd+GwP3eWDq6ymtO4vkJ3m4cZ1PEsv\n6Qet7qtkVhUfa+" - "FYKtDzSMRuSaFPW3Ro1PesFa1H3y+sw5HL36vhRaSK+T4Wy5k2\nf7EwNZIf/" - "ZZeA+" - "sEtqj33obOBIiuU9htAjN81Kz4a0Xlh6jc4q6iwYKL8nZ76JYV\n8hXWIz6OXxLXE158+" - "QtJSZaRCvdr6L5UtWfMPKSMqgfhXfTYViPDleQCkJ132mIS\nH28UoET0Y5wI8M6pMkWpSqW" - "WcKPFGwyLInvHdxgnTAsutowkldA7qFwoRz4=\n-----END RSA PRIVATE KEY-----\n"; - // Sample CA certificate(used to sign the server certificate) - std::string kCACertificate = - "-----BEGIN " - "CERTIFICATE-----" - "\nMIIDBjCCAe6gAwIBAgIJAPyCrKRDl3SWMA0GCSqGSIb3DQEBCwUAMBcxFTATBgNV\nBAMM" - "DDEyNy4wLjAuMSBDQTAgFw0xOTA1MDYxOTMyMzZaGA8yMTE5MDQxMjE5MzIz\nNlowFzEVMB" - "MGA1UEAwwMMTI3LjAuMC4xIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC\nAQ8AMIIBCgKCAQEA" - "xrNHoY5+" - "JCgnyvEdG2dvvOaZ3sg6uhuF5Ssb5snyo9ixrxO5\nZkDGWwUqFO9PiYuO5ovNeq9LZtPG6s" - "K8zsCS062ChZX/7tZHndf4MKCUDzv/" - "LPHe\nnROoPi9n2FAiOPctY5hpgpIJDPI5Ofx0el2KPeFGeUO/" - "W3yqnfol1ZqzZ2h3uErR\noHkgT2Ja4K+5gnPkr/" - "RluJIu3AmWYw4eKi8i3+" - "PoThGmCFvoGfcWvRoctgnOHYHE\n4bDRirXL9nGkZ5FMzOVDeoIAEAShOqJwL08VcY+Pg/" - "qFQjzRrTmiKgm6QsHNnm0q\nzg70XaD88VJimiGYZOuJHNZpX8o0W+1Ls/" - "NbawIDAQABo1MwUTAdBgNVHQ4EFgQU\nkW3hgFWYMpVUkq9AlzGlI3awTGEwHwYDVR0jBBgw" - "FoAUkW3hgFWYMpVUkq9AlzGl\nI3awTGEwDwYDVR0TAQH/BAUwAwEB/" - "zANBgkqhkiG9w0BAQsFAAOCAQEAZeMkILiG\neEcELWb8cktP3LzvS47O8hys9+" - "6TFmftuO7kjDBd9YH2v8iQ7qCcUvMJ7bqL5RP1\nQssKfNOHZtw/" - "MMcKE6E3nl4yQSKc8ymivvYSVu5SYtQCedcgfRLb5zvVxXw8JmCp\nGNS3/" - "OlIYZAamh76GxkKSaV3tv0XZB6n3rUVQZShncFbMpyJRW0XWxReGWrhXv4s\nNxMeC1r07EE" - "WIDecv8KKf1F8uT4UF48HnC0VBpXiOyDGvn35NiKp+" - "Q5k7QV6jdCS\ngPRcnZhs6jiU0jnV8C9A1A+" - "3pXSSPrAed7tvECOgHCfS10CLsLWsLuSjc93BE5Vt\nav7kmxSwrdvQ2A==\n-----END " - "CERTIFICATE-----\n"; - std::string kIncorrectCertificate = - "-----BEGIN " - "CERTIFICATE-----\nMIIC/" - "jCCAeagAwIBAgIJAIZjLucUID1mMA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV\nBAMMCTEyNy4" - "wLjAuMTAeFw0xOTA1MDYxNDA1MzdaFw0yOjA1MDZxNDA1MzdaMBQx\nEjAQBgNVBAMMCTEyN" - "y4wLjAuMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\nggEBALfE5Qhc2mHTIux30l7" - "eHFjFLdzYeXE8vcaXKodalCG8EkRxojDOfUv+y2DV\nJzHAMiGxMFAEcSz71k+" - "jFZwhNG8PLY0b36ChnUsrGkOSJWq3OKUFrg8KxO9At9dL\nJsa+" - "R0N0D1bMoPYdpCi3m0b0q2ITHe56qKuTLTrIPia+" - "qXGEVD7EoEhU9tnwlcwE\npsUURMXCn2+FhHyqN9wjFkldmu4k8U3OJOK4385L+" - "4RJoIV8dsYawAMAf+" - "WuxyWq\niPQTPxr8q33ZZm6z0XrgsgxHYCCsryx8L9Ub9Zwu0mie7eL63rYdr86gULvnF1bY" - "\ncOunKFc71rBYFalbD6YYsre733kCAwEAAaNTMFEwHQYDVR0OBBYEFKW9ByUNG84Z\nYiSc" - "hUrB7KV9FinZMB8GA1UdIwQYMBaAFKW9ByUNG84ZYiSchUrB7KV9FinZMA8G\nA1UdEwEB/" - "wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAHYROS1EL2qnrwmAsR6c\nqo8yrk8qzLKt4os" - "41nv7QMUQFIO+QLPE8SbH4fK1YOMMUJ4ppARQBDaIIR3UEhp1\nZHT/" - "wGjK9qxcuQ1EXLyHOY0rxS5q57dYGxOyQo4v6IoLVMZ1ij+RJGPYI/" - "2fDXs0\nbDldeJyC1voHmG6lqTN5nLG7Y3j9j6rtSqJyRz5muaecQNiFPQOM2OTp0rC4VeAF" - "\ndejmTmLhUFVuHMzLF+" - "bpzsN76GnnQquJy2jexzFoWgbEfFVLKuyhTHQAalRb4ccq\nXCIx1hecNDYRY3Sc2Gzv5qxk" - "kWF8zqltT/0d5tx0JwN3k5nP4SlaEldFvD6BELxy\nVkU=\n-----END " - "CERTIFICATE-----\n"; -}; + result_error = websocket_connection_->Disconnect(); -TEST_F(WebsocketConnectionTest, WSConnection_SUCCESS) { - transport_manager::transport_adapter::CloudAppProperties properties{ - .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 - StartWSServer("/"); - - // 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(); + ASSERT_EQ(TransportAdapter::Error::BAD_STATE, result_error); } -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", - .cloud_transport_type = "WS", - .hybrid_app_preference = "CLOUD"}; - - // Start server - StartWSServer(kPath); - - // 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(); -} +TEST_F(WebsocketNotSecureSessionConnectionTest, SUCCESS_SendData) { + auto message = CreateDefaultRawMessage(); -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 - StartWSServer("/"); - - // 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(); -} + auto error = websocket_connection_->SendData(message); -TEST_F(WebsocketConnectionTest, WSSConnection_SUCCESS) { - 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 - StartWSSServer("/"); - - // 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(); + ASSERT_EQ(TransportAdapter::Error::OK, error); } -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 - StartWSSServer((kPath + kQuery + kFragment)); - - // 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(); -} +TEST_F(WebsocketNotSecureSessionConnectionTest, UNSUCCESS_SendData_BAD_STATE) { + websocket_connection_->Run(); + auto message = CreateDefaultRawMessage(); + + auto disconnect_error = websocket_connection_->Disconnect(); + + auto send_data_error = websocket_connection_->SendData(message); -#ifdef ENABLE_SECURITY -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 - StartWSSServer(kPath); - - // 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(); + ASSERT_EQ(TransportAdapter::Error::OK, disconnect_error); + ASSERT_EQ(TransportAdapter::Error::BAD_STATE, send_data_error); } -TEST_F(WebsocketConnectionTest, WSSConnection_FAILURE_IncorrectCert) { - transport_manager::transport_adapter::CloudAppProperties properties{ - .endpoint = "wss://" + kHost + ":" + std::to_string(kPort), - .certificate = kIncorrectCertificate, - .enabled = true, - .auth_token = "auth_token", - .cloud_transport_type = "WSS", - .hybrid_app_preference = "CLOUD"}; - - // Start server - StartWSSServer("/"); - - // 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(); +TEST_F(WebsocketNotSecureSessionConnectionTest, DataReceive_SUCCESS) { + auto message = CreateDefaultRawMessage(); + + EXPECT_CALL(mock_transport_adapter_ctrl_, + DataReceiveDone(kDeviceUid_, kAppHandle, _)) + .Times(1); + + websocket_connection_->DataReceive(message); } -#endif // ENABLE_SECURITY + } // namespace transport_manager_test } // namespace components } // namespace test diff --git a/src/components/transport_manager/test/websocket_sample_client/websocket_sample_client.cc b/src/components/transport_manager/test/websocket_sample_client/websocket_sample_client.cc new file mode 100644 index 0000000000..88525ad29d --- /dev/null +++ b/src/components/transport_manager/test/websocket_sample_client/websocket_sample_client.cc @@ -0,0 +1,193 @@ +/* + * \file websocket_listener.h + * \brief WebSocketListener class header file. + * + * Copyright (c) 2020 + * 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/websocket_server/websocket_sample_client.h" + +namespace transport_manager { +namespace transport_adapter { + +template <> +WSSampleClient<websocket::stream<ssl::stream<tcp::socket> > >::WSSampleClient( + const std::string& host, + const std::string& port, + const SecurityParams& params) + : resolver_(ioc_) + , ctx_(ssl::context::sslv23_client) + , ws_(nullptr) + , io_pool_(1) + , host_(host) + , port_(port) + , handshake_successful_(false) { + ctx_.set_verify_mode(ssl::context::verify_peer); + ctx_.load_verify_file(params.ca_cert_); + ctx_.use_certificate_chain_file(params.client_cert_); + ctx_.use_private_key_file(params.client_key_, boost::asio::ssl::context::pem); + ws_.reset(new WSS(ioc_, ctx_)); +} + +template <> +WSSampleClient<WS>::WSSampleClient(const std::string& host, + const std::string& port) + : resolver_(ioc_) + , ctx_(ssl::context::sslv23_client) + , ws_(new WS(ioc_)) + , host_(host) + , port_(port) {} + +template <typename Stream> +bool WSSampleClient<Stream>::Run() { + boost::system::error_code ec; + ctx_.set_verify_mode(ssl::verify_none); + + auto results = resolver_.resolve(host_, port_, ec); + if (ec) { + std::cout << "ErrorMessage: " + ec.message() << std::endl; + return false; + } + + if (!Connect(results)) { + return false; + } + + if (!Handshake(host_, "/")) { + return false; + } + + ws_->async_read(buffer_, + std::bind(&WSSampleClient::OnRead, + this->shared_from_this(), + std::placeholders::_1, + std::placeholders::_2)); + boost::asio::post(io_pool_, [&]() { ioc_.run(); }); + return true; +} + +template <typename Stream> +void WSSampleClient<Stream>::OnRead(beast::error_code ec, + std::size_t bytes_transferred) { + boost::ignore_unused(bytes_transferred); +} + +template <> +bool WSSampleClient<WS>::Connect(tcp::resolver::results_type& results) { + boost::system::error_code ec; + boost::asio::connect(ws_->next_layer(), results.begin(), results.end(), ec); + if (ec) { + std::string str_err = "ErrorMessage: " + ec.message(); + return false; + } + return true; +} + +template <> +bool WSSampleClient<WSS>::Connect(tcp::resolver::results_type& results) { + boost::system::error_code ec; + boost::asio::connect(ws_->lowest_layer(), results.begin(), results.end(), ec); + if (ec) { + std::string str_err = "ErrorMessage: " + ec.message(); + return false; + } + return true; +} + +template <> +bool WSSampleClient<WS>::Handshake(const std::string& host, + const std::string& target) { + boost::system::error_code ec; + ws_->handshake(host, target, ec); + if (ec) { + std::string str_err = "ErrorMessage: " + ec.message(); + return false; + } + return true; +} + +template <> +void WSSampleClient<WS>::Stop() { + ioc_.stop(); + ws_->lowest_layer().close(); + + io_pool_.stop(); + io_pool_.join(); +} + +template <> +bool WSSampleClient<WSS>::Handshake(const std::string& host, + const std::string& target) { + boost::system::error_code ec; + + ws_->next_layer().handshake(ssl::stream_base::client, ec); + if (ec) { + std::cout << "ErrorMessage: " + ec.message() << std::endl; + return false; + } + + ws_->handshake(host, target, ec); + if (ec) { + std::string str_err = "ErrorMessage: " + ec.message(); + return false; + } + + handshake_successful_ = true; + return true; +} + +template <> +void WSSampleClient<WSS>::Stop() { + ioc_.stop(); + ws_->next_layer().next_layer().shutdown( + boost::asio::ip::tcp::socket::shutdown_both); + ws_->lowest_layer().close(); + + io_pool_.stop(); + io_pool_.join(); +} + +template <> +void WSSampleClient<WSS>::OnHandshakeTimeout() { + if (!handshake_successful_) { + Stop(); + } +} + +template <> +bool WSSampleClient<WSS>::IsHandshakeSuccessful() const { + return handshake_successful_; +} + +template class WSSampleClient<WS>; +template class WSSampleClient<WSS>; +} // namespace transport_adapter +} // namespace transport_manager diff --git a/src/components/transport_manager/test/websocket_server_listener_test.cc b/src/components/transport_manager/test/websocket_server_listener_test.cc new file mode 100644 index 0000000000..6f12173cad --- /dev/null +++ b/src/components/transport_manager/test/websocket_server_listener_test.cc @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2020, 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 "gmock/gmock.h" +#include "gtest/gtest.h" +#include "transport_manager/mock_transport_manager_settings.h" +#include "transport_manager/transport_adapter/mock_transport_adapter_controller.h" +#include "transport_manager/websocket/websocket_device.h" +#include "transport_manager/websocket/websocket_listener.h" +#include "utils/timer.h" +#include "utils/timer_task_impl.h" + +#include <thread> +#include "transport_manager/websocket_server/websocket_sample_client.h" + +namespace test { +namespace components { +namespace transport_manager_test { + +using ::testing::_; +using ::testing::NiceMock; +using ::testing::Return; +using ::testing::ReturnArg; +using ::testing::ReturnPointee; +using ::testing::ReturnRef; +using namespace ::transport_manager; +using namespace ::transport_manager::transport_adapter; + +namespace { +const std::string kDefaultAddress = "127.0.0.1"; +const std::string kDefaultCertPath = ""; +const std::string kDefaultKeyPath = ""; +const std::string kDefaultCACertPath = ""; +const uint32_t kDefaultPort = 2021; +const std::string kDefaultPortStr = "2021"; +const uint32_t kWrongPort = 1000; +const std::string kCACertPath = "./test_certs/ca-cert.pem"; +const std::string kClientCertPath = "./test_certs/client-cert.pem"; +const std::string kClientKeyPath = "./test_certs/client-key.pem"; +const std::string kServerCert = "./test_certs/server-cert.pem"; +const std::string kServerKey = "./test_certs/server-key.pem"; +const std::string kCACert = "./test_certs/ca-cert.pem"; +const std::string kDefaultDeviceName = "Web Engine"; +const int kNumThreads = 2; +} // namespace + +class WebSocketListenerTest : public ::testing::Test { + protected: + MockTransportAdapterController mock_ta_controller_; + MockTransportManagerSettings mock_tm_settings_; + + public: + WebSocketListenerTest() {} + ~WebSocketListenerTest() {} + + void SetUp() OVERRIDE { + ON_CALL(mock_tm_settings_, websocket_server_address()) + .WillByDefault(ReturnRef(kDefaultAddress)); + ON_CALL(mock_tm_settings_, websocket_server_port()) + .WillByDefault(Return(kDefaultPort)); + ON_CALL(mock_tm_settings_, ws_server_cert_path()) + .WillByDefault(ReturnRef(kServerCert)); + ON_CALL(mock_tm_settings_, ws_server_key_path()) + .WillByDefault(ReturnRef(kServerKey)); + ON_CALL(mock_tm_settings_, ws_server_ca_cert_path()) + .WillByDefault(ReturnRef(kCACert)); + ON_CALL(mock_tm_settings_, is_wss_settings_setup()) + .WillByDefault(Return(true)); + } + + std::shared_ptr<WebSocketDevice> CreateDevice(const bool secure_connection) { + return std::make_shared<WebSocketDevice>(kDefaultDeviceName, + kDefaultDeviceName); + } +}; + +TEST_F(WebSocketListenerTest, StartListening_ClientConnect_SUCCESS) { + ON_CALL(mock_tm_settings_, ws_server_cert_path()) + .WillByDefault(ReturnRef(kDefaultCertPath)); + ON_CALL(mock_tm_settings_, ws_server_key_path()) + .WillByDefault(ReturnRef(kDefaultKeyPath)); + ON_CALL(mock_tm_settings_, ws_server_ca_cert_path()) + .WillByDefault(ReturnRef(kDefaultCACertPath)); + ON_CALL(mock_tm_settings_, is_wss_settings_setup()) + .WillByDefault(Return(false)); + + const auto ws_listener = std::make_shared<WebSocketListener>( + &mock_ta_controller_, mock_tm_settings_, kNumThreads); + const auto ws_client = + std::make_shared<WSSampleClient<WS> >(kDefaultAddress, kDefaultPortStr); + + EXPECT_CALL(mock_ta_controller_, ConnectDone(_, _)); + EXPECT_CALL(mock_ta_controller_, ConnectionCreated(_, _, _)); + EXPECT_CALL(mock_ta_controller_, GetWebEngineDevice()) + .WillOnce(Return(CreateDevice(false))); + + ws_listener->StartListening(); + EXPECT_TRUE(ws_client->Run()); + ws_client->Stop(); +} + +TEST_F(WebSocketListenerTest, StartListening_ClientConnectSecure_SUCCESS) { + const auto ws_listener = std::make_shared<WebSocketListener>( + &mock_ta_controller_, mock_tm_settings_, kNumThreads); + const SecurityParams params{kCACertPath, kClientCertPath, kClientKeyPath}; + const auto wss_client = std::make_shared<WSSampleClient<WSS> >( + kDefaultAddress, kDefaultPortStr, params); + + EXPECT_CALL(mock_ta_controller_, ConnectDone(_, _)); + EXPECT_CALL(mock_ta_controller_, ConnectionCreated(_, _, _)); + EXPECT_CALL(mock_ta_controller_, GetWebEngineDevice()) + .WillOnce(Return(CreateDevice(true))); + + ws_listener->StartListening(); + EXPECT_TRUE(wss_client->Run()); + wss_client->Stop(); +} + +TEST_F(WebSocketListenerTest, + StartListening_ClientConnectSecureInvalidCert_FAIL) { + const auto ws_listener = std::make_shared<WebSocketListener>( + &mock_ta_controller_, mock_tm_settings_, kNumThreads); + const SecurityParams params{kCACertPath, + "./test_certs/invalid_cert.pem", + "./test_certs/invalid_key.pem"}; + const auto wss_client = std::make_shared<WSSampleClient<WSS> >( + kDefaultAddress, kDefaultPortStr, params); + + EXPECT_CALL(mock_ta_controller_, ConnectDone(_, _)); + EXPECT_CALL(mock_ta_controller_, ConnectionCreated(_, _, _)); + EXPECT_CALL(mock_ta_controller_, GetWebEngineDevice()) + .WillOnce(Return(CreateDevice(true))); + EXPECT_CALL(mock_ta_controller_, ConnectionAborted(_, _, _)); + + ws_listener->StartListening(); + timer::Timer handshake_timer( + "HandshakeTimer", + new ::timer::TimerTaskImpl<WSSampleClient<WSS> >( + wss_client.get(), &WSSampleClient<WSS>::OnHandshakeTimeout)); + handshake_timer.Start(3000, timer::kSingleShot); + wss_client->Run(); + EXPECT_EQ(wss_client->IsHandshakeSuccessful(), false); +} + +TEST_F(WebSocketListenerTest, StartListening_CertificateNotFound_Fail) { + const std::string server_cert = "./test_certs/server-cert.pem"; + const std::string server_key = "./test_certs/server-key.pem"; + const std::string ca_cert = "./not_valid_path/ca-cert.pem"; + ON_CALL(mock_tm_settings_, ws_server_cert_path()) + .WillByDefault(ReturnRef(server_cert)); + ON_CALL(mock_tm_settings_, ws_server_key_path()) + .WillByDefault(ReturnRef(server_key)); + ON_CALL(mock_tm_settings_, ws_server_ca_cert_path()) + .WillByDefault(ReturnRef(ca_cert)); + + const auto ws_listener_ = std::make_shared<WebSocketListener>( + &mock_ta_controller_, mock_tm_settings_, kNumThreads); + const auto ws_client_ = + std::make_shared<WSSampleClient<WS> >(kDefaultAddress, kDefaultPortStr); + + EXPECT_EQ(TransportAdapter::Error::FAIL, ws_listener_->StartListening()); +} + +TEST_F(WebSocketListenerTest, StartListening_WrongConfig_FAIL) { + const std::string server_cert = "./test_certs/server-cert.pem"; + + ON_CALL(mock_tm_settings_, ws_server_cert_path()) + .WillByDefault(ReturnRef(server_cert)); + ON_CALL(mock_tm_settings_, ws_server_key_path()) + .WillByDefault(ReturnRef(kDefaultKeyPath)); + EXPECT_CALL(mock_tm_settings_, ws_server_ca_cert_path()) + .WillOnce(ReturnRef(kDefaultCACertPath)); + + const auto ws_listener_ = std::make_shared<WebSocketListener>( + &mock_ta_controller_, mock_tm_settings_, kNumThreads); + const auto ws_client_ = + std::make_shared<WSSampleClient<WS> >(kDefaultAddress, kDefaultPortStr); + + EXPECT_EQ(TransportAdapter::Error::FAIL, ws_listener_->StartListening()); +} + +TEST_F(WebSocketListenerTest, StartListening_BindToTheServerAddress_FAIL) { + ON_CALL(mock_tm_settings_, ws_server_cert_path()) + .WillByDefault(ReturnRef(kDefaultCertPath)); + ON_CALL(mock_tm_settings_, ws_server_key_path()) + .WillByDefault(ReturnRef(kDefaultKeyPath)); + ON_CALL(mock_tm_settings_, is_wss_settings_setup()) + .WillByDefault(Return(false)); + EXPECT_CALL(mock_tm_settings_, ws_server_ca_cert_path()) + .WillOnce(ReturnRef(kDefaultCACertPath)); + EXPECT_CALL(mock_tm_settings_, websocket_server_port()) + .WillOnce(Return(kWrongPort)); + + const auto ws_listener_ = std::make_shared<WebSocketListener>( + &mock_ta_controller_, mock_tm_settings_, kNumThreads); + const auto ws_client_ = + std::make_shared<WSSampleClient<WS> >(kDefaultAddress, kDefaultPortStr); + + EXPECT_EQ(TransportAdapter::Error::FAIL, ws_listener_->StartListening()); +} + +TEST_F(WebSocketListenerTest, StartListening_AcceptorIsOpen_SUCCESS) { + ON_CALL(mock_tm_settings_, ws_server_cert_path()) + .WillByDefault(ReturnRef(kDefaultCertPath)); + ON_CALL(mock_tm_settings_, ws_server_key_path()) + .WillByDefault(ReturnRef(kDefaultKeyPath)); + ON_CALL(mock_tm_settings_, is_wss_settings_setup()) + .WillByDefault(Return(false)); + + EXPECT_CALL(mock_ta_controller_, ConnectDone(_, _)); + EXPECT_CALL(mock_ta_controller_, ConnectionCreated(_, _, _)); + EXPECT_CALL(mock_ta_controller_, GetWebEngineDevice()) + .WillOnce(Return(CreateDevice(false))); + + EXPECT_CALL(mock_tm_settings_, ws_server_ca_cert_path()) + .WillOnce(ReturnRef(kDefaultCACertPath)); + + const auto ws_listener = std::make_shared<WebSocketListener>( + &mock_ta_controller_, mock_tm_settings_, kNumThreads); + const auto ws_client = + std::make_shared<WSSampleClient<WS> >(kDefaultAddress, kDefaultPortStr); + + ws_listener->StartListening(); + ws_client->Run(); + EXPECT_EQ(TransportAdapter::Error::OK, ws_listener->StartListening()); + ws_client->Stop(); +} + +} // namespace transport_manager_test +} // namespace components +} // namespace test |