diff --git a/Cargo.lock b/Cargo.lock index 762c860..3d6a741 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1245,6 +1245,22 @@ dependencies = [ "url", ] +[[package]] +name = "http-relay" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e21bdfe99224d6d446eb1b6ecc139ed96a7a8f0a77a123158b9c31d1dbbbcb08" +dependencies = [ + "anyhow", + "axum", + "axum-server", + "futures-util", + "tokio", + "tower-http", + "tracing", + "url", +] + [[package]] name = "httparse" version = "1.9.5" @@ -2082,8 +2098,6 @@ name = "pubky" version = "0.3.0" dependencies = [ "anyhow", - "axum", - "axum-server", "base64 0.22.1", "bytes", "cfg_aliases", @@ -2095,14 +2109,14 @@ dependencies = [ "futures-util", "js-sys", "log", - "mainline", "pkarr", "pubky-common 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pubky-homeserver", + "pubky-testnet", "reqwest", "thiserror 2.0.11", "tokio", "tracing", + "tracing-subscriber", "url", "wasm-bindgen", "wasm-bindgen-futures", @@ -2186,9 +2200,11 @@ name = "pubky-testnet" version = "0.1.0" dependencies = [ "anyhow", + "http-relay 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "mainline", "pkarr-relay", "pubky", + "pubky-homeserver", "tokio", "url", ] diff --git a/examples/authz/authenticator.rs b/examples/authz/authenticator.rs index 8622547..9147c92 100644 --- a/examples/authz/authenticator.rs +++ b/examples/authz/authenticator.rs @@ -66,7 +66,7 @@ async fn main() -> Result<()> { println!("PublicKey: {}", keypair.public_key()); let client = if cli.testnet.unwrap_or_default() { - let client = Client::testnet()?; + let client = Client::builder().testnet().build()?; // For the purposes of this demo, we need to make sure // the user has an account on the local homeserver. diff --git a/examples/request/main.rs b/examples/request/main.rs index f8fde78..e94bfce 100644 --- a/examples/request/main.rs +++ b/examples/request/main.rs @@ -28,7 +28,7 @@ async fn main() -> Result<()> { .init(); let client = if args.testnet { - Client::testnet()? + Client::builder().testnet().build()? } else { Client::builder().build()? }; diff --git a/pubky-common/src/auth.rs b/pubky-common/src/auth.rs index 082b8e8..ba182a8 100644 --- a/pubky-common/src/auth.rs +++ b/pubky-common/src/auth.rs @@ -25,7 +25,7 @@ pub struct AuthToken { /// A namespace to ensure this signature can't be used for any /// other purposes that share the same message structurea by accident. namespace: [u8; 10], - /// Version of the [AuthToken], in case we need to upgrade it to support unforseen usecases. + /// Version of the [AuthToken], in case we need to upgrade it to support unforeseen usecases. /// /// Version 0: /// - Signer is implicitly the same as the root keypair for diff --git a/pubky-homeserver/src/config.rs b/pubky-homeserver/src/config.rs index 7eeb4e7..b2a82ed 100644 --- a/pubky-homeserver/src/config.rs +++ b/pubky-homeserver/src/config.rs @@ -106,6 +106,9 @@ impl Config { Self { io: IoConfig { bootstrap, + http_port: 0, + https_port: 0, + ..Default::default() }, core: CoreConfig::test(), @@ -244,6 +247,8 @@ mod tests { io: IoConfig { bootstrap: Some(vec![]), + http_port: 0, + https_port: 0, ..Default::default() }, diff --git a/pubky-homeserver/src/core/routes/tenants/mod.rs b/pubky-homeserver/src/core/routes/tenants/mod.rs index 15d7d2e..23c4126 100644 --- a/pubky-homeserver/src/core/routes/tenants/mod.rs +++ b/pubky-homeserver/src/core/routes/tenants/mod.rs @@ -19,10 +19,10 @@ pub fn router(state: AppState) -> Router { Router::new() // - Datastore routes .route("/pub/", get(read::get)) - .route("/pub/{path}", get(read::get)) - .route("/pub/{path}", head(read::head)) - .route("/pub/{path}", put(write::put)) - .route("/pub/{path}", delete(write::delete)) + .route("/pub/{*path}", get(read::get)) + .route("/pub/{*path}", head(read::head)) + .route("/pub/{*path}", put(write::put)) + .route("/pub/{*path}", delete(write::delete)) // - Session routes .route("/session", get(session::session)) .route("/session", delete(session::signout)) diff --git a/pubky-testnet/Cargo.toml b/pubky-testnet/Cargo.toml index aedccf4..a65565b 100644 --- a/pubky-testnet/Cargo.toml +++ b/pubky-testnet/Cargo.toml @@ -12,8 +12,10 @@ categories = ["web-programming", "authentication", "cryptography"] [dependencies] anyhow = "1.0.95" +http-relay = "0.1.0" mainline = "5.2.0" pkarr-relay = "0.2.0" pubky = { version = "0.3.0", path = "../pubky" } +pubky-homeserver = { version = "0.1.0", path = "../pubky-homeserver" } tokio = { version = "1.43.0", features = ["full"] } url = "2.5.4" diff --git a/pubky-testnet/README.md b/pubky-testnet/README.md index 08e1a66..cb39841 100644 --- a/pubky-testnet/README.md +++ b/pubky-testnet/README.md @@ -19,5 +19,5 @@ If you need to run the testnet in a separate process, for example to test Pubky 1. A local DHT with bootstrapping nodes: `&["localhost:6881"]` 3. A Pkarr Relay running on port [15411](pubky_common::constants::testnet_ports::PKARR_RELAY) -2. A Homeserver with address is hardcoded to `8pinxxgqs41n4aididenw5apqp1urfmzdztr8jt4abrkdn435ewo` -6. An HTTP relay running on port [15412](pubky_common::constants::testnet_ports::HTTP_RELAY) +2. A Homeserver with address is hardcoded to `8pinxxgqs41n4aididenw5apqp1urfmzdztr8jt4abrkdn435ewo` +4. An HTTP relay running on port [15412](pubky_common::constants::testnet_ports::HTTP_RELAY) diff --git a/pubky-testnet/src/lib.rs b/pubky-testnet/src/lib.rs index d5f1e12..d4fdcb5 100644 --- a/pubky-testnet/src/lib.rs +++ b/pubky-testnet/src/lib.rs @@ -1,4 +1,7 @@ use anyhow::Result; +use http_relay::HttpRelay; +use pubky::ClientBuilder; +use pubky_homeserver::Homeserver; use url::Url; pub struct Testnet { @@ -34,7 +37,31 @@ impl Testnet { // === Public Methods === - // pub fn pubky_client() -> std::result::Result {} + /// Run a Pubky Homeserver + pub async fn run_homeserver(&self) -> Result { + Homeserver::run_test(&self.dht.bootstrap).await + } + + /// Run an HTTP Relay + pub async fn run_http_relay(&self) -> Result { + HttpRelay::builder().build().await + } + + /// Create a [ClientBuilder] and configure it to use this local test network. + pub fn client_builder(&self) -> ClientBuilder { + let bootstrap = self.bootstrap(); + let relays = self.relays(); + + let mut builder = pubky::Client::builder(); + builder.pkarr(|builder| { + builder + .bootstrap(bootstrap) + .relays(&relays) + .expect("testnet relays should be valid urls") + }); + + builder + } /// Run a new Pkarr relay. /// @@ -42,6 +69,10 @@ impl Testnet { pub async fn run_pkarr_relay(&mut self) -> Result { let relay = pkarr_relay::Relay::run_test(&self.dht).await?; - Ok(relay.local_url()) + let url = relay.local_url(); + + self.relays.push(relay); + + Ok(url) } } diff --git a/pubky/Cargo.toml b/pubky/Cargo.toml index 1986ae8..877aa86 100644 --- a/pubky/Cargo.toml +++ b/pubky/Cargo.toml @@ -11,46 +11,44 @@ keywords = ["web", "dht", "dns", "decentralized", "identity"] crate-type = ["cdylib", "rlib"] [dependencies] -thiserror = "2.0.6" -wasm-bindgen = "0.2.99" +thiserror = "2.0.11" +wasm-bindgen = "0.2.100" url = "2.5.4" -bytes = "^1.9.0" +bytes = "^1.10.0" base64 = "0.22.1" -pkarr = { version = "3.0.0", features = ["full"] } -mainline = "5.0.0-rc.1" -pubky-common = { version = "0.2.0", path = "../pubky-common" } +pkarr = { version = "3.1.1", features = ["full"] } cookie = "0.18.1" tracing = "0.1.41" cookie_store = { version = "0.21.1", default-features = false } -anyhow = "1.0.94" +anyhow = "1.0.95" flume = { version = "0.11.1", default-features = false, features = ["async"] } futures-util = "0.3.31" +pubky-common = "0.2.0" # Native dependencies [target.'cfg(not(target_arch = "wasm32"))'.dependencies] -reqwest = { version = "0.12.9", features = ["cookies", "rustls-tls"], default-features = false } -tokio = { version = "1.42.0", features = ["full"] } +reqwest = { version = "0.12.12", features = ["cookies", "rustls-tls"], default-features = false } +tokio = { version = "1.43.0", features = ["full"] } # Wasm dependencies [target.'cfg(target_arch = "wasm32")'.dependencies] -reqwest = { version = "0.12.9", default-features = false } -futures-lite = { version = "2.5.0", default-features = false } -wasm-bindgen = "0.2.99" -wasm-bindgen-futures = "0.4.49" +reqwest = { version = "0.12.12", default-features = false } +futures-lite = { version = "2.6.0", default-features = false } +wasm-bindgen = "0.2.100" +wasm-bindgen-futures = "0.4.50" console_log = { version = "1.0.0", features = ["color"] } -log = "0.4.22" +log = "0.4.25" -js-sys = "0.3.76" -web-sys = "0.3.76" +js-sys = "0.3.77" +web-sys = "0.3.77" [dev-dependencies] -anyhow = "1.0.94" -axum = "0.7.9" -axum-server = "0.7.1" -http-relay = { path = "../http-relay" } -pubky-homeserver = { path = "../pubky-homeserver" } -tokio = "1.42.0" +anyhow = "1.0.95" +futures-lite = "2.6.0" +pubky-testnet = { path = "../pubky-testnet" } +tokio = "1.43.0" +tracing-subscriber = "0.3.19" [build-dependencies] cfg_aliases = "0.2.1" diff --git a/pubky/README.md b/pubky/README.md index 5ed0b10..2acb89a 100644 --- a/pubky/README.md +++ b/pubky/README.md @@ -14,7 +14,7 @@ use pubky::Client; async fn main () { // Mainline Dht testnet and a temporary homeserver for unit testing. let testnet = Testnet::new(10); - let server = Homeserver::start_test(&testnet).await.unwrap(); + let server = Homeserver::run_test(&testnet).await.unwrap(); let client = Client::test(&testnet); diff --git a/pubky/src/lib.rs b/pubky/src/lib.rs index 4cc1d41..339255f 100644 --- a/pubky/src/lib.rs +++ b/pubky/src/lib.rs @@ -34,3 +34,10 @@ pub use wasm::Client; // Re-exports pub use pkarr::{Keypair, PublicKey}; pub use pubky_common::recovery_file; + +pub mod errors { + pub use super::*; + + #[cfg(not(wasm_browser))] + pub use native::BuildError; +} diff --git a/pubky/src/native.rs b/pubky/src/native.rs index 4b1b1c7..f988280 100644 --- a/pubky/src/native.rs +++ b/pubky/src/native.rs @@ -16,9 +16,6 @@ use std::fmt::Debug; use std::sync::Arc; use std::time::Duration; -#[cfg(not(wasm_browser))] -use mainline::Testnet; - static DEFAULT_USER_AGENT: &str = concat!("pubky.org", "@", env!("CARGO_PKG_VERSION"),); #[macro_export] @@ -41,18 +38,14 @@ pub struct ClientBuilder { impl ClientBuilder { #[cfg(not(wasm_browser))] - /// Sets the following: - /// - Pkarr client's DHT bootstrap nodes = `testnet` bootstrap nodes. - /// - Pkarr client's resolvers = `testnet` bootstrap nodes. - /// - Pkarr client's DHT request timeout = 500 milliseconds. (unless in CI, then it is left as default 2000) - pub fn testnet(&mut self, testnet: &Testnet) -> &mut Self { - let bootstrap = testnet.bootstrap.clone(); - - self.pkarr.no_default_network().bootstrap(&bootstrap); - - if std::env::var("CI").is_err() { - self.pkarr.request_timeout(Duration::from_millis(500)); - } + /// Creates a client connected to a local test network with hardcoded configurations: + /// 1. local DHT with bootstrapping nodes: `&["localhost:6881"]` + /// 2. Pkarr Relay running on port [15411][pubky_common::constants::testnet_ports::PKARR_RELAY] + pub fn testnet(&mut self) -> &mut Self { + self.pkarr + .bootstrap(&["localhost:6881"]) + .relays(&["http://localhost:5411"]) + .expect("relays urls infallible"); self } @@ -152,22 +145,10 @@ impl Client { ClientBuilder::default() } - #[cfg(not(wasm_browser))] - /// Create a client connected to the local network - /// with the bootstrapping node: `localhost:6881` - pub fn testnet() -> Result { - Self::builder() - .testnet(&Testnet { - bootstrap: vec!["localhost:6881".to_string()], - nodes: vec![], - }) - .build() - } + // === Getters === - #[cfg(test)] - #[cfg(not(wasm_browser))] - /// Alias to `pubky::Client::builder().testnet(testnet).build().unwrap()` - pub(crate) fn test(testnet: &Testnet) -> Client { - Client::builder().testnet(testnet).build().unwrap() + /// Returns a reference to the internal Pkarr Client. + pub fn pkarr(&self) -> &pkarr::Client { + &self.pkarr } } diff --git a/pubky/src/native/api/auth.rs b/pubky/src/native/api/auth.rs index dbc355f..9c7e183 100644 --- a/pubky/src/native/api/auth.rs +++ b/pubky/src/native/api/auth.rs @@ -298,21 +298,17 @@ impl AuthRequest { mod tests { use std::time::Duration; - use crate::*; - - use http_relay::HttpRelay; - use mainline::Testnet; use pkarr::Keypair; use pubky_common::capabilities::{Capabilities, Capability}; - use pubky_homeserver::Homeserver; + use pubky_testnet::Testnet; use reqwest::StatusCode; #[tokio::test] async fn basic_authn() { - let testnet = Testnet::new(10).unwrap(); - let server = Homeserver::start_test(&testnet).await.unwrap(); + let testnet = Testnet::run().await.unwrap(); + let server = testnet.run_homeserver().await.unwrap(); - let client = Client::test(&testnet); + let client = testnet.client_builder().build().unwrap(); let keypair = Keypair::random(); @@ -350,10 +346,10 @@ mod tests { #[tokio::test] async fn authz() { - let testnet = Testnet::new(10).unwrap(); - let server = Homeserver::start_test(&testnet).await.unwrap(); + let testnet = Testnet::run().await.unwrap(); + let server = testnet.run_homeserver().await.unwrap(); - let http_relay = HttpRelay::builder().build().await.unwrap(); + let http_relay = testnet.run_http_relay().await.unwrap(); let http_relay_url = http_relay.local_link_url(); let keypair = Keypair::random(); @@ -363,13 +359,13 @@ mod tests { let capabilities: Capabilities = "/pub/pubky.app/:rw,/pub/foo.bar/file:r".try_into().unwrap(); - let client = Client::test(&testnet); + let client = testnet.client_builder().build().unwrap(); let pubky_auth_request = client.auth_request(http_relay_url, &capabilities).unwrap(); // Authenticator side { - let client = Client::test(&testnet); + let client = testnet.client_builder().build().unwrap(); client.signup(&keypair, &server.public_key()).await.unwrap(); @@ -422,10 +418,10 @@ mod tests { #[tokio::test] async fn multiple_users() { - let testnet = Testnet::new(10).unwrap(); - let server = Homeserver::start_test(&testnet).await.unwrap(); + let testnet = Testnet::run().await.unwrap(); + let server = testnet.run_homeserver().await.unwrap(); - let client = Client::test(&testnet); + let client = testnet.client_builder().build().unwrap(); let first_keypair = Keypair::random(); let second_keypair = Keypair::random(); @@ -461,10 +457,10 @@ mod tests { #[tokio::test] async fn authz_timeout_reconnect() { - let testnet = Testnet::new(10).unwrap(); - let server = Homeserver::start_test(&testnet).await.unwrap(); + let testnet = Testnet::run().await.unwrap(); + let server = testnet.run_homeserver().await.unwrap(); - let http_relay = HttpRelay::builder().build().await.unwrap(); + let http_relay = testnet.run_http_relay().await.unwrap(); let http_relay_url = http_relay.local_link_url(); let keypair = Keypair::random(); @@ -474,8 +470,8 @@ mod tests { let capabilities: Capabilities = "/pub/pubky.app/:rw,/pub/foo.bar/file:r".try_into().unwrap(); - let client = Client::builder() - .pkarr(|builder| builder.no_default_network().bootstrap(&testnet.bootstrap)) + let client = testnet + .client_builder() .request_timeout(Duration::from_millis(1000)) .build() .unwrap(); @@ -486,19 +482,14 @@ mod tests { { let url = pubky_auth_request.url().clone(); + let client = testnet.client_builder().build().unwrap(); + client.signup(&keypair, &server.public_key()).await.unwrap(); + tokio::spawn(async move { - loop { - tokio::time::sleep(Duration::from_millis(400)).await; - - let client = Client::builder() - .pkarr(|builder| builder.no_default_network().bootstrap(&testnet.bootstrap)) - .build() - .unwrap(); - - client.signup(&keypair, &server.public_key()).await.unwrap(); - - client.send_auth_token(&keypair, &url).await.unwrap(); - } + tokio::time::sleep(Duration::from_millis(400)).await; + // loop { + client.send_auth_token(&keypair, &url).await.unwrap(); + // } }); } diff --git a/pubky/src/native/api/http.rs b/pubky/src/native/api/http.rs index 4cef351..1cf4b0e 100644 --- a/pubky/src/native/api/http.rs +++ b/pubky/src/native/api/http.rs @@ -136,18 +136,14 @@ impl Client { #[cfg(test)] mod tests { - use mainline::Testnet; - use pubky_homeserver::Homeserver; - - use crate::Client; + use pubky_testnet::Testnet; #[tokio::test] async fn http_get_pubky() { - let testnet = Testnet::new(10).unwrap(); + let testnet = Testnet::run().await.unwrap(); + let homeserver = testnet.run_homeserver().await.unwrap(); - let homeserver = Homeserver::start_test(&testnet).await.unwrap(); - - let client = Client::test(&testnet); + let client = testnet.client_builder().build().unwrap(); let response = client .get(format!("https://{}/", homeserver.public_key())) @@ -160,9 +156,9 @@ mod tests { #[tokio::test] async fn http_get_icann() { - let testnet = Testnet::new(10).unwrap(); + let testnet = Testnet::run().await.unwrap(); - let client = Client::test(&testnet); + let client = testnet.client_builder().build().unwrap(); let response = client .request(Default::default(), "https://example.com/") diff --git a/pubky/src/native/api/public.rs b/pubky/src/native/api/public.rs index bbe0ad9..610b2f9 100644 --- a/pubky/src/native/api/public.rs +++ b/pubky/src/native/api/public.rs @@ -124,20 +124,17 @@ impl<'a> ListBuilder<'a> { #[cfg(test)] mod tests { - use crate::*; - use bytes::Bytes; - use mainline::Testnet; use pkarr::Keypair; - use pubky_homeserver::Homeserver; + use pubky_testnet::Testnet; use reqwest::{Method, StatusCode}; #[tokio::test] async fn put_get_delete() { - let testnet = Testnet::new(10).unwrap(); - let server = Homeserver::start_test(&testnet).await.unwrap(); + let testnet = Testnet::run().await.unwrap(); + let server = testnet.run_homeserver().await.unwrap(); - let client = Client::test(&testnet); + let client = testnet.client_builder().build().unwrap(); let keypair = Keypair::random(); @@ -174,10 +171,10 @@ mod tests { #[tokio::test] async fn unauthorized_put_delete() { - let testnet = Testnet::new(10).unwrap(); - let server = Homeserver::start_test(&testnet).await.unwrap(); + let testnet = Testnet::run().await.unwrap(); + let server = testnet.run_homeserver().await.unwrap(); - let client = Client::test(&testnet); + let client = testnet.client_builder().build().unwrap(); let keypair = Keypair::random(); @@ -188,7 +185,7 @@ mod tests { let url = format!("pubky://{public_key}/pub/foo.txt"); let url = url.as_str(); - let other_client = Client::test(&testnet); + let other_client = testnet.client_builder().build().unwrap(); { let other = Keypair::random(); @@ -239,10 +236,10 @@ mod tests { #[tokio::test] async fn list() { - let testnet = Testnet::new(10).unwrap(); - let server = Homeserver::start_test(&testnet).await.unwrap(); + let testnet = Testnet::run().await.unwrap(); + let server = testnet.run_homeserver().await.unwrap(); - let client = Client::test(&testnet); + let client = testnet.client_builder().build().unwrap(); let keypair = Keypair::random(); @@ -442,10 +439,10 @@ mod tests { #[tokio::test] async fn list_shallow() { - let testnet = Testnet::new(10).unwrap(); - let server = Homeserver::start_test(&testnet).await.unwrap(); + let testnet = Testnet::run().await.unwrap(); + let server = testnet.run_homeserver().await.unwrap(); - let client = Client::test(&testnet); + let client = testnet.client_builder().build().unwrap(); let keypair = Keypair::random(); @@ -652,10 +649,10 @@ mod tests { #[tokio::test] async fn list_events() { - let testnet = Testnet::new(10).unwrap(); - let server = Homeserver::start_test(&testnet).await.unwrap(); + let testnet = Testnet::run().await.unwrap(); + let server = testnet.run_homeserver().await.unwrap(); - let client = Client::test(&testnet); + let client = testnet.client_builder().build().unwrap(); let keypair = Keypair::random(); @@ -683,7 +680,7 @@ mod tests { let feed_url = format!("https://{}/events/", server.public_key()); - let client = Client::test(&testnet); + let client = testnet.client_builder().build().unwrap(); let cursor; @@ -748,10 +745,10 @@ mod tests { #[tokio::test] async fn read_after_event() { - let testnet = Testnet::new(10).unwrap(); - let server = Homeserver::start_test(&testnet).await.unwrap(); + let testnet = Testnet::run().await.unwrap(); + let server = testnet.run_homeserver().await.unwrap(); - let client = Client::test(&testnet); + let client = testnet.client_builder().build().unwrap(); let keypair = Keypair::random(); @@ -765,7 +762,7 @@ mod tests { let feed_url = format!("https://{}/events/", server.public_key()); - let client = Client::test(&testnet); + let client = testnet.client_builder().build().unwrap(); { let response = client @@ -798,9 +795,10 @@ mod tests { #[tokio::test] async fn dont_delete_shared_blobs() { - let testnet = Testnet::new(10).unwrap(); - let homeserver = Homeserver::start_test(&testnet).await.unwrap(); - let client = Client::test(&testnet); + let testnet = Testnet::run().await.unwrap(); + let homeserver = testnet.run_homeserver().await.unwrap(); + + let client = testnet.client_builder().build().unwrap(); let homeserver_pubky = homeserver.public_key(); @@ -867,11 +865,10 @@ mod tests { #[tokio::test] async fn stream() { // TODO: test better streaming API + let testnet = Testnet::run().await.unwrap(); + let server = testnet.run_homeserver().await.unwrap(); - let testnet = Testnet::new(10).unwrap(); - let server = Homeserver::start_test(&testnet).await.unwrap(); - - let client = Client::test(&testnet); + let client = testnet.client_builder().build().unwrap(); let keypair = Keypair::random();