diff --git a/Cargo.lock b/Cargo.lock index 8113208bd7..2ef5297923 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5433,6 +5433,7 @@ version = "0.0.0" dependencies = [ "bytes", "cbindgen", + "http 1.1.0", "hyper-rustls 0.25.0", "lazy_static", "libsql", diff --git a/bindings/c/Cargo.toml b/bindings/c/Cargo.toml index 8e293d69d6..17e61e1213 100644 --- a/bindings/c/Cargo.toml +++ b/bindings/c/Cargo.toml @@ -17,6 +17,7 @@ tokio = { version = "1.29.1", features = [ "rt-multi-thread" ] } hyper-rustls = { version = "0.25", features = ["webpki-roots"]} tracing = "0.1.40" tracing-subscriber = "0.3.18" +http = "1.1.0" [target.'cfg(not(any(target_os = "ios", target_os = "android")))'.dependencies] libsql = { path = "../../libsql", features = ["encryption"] } diff --git a/bindings/c/src/lib.rs b/bindings/c/src/lib.rs index 4c9e903cd2..6cb061ec5f 100644 --- a/bindings/c/src/lib.rs +++ b/bindings/c/src/lib.rs @@ -6,7 +6,7 @@ extern crate lazy_static; mod types; use crate::types::libsql_config; -use libsql::{errors, LoadExtensionGuard}; +use libsql::{errors, Builder, LoadExtensionGuard}; use tokio::runtime::Runtime; use types::{ blob, libsql_connection, libsql_connection_t, libsql_database, libsql_database_t, libsql_row, @@ -152,6 +152,44 @@ pub unsafe extern "C" fn libsql_open_sync_with_config( return 3; } }; + let uri: http::Uri = match primary_url.try_into() { + Ok(uri) => uri, + Err(e) => { + set_err_msg(format!("Wrong primary URL: {e}"), out_err_msg); + return 100; + } + }; + if let Some(query) = uri.query() { + if query.contains("offline") { + let mut builder = Builder::new_synced_database( + db_path, + primary_url.to_owned(), + auth_token.to_owned(), + ); + if config.with_webpki != 0 { + let https = hyper_rustls::HttpsConnectorBuilder::new() + .with_webpki_roots() + .https_or_http() + .enable_http1() + .build(); + builder = builder.connector(https); + } + match RT.block_on(builder.build()) { + Ok(db) => { + let db = Box::leak(Box::new(libsql_database { db })); + *out_db = libsql_database_t::from(db); + return 0; + } + Err(e) => { + set_err_msg( + format!("Error opening offline db path {db_path}, primary url {primary_url}: {e}"), + out_err_msg, + ); + return 101; + } + } + } + } let mut builder = libsql::Builder::new_remote_replica( db_path, primary_url.to_string(), diff --git a/libsql/src/database/builder.rs b/libsql/src/database/builder.rs index 5d60bdd370..f1c60ffb3c 100644 --- a/libsql/src/database/builder.rs +++ b/libsql/src/database/builder.rs @@ -1,6 +1,7 @@ cfg_core! { use crate::EncryptionConfig; } + use crate::{Database, Result}; use super::DbType; @@ -99,6 +100,7 @@ impl Builder<()> { connector: None, version: None, }, + connector:None, }, } } @@ -399,6 +401,7 @@ cfg_sync! { path: std::path::PathBuf, flags: crate::OpenFlags, remote: Remote, + connector: Option, } impl Builder { @@ -408,6 +411,18 @@ cfg_sync! { self } + /// Provide a custom http connector that will be used to create http connections. + pub fn connector(mut self, connector: C) -> Builder + where + C: tower::Service + Send + Clone + Sync + 'static, + C::Response: crate::util::Socket, + C::Future: Send + 'static, + C::Error: Into>, + { + self.inner.connector = Some(wrap_connector(connector)); + self + } + /// Build a connection to a local database that can be synced to remote server. pub async fn build(self) -> Result { let SyncedDatabase { @@ -420,11 +435,16 @@ cfg_sync! { connector: _, version: _, }, + connector, } = self.inner; let path = path.to_str().ok_or(crate::Error::InvalidUTF8Path)?.to_owned(); - let https = super::connector()?; + let https = if let Some(connector) = connector { + connector + } else { + wrap_connector(super::connector()?) + }; use tower::ServiceExt; let svc = https @@ -506,6 +526,22 @@ cfg_remote! { } cfg_replication_or_remote_or_sync! { + fn wrap_connector(connector: C) -> crate::util::ConnectorService + where + C: tower::Service + Send + Clone + Sync + 'static, + C::Response: crate::util::Socket, + C::Future: Send + 'static, + C::Error: Into>, + { + use tower::ServiceExt; + + let svc = connector + .map_err(|e| e.into()) + .map_response(|s| Box::new(s) as Box); + + crate::util::ConnectorService::new(svc) + } + impl Remote { fn connector(mut self, connector: C) -> Remote where @@ -514,15 +550,7 @@ cfg_replication_or_remote_or_sync! { C::Future: Send + 'static, C::Error: Into>, { - use tower::ServiceExt; - - let svc = connector - .map_err(|e| e.into()) - .map_response(|s| Box::new(s) as Box); - - let svc = crate::util::ConnectorService::new(svc); - - self.connector = Some(svc); + self.connector = Some(wrap_connector(connector)); self }