From 6263fc0134e6e291e4241baacfeeb58d41874fa2 Mon Sep 17 00:00:00 2001 From: Lee Maguire Date: Fri, 5 Jul 2024 17:32:05 +0100 Subject: [PATCH] Networking checkpoint --- .../internal/generic_network_transport.hpp | 41 ----- include/cpprealm/networking/networking.hpp | 50 +++--- .../networking/platform_networking.hpp | 32 ++-- src/CMakeLists.txt | 1 - src/cpprealm/analytics.cpp | 4 +- src/cpprealm/app.cpp | 23 +-- .../internal/curl/network_transport.cpp | 18 +-- .../internal/network/network_transport.cpp | 34 ++--- src/cpprealm/networking/networking.cpp | 59 +++++-- .../networking/platform_networking.cpp | 105 +++---------- tests/admin_utils.cpp | 4 +- tests/main.cpp | 16 +- tests/sync/networking_tests.cpp | 39 +++-- tests/utils/networking/proxy_server.cpp | 144 +++++++++--------- tests/utils/networking/proxy_server.hpp | 1 - 15 files changed, 239 insertions(+), 332 deletions(-) delete mode 100644 include/cpprealm/internal/generic_network_transport.hpp diff --git a/include/cpprealm/internal/generic_network_transport.hpp b/include/cpprealm/internal/generic_network_transport.hpp deleted file mode 100644 index ba4daf5f..00000000 --- a/include/cpprealm/internal/generic_network_transport.hpp +++ /dev/null @@ -1,41 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2022 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#ifndef CPPREALM_GENERIC_NETWORK_TRANSPORT_CPP -#define CPPREALM_GENERIC_NETWORK_TRANSPORT_CPP - -#include -#include - -namespace realm::internal::networking { - class DefaultTransport : public ::realm::networking::http_transport_client { - public: - DefaultTransport(const std::optional>& custom_http_headers = std::nullopt, - const std::optional& proxy_config = std::nullopt); - ~DefaultTransport() = default; - - void send_request_to_server(const ::realm::networking::request& request, - std::function&& completion); -private: - std::optional> m_custom_http_headers; - std::optional m_proxy_config; -}; - -} // namespace realm::internal::networking - -#endif //CPPREALM_GENERIC_NETWORK_TRANSPORT_CPP diff --git a/include/cpprealm/networking/networking.hpp b/include/cpprealm/networking/networking.hpp index d58ca391..024d38d1 100644 --- a/include/cpprealm/networking/networking.hpp +++ b/include/cpprealm/networking/networking.hpp @@ -94,36 +94,12 @@ namespace realm::networking { }; -// struct websocket_endpoint { -// // using port_type = std::uint_fast16_t; -// // /// Host address -// // std::string address; -// // /// Host port number -// // port_type port; -// // /// Includes access token in query. -// // std::string path; -// // /// Array of one or more websocket protocols -// std::vector protocols; -// // /// true if SSL should be used -// // bool is_ssl; -// std::string url; -// /// Optional proxy config -// std::optional proxy_configuration; -// }; - struct websocket_endpoint { - using port_type = std::uint_fast16_t; - /// Host address - std::string address; - /// Host port number - port_type port; - /// Includes access token in query. - std::string path; - /// Array of one or more websocket protocols + /// Array of one or more websocket protocols. std::vector protocols; - /// true if SSL should be used - bool is_ssl; - /// Optional proxy config + /// The websocket url to connect to. + std::string url; + /// Optional proxy config taken in from `realm::App::config`. std::optional proxy_configuration; }; @@ -157,12 +133,25 @@ namespace realm::networking { RLM_ERR_WEBSOCKET_FATAL_ERROR = 4405, }; - // Interface for providing http transport + // Interface for providing http transport struct http_transport_client { virtual ~http_transport_client() = default; virtual void send_request_to_server(const request& request, std::function&& completion) = 0; + + void set_proxy_configuration(::realm::internal::bridge::realm::sync_config::proxy_config proxy_config) { + m_proxy_config = proxy_config; + }; + + void set_custom_http_headers(std::map http_headers) { + m_custom_http_headers = http_headers; + }; + + protected: + std::optional> m_custom_http_headers; + std::optional<::realm::internal::bridge::realm::sync_config::proxy_config> m_proxy_config; }; + } //namespace realm::networking namespace realm { @@ -183,7 +172,8 @@ namespace realm::internal::networking { ::realm::app::Response to_core_response(const ::realm::networking::response&); ::realm::sync::WebSocketEndpoint to_core_websocket_endpoint(const ::realm::networking::websocket_endpoint & ep); - ::realm::networking::websocket_endpoint to_websocket_endpoint(const ::realm::sync::WebSocketEndpoint& ep); + ::realm::networking::websocket_endpoint to_websocket_endpoint(const ::realm::sync::WebSocketEndpoint& ep, + std::optional<::realm::internal::bridge::realm::sync_config::proxy_config> pc); } //namespace realm::internal::networking #endif//CPPREALM_NETWORKING_HPP diff --git a/include/cpprealm/networking/platform_networking.hpp b/include/cpprealm/networking/platform_networking.hpp index a3325a86..d072661e 100644 --- a/include/cpprealm/networking/platform_networking.hpp +++ b/include/cpprealm/networking/platform_networking.hpp @@ -20,7 +20,7 @@ #define CPPREALM_PLATFORM_NETWORKING_HPP #include -#include +#include #ifndef REALMCXX_VERSION_MAJOR #include @@ -42,15 +42,6 @@ namespace realm { namespace realm::networking { - /** - * Class used for setting a callback which is used - * to pass network configuration options to the sync_socket_provider - * before the websocket establishes a connection. - */ - struct websocket_event_handler { - std::function on_connect; - }; - /// The WebSocket base class that is used by the SyncClient to send data over the /// WebSocket connection with the server. This is the class that is returned by /// SyncSocketProvider::connect() when a connection to an endpoint is requested. @@ -233,6 +224,14 @@ namespace realm::networking { std::shared_ptr make_http_client(); void set_http_client_factory(std::function()>&&); + struct default_http_transport : public ::realm::networking::http_transport_client { + default_http_transport() = default; + ~default_http_transport() = default; + + void send_request_to_server(const ::realm::networking::request& request, + std::function&& completion); + }; + struct default_socket : public websocket_interface { default_socket(std::unique_ptr<::realm::sync::WebSocketInterface>&&); ~default_socket() = default; @@ -243,31 +242,24 @@ namespace realm::networking { std::shared_ptr<::realm::sync::WebSocketInterface> m_ws_interface; }; - struct default_socket_provider : public sync_socket_provider { - default_socket_provider(); - ~default_socket_provider() = default; std::unique_ptr connect(std::unique_ptr, websocket_endpoint &&) override; - void post(FunctionHandler&&) override; - SyncTimer create_timer(std::chrono::milliseconds delay, FunctionHandler&&) override; + private: std::shared_ptr<::realm::sync::SyncSocketProvider> m_provider; }; } namespace realm::internal::networking { - std::shared_ptr default_sync_socket_provider_factory(const std::shared_ptr& logger, - const std::string& user_agent_binding_info, - const std::string& user_agent_application_info); - std::shared_ptr create_http_client_shim(const std::shared_ptr<::realm::networking::http_transport_client>&); - std::unique_ptr<::realm::sync::SyncSocketProvider> create_sync_socket_provider_shim(const std::shared_ptr<::realm::networking::sync_socket_provider>& provider); + std::unique_ptr<::realm::sync::SyncSocketProvider> create_sync_socket_provider_shim(const std::shared_ptr<::realm::networking::sync_socket_provider>& provider, + std::optional proxy); } #endif//CPPREALM_PLATFORM_NETWORKING_HPP diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 93803197..b5ff0f11 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -124,7 +124,6 @@ set(HEADERS ../include/cpprealm/internal/bridge/timestamp.hpp ../include/cpprealm/internal/bridge/utils.hpp ../include/cpprealm/internal/bridge/uuid.hpp - ../include/cpprealm/internal/generic_network_transport.hpp ../include/cpprealm/internal/type_info.hpp ../include/cpprealm/networking/networking.hpp ../include/cpprealm/networking/platform_networking.hpp diff --git a/src/cpprealm/analytics.cpp b/src/cpprealm/analytics.cpp index fc8264f7..bd52efb8 100644 --- a/src/cpprealm/analytics.cpp +++ b/src/cpprealm/analytics.cpp @@ -37,7 +37,7 @@ #endif #include -#include +#include #include #include @@ -273,7 +273,7 @@ namespace realm { std::stringstream json_ss; json_ss << post_data; auto json_str = json_ss.str(); - auto transport = std::make_unique(); + auto transport = std::make_unique(); std::vector buffer; buffer.resize(5000); diff --git a/src/cpprealm/app.cpp b/src/cpprealm/app.cpp index 54e03572..f45b40ac 100644 --- a/src/cpprealm/app.cpp +++ b/src/cpprealm/app.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #ifndef REALMCXX_VERSION_MAJOR @@ -494,22 +493,24 @@ namespace realm { client_config.user_agent_application_info = config.app_id; app_config.app_id = config.app_id; -// if (config.websocket_event_handler) { -// auto websocket_provider = ::realm::internal::networking::default_sync_socket_provider_factory(util::Logger::get_default_logger(), -// client_config.user_agent_binding_info, -// client_config.user_agent_application_info, -// config.websocket_event_handler); -// client_config.socket_provider = websocket_provider; -// } + // Websocket provider configuration if (config.sync_socket_provider) { - client_config.socket_provider = ::realm::internal::networking::create_sync_socket_provider_shim(config.sync_socket_provider); + client_config.socket_provider = ::realm::internal::networking::create_sync_socket_provider_shim(config.sync_socket_provider, + config.proxy_configuration); } + // HTTP Transport configuration if (config.http_transport_client) { app_config.transport = internal::networking::create_http_client_shim(config.http_transport_client); } else { - app_config.transport = internal::networking::create_http_client_shim(std::make_shared(config.custom_http_headers, - config.proxy_configuration)); + app_config.transport = internal::networking::create_http_client_shim(std::make_shared()); + } + + if (config.proxy_configuration) { + config.http_transport_client->set_proxy_configuration(*config.proxy_configuration); + } + if (config.custom_http_headers) { + config.http_transport_client->set_custom_http_headers(*config.custom_http_headers); } app_config.base_url = config.base_url; diff --git a/src/cpprealm/internal/curl/network_transport.cpp b/src/cpprealm/internal/curl/network_transport.cpp index 35a9b3b6..4d28fc6e 100644 --- a/src/cpprealm/internal/curl/network_transport.cpp +++ b/src/cpprealm/internal/curl/network_transport.cpp @@ -17,13 +17,13 @@ //////////////////////////////////////////////////////////////////////////// #include -#include +#include #include #include -namespace realm::internal::networking { +namespace realm::networking { namespace { @@ -90,7 +90,7 @@ namespace realm::internal::networking { } static ::realm::networking::response do_http_request(const ::realm::networking::request& request, - const std::optional& proxy_config = std::nullopt) + const std::optional& proxy_config = std::nullopt) { CurlGlobalGuard curl_global_guard; auto curl = curl_easy_init(); @@ -168,14 +168,8 @@ namespace realm::internal::networking { } } // namespace - DefaultTransport::DefaultTransport(const std::optional>& custom_http_headers, - const std::optional& proxy_config) { - m_custom_http_headers = custom_http_headers; - m_proxy_config = proxy_config; - } - - void DefaultTransport::send_request_to_server(const ::realm::networking::request& request, - std::function&& completion_block) { + void default_http_transport::send_request_to_server(const ::realm::networking::request& request, + std::function&& completion_block) { { if (m_custom_http_headers) { auto req_copy = request; @@ -186,4 +180,4 @@ namespace realm::internal::networking { completion_block(do_http_request(request, m_proxy_config)); } } -} // namespace realm::internal::networking +} // namespace realm::networking diff --git a/src/cpprealm/internal/network/network_transport.cpp b/src/cpprealm/internal/network/network_transport.cpp index 41d6eb39..1a25834f 100644 --- a/src/cpprealm/internal/network/network_transport.cpp +++ b/src/cpprealm/internal/network/network_transport.cpp @@ -20,7 +20,7 @@ #include #endif #include -#include +#include #include #include @@ -32,7 +32,7 @@ #include -namespace realm::internal::networking { +namespace realm::networking { struct DefaultSocket : realm::sync::network::Socket { DefaultSocket(realm::sync::network::Service& service) : realm::sync::network::Socket(service) @@ -84,12 +84,6 @@ namespace realm::internal::networking { realm::sync::network::ReadAheadBuffer m_read_buffer; }; - DefaultTransport::DefaultTransport(const std::optional>& custom_http_headers, - const std::optional& proxy_config) { - m_custom_http_headers = custom_http_headers; - m_proxy_config = proxy_config; - } - inline bool is_valid_ipv4(const std::string& ip) { std::stringstream ss(ip); std::string segment; @@ -130,10 +124,8 @@ namespace realm::internal::networking { } } - void DefaultTransport::send_request_to_server(const ::realm::networking::request& request, - std::function&& completion_block) { - bool disable_ssl = true; // TODO: pull this from proxy config - + void default_http_transport::send_request_to_server(const ::realm::networking::request& request, + std::function&& completion_block) { const auto uri = realm::util::Uri(request.url); std::string userinfo, host, port; uri.get_auth(userinfo, host, port); @@ -191,7 +183,13 @@ namespace realm::internal::networking { if (m_proxy_config) { realm::sync::HTTPRequest req; req.method = realm::sync::HTTPMethod::Connect; - req.headers.emplace("Host", util::format("%1:%2", host, is_localhost ? "9090" : "443")); + + if (is_ipv4) { + req.headers.emplace("Host", util::format("%1:%2", host, port)); + } else { + req.headers.emplace("Host", util::format("%1:%2", host, is_localhost ? "9090" : "443")); + } + if (m_proxy_config->username_password) { auto userpass = util::format("%1:%2", m_proxy_config->username_password->first, m_proxy_config->username_password->second); std::string encoded_userpass; @@ -227,7 +225,7 @@ namespace realm::internal::networking { m_ssl_context.use_included_certificate_roots(); #endif - if (url_scheme == URLScheme::HTTPS && !disable_ssl) { + if (url_scheme == URLScheme::HTTPS) { socket.ssl_stream.emplace(socket, m_ssl_context, Stream::client); socket.ssl_stream->set_host_name(host); // Throws @@ -246,8 +244,8 @@ namespace realm::internal::networking { headers["Content-Length"] = util::to_string(request.body.size()); } - if (m_custom_http_headers) { - for (auto& header : *m_custom_http_headers) { + if (this->m_custom_http_headers) { + for (auto& header : *this->m_custom_http_headers) { headers.emplace(header); } } @@ -310,7 +308,7 @@ namespace realm::internal::networking { return; } }; - if (url_scheme == URLScheme::HTTPS && !disable_ssl) { + if (url_scheme == URLScheme::HTTPS) { socket.async_handshake(std::move(handler)); // Throws } else { handler(std::error_code()); @@ -319,5 +317,5 @@ namespace realm::internal::networking { service.run(); } -} //namespace realm::internal::networking +} //namespace realm::networking diff --git a/src/cpprealm/networking/networking.cpp b/src/cpprealm/networking/networking.cpp index e63c0e60..005d8524 100644 --- a/src/cpprealm/networking/networking.cpp +++ b/src/cpprealm/networking/networking.cpp @@ -3,6 +3,8 @@ #include #include #include +#include +#include namespace realm::internal::networking { ::realm::networking::request to_request(const ::realm::app::Request& core_request) { @@ -45,13 +47,48 @@ namespace realm::internal::networking { return core_response; } - ::realm::sync::WebSocketEndpoint to_core_websocket_endpoint(const ::realm::networking::websocket_endpoint & ep) { + ::realm::sync::ProtocolEnvelope to_protocol_envelope(const std::string& protocol) noexcept + { + if (protocol == "realm:") { + return ::realm::sync::ProtocolEnvelope::realm; + } else if (protocol == "realms:") { + return ::realm::sync::ProtocolEnvelope::realms; + } else if (protocol == "ws:") { + return ::realm::sync::ProtocolEnvelope::ws; + } else if (protocol == "wss:") { + return ::realm::sync::ProtocolEnvelope::wss; + } + REALM_TERMINATE("Unrecognized websocket protocol"); + } + + ::realm::sync::WebSocketEndpoint to_core_websocket_endpoint(const ::realm::networking::websocket_endpoint& ep) { ::realm::sync::WebSocketEndpoint core_ep; - core_ep.address = ep.address; - core_ep.port = ep.port; - core_ep.path = ep.path; + + auto uri = util::Uri(ep.url); + auto protocol = to_protocol_envelope(uri.get_scheme()); + + auto uri_path = uri.get_auth(); + if (uri_path.find("//") == 0) { + uri_path = uri_path.substr(2); + } + + std::string address; + std::string port; + size_t colon_pos = uri_path.find(':'); + if (colon_pos != std::string::npos) { + // Extract the address + address = uri_path.substr(0, colon_pos); + // Extract the port + port = uri_path.substr(colon_pos + 1); + } else { + REALM_TERMINATE("Invalid URL"); + } + + core_ep.address = address; + core_ep.port = std::stoi(port); + core_ep.path = uri.get_path() + uri.get_query(); core_ep.protocols = ep.protocols; - core_ep.is_ssl = ep.is_ssl; + core_ep.is_ssl = ::realm::sync::is_ssl(protocol); if (ep.proxy_configuration) { core_ep.proxy = SyncConfig::ProxyConfig(); @@ -69,13 +106,13 @@ namespace realm::internal::networking { return core_ep; } - ::realm::networking::websocket_endpoint to_websocket_endpoint(const ::realm::sync::WebSocketEndpoint& core_ep) { + ::realm::networking::websocket_endpoint to_websocket_endpoint(const ::realm::sync::WebSocketEndpoint& core_ep, + std::optional pc) { ::realm::networking::websocket_endpoint ep; - ep.address = core_ep.address; - ep.port = core_ep.port; - ep.path = core_ep.path; ep.protocols = core_ep.protocols; - ep.is_ssl = core_ep.is_ssl; + const auto& port = core_ep.proxy ? core_ep.proxy->port : core_ep.port; + ep.url = util::format("%1://%2:%3%4", core_ep.is_ssl ? "wss" : "ws", core_ep.proxy ? core_ep.proxy->address : core_ep.address, port, core_ep.path); + ep.proxy_configuration = pc; return ep; } -} //namespace internal::networking \ No newline at end of file +} //namespace internal::networking diff --git a/src/cpprealm/networking/platform_networking.cpp b/src/cpprealm/networking/platform_networking.cpp index b95c9d22..9d296124 100644 --- a/src/cpprealm/networking/platform_networking.cpp +++ b/src/cpprealm/networking/platform_networking.cpp @@ -8,7 +8,7 @@ namespace realm::networking { std::shared_ptr make_default_http_client() { - return std::make_shared(); + return std::make_shared(); } std::function()> s_http_client_factory = make_default_http_client; @@ -16,31 +16,6 @@ namespace realm::networking { s_http_client_factory = std::move(factory_fn); } - struct default_websocket_observer_cpprealm : public websocket_observer { - default_websocket_observer_cpprealm(std::unique_ptr<::realm::sync::WebSocketObserver>&& o) : m_observer(std::move(o)) { } - ~default_websocket_observer_cpprealm() = default; - - void websocket_connected_handler(const std::string& protocol) override { - m_observer->websocket_connected_handler(protocol); - } - - void websocket_error_handler() override { - m_observer->websocket_error_handler(); - } - - bool websocket_binary_message_received(std::string_view data) override { - return m_observer->websocket_binary_message_received(data); - } - - bool websocket_closed_handler(bool was_clean, websocket_err_codes error_code, - std::string_view message) override { - return m_observer->websocket_closed_handler(was_clean, static_cast<::realm::sync::websocket::WebSocketError>(error_code), message); - } - - private: - std::shared_ptr<::realm::sync::WebSocketObserver> m_observer; - }; - struct default_websocket_observer_core : public ::realm::sync::WebSocketObserver { default_websocket_observer_core(std::unique_ptr&& o) : m_observer(std::move(o)) { } ~default_websocket_observer_core() = default; @@ -54,7 +29,7 @@ namespace realm::networking { } bool websocket_binary_message_received(util::Span data) override { - return m_observer->websocket_binary_message_received(data.data()); + return m_observer->websocket_binary_message_received(std::string_view(data.data(), data.size())); } bool websocket_closed_handler(bool was_clean, ::realm::sync::websocket::WebSocketError error_code, @@ -77,16 +52,14 @@ namespace realm::networking { }; default_socket_provider::default_socket_provider() { - auto user_agent = util::format("RealmSync/%1 (%2) %3 %4", REALM_VERSION_STRING, util::get_platform_info(), "user_agent_binding_info", "user_agent_application_info"); + auto user_agent_binding_info = std::string("RealmCpp/") + std::string(REALMCXX_VERSION_STRING); + auto user_agent_application_info = "";//app_id; TODO: Should we pass the app id? + + auto user_agent = util::format("RealmSync/%1 (%2) %3 %4", REALM_VERSION_STRING, util::get_platform_info(), user_agent_binding_info, user_agent_application_info); m_provider = std::make_unique<::realm::sync::websocket::DefaultSocketProvider>(util::Logger::get_default_logger(), user_agent); } std::unique_ptr default_socket_provider::connect(std::unique_ptr o, websocket_endpoint&& ep) { -// internal::bridge::realm::sync_config::proxy_config pc; -// pc.port = 1234; -// pc.address = "127.0.0.1"; -// ep.proxy_configuration = pc; - auto ws_interface = m_provider->connect(std::make_unique(std::move(o)), internal::networking::to_core_websocket_endpoint(ep)); return std::make_unique(std::move(ws_interface)); } @@ -115,53 +88,6 @@ namespace realm::networking { } namespace realm::internal::networking { - std::shared_ptr default_sync_socket_provider_factory(const std::shared_ptr& logger, - const std::string& user_agent_binding_info, - const std::string& user_agent_application_info, - const std::shared_ptr<::realm::networking::websocket_event_handler>& configuration_handler) { - class default_sync_socket_provider_shim final : public ::realm::sync::SyncSocketProvider { - public: - explicit default_sync_socket_provider_shim(const std::shared_ptr& logger, - const std::string& user_agent_binding_info, - const std::string& user_agent_application_info, - const std::shared_ptr<::realm::networking::websocket_event_handler>& configuration_handler) { - auto user_agent = util::format("RealmSync/%1 (%2) %3 %4", REALM_VERSION_STRING, util::get_platform_info(), user_agent_binding_info, user_agent_application_info); - m_default_provider = std::make_unique<::realm::sync::websocket::DefaultSocketProvider>(logger, user_agent); - m_websocket_event_handler = configuration_handler; - } - - ~default_sync_socket_provider_shim() = default; - - std::unique_ptr<::realm::sync::WebSocketInterface> connect(std::unique_ptr<::realm::sync::WebSocketObserver> observer, - ::realm::sync::WebSocketEndpoint&& endpoint) override { - if (m_websocket_event_handler) { - auto ep = m_websocket_event_handler->on_connect(to_websocket_endpoint(endpoint)); - return m_default_provider->connect(std::move(observer), to_core_websocket_endpoint(std::move(ep))); - } - - return m_default_provider->connect(std::move(observer), std::move(endpoint)); - } - - void post(FunctionHandler&& handler) override { - m_default_provider->post(std::move(handler)); - } - - ::realm::sync::SyncSocketProvider::SyncTimer create_timer(std::chrono::milliseconds delay, - ::realm::sync::SyncSocketProvider::FunctionHandler&& handler) override { - return m_default_provider->create_timer(delay, std::move(handler)); - } - - void stop(bool b = false) override { - m_default_provider->stop(b); - } - private: - std::shared_ptr<::realm::networking::websocket_event_handler> m_websocket_event_handler; - std::unique_ptr<::realm::sync::websocket::DefaultSocketProvider> m_default_provider; - }; - - return std::make_shared(logger, user_agent_binding_info, - user_agent_application_info, configuration_handler); - } std::unique_ptr<::realm::sync::WebSocketInterface> create_websocket_interface_shim(std::unique_ptr<::realm::networking::websocket_interface>&& m_interface) { struct core_websocket_interface_shim : public ::realm::sync::WebSocketInterface { @@ -170,7 +96,8 @@ namespace realm::internal::networking { void async_write_binary(util::Span data, sync::SyncSocketProvider::FunctionHandler&& handler) override { auto handler_ptr = handler.release(); - m_interface->async_write_binary(data.data(), [ptr = std::move(handler_ptr)](::realm::networking::websocket_interface::status s) { + auto b = std::string_view(data.data(), data.size()); + m_interface->async_write_binary(b, [ptr = std::move(handler_ptr)](::realm::networking::websocket_interface::status s) { auto uf = util::UniqueFunction(std::move(ptr)); return uf(s.operator ::realm::Status()); }); @@ -235,8 +162,8 @@ namespace realm::internal::networking { return std::make_unique(std::move(m_observer)); } - std::unique_ptr<::realm::sync::SyncSocketProvider> create_sync_socket_provider_shim(const std::shared_ptr<::realm::networking::sync_socket_provider>& provider) { - + std::unique_ptr<::realm::sync::SyncSocketProvider> create_sync_socket_provider_shim(const std::shared_ptr<::realm::networking::sync_socket_provider>& provider, + std::optional pc) { struct sync_timer_shim final : public ::realm::sync::SyncSocketProvider::Timer { sync_timer_shim(std::unique_ptr<::realm::networking::sync_socket_provider::timer>&& timer) : m_timer(std::move(timer)) {} ~sync_timer_shim() = default; @@ -250,15 +177,17 @@ namespace realm::internal::networking { }; struct sync_socket_provider_shim final : public ::realm::sync::SyncSocketProvider { - explicit sync_socket_provider_shim(const std::shared_ptr<::realm::networking::sync_socket_provider>& provider) { + explicit sync_socket_provider_shim(const std::shared_ptr<::realm::networking::sync_socket_provider>& provider, + std::optional proxy_config) { m_provider = provider; + m_proxy_config = proxy_config; } sync_socket_provider_shim() = delete; ~sync_socket_provider_shim() = default; std::unique_ptr<::realm::sync::WebSocketInterface> connect(std::unique_ptr<::realm::sync::WebSocketObserver> observer, ::realm::sync::WebSocketEndpoint&& ep) override { - auto cpprealm_ws_interface = m_provider->connect(create_websocket_observer_from_core_shim(std::move(observer)), to_websocket_endpoint(std::move(ep))); + auto cpprealm_ws_interface = m_provider->connect(create_websocket_observer_from_core_shim(std::move(observer)), to_websocket_endpoint(std::move(ep), m_proxy_config)); return create_websocket_interface_shim(std::move(cpprealm_ws_interface)); } @@ -279,10 +208,10 @@ namespace realm::internal::networking { return std::make_unique(m_provider->create_timer(delay, std::move(fn))); } private: - std::shared_ptr<::realm::networking::websocket_event_handler> m_websocket_event_handler; std::shared_ptr<::realm::networking::sync_socket_provider> m_provider; + std::optional m_proxy_config; }; - return std::make_unique(std::move(provider)); + return std::make_unique(std::move(provider), pc); } -} //namespace internal::networking \ No newline at end of file +} //namespace internal::networking diff --git a/tests/admin_utils.cpp b/tests/admin_utils.cpp index 6b1d9014..a64c6865 100644 --- a/tests/admin_utils.cpp +++ b/tests/admin_utils.cpp @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include "admin_utils.hpp" #include "external/json/json.hpp" @@ -30,7 +30,7 @@ namespace Admin { std::mutex Admin::Session::mutex; static app::Response do_http_request(app::Request &&request) { - internal::networking::DefaultTransport transport; + networking::default_http_transport transport; std::promise p; std::future f = p.get_future(); transport.send_request_to_server(::realm::internal::networking::to_request(std::move(request)), diff --git a/tests/main.cpp b/tests/main.cpp index 5c65451f..2210994e 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -41,15 +41,15 @@ int main(int argc, char *argv[]) { } #ifdef CPPREALM_ENABLE_SYNC_TESTS -// std::optional baas_manager; -// if (const char* api_key = getenv("APIKEY")) { -// baas_manager.emplace(std::string(api_key)); -// baas_manager->start(); -// auto url = baas_manager->wait_for_container(); -// Admin::Session::shared().prepare(url); -// } else { + std::optional baas_manager; + if (const char* api_key = getenv("APIKEY")) { + baas_manager.emplace(std::string(api_key)); + baas_manager->start(); + auto url = baas_manager->wait_for_container(); + Admin::Session::shared().prepare(url); + } else { Admin::Session::shared().prepare(); -// } + } auto app_id = Admin::Session::shared().create_app({"str_col", "_id"}); Admin::Session::shared().cache_app_id(app_id); diff --git a/tests/sync/networking_tests.cpp b/tests/sync/networking_tests.cpp index 58785b36..a4c71d52 100644 --- a/tests/sync/networking_tests.cpp +++ b/tests/sync/networking_tests.cpp @@ -4,34 +4,50 @@ #include "../utils/networking/proxy_server.hpp" using namespace realm; -TEST_CASE("websocket_handler_called", "[sync]") { +TEST_CASE("sends plaintext data to proxy", "[proxy]") { -// tests::utils::proxy_server::config cfg; -// cfg.port = 1234; -// cfg.client_uses_ssl = false; -// cfg.server_uses_ssl = false; -// tests::utils::proxy_server server(std::move(cfg)); + tests::utils::proxy_server::config cfg; + cfg.port = 1234; + cfg.server_uses_ssl = false; // Set to true if using services.cloud.mongodb.com + tests::utils::proxy_server server(std::move(cfg)); proxy_config pc; pc.port = 1234; pc.address = "127.0.0.1"; realm::App::configuration config; -// config.proxy_configuration = pc; - config.app_id = Admin::Session::shared().cached_app_id(); + config.proxy_configuration = pc; + config.app_id =Admin::Session::shared().cached_app_id(); config.base_url = Admin::Session::shared().base_url(); - struct foo_socket_provider : public ::realm::networking::default_socket_provider { std::unique_ptr<::realm::networking::websocket_interface> connect(std::unique_ptr<::realm::networking::websocket_observer> o, ::realm::networking::websocket_endpoint&& ep) override { -// url = url.replace("wss://", "ws://"); + const std::string from = "wss:"; + const std::string to = "ws:"; + if (ep.url.find(from) == 0) { + ep.url.replace(0, from.length(), to); + } return ::realm::networking::default_socket_provider::connect(std::move(o), std::move(ep)); } }; - config.sync_socket_provider = std::make_shared(); + struct foo_http_transport : public ::realm::networking::default_http_transport { + void send_request_to_server(const ::realm::networking::request& request, + std::function&& completion) override { + auto req_copy = request; + const std::string from = "https:"; + const std::string to = "http:"; + if (req_copy.url.find(from) == 0) { + req_copy.url.replace(0, from.length(), to); + } + + return ::realm::networking::default_http_transport::send_request_to_server(req_copy, std::move(completion)); + } + }; + config.http_transport_client = std::make_shared(); + auto app = realm::App(config); app.get_sync_manager().set_log_level(logger::level::all); @@ -58,5 +74,4 @@ TEST_CASE("websocket_handler_called", "[sync]") { CHECK(sub.name == "foo-strings"); CHECK(sub.object_class_name == "AllTypesObject"); CHECK(sub.query_string == "str_col == \"foo\""); - } \ No newline at end of file diff --git a/tests/utils/networking/proxy_server.cpp b/tests/utils/networking/proxy_server.cpp index 86df8ea4..68fc8a79 100644 --- a/tests/utils/networking/proxy_server.cpp +++ b/tests/utils/networking/proxy_server.cpp @@ -1,4 +1,5 @@ #include "proxy_server.hpp" +#include "realm/util/terminate.hpp" #include #include @@ -9,13 +10,12 @@ namespace realm::tests::utils { class proxy_session : public std::enable_shared_from_this { public: - proxy_session(tcp::socket client_socket, std::shared_ptr ctx, bool client_uses_ssl, bool server_uses_ssl) + proxy_session(tcp::socket client_socket, std::shared_ptr ctx, bool server_uses_ssl) : m_client_socket(std::move(client_socket)), m_server_socket(m_client_socket.get_executor()), m_resolver(m_client_socket.get_executor()), m_ssl_ctx(ctx), m_ssl_server_socket(m_client_socket.get_executor(), *ctx), - m_client_uses_ssl(client_uses_ssl), m_server_uses_ssl(server_uses_ssl) {} void start() { @@ -32,10 +32,10 @@ namespace realm::tests::utils { if (request.substr(0, 7) == "CONNECT") { handle_connect(request); } else { - std::cerr << "Unsupported method: " << request.substr(0, 7) << std::endl; + REALM_TERMINATE("Unsupported HTTP method in proxy"); } } else { - std::cerr << "Error reading client request: " << ec.message() << std::endl; + REALM_TERMINATE("Error reading client request"); } }); } @@ -57,7 +57,7 @@ namespace realm::tests::utils { if (!ec) { connect_to_server(*results.begin(), host); } else { - std::cerr << "Error resolving host: " << ec.message() << std::endl; + REALM_TERMINATE("Proxy: Error resolving host"); } }); } else { @@ -76,7 +76,7 @@ namespace realm::tests::utils { if (!ec) { do_ssl_handshake_to_server(host); } else { - std::cerr << "Error connecting to server: " << ec.message() << std::endl; + REALM_TERMINATE("Proxy: Error connecting to server"); } }); } else { @@ -87,7 +87,7 @@ namespace realm::tests::utils { if (!ec) { do_write_connect_response(); } else { - std::cerr << "Error connecting to server: " << ec.message() << std::endl; + REALM_TERMINATE("Proxy: Error connecting to server"); } }); } @@ -101,7 +101,7 @@ namespace realm::tests::utils { // Set SNI Hostname (many hosts need this to handshake successfully) if (!SSL_set_tlsext_host_name(m_ssl_server_socket.native_handle(), hostname.c_str())) { - throw std::runtime_error("Could not set hostname"); + REALM_TERMINATE("Proxy: Could not set host name"); } m_ssl_server_socket.async_handshake(asio::ssl::stream_base::client, @@ -109,7 +109,7 @@ namespace realm::tests::utils { if (!ec) { do_write_connect_response(); } else { - std::cerr << "Error performing SSL handshake: " << ec.message() << std::endl; + REALM_TERMINATE("Proxy: Error performing SSL handshake."); } }); } @@ -121,7 +121,7 @@ namespace realm::tests::utils { if (!ec) { do_read_client(); } else { - std::cerr << "Error writing connect response: " << ec.message() << std::endl; + REALM_TERMINATE("Proxy: Error writing CONNECT response."); } }); } @@ -133,9 +133,7 @@ namespace realm::tests::utils { if (!ec) { do_ssl_read_server(); } else { - std::cerr << "Error writing to server: " << ec.message() << std::endl; - m_client_socket.close(); - m_server_socket.close(); + REALM_TERMINATE("Proxy: Error writing to server."); } }); } @@ -147,9 +145,7 @@ namespace realm::tests::utils { if (!ec) { do_write_to_client(length); } else { - std::cerr << "Error reading from server (SSL): " << ec.message() << std::endl; - m_client_socket.close(); - m_server_socket.close(); + REALM_TERMINATE("Proxy: Error reading from server."); } }); } @@ -167,26 +163,11 @@ namespace realm::tests::utils { do_write_to_server(length); } } else { - std::cerr << "Error reading from client: " << ec.message() << std::endl; - m_client_socket.close(); - m_server_socket.close(); + REALM_TERMINATE("Proxy: Error reading from client."); } }); } - void handle_http_req_res() { - auto length = m_client_socket.read_some(boost::asio::buffer(m_client_buffer)); - boost::asio::write(m_server_socket, boost::asio::buffer(std::string(m_client_buffer.data()), length)); - length = m_server_socket.read_some(boost::asio::buffer(m_server_buffer)); - - auto res = std::string(m_server_buffer.data(), length); - bool upgrade_to_websocket = res.find("HTTP/1.1 101 Switching Protocols") != std::string::npos; - auto bytes_written = boost::asio::write(m_client_socket, boost::asio::buffer(std::string(m_server_buffer.data()), length)); - if (upgrade_to_websocket) { - upgrade_client_to_websocket(bytes_written); - } - } - void do_read_from_websocket_client() { auto self(shared_from_this()); m_client_socket.async_read_some(boost::asio::buffer(m_client_buffer), @@ -195,51 +176,74 @@ namespace realm::tests::utils { do_write_to_websocket_server(length); do_read_from_websocket_client(); } else { - // Handle error + REALM_TERMINATE("Proxy: Error reading from websocket client."); } }); + } void do_write_to_websocket_server(std::size_t length) { auto self(shared_from_this()); - boost::asio::async_write(m_server_socket, boost::asio::buffer(m_client_buffer.data(), length), - [this, self](boost::system::error_code ec, std::size_t /*length*/) { - if (!ec) { - do_read_from_websocket_client(); - } else { - // Handle error - } - }); + if (m_server_uses_ssl) { + boost::asio::async_write(m_ssl_server_socket, boost::asio::buffer(m_client_buffer.data(), length), + [this, self](boost::system::error_code ec, std::size_t) { + if (!ec) { + do_read_from_websocket_client(); + } else { + REALM_TERMINATE("Proxy: Error writing to websocket server."); + } + }); + } else { + boost::asio::async_write(m_server_socket, boost::asio::buffer(m_client_buffer.data(), length), + [this, self](boost::system::error_code ec, std::size_t /*length*/) { + if (!ec) { + do_read_from_websocket_client(); + } else { + REALM_TERMINATE("Proxy: Error writing to websocket server."); + } + }); + } } void do_read_from_websocket_server() { auto self(shared_from_this()); - m_server_socket.async_read_some(boost::asio::buffer(m_server_buffer), - [this, self](boost::system::error_code ec, std::size_t length) { - if (!ec) { - do_write_to_websocket_client(length); - do_read_from_websocket_server(); - } else { - // Handle error - } - }); + if (m_server_uses_ssl) { + m_ssl_server_socket.async_read_some(boost::asio::buffer(m_server_buffer), + [this, self](boost::system::error_code ec, std::size_t length) { + if (!ec) { + do_write_to_websocket_client(length); + do_read_from_websocket_server(); + } else { + REALM_TERMINATE("Proxy: Error reading from websocket server."); + } + }); + } else { + m_server_socket.async_read_some(boost::asio::buffer(m_server_buffer), + [this, self](boost::system::error_code ec, std::size_t length) { + if (!ec) { + do_write_to_websocket_client(length); + do_read_from_websocket_server(); + } else { + REALM_TERMINATE("Proxy: Error reading from websocket server."); + } + }); + } } void do_write_to_websocket_client(std::size_t length) { auto self(shared_from_this()); - boost::asio::async_write(m_client_socket, boost::asio::buffer(m_server_buffer.data(), length), - [this, self](boost::system::error_code ec, std::size_t /*length*/) { - if (!ec) { - do_read_from_websocket_server(); - } else { - // Handle error - } - }); - } + boost::asio::async_write(m_client_socket, boost::asio::buffer(m_server_buffer.data(), length), + [this, self](boost::system::error_code ec, std::size_t) { + if (!ec) { + do_read_from_websocket_server(); + } else { + REALM_TERMINATE("Proxy: Error writing from websocket client."); + } + }); + } void upgrade_client_to_websocket(std::size_t length) { - auto self(shared_from_this()); do_read_from_websocket_client(); do_read_from_websocket_server(); } @@ -251,9 +255,7 @@ namespace realm::tests::utils { if (!ec) { do_read_server(); } else { - std::cerr << "Error writing to server: " << ec.message() << std::endl; - m_client_socket.close(); - m_server_socket.close(); + REALM_TERMINATE("Proxy: Error writing to server."); } }); } @@ -265,9 +267,7 @@ namespace realm::tests::utils { if (!ec) { do_write_to_client(length); } else { - std::cerr << "Error reading from server: " << ec.message() << std::endl; - m_client_socket.close(); - m_server_socket.close(); + REALM_TERMINATE("Proxy: Error reading from server."); } }); } @@ -275,13 +275,11 @@ namespace realm::tests::utils { void do_write_to_client(std::size_t length) { auto self(shared_from_this()); auto res = std::string(m_server_buffer.data(), length); - std::cout << "Server to Client: " << res << std::endl; bool upgrade_to_websocket = res.find("HTTP/1.1 101 Switching Protocols") != std::string::npos; boost::asio::async_write(m_client_socket, boost::asio::buffer(std::string(m_server_buffer.data()), length), [this, self, upgrade_to_websocket](boost::system::error_code ec, std::size_t bytes_written) { if (!ec) { - std::cout << "BYTES WRITTEN: " << bytes_written << std::endl; if (upgrade_to_websocket) { upgrade_client_to_websocket(bytes_written); } else { @@ -292,14 +290,11 @@ namespace realm::tests::utils { } } } else { - std::cerr << "Error writing to client: " << ec.message() << std::endl; - m_client_socket.close(); - m_server_socket.close(); + REALM_TERMINATE("Proxy: Error writing to client."); } }); } - bool m_client_uses_ssl = false; bool m_server_uses_ssl = false; tcp::socket m_client_socket; @@ -318,7 +313,6 @@ namespace realm::tests::utils { proxy_server::proxy_server(const config &cfg) : m_config(cfg), m_strand(m_io_context) { m_acceptor = std::make_unique(m_io_context, tcp::endpoint(tcp::v4(), m_config.port)); - asio::ssl::context ctx(asio::ssl::context::tlsv13_client); m_ssl_ctx = std::make_shared(asio::ssl::context::tlsv13_client); m_ssl_ctx->set_default_verify_paths(); @@ -343,9 +337,9 @@ namespace realm::tests::utils { asio::bind_executor(m_strand, [this](std::error_code ec, asio::ip::tcp::socket socket) { if (!ec) { - std::make_shared(std::move(socket), m_ssl_ctx, m_config.client_uses_ssl, m_config.server_uses_ssl)->start(); + std::make_shared(std::move(socket), m_ssl_ctx, m_config.server_uses_ssl)->start(); } do_accept(); })); } -} // namespace realm::tests::utils \ No newline at end of file +} // namespace realm::tests::utils diff --git a/tests/utils/networking/proxy_server.hpp b/tests/utils/networking/proxy_server.hpp index 93655e80..764f6d12 100644 --- a/tests/utils/networking/proxy_server.hpp +++ b/tests/utils/networking/proxy_server.hpp @@ -13,7 +13,6 @@ namespace realm::tests::utils { public: struct config { short port = 1234; - bool client_uses_ssl = false; bool server_uses_ssl = false; };