diff --git a/CMakeLists.txt b/CMakeLists.txt index d6abfe709..f8b067590 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -122,24 +122,8 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang" target_compile_options(cpprealm PRIVATE -Wall -Wextra -pedantic -Werror) endif() -if(APPLE OR MSVC) - target_sources(cpprealm PRIVATE src/cpprealm/internal/network/network_transport.cpp) -elseif(ANDROID) - set(REALM_ANDROID) - target_sources(cpprealm PRIVATE src/cpprealm/internal/network/network_transport.cpp) -else() - find_package(CURL) - if(NOT CURL_FOUND) - message(WARNING "CURL not found. Realm C++ will use internal networking components instead.") - target_sources(cpprealm PRIVATE src/cpprealm/internal/network/network_transport.cpp) - else() - target_link_libraries(cpprealm PUBLIC CURL::libcurl) - target_sources(cpprealm PRIVATE src/cpprealm/internal/curl/network_transport.cpp) - endif() - - if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.0) - target_link_libraries(cpprealm PRIVATE stdc++fs) - endif() +if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.0) + target_link_libraries(cpprealm PRIVATE stdc++fs) endif() if(MSVC AND NOT DEFINED CMAKE_MSVC_RUNTIME_LIBRARY) diff --git a/Package.swift b/Package.swift index 2bffd245f..644a19c07 100644 --- a/Package.swift +++ b/Package.swift @@ -41,8 +41,6 @@ let cppSdkTarget: Target = .target( ], path: ".", exclude: [ - "src/cpprealm/internal/curl", - "src/cpprealm/internal/network", "src/cpprealm/util/config.in.h", "realm-core" ], diff --git a/include/cpprealm/networking/websocket.hpp b/include/cpprealm/networking/websocket.hpp index 1fabb51e5..1e4ea9889 100644 --- a/include/cpprealm/networking/websocket.hpp +++ b/include/cpprealm/networking/websocket.hpp @@ -77,7 +77,7 @@ namespace realm::networking { read_error = 4402, write_error = 4403, retry_error = 4404, - fata_error = 4405, + fatal_error = 4405, }; using status = ::realm::internal::bridge::status; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 54acc4e4e..654279808 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -51,6 +51,7 @@ set(SOURCES cpprealm/internal/bridge/thread_safe_reference.cpp cpprealm/internal/bridge/timestamp.cpp cpprealm/internal/bridge/uuid.cpp + cpprealm/internal/networking/network_transport.cpp cpprealm/internal/networking/shims.cpp cpprealm/internal/networking/utils.cpp cpprealm/internal/scheduler/realm_core_scheduler.cpp diff --git a/src/cpprealm/internal/apple/network_transport.mm b/src/cpprealm/internal/apple/network_transport.mm deleted file mode 100644 index c4c5da1db..000000000 --- a/src/cpprealm/internal/apple/network_transport.mm +++ /dev/null @@ -1,122 +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. -// -//////////////////////////////////////////////////////////////////////////// - -#include -#include - -#include -#include -#include -#include -#include - -#include -#include - -namespace realm::networking { - - void default_http_transport::send_request_to_server(const request& request, - std::function&& completion_block) { - NSURL* url = [NSURL URLWithString:[NSString stringWithCString:request.url.c_str() - encoding:NSUTF8StringEncoding]]; - NSMutableURLRequest *urlRequest = [[NSMutableURLRequest alloc] initWithURL:url]; - - switch (request.method) { - case http_method::get: - [urlRequest setHTTPMethod:@"GET"]; - break; - case http_method::post: - [urlRequest setHTTPMethod:@"POST"]; - break; - case http_method::put: - [urlRequest setHTTPMethod:@"PUT"]; - break; - case http_method::patch: - [urlRequest setHTTPMethod:@"PATCH"]; - break; - case http_method::del: - [urlRequest setHTTPMethod:@"DELETE"]; - break; - } - - for (auto& header : request.headers) { - [urlRequest addValue:[NSString stringWithCString:header.second.c_str() encoding:NSUTF8StringEncoding] - forHTTPHeaderField:[NSString stringWithCString:header.first.c_str() encoding:NSUTF8StringEncoding]]; - } - if (m_configuration.custom_http_headers) { - for (auto& header : *m_configuration.custom_http_headers) { - [urlRequest addValue:[NSString stringWithCString:header.second.c_str() encoding:NSUTF8StringEncoding] - forHTTPHeaderField:[NSString stringWithCString:header.first.c_str() encoding:NSUTF8StringEncoding]]; - } - } - if (request.method != http_method::get && !request.body.empty()) { - [urlRequest setHTTPBody:[[NSString stringWithCString:request.body.c_str() encoding:NSUTF8StringEncoding] - dataUsingEncoding:NSUTF8StringEncoding]]; - } - - NSURLSession *session; - if (m_configuration.proxy_config) { - NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration]; - NSString *proxyHost = @(m_configuration.proxy_config->address.c_str()); - NSInteger proxyPort = m_configuration.proxy_config->port; - sessionConfiguration.connectionProxyDictionary = @{ - @"HTTPSEnable": @YES, - @"HTTPSProxy": @(proxyPort), - @"HTTPSPort": proxyHost, - }; - sessionConfiguration.requestCachePolicy = NSURLRequestCachePolicy::NSURLRequestReloadIgnoringLocalCacheData; - urlRequest.cachePolicy = NSURLRequestCachePolicy::NSURLRequestReloadIgnoringLocalCacheData; - [[NSURLCache sharedURLCache] removeAllCachedResponses]; - - session = [NSURLSession sessionWithConfiguration:sessionConfiguration]; - if (m_configuration.proxy_config->username_password) { - auto userpass = util::format("%1:%2", m_configuration.proxy_config->username_password->first, - m_configuration.proxy_config->username_password->second); - std::string encoded_userpass; - encoded_userpass.resize(realm::util::base64_encoded_size(userpass.length())); - realm::util::base64_encode(userpass, encoded_userpass); - [urlRequest addValue:@(util::format("Basic %1", encoded_userpass).c_str()) forHTTPHeaderField:@"'Proxy-Authorization'"]; - } - - } else { - session = [NSURLSession sharedSession]; - } - - NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:urlRequest - completionHandler:[request = std::move(request), - completion = completion_block](NSData *data, NSURLResponse *response, NSError *error) { - auto httpResponse = (NSHTTPURLResponse *)response; - std::string body; - if (data) { - body = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] cStringUsingEncoding:NSUTF8StringEncoding]; - } - int status_code; - if (error) { - status_code = 500; - } else { - status_code = static_cast(httpResponse.statusCode); - } - - completion({ - .http_status_code=status_code, - .body=body - }); - }]; - [dataTask resume]; - } -} diff --git a/src/cpprealm/internal/curl/network_transport.cpp b/src/cpprealm/internal/curl/network_transport.cpp deleted file mode 100644 index c668018f4..000000000 --- a/src/cpprealm/internal/curl/network_transport.cpp +++ /dev/null @@ -1,209 +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. -// -//////////////////////////////////////////////////////////////////////////// - -#include -#include - -#include - -#include -#include - -namespace realm::networking { - - namespace { - - class CurlGlobalGuard { - public: - CurlGlobalGuard() - { - std::lock_guard lk(m_mutex); - if (++m_users == 1) { - curl_global_init(CURL_GLOBAL_ALL); - } - } - - ~CurlGlobalGuard() - { - std::lock_guard lk(m_mutex); - if (--m_users == 0) { - curl_global_cleanup(); - } - } - - CurlGlobalGuard(const CurlGlobalGuard&) = delete; - CurlGlobalGuard(CurlGlobalGuard&&) = delete; - CurlGlobalGuard& operator=(const CurlGlobalGuard&) = delete; - CurlGlobalGuard& operator=(CurlGlobalGuard&&) = delete; - - private: - static std::mutex m_mutex; - static int m_users; - }; - - std::mutex CurlGlobalGuard::m_mutex = {}; - int CurlGlobalGuard::m_users = 0; - - static size_t curl_write_cb(char* ptr, size_t size, size_t nmemb, std::string* response) - { - size_t realsize = size * nmemb; - response->append(ptr, realsize); - return realsize; - } - - static size_t curl_header_cb(char* buffer, size_t size, size_t nitems, std::map* response_headers) - { - std::string combined(buffer, size * nitems); - if (auto pos = combined.find(':'); pos != std::string::npos) { - std::string key = combined.substr(0, pos); - std::string value = combined.substr(pos + 1); - while (value.size() > 0 && value[0] == ' ') { - value = value.substr(1); - } - while (value.size() > 0 && (value[value.size() - 1] == '\r' || value[value.size() - 1] == '\n')) { - value = value.substr(0, value.size() - 1); - } - response_headers->insert({key, value}); - } - else { - if (combined.size() > 5 && combined.substr(0, 5) != "HTTP/") { // ignore for now HTTP/1.1 ... - std::cerr << "test transport skipping header: " << combined << std::endl; - } - } - return nitems * size; - } - } // namespace - - using SSLVerifyCallback = std::function; - - CURLcode ssl_ctx_callback(CURL *curl, void */*sslctx*/, SSLVerifyCallback *parm) { - auto verify_callback = (SSLVerifyCallback)(*parm); - - char *url; - curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url); - std::string server_address(url); - - long port; - curl_easy_getinfo(curl, CURLINFO_PRIMARY_PORT, &port); - - const char *pem_data = "-----BEGIN CERTIFICATE-----\n...certificate data...\n-----END CERTIFICATE-----\n"; - size_t pem_size = std::strlen(pem_data); - - int preverify_ok = 1; - int depth = 0; - - bool result = verify_callback(server_address, port, pem_data, pem_size, preverify_ok, depth); - return result ? CURLE_OK : CURLE_SSL_CERTPROBLEM; - } - - void default_http_transport::send_request_to_server(const ::realm::networking::request& request, - std::function&& completion_block) { - CurlGlobalGuard curl_global_guard; - auto curl = curl_easy_init(); - if (!curl) { - completion_block(::realm::networking::response{500, -1, {}, "", std::nullopt}); - return; - } - - struct curl_slist* list = nullptr; - - std::string response; - ::realm::networking::http_headers response_headers; - - curl_easy_setopt(curl, CURLOPT_URL, request.url.c_str()); - - - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, m_configuration.client_validate_ssl); - - if (m_configuration.ssl_verify_callback) { - curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, ssl_ctx_callback); - curl_easy_setopt(curl, CURLOPT_SSL_CTX_DATA, &m_configuration.ssl_verify_callback); - } - - if (m_configuration.ssl_trust_certificate_path) { - curl_easy_setopt(curl, CURLOPT_CAINFO, m_configuration.ssl_trust_certificate_path->c_str()); - } - - if (m_configuration.proxy_config) { - curl_easy_setopt(curl, CURLOPT_PROXY, util::format("%1:%2", m_configuration.proxy_config->address, m_configuration.proxy_config->port).c_str()); - curl_easy_setopt(curl, CURLOPT_HTTPPROXYTUNNEL, 1L); - if (m_configuration.proxy_config->username_password) { - curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, util::format("%1:%2", m_configuration.proxy_config->username_password->first, m_configuration.proxy_config->username_password->second).c_str()); - } - } - - /* Now specify the POST data */ - if (request.method == ::realm::networking::http_method::post) { - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request.body.c_str()); - } - else if (request.method == ::realm::networking::http_method::put) { - curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT"); - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request.body.c_str()); - } - else if (request.method == ::realm::networking::http_method::patch) { - curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PATCH"); - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request.body.c_str()); - } - else if (request.method == ::realm::networking::http_method::del) { - curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request.body.c_str()); - } - else if (request.method == ::realm::networking::http_method::patch) { - curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PATCH"); - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request.body.c_str()); - } - - curl_easy_setopt(curl, CURLOPT_TIMEOUT, request.timeout_ms); - - for (auto header : request.headers) { - auto header_str = util::format("%1: %2", header.first, header.second); - list = curl_slist_append(list, header_str.c_str()); - } - if (m_configuration.custom_http_headers) { - for (auto header : *m_configuration.custom_http_headers) { - auto header_str = util::format("%1: %2", header.first, header.second); - list = curl_slist_append(list, header_str.c_str()); - } - } - curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_cb); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response); - curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, curl_header_cb); - curl_easy_setopt(curl, CURLOPT_HEADERDATA, &response_headers); - - auto response_code = curl_easy_perform(curl); - if (response_code != CURLE_OK) { - fprintf(stderr, "curl_easy_perform() failed when sending request to '%s' with body '%s': %s\n", - request.url.c_str(), request.body.c_str(), curl_easy_strerror(response_code)); - } - long http_code = 0; - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); - curl_easy_cleanup(curl); - curl_slist_free_all(list); - completion_block({ - static_cast(http_code), - 0, // binding_response_code - std::move(response_headers), - std::move(response), - std::nullopt - }); - } -} // namespace realm::networking diff --git a/src/cpprealm/internal/network/network_transport.cpp b/src/cpprealm/internal/networking/network_transport.cpp similarity index 97% rename from src/cpprealm/internal/network/network_transport.cpp rename to src/cpprealm/internal/networking/network_transport.cpp index 6054a70d3..543d05a26 100644 --- a/src/cpprealm/internal/network/network_transport.cpp +++ b/src/cpprealm/internal/networking/network_transport.cpp @@ -17,17 +17,17 @@ //////////////////////////////////////////////////////////////////////////// #if __has_include() -#include +#include "realm/util/config.h" #endif -#include -#include - -#include -#include -#include -#include -#include -#include +#include "cpprealm/app.hpp" +#include "cpprealm/networking/http.hpp" + +#include "realm/object-store/sync/generic_network_transport.hpp" +#include "realm/sync/network/http.hpp" +#include "realm/sync/network/network.hpp" +#include "realm/sync/network/network_ssl.hpp" +#include "realm/util/base64.hpp" +#include "realm/util/uri.hpp" #include diff --git a/tools/cmake/Config.cmake.in b/tools/cmake/Config.cmake.in index 91f24b089..bbb11e2ec 100644 --- a/tools/cmake/Config.cmake.in +++ b/tools/cmake/Config.cmake.in @@ -11,7 +11,6 @@ if(NOT APPLE AND NOT EMSCRIPTEN AND NOT WINDOWS_STORE AND NOT ANDROID) set(REALM_HAVE_UV 1) find_package(libuv REQUIRED) find_dependency(libuv) - find_package(CURL) endif() find_package(Realm CONFIG REQUIRED)