From 4adf396c4ec0aea17a35291da3ccbd891ba9c30f Mon Sep 17 00:00:00 2001 From: nazeh Date: Tue, 17 Sep 2024 15:34:12 +0300 Subject: [PATCH 01/14] chore: change 'strict monotonic' to 'absolute monotonic' in code comements --- pubky-common/src/timestamp.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pubky-common/src/timestamp.rs b/pubky-common/src/timestamp.rs index c3c9846..4317484 100644 --- a/pubky-common/src/timestamp.rs +++ b/pubky-common/src/timestamp.rs @@ -1,4 +1,4 @@ -//! Strictly monotonic unix timestamp in microseconds +//! Absolutely monotonic unix timestamp in microseconds use serde::{Deserialize, Serialize}; use std::fmt::Display; @@ -31,7 +31,7 @@ impl TimestampFactory { } pub fn now(&mut self) -> Timestamp { - // Ensure strict monotonicity. + // Ensure absolute monotonicity. self.last_time = (system_time() & TIME_MASK).max(self.last_time + CLOCK_MASK + 1); // Add clock_id to the end of the timestamp @@ -48,13 +48,13 @@ impl Default for TimestampFactory { static DEFAULT_FACTORY: Lazy> = Lazy::new(|| Mutex::new(TimestampFactory::default())); -/// STrictly monotonic timestamp since [SystemTime::UNIX_EPOCH] in microseconds as u64. +/// Absolutely monotonic timestamp since [SystemTime::UNIX_EPOCH] in microseconds as u64. /// /// The purpose of this timestamp is to unique per "user", not globally, /// it achieves this by: /// 1. Override the last byte with a random `clock_id`, reducing the probability /// of two matching timestamps across multiple machines/threads. -/// 2. Gurantee that the remaining 3 bytes are ever increasing (strictly monotonic) within +/// 2. Gurantee that the remaining 3 bytes are ever increasing (absolutely monotonic) within /// the same thread regardless of the wall clock value /// /// This timestamp is also serialized as BE bytes to remain sortable. @@ -215,7 +215,7 @@ mod tests { use super::*; #[test] - fn strictly_monotonic() { + fn absolutely_monotonic() { const COUNT: usize = 100; let mut set = HashSet::with_capacity(COUNT); From 4620b947996260a5a7eb5a702a4b63010c7a9d99 Mon Sep 17 00:00:00 2001 From: nazeh Date: Tue, 17 Sep 2024 17:31:32 +0300 Subject: [PATCH 02/14] feat(js): add PubkyClient.fetch() --- pubky/pkg/test/http.js | 16 ++++++++++++ pubky/src/wasm/api.rs | 3 +++ pubky/src/wasm/api/fetch.rs | 52 +++++++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+) create mode 100644 pubky/pkg/test/http.js create mode 100644 pubky/src/wasm/api.rs create mode 100644 pubky/src/wasm/api/fetch.rs diff --git a/pubky/pkg/test/http.js b/pubky/pkg/test/http.js new file mode 100644 index 0000000..8f27326 --- /dev/null +++ b/pubky/pkg/test/http.js @@ -0,0 +1,16 @@ +import test from 'tape' + +import { PubkyClient, Keypair, PublicKey } from '../index.cjs' + +const TLD = '8pinxxgqs41n4aididenw5apqp1urfmzdztr8jt4abrkdn435ewo'; + +// TODO: test HTTPs too somehow. + +test("basic fetch", async (t) => { + let client = PubkyClient.testnet(); + + let response = await client.fetch(`https://${TLD}/`, new Uint8Array([])); + + t.equal(response.status, 200); +}) + diff --git a/pubky/src/wasm/api.rs b/pubky/src/wasm/api.rs new file mode 100644 index 0000000..829640e --- /dev/null +++ b/pubky/src/wasm/api.rs @@ -0,0 +1,3 @@ +//! Public API module + +mod fetch; diff --git a/pubky/src/wasm/api/fetch.rs b/pubky/src/wasm/api/fetch.rs new file mode 100644 index 0000000..562bf66 --- /dev/null +++ b/pubky/src/wasm/api/fetch.rs @@ -0,0 +1,52 @@ +//! Fetch method handling HTTP and Pubky urls with Pkarr TLD. + +use js_sys::Promise; +use wasm_bindgen::prelude::*; + +use reqwest::Url; + +use crate::PubkyClient; + +#[wasm_bindgen] +impl PubkyClient { + #[wasm_bindgen] + pub async fn fetch( + &self, + url: &str, + init: &web_sys::RequestInit, + ) -> Result { + let mut url: Url = url.try_into().map_err(|err| { + JsValue::from_str(&format!("PubkyClient::fetch(): Invalid `url`; {:?}", err)) + })?; + + self.resolve_url(&mut url).await.map_err(JsValue::from)?; + + let js_req = + web_sys::Request::new_with_str_and_init(url.as_str(), init).map_err(|err| { + JsValue::from_str(&format!("PubkyClient::fetch(): Invalid `init`; {:?}", err)) + })?; + + Ok(js_fetch(&js_req)) + } +} + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(js_name = fetch)] + fn fetch_with_request(input: &web_sys::Request) -> Promise; +} + +fn js_fetch(req: &web_sys::Request) -> Promise { + use wasm_bindgen::{JsCast, JsValue}; + let global = js_sys::global(); + + if let Ok(true) = js_sys::Reflect::has(&global, &JsValue::from_str("ServiceWorkerGlobalScope")) + { + global + .unchecked_into::() + .fetch_with_request(req) + } else { + // browser + fetch_with_request(req) + } +} From 3d1a08141494539215c1e970e6b6d441b1a9f504 Mon Sep 17 00:00:00 2001 From: nazeh Date: Thu, 19 Sep 2024 08:55:21 +0300 Subject: [PATCH 03/14] refactor(pubky): separate public API modules from internals --- pubky/src/native.rs | 160 +---------------- pubky/src/native/api.rs | 5 + pubky/src/native/api/auth.rs | 85 +++++++++ pubky/src/native/api/public.rs | 28 +++ pubky/src/native/api/recovery_file.rs | 19 ++ pubky/src/native/internals.rs | 31 ++++ pubky/src/wasm.rs | 202 +--------------------- pubky/src/wasm/api.rs | 7 +- pubky/src/wasm/api/auth.rs | 118 +++++++++++++ pubky/src/wasm/api/{fetch.rs => http.rs} | 0 pubky/src/wasm/api/public.rs | 87 ++++++++++ pubky/src/wasm/{ => api}/recovery_file.rs | 2 +- pubky/src/wasm/{ => internals}/http.rs | 0 pubky/src/wasm/internals/mod.rs | 2 + pubky/src/wasm/{ => internals}/pkarr.rs | 0 pubky/src/wasm/{ => wrappers}/keys.rs | 0 pubky/src/wasm/wrappers/mod.rs | 5 + pubky/src/wasm/{ => wrappers}/session.rs | 0 18 files changed, 393 insertions(+), 358 deletions(-) create mode 100644 pubky/src/native/api.rs create mode 100644 pubky/src/native/api/auth.rs create mode 100644 pubky/src/native/api/public.rs create mode 100644 pubky/src/native/api/recovery_file.rs create mode 100644 pubky/src/native/internals.rs create mode 100644 pubky/src/wasm/api/auth.rs rename pubky/src/wasm/api/{fetch.rs => http.rs} (100%) create mode 100644 pubky/src/wasm/api/public.rs rename pubky/src/wasm/{ => api}/recovery_file.rs (95%) rename pubky/src/wasm/{ => internals}/http.rs (100%) create mode 100644 pubky/src/wasm/internals/mod.rs rename pubky/src/wasm/{ => internals}/pkarr.rs (100%) rename pubky/src/wasm/{ => wrappers}/keys.rs (100%) create mode 100644 pubky/src/wasm/wrappers/mod.rs rename pubky/src/wasm/{ => wrappers}/session.rs (100%) diff --git a/pubky/src/native.rs b/pubky/src/native.rs index ba0f086..572d62b 100644 --- a/pubky/src/native.rs +++ b/pubky/src/native.rs @@ -1,25 +1,12 @@ use std::net::ToSocketAddrs; use std::time::Duration; -use bytes::Bytes; -use pubky_common::{ - capabilities::Capabilities, - recovery_file::{create_recovery_file, decrypt_recovery_file}, - session::Session, -}; -use reqwest::{RequestBuilder, Response}; -use tokio::sync::oneshot; -use url::Url; +use ::pkarr::{mainline::dht::Testnet, PkarrClient}; -use pkarr::Keypair; +use crate::PubkyClient; -use ::pkarr::{mainline::dht::Testnet, PkarrClient, PublicKey, SignedPacket}; - -use crate::{ - error::{Error, Result}, - shared::list_builder::ListBuilder, - PubkyClient, -}; +mod api; +mod internals; static DEFAULT_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"),); @@ -80,8 +67,6 @@ impl Default for PubkyClient { } } -// === Public API === - impl PubkyClient { /// Returns a builder to edit settings before creating [PubkyClient]. pub fn builder() -> PubkyClientBuilder { @@ -111,141 +96,4 @@ impl PubkyClient { builder.build() } - - // === Auth === - - /// Signup to a homeserver and update Pkarr accordingly. - /// - /// The homeserver is a Pkarr domain name, where the TLD is a Pkarr public key - /// for example "pubky.o4dksfbqk85ogzdb5osziw6befigbuxmuxkuxq8434q89uj56uyy" - pub async fn signup(&self, keypair: &Keypair, homeserver: &PublicKey) -> Result { - self.inner_signup(keypair, homeserver).await - } - - /// Check the current sesison for a given Pubky in its homeserver. - /// - /// Returns [Session] or `None` (if recieved `404 NOT_FOUND`), - /// or [reqwest::Error] if the response has any other `>=400` status code. - pub async fn session(&self, pubky: &PublicKey) -> Result> { - self.inner_session(pubky).await - } - - /// Signout from a homeserver. - pub async fn signout(&self, pubky: &PublicKey) -> Result<()> { - self.inner_signout(pubky).await - } - - /// Signin to a homeserver. - pub async fn signin(&self, keypair: &Keypair) -> Result { - self.inner_signin(keypair).await - } - - // === Public data === - - /// Upload a small payload to a given path. - pub async fn put>(&self, url: T, content: &[u8]) -> Result<()> { - self.inner_put(url, content).await - } - - /// Download a small payload from a given path relative to a pubky author. - pub async fn get>(&self, url: T) -> Result> { - self.inner_get(url).await - } - - /// Delete a file at a path relative to a pubky author. - pub async fn delete>(&self, url: T) -> Result<()> { - self.inner_delete(url).await - } - - /// Returns a [ListBuilder] to help pass options before calling [ListBuilder::send]. - /// - /// `url` sets the path you want to lest within. - pub fn list>(&self, url: T) -> Result { - self.inner_list(url) - } - - // === Helpers === - - /// Create a recovery file of the `keypair`, containing the secret key encrypted - /// using the `passphrase`. - pub fn create_recovery_file(keypair: &Keypair, passphrase: &str) -> Result> { - Ok(create_recovery_file(keypair, passphrase)?) - } - - /// Recover a keypair from a recovery file by decrypting the secret key using `passphrase`. - pub fn decrypt_recovery_file(recovery_file: &[u8], passphrase: &str) -> Result { - Ok(decrypt_recovery_file(recovery_file, passphrase)?) - } - - /// Return `pubkyauth://` url and wait for the incoming [AuthToken] - /// verifying that AuthToken, and if capabilities were requested, signing in to - /// the Pubky's homeserver and returning the [Session] information. - pub fn auth_request( - &self, - relay: impl TryInto, - capabilities: &Capabilities, - ) -> Result<(Url, tokio::sync::oneshot::Receiver>)> { - let mut relay: Url = relay - .try_into() - .map_err(|_| Error::Generic("Invalid relay Url".into()))?; - - let (pubkyauth_url, client_secret) = self.create_auth_request(&mut relay, capabilities)?; - - let (tx, rx) = oneshot::channel::>(); - - let this = self.clone(); - - tokio::spawn(async move { - let to_send = this - .subscribe_to_auth_response(relay, &client_secret) - .await?; - - tx.send(to_send) - .map_err(|_| Error::Generic("Failed to send the session after signing in with token, since the receiver is dropped".into()))?; - - Ok::<(), Error>(()) - }); - - Ok((pubkyauth_url, rx)) - } - - /// Sign an [pubky_common::auth::AuthToken], encrypt it and send it to the - /// source of the pubkyauth request url. - pub async fn send_auth_token>( - &self, - keypair: &Keypair, - pubkyauth_url: T, - ) -> Result<()> { - let url: Url = pubkyauth_url.try_into().map_err(|_| Error::InvalidUrl)?; - - self.inner_send_auth_token(keypair, url).await?; - - Ok(()) - } -} - -// === Internals === - -impl PubkyClient { - // === Pkarr === - - pub(crate) async fn pkarr_resolve( - &self, - public_key: &PublicKey, - ) -> Result> { - Ok(self.pkarr.resolve(public_key).await?) - } - - pub(crate) async fn pkarr_publish(&self, signed_packet: &SignedPacket) -> Result<()> { - Ok(self.pkarr.publish(signed_packet).await?) - } - - // === HTTP === - - pub(crate) fn request(&self, method: reqwest::Method, url: Url) -> RequestBuilder { - self.http.request(method, url) - } - - pub(crate) fn store_session(&self, _: &Response) {} - pub(crate) fn remove_session(&self, _: &PublicKey) {} } diff --git a/pubky/src/native/api.rs b/pubky/src/native/api.rs new file mode 100644 index 0000000..d24c0c4 --- /dev/null +++ b/pubky/src/native/api.rs @@ -0,0 +1,5 @@ +//! Public API modules + +pub mod auth; +pub mod public; +pub mod recovery_file; diff --git a/pubky/src/native/api/auth.rs b/pubky/src/native/api/auth.rs new file mode 100644 index 0000000..1bd23d5 --- /dev/null +++ b/pubky/src/native/api/auth.rs @@ -0,0 +1,85 @@ +use pkarr::Keypair; +use pubky_common::session::Session; +use tokio::sync::oneshot; +use url::Url; + +use pkarr::PublicKey; + +use pubky_common::capabilities::Capabilities; + +use crate::error::{Error, Result}; +use crate::PubkyClient; + +impl PubkyClient { + /// Signup to a homeserver and update Pkarr accordingly. + /// + /// The homeserver is a Pkarr domain name, where the TLD is a Pkarr public key + /// for example "pubky.o4dksfbqk85ogzdb5osziw6befigbuxmuxkuxq8434q89uj56uyy" + pub async fn signup(&self, keypair: &Keypair, homeserver: &PublicKey) -> Result { + self.inner_signup(keypair, homeserver).await + } + + /// Check the current sesison for a given Pubky in its homeserver. + /// + /// Returns [Session] or `None` (if recieved `404 NOT_FOUND`), + /// or [reqwest::Error] if the response has any other `>=400` status code. + pub async fn session(&self, pubky: &PublicKey) -> Result> { + self.inner_session(pubky).await + } + + /// Signout from a homeserver. + pub async fn signout(&self, pubky: &PublicKey) -> Result<()> { + self.inner_signout(pubky).await + } + + /// Signin to a homeserver. + pub async fn signin(&self, keypair: &Keypair) -> Result { + self.inner_signin(keypair).await + } + + /// Return `pubkyauth://` url and wait for the incoming [AuthToken] + /// verifying that AuthToken, and if capabilities were requested, signing in to + /// the Pubky's homeserver and returning the [Session] information. + pub fn auth_request( + &self, + relay: impl TryInto, + capabilities: &Capabilities, + ) -> Result<(Url, tokio::sync::oneshot::Receiver>)> { + let mut relay: Url = relay + .try_into() + .map_err(|_| Error::Generic("Invalid relay Url".into()))?; + + let (pubkyauth_url, client_secret) = self.create_auth_request(&mut relay, capabilities)?; + + let (tx, rx) = oneshot::channel::>(); + + let this = self.clone(); + + tokio::spawn(async move { + let to_send = this + .subscribe_to_auth_response(relay, &client_secret) + .await?; + + tx.send(to_send) + .map_err(|_| Error::Generic("Failed to send the session after signing in with token, since the receiver is dropped".into()))?; + + Ok::<(), Error>(()) + }); + + Ok((pubkyauth_url, rx)) + } + + /// Sign an [pubky_common::auth::AuthToken], encrypt it and send it to the + /// source of the pubkyauth request url. + pub async fn send_auth_token>( + &self, + keypair: &Keypair, + pubkyauth_url: T, + ) -> Result<()> { + let url: Url = pubkyauth_url.try_into().map_err(|_| Error::InvalidUrl)?; + + self.inner_send_auth_token(keypair, url).await?; + + Ok(()) + } +} diff --git a/pubky/src/native/api/public.rs b/pubky/src/native/api/public.rs new file mode 100644 index 0000000..dfee053 --- /dev/null +++ b/pubky/src/native/api/public.rs @@ -0,0 +1,28 @@ +use bytes::Bytes; +use url::Url; + +use crate::{error::Result, shared::list_builder::ListBuilder, PubkyClient}; + +impl PubkyClient { + /// Upload a small payload to a given path. + pub async fn put>(&self, url: T, content: &[u8]) -> Result<()> { + self.inner_put(url, content).await + } + + /// Download a small payload from a given path relative to a pubky author. + pub async fn get>(&self, url: T) -> Result> { + self.inner_get(url).await + } + + /// Delete a file at a path relative to a pubky author. + pub async fn delete>(&self, url: T) -> Result<()> { + self.inner_delete(url).await + } + + /// Returns a [ListBuilder] to help pass options before calling [ListBuilder::send]. + /// + /// `url` sets the path you want to lest within. + pub fn list>(&self, url: T) -> Result { + self.inner_list(url) + } +} diff --git a/pubky/src/native/api/recovery_file.rs b/pubky/src/native/api/recovery_file.rs new file mode 100644 index 0000000..0eace09 --- /dev/null +++ b/pubky/src/native/api/recovery_file.rs @@ -0,0 +1,19 @@ +use pubky_common::{ + crypto::Keypair, + recovery_file::{create_recovery_file, decrypt_recovery_file}, +}; + +use crate::{error::Result, PubkyClient}; + +impl PubkyClient { + /// Create a recovery file of the `keypair`, containing the secret key encrypted + /// using the `passphrase`. + pub fn create_recovery_file(keypair: &Keypair, passphrase: &str) -> Result> { + Ok(create_recovery_file(keypair, passphrase)?) + } + + /// Recover a keypair from a recovery file by decrypting the secret key using `passphrase`. + pub fn decrypt_recovery_file(recovery_file: &[u8], passphrase: &str) -> Result { + Ok(decrypt_recovery_file(recovery_file, passphrase)?) + } +} diff --git a/pubky/src/native/internals.rs b/pubky/src/native/internals.rs new file mode 100644 index 0000000..21ead99 --- /dev/null +++ b/pubky/src/native/internals.rs @@ -0,0 +1,31 @@ +use pkarr::SignedPacket; +use pubky_common::crypto::PublicKey; +use reqwest::{RequestBuilder, Response}; +use url::Url; + +use crate::error::Result; +use crate::PubkyClient; + +impl PubkyClient { + // === Pkarr === + + pub(crate) async fn pkarr_resolve( + &self, + public_key: &PublicKey, + ) -> Result> { + Ok(self.pkarr.resolve(public_key).await?) + } + + pub(crate) async fn pkarr_publish(&self, signed_packet: &SignedPacket) -> Result<()> { + Ok(self.pkarr.publish(signed_packet).await?) + } + + // === HTTP === + + pub(crate) fn request(&self, method: reqwest::Method, url: Url) -> RequestBuilder { + self.http.request(method, url) + } + + pub(crate) fn store_session(&self, _: &Response) {} + pub(crate) fn remove_session(&self, _: &PublicKey) {} +} diff --git a/pubky/src/wasm.rs b/pubky/src/wasm.rs index 09dc045..be36a0e 100644 --- a/pubky/src/wasm.rs +++ b/pubky/src/wasm.rs @@ -3,24 +3,13 @@ use std::{ sync::{Arc, RwLock}, }; -use js_sys::{Array, Uint8Array}; use wasm_bindgen::prelude::*; -use url::Url; - -use pubky_common::capabilities::Capabilities; - -use crate::error::Error; use crate::PubkyClient; -mod http; -mod keys; -mod pkarr; -mod recovery_file; -mod session; - -use keys::{Keypair, PublicKey}; -use session::Session; +mod api; +mod internals; +mod wrappers; impl Default for PubkyClient { fn default() -> Self { @@ -67,189 +56,4 @@ impl PubkyClient { pub fn get_pkarr_relays(&self) -> Vec { self.pkarr_relays.clone() } - - /// Signup to a homeserver and update Pkarr accordingly. - /// - /// The homeserver is a Pkarr domain name, where the TLD is a Pkarr public key - /// for example "pubky.o4dksfbqk85ogzdb5osziw6befigbuxmuxkuxq8434q89uj56uyy" - #[wasm_bindgen] - pub async fn signup( - &self, - keypair: &Keypair, - homeserver: &PublicKey, - ) -> Result { - Ok(Session( - self.inner_signup(keypair.as_inner(), homeserver.as_inner()) - .await - .map_err(|e| JsValue::from(e))?, - )) - } - - /// Check the current sesison for a given Pubky in its homeserver. - /// - /// Returns [Session] or `None` (if recieved `404 NOT_FOUND`), - /// or throws the recieved error if the response has any other `>=400` status code. - #[wasm_bindgen] - pub async fn session(&self, pubky: &PublicKey) -> Result, JsValue> { - self.inner_session(pubky.as_inner()) - .await - .map(|s| s.map(Session)) - .map_err(|e| e.into()) - } - - /// Signout from a homeserver. - #[wasm_bindgen] - pub async fn signout(&self, pubky: &PublicKey) -> Result<(), JsValue> { - self.inner_signout(pubky.as_inner()) - .await - .map_err(|e| e.into()) - } - - /// Signin to a homeserver using the root Keypair. - #[wasm_bindgen] - pub async fn signin(&self, keypair: &Keypair) -> Result<(), JsValue> { - self.inner_signin(keypair.as_inner()) - .await - .map(|_| ()) - .map_err(|e| e.into()) - } - - /// Return `pubkyauth://` url and wait for the incoming [AuthToken] - /// verifying that AuthToken, and if capabilities were requested, signing in to - /// the Pubky's homeserver and returning the [Session] information. - /// - /// Returns a tuple of [pubkyAuthUrl, Promise] - #[wasm_bindgen(js_name = "authRequest")] - pub fn auth_request(&self, relay: &str, capabilities: &str) -> Result { - let mut relay: Url = relay - .try_into() - .map_err(|_| Error::Generic("Invalid relay Url".into()))?; - - let (pubkyauth_url, client_secret) = self.create_auth_request( - &mut relay, - &Capabilities::try_from(capabilities).map_err(|_| "Invalid capaiblities")?, - )?; - - let this = self.clone(); - - let future = async move { - this.subscribe_to_auth_response(relay, &client_secret) - .await - .map(|opt| { - opt.map_or_else( - || JsValue::NULL, // Convert `None` to `JsValue::NULL` - |session| JsValue::from(Session(session)), - ) - }) - .map_err(|err| JsValue::from_str(&format!("{:?}", err))) - }; - - let promise = wasm_bindgen_futures::future_to_promise(future); - - // Return the URL and the promise - let js_tuple = js_sys::Array::new(); - js_tuple.push(&JsValue::from_str(pubkyauth_url.as_ref())); - js_tuple.push(&promise); - - Ok(js_tuple) - } - - /// Sign an [pubky_common::auth::AuthToken], encrypt it and send it to the - /// source of the pubkyauth request url. - #[wasm_bindgen(js_name = "sendAuthToken")] - pub async fn send_auth_token( - &self, - keypair: &Keypair, - pubkyauth_url: &str, - ) -> Result<(), JsValue> { - let pubkyauth_url: Url = pubkyauth_url - .try_into() - .map_err(|_| Error::Generic("Invalid relay Url".into()))?; - - self.inner_send_auth_token(keypair.as_inner(), pubkyauth_url) - .await?; - - Ok(()) - } - - // === Public data === - - #[wasm_bindgen] - /// Upload a small payload to a given path. - pub async fn put(&self, url: &str, content: &[u8]) -> Result<(), JsValue> { - self.inner_put(url, content).await.map_err(|e| e.into()) - } - - /// Download a small payload from a given path relative to a pubky author. - #[wasm_bindgen] - pub async fn get(&self, url: &str) -> Result, JsValue> { - self.inner_get(url) - .await - .map(|b| b.map(|b| (&*b).into())) - .map_err(|e| e.into()) - } - - /// Delete a file at a path relative to a pubky author. - #[wasm_bindgen] - pub async fn delete(&self, url: &str) -> Result<(), JsValue> { - self.inner_delete(url).await.map_err(|e| e.into()) - } - - /// Returns a list of Pubky urls (as strings). - /// - /// - `url`: The Pubky url (string) to the directory you want to list its content. - /// - `cursor`: Either a full `pubky://` Url (from previous list response), - /// or a path (to a file or directory) relative to the `url` - /// - `reverse`: List in reverse order - /// - `limit` Limit the number of urls in the response - /// - `shallow`: List directories and files, instead of flat list of files. - #[wasm_bindgen] - pub async fn list( - &self, - url: &str, - cursor: Option, - reverse: Option, - limit: Option, - shallow: Option, - ) -> Result { - // TODO: try later to return Vec from async function. - - if let Some(cursor) = cursor { - return self - .inner_list(url)? - .reverse(reverse.unwrap_or(false)) - .limit(limit.unwrap_or(u16::MAX)) - .cursor(&cursor) - .shallow(shallow.unwrap_or(false)) - .send() - .await - .map(|urls| { - let js_array = Array::new(); - - for url in urls { - js_array.push(&JsValue::from_str(&url)); - } - - js_array - }) - .map_err(|e| e.into()); - } - - self.inner_list(url)? - .reverse(reverse.unwrap_or(false)) - .limit(limit.unwrap_or(u16::MAX)) - .shallow(shallow.unwrap_or(false)) - .send() - .await - .map(|urls| { - let js_array = Array::new(); - - for url in urls { - js_array.push(&JsValue::from_str(&url)); - } - - js_array - }) - .map_err(|e| e.into()) - } } diff --git a/pubky/src/wasm/api.rs b/pubky/src/wasm/api.rs index 829640e..e339f11 100644 --- a/pubky/src/wasm/api.rs +++ b/pubky/src/wasm/api.rs @@ -1,3 +1,6 @@ -//! Public API module +//! Public API modules -mod fetch; +pub mod auth; +pub mod http; +pub mod public; +pub mod recovery_file; diff --git a/pubky/src/wasm/api/auth.rs b/pubky/src/wasm/api/auth.rs new file mode 100644 index 0000000..bb0a4b9 --- /dev/null +++ b/pubky/src/wasm/api/auth.rs @@ -0,0 +1,118 @@ +use url::Url; + +use pubky_common::capabilities::Capabilities; + +use crate::Error; +use crate::PubkyClient; + +use crate::wasm::wrappers::keys::{Keypair, PublicKey}; +use crate::wasm::wrappers::session::Session; + +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +impl PubkyClient { + /// Signup to a homeserver and update Pkarr accordingly. + /// + /// The homeserver is a Pkarr domain name, where the TLD is a Pkarr public key + /// for example "pubky.o4dksfbqk85ogzdb5osziw6befigbuxmuxkuxq8434q89uj56uyy" + #[wasm_bindgen] + pub async fn signup( + &self, + keypair: &Keypair, + homeserver: &PublicKey, + ) -> Result { + Ok(Session( + self.inner_signup(keypair.as_inner(), homeserver.as_inner()) + .await + .map_err(JsValue::from)?, + )) + } + + /// Check the current sesison for a given Pubky in its homeserver. + /// + /// Returns [Session] or `None` (if recieved `404 NOT_FOUND`), + /// or throws the recieved error if the response has any other `>=400` status code. + #[wasm_bindgen] + pub async fn session(&self, pubky: &PublicKey) -> Result, JsValue> { + self.inner_session(pubky.as_inner()) + .await + .map(|s| s.map(Session)) + .map_err(|e| e.into()) + } + + /// Signout from a homeserver. + #[wasm_bindgen] + pub async fn signout(&self, pubky: &PublicKey) -> Result<(), JsValue> { + self.inner_signout(pubky.as_inner()) + .await + .map_err(|e| e.into()) + } + + /// Signin to a homeserver using the root Keypair. + #[wasm_bindgen] + pub async fn signin(&self, keypair: &Keypair) -> Result<(), JsValue> { + self.inner_signin(keypair.as_inner()) + .await + .map(|_| ()) + .map_err(|e| e.into()) + } + + /// Return `pubkyauth://` url and wait for the incoming [AuthToken] + /// verifying that AuthToken, and if capabilities were requested, signing in to + /// the Pubky's homeserver and returning the [Session] information. + /// + /// Returns a tuple of [pubkyAuthUrl, Promise] + #[wasm_bindgen(js_name = "authRequest")] + pub fn auth_request(&self, relay: &str, capabilities: &str) -> Result { + let mut relay: Url = relay + .try_into() + .map_err(|_| Error::Generic("Invalid relay Url".into()))?; + + let (pubkyauth_url, client_secret) = self.create_auth_request( + &mut relay, + &Capabilities::try_from(capabilities).map_err(|_| "Invalid capaiblities")?, + )?; + + let this = self.clone(); + + let future = async move { + this.subscribe_to_auth_response(relay, &client_secret) + .await + .map(|opt| { + opt.map_or_else( + || JsValue::NULL, // Convert `None` to `JsValue::NULL` + |session| JsValue::from(Session(session)), + ) + }) + .map_err(|err| JsValue::from_str(&format!("{:?}", err))) + }; + + let promise = wasm_bindgen_futures::future_to_promise(future); + + // Return the URL and the promise + let js_tuple = js_sys::Array::new(); + js_tuple.push(&JsValue::from_str(pubkyauth_url.as_ref())); + js_tuple.push(&promise); + + Ok(js_tuple) + } + + /// Sign an [pubky_common::auth::AuthToken], encrypt it and send it to the + /// source of the pubkyauth request url. + #[wasm_bindgen(js_name = "sendAuthToken")] + pub async fn send_auth_token( + &self, + keypair: &Keypair, + pubkyauth_url: &str, + ) -> Result<(), JsValue> { + let pubkyauth_url: Url = pubkyauth_url + .try_into() + .map_err(|_| Error::Generic("Invalid relay Url".into()))?; + + self.inner_send_auth_token(keypair.as_inner(), pubkyauth_url) + .await?; + + Ok(()) + } +} diff --git a/pubky/src/wasm/api/fetch.rs b/pubky/src/wasm/api/http.rs similarity index 100% rename from pubky/src/wasm/api/fetch.rs rename to pubky/src/wasm/api/http.rs diff --git a/pubky/src/wasm/api/public.rs b/pubky/src/wasm/api/public.rs new file mode 100644 index 0000000..9bd8a17 --- /dev/null +++ b/pubky/src/wasm/api/public.rs @@ -0,0 +1,87 @@ +use wasm_bindgen::prelude::*; + +use js_sys::{Array, Uint8Array}; + +use crate::PubkyClient; + +#[wasm_bindgen] +impl PubkyClient { + #[wasm_bindgen] + /// Upload a small payload to a given path. + pub async fn put(&self, url: &str, content: &[u8]) -> Result<(), JsValue> { + self.inner_put(url, content).await.map_err(|e| e.into()) + } + + /// Download a small payload from a given path relative to a pubky author. + #[wasm_bindgen] + pub async fn get(&self, url: &str) -> Result, JsValue> { + self.inner_get(url) + .await + .map(|b| b.map(|b| (&*b).into())) + .map_err(|e| e.into()) + } + + /// Delete a file at a path relative to a pubky author. + #[wasm_bindgen] + pub async fn delete(&self, url: &str) -> Result<(), JsValue> { + self.inner_delete(url).await.map_err(|e| e.into()) + } + + /// Returns a list of Pubky urls (as strings). + /// + /// - `url`: The Pubky url (string) to the directory you want to list its content. + /// - `cursor`: Either a full `pubky://` Url (from previous list response), + /// or a path (to a file or directory) relative to the `url` + /// - `reverse`: List in reverse order + /// - `limit` Limit the number of urls in the response + /// - `shallow`: List directories and files, instead of flat list of files. + #[wasm_bindgen] + pub async fn list( + &self, + url: &str, + cursor: Option, + reverse: Option, + limit: Option, + shallow: Option, + ) -> Result { + // TODO: try later to return Vec from async function. + + if let Some(cursor) = cursor { + return self + .inner_list(url)? + .reverse(reverse.unwrap_or(false)) + .limit(limit.unwrap_or(u16::MAX)) + .cursor(&cursor) + .shallow(shallow.unwrap_or(false)) + .send() + .await + .map(|urls| { + let js_array = Array::new(); + + for url in urls { + js_array.push(&JsValue::from_str(&url)); + } + + js_array + }) + .map_err(|e| e.into()); + } + + self.inner_list(url)? + .reverse(reverse.unwrap_or(false)) + .limit(limit.unwrap_or(u16::MAX)) + .shallow(shallow.unwrap_or(false)) + .send() + .await + .map(|urls| { + let js_array = Array::new(); + + for url in urls { + js_array.push(&JsValue::from_str(&url)); + } + + js_array + }) + .map_err(|e| e.into()) + } +} diff --git a/pubky/src/wasm/recovery_file.rs b/pubky/src/wasm/api/recovery_file.rs similarity index 95% rename from pubky/src/wasm/recovery_file.rs rename to pubky/src/wasm/api/recovery_file.rs index 7b85178..89cff37 100644 --- a/pubky/src/wasm/recovery_file.rs +++ b/pubky/src/wasm/api/recovery_file.rs @@ -3,7 +3,7 @@ use wasm_bindgen::prelude::{wasm_bindgen, JsValue}; use crate::error::Error; -use super::keys::Keypair; +use crate::wasm::wrappers::keys::Keypair; /// Create a recovery file of the `keypair`, containing the secret key encrypted /// using the `passphrase`. diff --git a/pubky/src/wasm/http.rs b/pubky/src/wasm/internals/http.rs similarity index 100% rename from pubky/src/wasm/http.rs rename to pubky/src/wasm/internals/http.rs diff --git a/pubky/src/wasm/internals/mod.rs b/pubky/src/wasm/internals/mod.rs new file mode 100644 index 0000000..53e5f4b --- /dev/null +++ b/pubky/src/wasm/internals/mod.rs @@ -0,0 +1,2 @@ +pub mod http; +pub mod pkarr; diff --git a/pubky/src/wasm/pkarr.rs b/pubky/src/wasm/internals/pkarr.rs similarity index 100% rename from pubky/src/wasm/pkarr.rs rename to pubky/src/wasm/internals/pkarr.rs diff --git a/pubky/src/wasm/keys.rs b/pubky/src/wasm/wrappers/keys.rs similarity index 100% rename from pubky/src/wasm/keys.rs rename to pubky/src/wasm/wrappers/keys.rs diff --git a/pubky/src/wasm/wrappers/mod.rs b/pubky/src/wasm/wrappers/mod.rs new file mode 100644 index 0000000..f632a1f --- /dev/null +++ b/pubky/src/wasm/wrappers/mod.rs @@ -0,0 +1,5 @@ +//! Wasm wrappers around structs that we need to be turned into Classes +//! in JavaScript. + +pub mod keys; +pub mod session; diff --git a/pubky/src/wasm/session.rs b/pubky/src/wasm/wrappers/session.rs similarity index 100% rename from pubky/src/wasm/session.rs rename to pubky/src/wasm/wrappers/session.rs From c4af158fc84e81a7d15a8cacf6039f24c0911287 Mon Sep 17 00:00:00 2001 From: nazeh Date: Thu, 19 Sep 2024 17:00:31 +0300 Subject: [PATCH 04/14] feat(pubky): Resolve and connect to a direct server (localhost) --- Cargo.lock | 319 +++++++++++++++--------- Cargo.toml | 3 + pubky-homeserver/src/pkarr.rs | 7 +- pubky-homeserver/src/server.rs | 2 +- pubky/Cargo.toml | 6 +- pubky/pkg/test/http.js | 4 +- pubky/src/error.rs | 6 +- pubky/src/native.rs | 12 +- pubky/src/native/api.rs | 5 +- pubky/src/native/api/http.rs | 48 ++++ pubky/src/native/internals.rs | 4 +- pubky/src/native/internals/endpoints.rs | 230 +++++++++++++++++ pubky/src/shared/auth.rs | 10 +- pubky/src/shared/list_builder.rs | 2 +- pubky/src/shared/public.rs | 14 +- pubky/src/wasm/internals/http.rs | 2 +- 16 files changed, 522 insertions(+), 152 deletions(-) create mode 100644 pubky/src/native/api/http.rs create mode 100644 pubky/src/native/internals/endpoints.rs diff --git a/Cargo.lock b/Cargo.lock index a712105..7093643 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,18 +4,18 @@ version = 3 [[package]] name = "addr2line" -version = "0.22.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" dependencies = [ "gimli", ] [[package]] -name = "adler" -version = "1.0.2" +name = "adler2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "aead" @@ -87,9 +87,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.86" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" [[package]] name = "argon2" @@ -105,21 +105,21 @@ dependencies = [ [[package]] name = "arrayref" -version = "0.3.7" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" [[package]] name = "arrayvec" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "async-trait" -version = "0.1.81" +version = "0.1.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" +checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" dependencies = [ "proc-macro2", "quote", @@ -250,17 +250,17 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.73" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ "addr2line", - "cc", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", + "windows-targets 0.52.6", ] [[package]] @@ -316,9 +316,9 @@ dependencies = [ [[package]] name = "blake3" -version = "1.5.2" +version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d08263faac5cde2a4d52b513dadb80846023aade56fcd8fc99ba73ba8050e92" +checksum = "d82033247fd8e890df8f740e407ad4d038debb9eb1f40533fffb32e7d17dc6f7" dependencies = [ "arrayref", "arrayvec", @@ -350,15 +350,18 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.1" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" [[package]] name = "cc" -version = "1.1.3" +version = "1.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18e2d530f35b40a84124146478cd16f34225306a8441998836466a2e2961c950" +checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0" +dependencies = [ + "shlex", +] [[package]] name = "cfg-if" @@ -379,9 +382,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.16" +version = "4.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019" +checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac" dependencies = [ "clap_builder", "clap_derive", @@ -389,9 +392,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.15" +version = "4.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6" +checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73" dependencies = [ "anstream", "anstyle", @@ -437,9 +440,9 @@ checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "constant_time_eq" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" +checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" [[package]] name = "cookie" @@ -471,9 +474,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.12" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" dependencies = [ "libc", ] @@ -495,9 +498,9 @@ checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" [[package]] name = "critical-section" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" +checksum = "f64009896348fc5af4222e9cf7d7d82a95a256c634ebcf61c53e4ea461422242" [[package]] name = "crossbeam-queue" @@ -674,6 +677,12 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + [[package]] name = "equivalent" version = "1.0.1" @@ -828,9 +837,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.29.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" [[package]] name = "hash32" @@ -899,9 +908,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "heed" -version = "0.20.3" +version = "0.20.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bc30da4a93ff8cb98e535d595d6de42731d4719d707bc1c86f579158751a24e" +checksum = "7d4f449bab7320c56003d37732a917e18798e2f1709d80263face2b4f9436ddb" dependencies = [ "bitflags", "byteorder", @@ -1015,9 +1024,9 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.2" +version = "0.27.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155" +checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" dependencies = [ "futures-util", "http", @@ -1033,9 +1042,8 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ab92f4f49ee4fb4f997c784b7a2e0fa70050211e0b6a287f898c3c9785ca956" +version = "0.1.8" +source = "git+https://github.com/hyperium/hyper-util.git#2639193e9134a235db42cca16c8cff7f21f61661" dependencies = [ "bytes", "futures-channel", @@ -1073,9 +1081,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" +checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" dependencies = [ "equivalent", "hashbrown", @@ -1092,9 +1100,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4" [[package]] name = "is_terminal_polyfill" @@ -1125,9 +1133,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.155" +version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" [[package]] name = "libredox" @@ -1147,9 +1155,9 @@ checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" [[package]] name = "lmdb-master-sys" -version = "0.2.2" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57640c190703d5ccf4a86aff4aeb749b2d287a8cb1723c76b51f39d77ab53b24" +checksum = "472c3760e2a8d0f61f322fb36788021bb36d573c502b50fa3e2bcaac3ec326c9" dependencies = [ "cc", "doxygen-rs", @@ -1174,9 +1182,9 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "lru" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" +checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" [[package]] name = "mainline" @@ -1227,11 +1235,11 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniz_oxide" -version = "0.7.4" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" dependencies = [ - "adler", + "adler2", ] [[package]] @@ -1273,9 +1281,9 @@ checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] name = "object" -version = "0.36.1" +version = "0.36.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce" +checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" dependencies = [ "memchr", ] @@ -1425,7 +1433,7 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkarr" version = "2.2.0" -source = "git+https://github.com/Pubky/pkarr?branch=v3#17975121c809d97dcad907fbb2ffc782e994d270" +source = "git+https://github.com/Pubky/pkarr?branch=v3#5b2f8e53735f63521b0d587167609a2cb9b8f2ab" dependencies = [ "base32", "bytes", @@ -1471,12 +1479,13 @@ dependencies = [ [[package]] name = "postcard" -version = "1.0.8" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a55c51ee6c0db07e68448e336cf8ea4131a620edefebf9893e759b2d793420f8" +checksum = "5f7f0a8d620d71c457dd1d47df76bb18960378da56af4527aaa10f515eee732e" dependencies = [ "cobs", - "embedded-io", + "embedded-io 0.4.0", + "embedded-io 0.6.1", "heapless", "serde", ] @@ -1489,9 +1498,12 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] [[package]] name = "proc-macro2" @@ -1524,6 +1536,7 @@ dependencies = [ "url", "wasm-bindgen", "wasm-bindgen-futures", + "web-sys", ] [[package]] @@ -1583,9 +1596,9 @@ dependencies = [ [[package]] name = "quinn" -version = "0.11.3" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b22d8e7369034b9a7132bc2008cac12f2013c8132b45e0554e6e20e2617f2156" +checksum = "8c7c5fdde3cdae7203427dc4f0a68fe0ed09833edc525a03456b153b79828684" dependencies = [ "bytes", "pin-project-lite", @@ -1601,9 +1614,9 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.6" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba92fb39ec7ad06ca2582c0ca834dfeadcaf06ddfc8e635c80aa7e1c05315fdd" +checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6" dependencies = [ "bytes", "rand", @@ -1618,22 +1631,22 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bffec3605b73c6f1754535084a85229fa8a30f86014e6c81aeec4abb68b0285" +checksum = "4fe68c2e9e1a1234e218683dbdf9f9dfcb094113c5ac2b938dfcb9bab4c4140b" dependencies = [ "libc", "once_cell", "socket2", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -1670,18 +1683,18 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.2" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" +checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853" dependencies = [ "bitflags", ] [[package]] name = "redox_users" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ "getrandom", "libredox", @@ -1690,9 +1703,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.5" +version = "1.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ "aho-corasick", "memchr", @@ -1734,9 +1747,9 @@ checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "reqwest" -version = "0.12.5" +version = "0.12.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37" +checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63" dependencies = [ "base64 0.22.1", "bytes", @@ -1773,7 +1786,7 @@ dependencies = [ "wasm-bindgen-futures", "web-sys", "webpki-roots", - "winreg", + "windows-registry", ] [[package]] @@ -1826,18 +1839,18 @@ checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" [[package]] name = "rustc_version" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ "semver", ] [[package]] name = "rustls" -version = "0.23.12" +version = "0.23.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" +checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8" dependencies = [ "once_cell", "ring", @@ -1865,9 +1878,9 @@ checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" [[package]] name = "rustls-webpki" -version = "0.102.6" +version = "0.102.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" dependencies = [ "ring", "rustls-pki-types", @@ -1915,9 +1928,9 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.209" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] @@ -1943,9 +1956,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.209" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", @@ -1954,11 +1967,12 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.120" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] @@ -2007,9 +2021,9 @@ dependencies = [ [[package]] name = "sha1_smol" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" +checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" [[package]] name = "sha2" @@ -2031,6 +2045,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "signal-hook-registry" version = "1.4.2" @@ -2128,9 +2148,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.71" +version = "2.0.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" dependencies = [ "proc-macro2", "quote", @@ -2148,6 +2168,9 @@ name = "sync_wrapper" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +dependencies = [ + "futures-core", +] [[package]] name = "synchronoise" @@ -2160,18 +2183,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.62" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2675633b1499176c2dff06b0856a27976a8f9d436737b4cf4f312d4d91d8bbb" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.62" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d20468752b09f49e909e55a5d338caa8bedf615594e9d80bc4c565d30faf798c" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", @@ -2276,9 +2299,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.11" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" dependencies = [ "bytes", "futures-core", @@ -2310,9 +2333,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.20" +version = "0.22.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" +checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf" dependencies = [ "indexmap", "serde", @@ -2373,15 +2396,15 @@ dependencies = [ [[package]] name = "tower-layer" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" [[package]] name = "tower-service" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" @@ -2465,15 +2488,15 @@ checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-normalization" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" dependencies = [ "tinyvec", ] @@ -2519,9 +2542,9 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "want" @@ -2566,9 +2589,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.42" +version = "0.4.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" dependencies = [ "cfg-if", "js-sys", @@ -2617,9 +2640,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.26.3" +version = "0.26.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd7c23921eeb1713a4e851530e9b9756e4fb0e89978582942612524cf09f01cd" +checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958" dependencies = [ "rustls-pki-types", ] @@ -2646,6 +2669,36 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +dependencies = [ + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -2664,6 +2717,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-targets" version = "0.48.5" @@ -2795,13 +2857,24 @@ dependencies = [ ] [[package]] -name = "winreg" -version = "0.52.0" +name = "zerocopy" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ - "cfg-if", - "windows-sys 0.48.0", + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 8514809..d5a1fc7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,3 +16,6 @@ serde = { version = "^1.0.209", features = ["derive"] } [profile.release] lto = true opt-level = 'z' + +[patch.crates-io] +hyper-util = { git = "https://github.com/hyperium/hyper-util.git" } diff --git a/pubky-homeserver/src/pkarr.rs b/pubky-homeserver/src/pkarr.rs index cf4d7b7..1a680cd 100644 --- a/pubky-homeserver/src/pkarr.rs +++ b/pubky-homeserver/src/pkarr.rs @@ -28,16 +28,15 @@ pub async fn publish_server_packet( // svcb.set_param(key, value) }; - // TODO: announce A/AAAA records as well for Noise connections? - // Or maybe Iroh's magic socket - packet.answers.push(pkarr::dns::ResourceRecord::new( "@".try_into().unwrap(), pkarr::dns::CLASS::IN, 60 * 60, - pkarr::dns::rdata::RData::SVCB(svcb), + pkarr::dns::rdata::RData::HTTPS(svcb.clone().into()), )); + // TODO: announce A/AAAA records as well for TLS connections? + let signed_packet = SignedPacket::from_packet(keypair, &packet)?; pkarr_client.publish(&signed_packet).await?; diff --git a/pubky-homeserver/src/server.rs b/pubky-homeserver/src/server.rs index c94a803..9558780 100644 --- a/pubky-homeserver/src/server.rs +++ b/pubky-homeserver/src/server.rs @@ -74,7 +74,7 @@ impl Homeserver { publish_server_packet(pkarr_client, &keypair, config.domain(), port).await?; - info!("Homeserver listening on pubky://{}", keypair.public_key()); + info!("Homeserver listening on http://{}", keypair.public_key()); Ok(Self { tasks, diff --git a/pubky/Cargo.toml b/pubky/Cargo.toml index 6871377..84f8bba 100644 --- a/pubky/Cargo.toml +++ b/pubky/Cargo.toml @@ -20,17 +20,21 @@ base64 = "0.22.1" pubky-common = { version = "0.1.0", path = "../pubky-common" } pkarr = { workspace = true, features = ["async"] } +# Native dependencies [target.'cfg(not(target_arch = "wasm32"))'.dependencies] reqwest = { version = "0.12.5", features = ["cookies", "rustls-tls"], default-features = false } tokio = { version = "1.37.0", features = ["full"] } +# Wasm dependencies [target.'cfg(target_arch = "wasm32")'.dependencies] reqwest = { version = "0.12.5", default-features = false } -js-sys = "0.3.69" wasm-bindgen = "0.2.92" wasm-bindgen-futures = "0.4.42" +js-sys = "0.3.69" +web-sys = "0.3.70" + [dev-dependencies] pubky_homeserver = { path = "../pubky-homeserver" } tokio = "1.37.0" diff --git a/pubky/pkg/test/http.js b/pubky/pkg/test/http.js index 8f27326..5348d27 100644 --- a/pubky/pkg/test/http.js +++ b/pubky/pkg/test/http.js @@ -6,10 +6,10 @@ const TLD = '8pinxxgqs41n4aididenw5apqp1urfmzdztr8jt4abrkdn435ewo'; // TODO: test HTTPs too somehow. -test("basic fetch", async (t) => { +test.skip("basic fetch", async (t) => { let client = PubkyClient.testnet(); - let response = await client.fetch(`https://${TLD}/`, new Uint8Array([])); + let response = await client.fetch(`http://${TLD}/`, new Uint8Array([])); t.equal(response.status, 200); }) diff --git a/pubky/src/error.rs b/pubky/src/error.rs index c8d80e1..ec6d125 100644 --- a/pubky/src/error.rs +++ b/pubky/src/error.rs @@ -12,9 +12,6 @@ pub enum Error { #[error("Generic error: {0}")] Generic(String), - #[error("Could not resolve endpoint for {0}")] - ResolveEndpoint(String), - #[error("Could not convert the passed type into a Url")] InvalidUrl, @@ -42,6 +39,9 @@ pub enum Error { #[error(transparent)] AuthToken(#[from] pubky_common::auth::Error), + + #[error("Could not resolve Endpoint for domain: {0}")] + ResolveEndpoint(String), } #[cfg(target_arch = "wasm32")] diff --git a/pubky/src/native.rs b/pubky/src/native.rs index 572d62b..b6b1522 100644 --- a/pubky/src/native.rs +++ b/pubky/src/native.rs @@ -1,5 +1,5 @@ -use std::net::ToSocketAddrs; use std::time::Duration; +use std::{net::ToSocketAddrs, sync::Arc}; use ::pkarr::{mainline::dht::Testnet, PkarrClient}; @@ -8,6 +8,8 @@ use crate::PubkyClient; mod api; mod internals; +use internals::endpoints::PkarrResolver; + static DEFAULT_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"),); #[derive(Debug, Default)] @@ -50,13 +52,19 @@ impl PubkyClientBuilder { /// Build [PubkyClient] pub fn build(self) -> PubkyClient { + // TODO: convert to Result + + let pkarr = PkarrClient::new(self.pkarr_settings).unwrap().as_async(); + let dns_resolver = PkarrResolver::new(pkarr.clone()); + PubkyClient { http: reqwest::Client::builder() .cookie_store(true) + .dns_resolver(Arc::new(dns_resolver)) .user_agent(DEFAULT_USER_AGENT) .build() .unwrap(), - pkarr: PkarrClient::new(self.pkarr_settings).unwrap().as_async(), + pkarr, } } } diff --git a/pubky/src/native/api.rs b/pubky/src/native/api.rs index d24c0c4..a506a10 100644 --- a/pubky/src/native/api.rs +++ b/pubky/src/native/api.rs @@ -1,5 +1,8 @@ //! Public API modules +pub mod http; +pub mod recovery_file; + +// TODO: put the Homeserver API behind a feature flag pub mod auth; pub mod public; -pub mod recovery_file; diff --git a/pubky/src/native/api/http.rs b/pubky/src/native/api/http.rs new file mode 100644 index 0000000..ade2c8e --- /dev/null +++ b/pubky/src/native/api/http.rs @@ -0,0 +1,48 @@ +use reqwest::{IntoUrl, Method, RequestBuilder}; + +use crate::PubkyClient; + +impl PubkyClient { + /// Start building a `Request` with the `Method` and `Url`. + /// + /// Returns a `RequestBuilder`, which will allow setting headers and + /// the request body before sending. + /// + /// Differs from [reqwest::Client::request], in that it can make requests + /// to URLs with a [pkarr::PublicKey] as Top Level Domain, by resolving + /// corresponding endpoints, and verifying TLS certificates accordingly. + /// + /// # Errors + /// + /// This method fails whenever the supplied `Url` cannot be parsed. + pub fn request(&self, method: Method, url: U) -> RequestBuilder { + self.http.request(method, url) + } +} + +#[cfg(test)] +mod tests { + use pkarr::mainline::Testnet; + use pubky_homeserver::Homeserver; + + use crate::*; + + #[tokio::test] + async fn http_get() { + let testnet = Testnet::new(10); + + let homeserver = Homeserver::start_test(&testnet).await.unwrap(); + + let client = PubkyClient::testnet(); + + let url = format!("http://{}/", homeserver.public_key()); + + let response = client + .request(Default::default(), url) + .send() + .await + .unwrap(); + + assert_eq!(response.status(), 200) + } +} diff --git a/pubky/src/native/internals.rs b/pubky/src/native/internals.rs index 21ead99..822ecbb 100644 --- a/pubky/src/native/internals.rs +++ b/pubky/src/native/internals.rs @@ -6,6 +6,8 @@ use url::Url; use crate::error::Result; use crate::PubkyClient; +pub mod endpoints; + impl PubkyClient { // === Pkarr === @@ -22,7 +24,7 @@ impl PubkyClient { // === HTTP === - pub(crate) fn request(&self, method: reqwest::Method, url: Url) -> RequestBuilder { + pub(crate) fn inner_request(&self, method: reqwest::Method, url: Url) -> RequestBuilder { self.http.request(method, url) } diff --git a/pubky/src/native/internals/endpoints.rs b/pubky/src/native/internals/endpoints.rs new file mode 100644 index 0000000..64352d4 --- /dev/null +++ b/pubky/src/native/internals/endpoints.rs @@ -0,0 +1,230 @@ +use std::net::ToSocketAddrs; + +use pkarr::dns::rdata::{RData, SVCB}; +use pkarr::{PkarrClientAsync, PublicKey, SignedPacket}; +use reqwest::dns::{Addrs, Resolve}; + +use crate::error::{Error, Result}; + +const MAX_ENDPOINT_RESOLUTION_RECURSION: u8 = 3; + +#[derive(Debug, Clone)] +pub struct PkarrResolver { + pkarr: PkarrClientAsync, +} + +impl PkarrResolver { + pub fn new(pkarr: PkarrClientAsync) -> Self { + Self { pkarr } + } + + /// Resolve a `qname` to an alternative [Endpoint] as defined in [RFC9460](https://www.rfc-editor.org/rfc/rfc9460#name-terminology). + /// + /// A `qname` is can be either a regular domain name for HTTPS endpoints, + /// or it could use Attrleaf naming pattern for cusotm protcol. For example: + /// `_foo.example.com` for `foo://example.com`. + async fn resolve_endpoint(&self, qname: &str) -> Result { + let target = qname; + // TODO: cache the result of this function? + + let mut step = 0; + let mut svcb: Option = None; + + loop { + let current = svcb.clone().map_or(target.to_string(), |s| s.target); + if let Ok(tld) = PublicKey::try_from(current.clone()) { + if let Ok(Some(signed_packet)) = self.pkarr.resolve(&tld).await { + if step >= MAX_ENDPOINT_RESOLUTION_RECURSION { + break; + }; + step += 1; + + // Choose most prior SVCB record + svcb = get_endpoint(&signed_packet, ¤t); + + // TODO: support wildcard? + + if step >= MAX_ENDPOINT_RESOLUTION_RECURSION { + break; + }; + } else { + break; + } + } else { + break; + } + } + + if let Some(svcb) = svcb { + if PublicKey::try_from(svcb.target.as_str()).is_err() { + return Ok(svcb); + } + } + + Err(Error::ResolveEndpoint(target.into())) + } +} + +impl Resolve for PkarrResolver { + fn resolve(&self, name: reqwest::dns::Name) -> reqwest::dns::Resolving { + let client = self.clone(); + + Box::pin(async move { + let name = name.as_str(); + + if PublicKey::try_from(name).is_ok() { + let x = client.resolve_endpoint(name).await?; + + let addrs = format!("{}:{}", x.target, x.port).to_socket_addrs()?; + + let addrs: Addrs = Box::new(addrs); + + return Ok(addrs); + }; + + Ok(Box::new(format!("{name}:0").to_socket_addrs().unwrap())) + }) + } +} + +#[derive(Debug, Clone)] +struct Endpoint { + target: String, + // public_key: PublicKey, + port: u16, +} + +fn get_endpoint(signed_packet: &SignedPacket, target: &str) -> Option { + let is_svcb = target.starts_with('_'); + + signed_packet + .resource_records(target) + .fold(None, |prev: Option, answer| { + if let Some(svcb) = match &answer.rdata { + RData::SVCB(svcb) => { + if is_svcb { + Some(svcb) + } else { + None + } + } + RData::HTTPS(curr) => { + if is_svcb { + None + } else { + Some(&curr.0) + } + } + _ => None, + } { + let curr = svcb.clone(); + + if curr.priority == 0 { + return Some(curr); + } + if let Some(prev) = &prev { + if curr.priority >= prev.priority { + return Some(curr); + } + } else { + return Some(curr); + } + } + + prev + }) + .map(|s| Endpoint { + target: s.target.to_string(), + // public_key: signed_packet.public_key(), + port: u16::from_be_bytes( + s.get_param(SVCB::PORT) + .unwrap_or_default() + .try_into() + .unwrap_or([0, 0]), + ), + }) +} + +#[cfg(test)] +mod tests { + use super::*; + use pkarr::dns::{self, rdata::RData}; + use pkarr::PkarrClient; + use pkarr::{mainline::Testnet, Keypair}; + + async fn publish_packets( + client: &PkarrClientAsync, + tree: Vec)>>, + ) -> Vec { + let mut keypairs: Vec = Vec::with_capacity(tree.len()); + for node in tree { + let mut packet = dns::Packet::new_reply(0); + for record in node { + packet.answers.push(dns::ResourceRecord::new( + dns::Name::new(record.0).unwrap(), + dns::CLASS::IN, + 3600, + record.1, + )); + } + let keypair = Keypair::random(); + let signed_packet = SignedPacket::from_packet(&keypair, &packet).unwrap(); + keypairs.push(keypair); + client.publish(&signed_packet).await.unwrap(); + } + keypairs + } + + #[tokio::test] + async fn resolve_direct_endpoint() { + let testnet = Testnet::new(3); + let pkarr = PkarrClient::builder() + .testnet(&testnet) + .build() + .unwrap() + .as_async(); + + let keypairs = publish_packets( + &pkarr, + vec![vec![ + ( + "foo", + RData::HTTPS(SVCB::new(0, "https.example.com".try_into().unwrap()).into()), + ), + // Make sure HTTPS only follows HTTPs + ( + "foo", + RData::SVCB(SVCB::new(0, "protocol.example.com".try_into().unwrap())), + ), + // Make sure SVCB only follows SVCB + ( + "foo", + RData::HTTPS(SVCB::new(0, "https.example.com".try_into().unwrap()).into()), + ), + ( + "_foo", + RData::SVCB(SVCB::new(0, "protocol.example.com".try_into().unwrap())), + ), + ]], + ) + .await; + + let resolver = PkarrResolver { pkarr }; + + let tld = keypairs.first().unwrap().public_key(); + + // Follow foo.tld HTTPS records + let endpoint = resolver + .resolve_endpoint(&format!("foo.{tld}")) + .await + .unwrap(); + assert_eq!(endpoint.target, "https.example.com"); + + // Follow _foo.tld SVCB records + let endpoint = resolver + .resolve_endpoint(&format!("_foo.{tld}")) + .await + .unwrap(); + assert_eq!(endpoint.target, "protocol.example.com"); + } +} diff --git a/pubky/src/shared/auth.rs b/pubky/src/shared/auth.rs index 88c4259..a02a1c8 100644 --- a/pubky/src/shared/auth.rs +++ b/pubky/src/shared/auth.rs @@ -38,7 +38,7 @@ impl PubkyClient { let body = AuthToken::sign(keypair, vec![Capability::root()]).serialize(); let response = self - .request(Method::POST, url.clone()) + .inner_request(Method::POST, url.clone()) .body(body) .send() .await?; @@ -61,7 +61,7 @@ impl PubkyClient { url.set_path(&format!("/{}/session", pubky)); - let res = self.request(Method::GET, url).send().await?; + let res = self.inner_request(Method::GET, url).send().await?; if res.status() == StatusCode::NOT_FOUND { return Ok(None); @@ -82,7 +82,7 @@ impl PubkyClient { url.set_path(&format!("/{}/session", pubky)); - self.request(Method::DELETE, url).send().await?; + self.inner_request(Method::DELETE, url).send().await?; self.remove_session(pubky); @@ -143,7 +143,7 @@ impl PubkyClient { path_segments.push(&channel_id); drop(path_segments); - self.request(Method::POST, callback) + self.inner_request(Method::POST, callback) .body(encrypted_token) .send() .await?; @@ -170,7 +170,7 @@ impl PubkyClient { self.resolve_url(&mut url).await?; let response = self - .request(Method::POST, url) + .inner_request(Method::POST, url) .body(token.serialize()) .send() .await?; diff --git a/pubky/src/shared/list_builder.rs b/pubky/src/shared/list_builder.rs index 0eaec77..2087274 100644 --- a/pubky/src/shared/list_builder.rs +++ b/pubky/src/shared/list_builder.rs @@ -90,7 +90,7 @@ impl<'a> ListBuilder<'a> { drop(query); - let response = self.client.request(Method::GET, url).send().await?; + let response = self.client.inner_request(Method::GET, url).send().await?; response.error_for_status_ref()?; diff --git a/pubky/src/shared/public.rs b/pubky/src/shared/public.rs index becf2fb..366f78c 100644 --- a/pubky/src/shared/public.rs +++ b/pubky/src/shared/public.rs @@ -16,7 +16,7 @@ impl PubkyClient { let url = self.pubky_to_http(url).await?; let response = self - .request(Method::PUT, url) + .inner_request(Method::PUT, url) .body(content.to_owned()) .send() .await?; @@ -29,7 +29,7 @@ impl PubkyClient { pub(crate) async fn inner_get>(&self, url: T) -> Result> { let url = self.pubky_to_http(url).await?; - let response = self.request(Method::GET, url).send().await?; + let response = self.inner_request(Method::GET, url).send().await?; if response.status() == StatusCode::NOT_FOUND { return Ok(None); @@ -46,7 +46,7 @@ impl PubkyClient { pub(crate) async fn inner_delete>(&self, url: T) -> Result<()> { let url = self.pubky_to_http(url).await?; - let response = self.request(Method::DELETE, url).send().await?; + let response = self.inner_request(Method::DELETE, url).send().await?; response.error_for_status_ref()?; @@ -650,7 +650,7 @@ mod tests { { let response = client - .request( + .inner_request( Method::GET, format!("{feed_url}?limit=10").as_str().try_into().unwrap(), ) @@ -683,7 +683,7 @@ mod tests { { let response = client - .request( + .inner_request( Method::GET, format!("{feed_url}?limit=10&cursor={cursor}") .as_str() @@ -740,7 +740,7 @@ mod tests { { let response = client - .request( + .inner_request( Method::GET, format!("{feed_url}?limit=10").as_str().try_into().unwrap(), ) @@ -800,7 +800,7 @@ mod tests { let feed_url = format!("http://localhost:{}/events/", homeserver.port()); let response = client - .request( + .inner_request( Method::GET, format!("{feed_url}").as_str().try_into().unwrap(), ) diff --git a/pubky/src/wasm/internals/http.rs b/pubky/src/wasm/internals/http.rs index 61fee29..b28d9f9 100644 --- a/pubky/src/wasm/internals/http.rs +++ b/pubky/src/wasm/internals/http.rs @@ -4,7 +4,7 @@ use reqwest::{Method, RequestBuilder, Response}; use url::Url; impl PubkyClient { - pub(crate) fn request(&self, method: Method, url: Url) -> RequestBuilder { + pub(crate) fn inner_request(&self, method: Method, url: Url) -> RequestBuilder { let mut request = self.http.request(method, url).fetch_credentials_include(); for cookie in self.session_cookies.read().unwrap().iter() { From bcaebc55d215c1583633c6d3d36bb5b81edffff3 Mon Sep 17 00:00:00 2001 From: nazeh Date: Fri, 20 Sep 2024 10:17:05 +0300 Subject: [PATCH 05/14] feat(pubky): Resolve through intermediate pkarr packet --- pubky/src/native/internals/endpoints.rs | 153 +++++++++++++++--------- 1 file changed, 95 insertions(+), 58 deletions(-) diff --git a/pubky/src/native/internals/endpoints.rs b/pubky/src/native/internals/endpoints.rs index 64352d4..b650e75 100644 --- a/pubky/src/native/internals/endpoints.rs +++ b/pubky/src/native/internals/endpoints.rs @@ -6,7 +6,7 @@ use reqwest::dns::{Addrs, Resolve}; use crate::error::{Error, Result}; -const MAX_ENDPOINT_RESOLUTION_RECURSION: u8 = 3; +const MAX_CHAIN_LENGTH: u8 = 3; #[derive(Debug, Clone)] pub struct PkarrResolver { @@ -27,6 +27,8 @@ impl PkarrResolver { let target = qname; // TODO: cache the result of this function? + let is_svcb = target.starts_with('_'); + let mut step = 0; let mut svcb: Option = None; @@ -34,19 +36,15 @@ impl PkarrResolver { let current = svcb.clone().map_or(target.to_string(), |s| s.target); if let Ok(tld) = PublicKey::try_from(current.clone()) { if let Ok(Some(signed_packet)) = self.pkarr.resolve(&tld).await { - if step >= MAX_ENDPOINT_RESOLUTION_RECURSION { + if step >= MAX_CHAIN_LENGTH { break; }; step += 1; // Choose most prior SVCB record - svcb = get_endpoint(&signed_packet, ¤t); + svcb = get_endpoint(&signed_packet, ¤t, is_svcb); // TODO: support wildcard? - - if step >= MAX_ENDPOINT_RESOLUTION_RECURSION { - break; - }; } else { break; } @@ -94,9 +92,7 @@ struct Endpoint { port: u16, } -fn get_endpoint(signed_packet: &SignedPacket, target: &str) -> Option { - let is_svcb = target.starts_with('_'); - +fn get_endpoint(signed_packet: &SignedPacket, target: &str, is_svcb: bool) -> Option { signed_packet .resource_records(target) .fold(None, |prev: Option, answer| { @@ -152,29 +148,6 @@ mod tests { use pkarr::PkarrClient; use pkarr::{mainline::Testnet, Keypair}; - async fn publish_packets( - client: &PkarrClientAsync, - tree: Vec)>>, - ) -> Vec { - let mut keypairs: Vec = Vec::with_capacity(tree.len()); - for node in tree { - let mut packet = dns::Packet::new_reply(0); - for record in node { - packet.answers.push(dns::ResourceRecord::new( - dns::Name::new(record.0).unwrap(), - dns::CLASS::IN, - 3600, - record.1, - )); - } - let keypair = Keypair::random(); - let signed_packet = SignedPacket::from_packet(&keypair, &packet).unwrap(); - keypairs.push(keypair); - client.publish(&signed_packet).await.unwrap(); - } - keypairs - } - #[tokio::test] async fn resolve_direct_endpoint() { let testnet = Testnet::new(3); @@ -184,34 +157,40 @@ mod tests { .unwrap() .as_async(); - let keypairs = publish_packets( - &pkarr, - vec![vec![ - ( - "foo", - RData::HTTPS(SVCB::new(0, "https.example.com".try_into().unwrap()).into()), - ), - // Make sure HTTPS only follows HTTPs - ( - "foo", - RData::SVCB(SVCB::new(0, "protocol.example.com".try_into().unwrap())), - ), - // Make sure SVCB only follows SVCB - ( - "foo", - RData::HTTPS(SVCB::new(0, "https.example.com".try_into().unwrap()).into()), - ), - ( - "_foo", - RData::SVCB(SVCB::new(0, "protocol.example.com".try_into().unwrap())), - ), - ]], - ) - .await; + let mut packet = dns::Packet::new_reply(0); + packet.answers.push(dns::ResourceRecord::new( + dns::Name::new("foo").unwrap(), + dns::CLASS::IN, + 3600, + RData::HTTPS(SVCB::new(0, "https.example.com".try_into().unwrap()).into()), + )); + // Make sure HTTPS only follows HTTPs + packet.answers.push(dns::ResourceRecord::new( + dns::Name::new("foo").unwrap(), + dns::CLASS::IN, + 3600, + RData::SVCB(SVCB::new(0, "protocol.example.com".try_into().unwrap())), + )); + // Make sure SVCB only follows SVCB + packet.answers.push(dns::ResourceRecord::new( + dns::Name::new("foo").unwrap(), + dns::CLASS::IN, + 3600, + RData::HTTPS(SVCB::new(0, "https.example.com".try_into().unwrap()).into()), + )); + packet.answers.push(dns::ResourceRecord::new( + dns::Name::new("_foo").unwrap(), + dns::CLASS::IN, + 3600, + RData::SVCB(SVCB::new(0, "protocol.example.com".try_into().unwrap())), + )); + let keypair = Keypair::random(); + let inter_signed_packet = SignedPacket::from_packet(&keypair, &packet).unwrap(); + pkarr.publish(&inter_signed_packet).await.unwrap(); let resolver = PkarrResolver { pkarr }; - let tld = keypairs.first().unwrap().public_key(); + let tld = keypair.public_key(); // Follow foo.tld HTTPS records let endpoint = resolver @@ -227,4 +206,62 @@ mod tests { .unwrap(); assert_eq!(endpoint.target, "protocol.example.com"); } + + #[tokio::test] + async fn resolve_endpoint_with_intermediate_pubky() { + let testnet = Testnet::new(3); + let pkarr = PkarrClient::builder() + .testnet(&testnet) + .build() + .unwrap() + .as_async(); + + // USER => Server Owner => Server + // pubky. => pubky-homeserver. => @. + + let mut packet = dns::Packet::new_reply(0); + packet.answers.push(dns::ResourceRecord::new( + dns::Name::new("@").unwrap(), + dns::CLASS::IN, + 3600, + RData::HTTPS(SVCB::new(0, "example.com".try_into().unwrap()).into()), + )); + let keypair = Keypair::random(); + let inter_signed_packet = SignedPacket::from_packet(&keypair, &packet).unwrap(); + pkarr.publish(&inter_signed_packet).await.unwrap(); + + let end_target = format!("{}", keypair.public_key()); + let mut packet = dns::Packet::new_reply(0); + packet.answers.push(dns::ResourceRecord::new( + dns::Name::new("pubky-homeserver.").unwrap(), + dns::CLASS::IN, + 3600, + RData::HTTPS(SVCB::new(0, end_target.as_str().try_into().unwrap()).into()), + )); + let keypair = Keypair::random(); + let inter_signed_packet = SignedPacket::from_packet(&keypair, &packet).unwrap(); + pkarr.publish(&inter_signed_packet).await.unwrap(); + + let inter_target = format!("pubky-homeserver.{}", keypair.public_key()); + let mut packet = dns::Packet::new_reply(0); + packet.answers.push(dns::ResourceRecord::new( + dns::Name::new("pubky.").unwrap(), + dns::CLASS::IN, + 3600, + RData::HTTPS(SVCB::new(0, inter_target.as_str().try_into().unwrap()).into()), + )); + let keypair = Keypair::random(); + let inter_signed_packet = SignedPacket::from_packet(&keypair, &packet).unwrap(); + pkarr.publish(&inter_signed_packet).await.unwrap(); + + let resolver = PkarrResolver { pkarr }; + + let tld = keypair.public_key(); + + let endpoint = resolver + .resolve_endpoint(&format!("pubky.{tld}")) + .await + .unwrap(); + assert_eq!(endpoint.target, "example.com"); + } } From a373605af725fe7fbebf21363e4faf3f081064af Mon Sep 17 00:00:00 2001 From: nazeh Date: Fri, 20 Sep 2024 17:59:06 +0300 Subject: [PATCH 06/14] feat(pubky): endpoints return A and AAAA records --- pubky-homeserver/src/config.rs | 9 +- pubky-homeserver/src/config.toml | 12 +- pubky-homeserver/src/pkarr.rs | 47 ++-- pubky/src/native.rs | 4 +- pubky/src/native/internals.rs | 3 +- pubky/src/native/internals/endpoints.rs | 322 ++++++++++-------------- pubky/src/native/internals/resolver.rs | 246 ++++++++++++++++++ pubky/src/shared/public.rs | 5 +- 8 files changed, 433 insertions(+), 215 deletions(-) create mode 100644 pubky/src/native/internals/resolver.rs diff --git a/pubky-homeserver/src/config.rs b/pubky-homeserver/src/config.rs index 55f015c..b855111 100644 --- a/pubky-homeserver/src/config.rs +++ b/pubky-homeserver/src/config.rs @@ -21,7 +21,7 @@ pub struct Config { testnet: bool, port: Option, bootstrap: Option>, - domain: String, + domain: Option, /// Path to the storage directory /// /// Defaults to a directory in the OS data directory @@ -98,8 +98,8 @@ impl Config { self.bootstrap.to_owned() } - pub fn domain(&self) -> &str { - &self.domain + pub fn domain(&self) -> Option<&String> { + self.domain.as_ref() } /// Get the path to the storage directory @@ -131,7 +131,7 @@ impl Default for Config { testnet: false, port: Some(0), bootstrap: None, - domain: "localhost".to_string(), + domain: None, storage: None, secret_key: None, dht_request_timeout: None, @@ -168,6 +168,7 @@ impl Debug for Config { .entry(&"port", &self.port()) .entry(&"storage", &self.storage()) .entry(&"public_key", &self.keypair().public_key()) + .entry(&"domain", &self.domain()) .finish() } } diff --git a/pubky-homeserver/src/config.toml b/pubky-homeserver/src/config.toml index 2012efc..cb47003 100644 --- a/pubky-homeserver/src/config.toml +++ b/pubky-homeserver/src/config.toml @@ -1,10 +1,14 @@ # Use testnet network (local DHT) for testing. -testnet = false +# testnet = false + # Secret key (in hex) to generate the Homeserver's Keypair -secret_key = "0000000000000000000000000000000000000000000000000000000000000000" -# Domain to be published in Pkarr records for this server to be accessible by. -domain = "localhost" +# secret_key = "0000000000000000000000000000000000000000000000000000000000000000" + +# ICANN domain pointing to this server to allow browsers to connect to it. +# domain = "example.com" + # Port for the Homeserver to listen on. port = 6287 + # Storage directory Defaults to # storage = "" diff --git a/pubky-homeserver/src/pkarr.rs b/pubky-homeserver/src/pkarr.rs index 1a680cd..a4dac80 100644 --- a/pubky-homeserver/src/pkarr.rs +++ b/pubky-homeserver/src/pkarr.rs @@ -1,40 +1,59 @@ //! Pkarr related task +use std::net::Ipv4Addr; + use pkarr::{ - dns::{rdata::SVCB, Packet}, + dns::{ + rdata::{RData, A, SVCB}, + Packet, + }, Keypair, PkarrClientAsync, SignedPacket, }; pub async fn publish_server_packet( pkarr_client: PkarrClientAsync, keypair: &Keypair, - domain: &str, + domain: Option<&String>, port: u16, ) -> anyhow::Result<()> { // TODO: Try to resolve first before publishing. let mut packet = Packet::new_reply(0); - let mut svcb = SVCB::new(0, domain.try_into()?); + let default = ".".to_string(); + let target = domain.unwrap_or(&default); + let mut svcb = SVCB::new(0, target.as_str().try_into()?); - // Publishing port only for localhost domain, - // assuming any other domain will point to a reverse proxy - // at the conventional ports. - if domain == "localhost" { - svcb.priority = 1; - svcb.set_port(port); - - // TODO: Add more parameteres like the signer key! - // svcb.set_param(key, value) - }; + svcb.priority = 1; + svcb.set_port(port); packet.answers.push(pkarr::dns::ResourceRecord::new( "@".try_into().unwrap(), pkarr::dns::CLASS::IN, 60 * 60, - pkarr::dns::rdata::RData::HTTPS(svcb.clone().into()), + RData::HTTPS(svcb.clone().into()), )); + if domain.is_none() { + // TODO: remove after remvoing Pubky shared/public + // and add local host IP address instead. + svcb.target = "localhost".try_into().unwrap(); + + packet.answers.push(pkarr::dns::ResourceRecord::new( + "@".try_into().unwrap(), + pkarr::dns::CLASS::IN, + 60 * 60, + RData::HTTPS(svcb.clone().into()), + )); + + packet.answers.push(pkarr::dns::ResourceRecord::new( + "@".try_into().unwrap(), + pkarr::dns::CLASS::IN, + 60 * 60, + RData::A(A::from(Ipv4Addr::from([127, 0, 0, 1]))), + )); + } + // TODO: announce A/AAAA records as well for TLS connections? let signed_packet = SignedPacket::from_packet(keypair, &packet)?; diff --git a/pubky/src/native.rs b/pubky/src/native.rs index b6b1522..e5406de 100644 --- a/pubky/src/native.rs +++ b/pubky/src/native.rs @@ -8,7 +8,7 @@ use crate::PubkyClient; mod api; mod internals; -use internals::endpoints::PkarrResolver; +use internals::resolver::PkarrResolver; static DEFAULT_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"),); @@ -55,7 +55,7 @@ impl PubkyClientBuilder { // TODO: convert to Result let pkarr = PkarrClient::new(self.pkarr_settings).unwrap().as_async(); - let dns_resolver = PkarrResolver::new(pkarr.clone()); + let dns_resolver: PkarrResolver = pkarr.clone().into(); PubkyClient { http: reqwest::Client::builder() diff --git a/pubky/src/native/internals.rs b/pubky/src/native/internals.rs index 822ecbb..9f3ae40 100644 --- a/pubky/src/native/internals.rs +++ b/pubky/src/native/internals.rs @@ -6,7 +6,8 @@ use url::Url; use crate::error::Result; use crate::PubkyClient; -pub mod endpoints; +mod endpoints; +pub mod resolver; impl PubkyClient { // === Pkarr === diff --git a/pubky/src/native/internals/endpoints.rs b/pubky/src/native/internals/endpoints.rs index b650e75..8d30d30 100644 --- a/pubky/src/native/internals/endpoints.rs +++ b/pubky/src/native/internals/endpoints.rs @@ -1,162 +1,130 @@ -use std::net::ToSocketAddrs; +use std::net::{IpAddr, SocketAddr, ToSocketAddrs}; use pkarr::dns::rdata::{RData, SVCB}; -use pkarr::{PkarrClientAsync, PublicKey, SignedPacket}; -use reqwest::dns::{Addrs, Resolve}; - -use crate::error::{Error, Result}; - -const MAX_CHAIN_LENGTH: u8 = 3; +use pkarr::dns::ResourceRecord; +use pkarr::SignedPacket; +use pubky_common::timestamp::Timestamp; #[derive(Debug, Clone)] -pub struct PkarrResolver { - pkarr: PkarrClientAsync, -} - -impl PkarrResolver { - pub fn new(pkarr: PkarrClientAsync) -> Self { - Self { pkarr } - } - - /// Resolve a `qname` to an alternative [Endpoint] as defined in [RFC9460](https://www.rfc-editor.org/rfc/rfc9460#name-terminology). - /// - /// A `qname` is can be either a regular domain name for HTTPS endpoints, - /// or it could use Attrleaf naming pattern for cusotm protcol. For example: - /// `_foo.example.com` for `foo://example.com`. - async fn resolve_endpoint(&self, qname: &str) -> Result { - let target = qname; - // TODO: cache the result of this function? - - let is_svcb = target.starts_with('_'); - - let mut step = 0; - let mut svcb: Option = None; - - loop { - let current = svcb.clone().map_or(target.to_string(), |s| s.target); - if let Ok(tld) = PublicKey::try_from(current.clone()) { - if let Ok(Some(signed_packet)) = self.pkarr.resolve(&tld).await { - if step >= MAX_CHAIN_LENGTH { - break; - }; - step += 1; - - // Choose most prior SVCB record - svcb = get_endpoint(&signed_packet, ¤t, is_svcb); - - // TODO: support wildcard? - } else { - break; - } - } else { - break; - } - } - - if let Some(svcb) = svcb { - if PublicKey::try_from(svcb.target.as_str()).is_err() { - return Ok(svcb); - } - } - - Err(Error::ResolveEndpoint(target.into())) - } -} - -impl Resolve for PkarrResolver { - fn resolve(&self, name: reqwest::dns::Name) -> reqwest::dns::Resolving { - let client = self.clone(); - - Box::pin(async move { - let name = name.as_str(); - - if PublicKey::try_from(name).is_ok() { - let x = client.resolve_endpoint(name).await?; - - let addrs = format!("{}:{}", x.target, x.port).to_socket_addrs()?; - - let addrs: Addrs = Box::new(addrs); - - return Ok(addrs); - }; - - Ok(Box::new(format!("{name}:0").to_socket_addrs().unwrap())) - }) - } -} - -#[derive(Debug, Clone)] -struct Endpoint { - target: String, +pub struct Endpoint { + pub(crate) target: String, // public_key: PublicKey, - port: u16, + pub(crate) port: u16, + pub(crate) addrs: Vec, } -fn get_endpoint(signed_packet: &SignedPacket, target: &str, is_svcb: bool) -> Option { - signed_packet - .resource_records(target) - .fold(None, |prev: Option, answer| { - if let Some(svcb) = match &answer.rdata { - RData::SVCB(svcb) => { - if is_svcb { - Some(svcb) - } else { - None - } - } - RData::HTTPS(curr) => { - if is_svcb { - None - } else { - Some(&curr.0) - } - } - _ => None, - } { - let curr = svcb.clone(); +impl Endpoint { + /// 1. Find the SVCB or HTTPS records with the lowest priority + /// 2. Choose a random one of the list of the above + /// 3. If the target is `.`, check A and AAAA records (https://www.rfc-editor.org/rfc/rfc9460#name-special-handling-of-in-targ) + pub(crate) fn find( + signed_packet: &SignedPacket, + target: &str, + is_svcb: bool, + ) -> Option { + let mut lowest_priority = u16::MAX; + let mut lowest_priority_index = 0; + let mut records = vec![]; - if curr.priority == 0 { - return Some(curr); - } - if let Some(prev) = &prev { - if curr.priority >= prev.priority { - return Some(curr); + for record in signed_packet.resource_records(target) { + if let Some(svcb) = get_svcb(record, is_svcb) { + match svcb.priority.cmp(&lowest_priority) { + std::cmp::Ordering::Equal => records.push(svcb), + std::cmp::Ordering::Less => { + lowest_priority_index = records.len(); + lowest_priority = svcb.priority; + records.push(svcb) + } + _ => {} + } + } + } + + // Good enough random selection + let now = Timestamp::now(); + let slice = &records[lowest_priority_index..]; + let index = if slice.is_empty() { + 0 + } else { + (now.into_inner() as usize) % slice.len() + }; + + slice.get(index).map(|s| { + let target = s.target.to_string(); + + let mut addrs: Vec = vec![]; + + if &target == "." { + for record in signed_packet.resource_records("@") { + match &record.rdata { + RData::A(ip) => addrs.push(IpAddr::V4(ip.address.into())), + RData::AAAA(ip) => addrs.push(IpAddr::V6(ip.address.into())), + _ => {} } - } else { - return Some(curr); } } - prev - }) - .map(|s| Endpoint { - target: s.target.to_string(), - // public_key: signed_packet.public_key(), - port: u16::from_be_bytes( - s.get_param(SVCB::PORT) - .unwrap_or_default() - .try_into() - .unwrap_or([0, 0]), - ), + Endpoint { + target, + // public_key: signed_packet.public_key(), + port: u16::from_be_bytes( + s.get_param(SVCB::PORT) + .unwrap_or_default() + .try_into() + .unwrap_or([0, 0]), + ), + addrs, + } }) + } + + pub fn to_socket_addrs(&self) -> std::io::Result> { + if self.target == "." { + let port = self.port; + return Ok(self + .addrs + .iter() + .map(|addr| SocketAddr::from((*addr, port))) + .collect::>() + .into_iter()); + } + + format!("{}:{}", self.target, self.port).to_socket_addrs() + } +} + +fn get_svcb<'a>(record: &'a ResourceRecord, is_svcb: bool) -> Option<&'a SVCB<'a>> { + match &record.rdata { + RData::SVCB(svcb) => { + if is_svcb { + Some(svcb) + } else { + None + } + } + + RData::HTTPS(curr) => { + if is_svcb { + None + } else { + Some(&curr.0) + } + } + _ => None, + } } #[cfg(test)] mod tests { + use std::net::{Ipv4Addr, Ipv6Addr}; + use std::str::FromStr; + use super::*; - use pkarr::dns::{self, rdata::RData}; - use pkarr::PkarrClient; - use pkarr::{mainline::Testnet, Keypair}; + + use pkarr::{dns, Keypair}; #[tokio::test] - async fn resolve_direct_endpoint() { - let testnet = Testnet::new(3); - let pkarr = PkarrClient::builder() - .testnet(&testnet) - .build() - .unwrap() - .as_async(); - + async fn endpoint_target() { let mut packet = dns::Packet::new_reply(0); packet.answers.push(dns::ResourceRecord::new( dns::Name::new("foo").unwrap(), @@ -185,83 +153,61 @@ mod tests { RData::SVCB(SVCB::new(0, "protocol.example.com".try_into().unwrap())), )); let keypair = Keypair::random(); - let inter_signed_packet = SignedPacket::from_packet(&keypair, &packet).unwrap(); - pkarr.publish(&inter_signed_packet).await.unwrap(); - - let resolver = PkarrResolver { pkarr }; + let signed_packet = SignedPacket::from_packet(&keypair, &packet).unwrap(); let tld = keypair.public_key(); // Follow foo.tld HTTPS records - let endpoint = resolver - .resolve_endpoint(&format!("foo.{tld}")) - .await - .unwrap(); + let endpoint = Endpoint::find(&signed_packet, &format!("foo.{tld}"), false).unwrap(); assert_eq!(endpoint.target, "https.example.com"); // Follow _foo.tld SVCB records - let endpoint = resolver - .resolve_endpoint(&format!("_foo.{tld}")) - .await - .unwrap(); + let endpoint = Endpoint::find(&signed_packet, &format!("_foo.{tld}"), true).unwrap(); assert_eq!(endpoint.target, "protocol.example.com"); } - #[tokio::test] - async fn resolve_endpoint_with_intermediate_pubky() { - let testnet = Testnet::new(3); - let pkarr = PkarrClient::builder() - .testnet(&testnet) - .build() - .unwrap() - .as_async(); - - // USER => Server Owner => Server - // pubky. => pubky-homeserver. => @. - + #[test] + fn endpoint_to_socket_addrs() { let mut packet = dns::Packet::new_reply(0); packet.answers.push(dns::ResourceRecord::new( dns::Name::new("@").unwrap(), dns::CLASS::IN, 3600, - RData::HTTPS(SVCB::new(0, "example.com".try_into().unwrap()).into()), + RData::A(Ipv4Addr::from_str("209.151.148.15").unwrap().into()), )); - let keypair = Keypair::random(); - let inter_signed_packet = SignedPacket::from_packet(&keypair, &packet).unwrap(); - pkarr.publish(&inter_signed_packet).await.unwrap(); - - let end_target = format!("{}", keypair.public_key()); - let mut packet = dns::Packet::new_reply(0); packet.answers.push(dns::ResourceRecord::new( - dns::Name::new("pubky-homeserver.").unwrap(), + dns::Name::new("@").unwrap(), dns::CLASS::IN, 3600, - RData::HTTPS(SVCB::new(0, end_target.as_str().try_into().unwrap()).into()), + RData::AAAA(Ipv6Addr::from_str("2a05:d014:275:6201::64").unwrap().into()), )); - let keypair = Keypair::random(); - let inter_signed_packet = SignedPacket::from_packet(&keypair, &packet).unwrap(); - pkarr.publish(&inter_signed_packet).await.unwrap(); - let inter_target = format!("pubky-homeserver.{}", keypair.public_key()); - let mut packet = dns::Packet::new_reply(0); + let mut svcb = SVCB::new(1, ".".try_into().unwrap()); + svcb.set_port(6881); + packet.answers.push(dns::ResourceRecord::new( - dns::Name::new("pubky.").unwrap(), + dns::Name::new("@").unwrap(), dns::CLASS::IN, 3600, - RData::HTTPS(SVCB::new(0, inter_target.as_str().try_into().unwrap()).into()), + RData::HTTPS(svcb.into()), )); let keypair = Keypair::random(); - let inter_signed_packet = SignedPacket::from_packet(&keypair, &packet).unwrap(); - pkarr.publish(&inter_signed_packet).await.unwrap(); + let signed_packet = SignedPacket::from_packet(&keypair, &packet).unwrap(); - let resolver = PkarrResolver { pkarr }; + // Follow foo.tld HTTPS records + let endpoint = Endpoint::find( + &signed_packet, + &signed_packet.public_key().to_string(), + false, + ) + .unwrap(); - let tld = keypair.public_key(); + assert_eq!(endpoint.target, "."); - let endpoint = resolver - .resolve_endpoint(&format!("pubky.{tld}")) - .await - .unwrap(); - assert_eq!(endpoint.target, "example.com"); + let addrs = endpoint.to_socket_addrs().unwrap(); + assert_eq!( + addrs.map(|s| s.to_string()).collect::>(), + vec!["209.151.148.15:6881", "[2a05:d014:275:6201::64]:6881"] + ) } } diff --git a/pubky/src/native/internals/resolver.rs b/pubky/src/native/internals/resolver.rs new file mode 100644 index 0000000..ff2b2bb --- /dev/null +++ b/pubky/src/native/internals/resolver.rs @@ -0,0 +1,246 @@ +use std::net::ToSocketAddrs; + +use pkarr::{PkarrClientAsync, PublicKey}; +use reqwest::dns::{Addrs, Resolve}; + +use crate::error::{Error, Result}; + +use super::endpoints::Endpoint; + +const DEFAULT_MAX_CHAIN_LENGTH: u8 = 3; + +#[derive(Debug, Clone)] +pub struct PkarrResolver { + pkarr: PkarrClientAsync, + max_chain_length: u8, +} + +impl PkarrResolver { + pub fn new(pkarr: PkarrClientAsync, max_chain_length: u8) -> Self { + PkarrResolver { + pkarr, + max_chain_length, + } + } + + /// Resolve a `qname` to an alternative [Endpoint] as defined in [RFC9460](https://www.rfc-editor.org/rfc/rfc9460#name-terminology). + /// + /// A `qname` is can be either a regular domain name for HTTPS endpoints, + /// or it could use Attrleaf naming pattern for cusotm protcol. For example: + /// `_foo.example.com` for `foo://example.com`. + async fn resolve_endpoint(&self, qname: &str) -> Result { + let target = qname; + // TODO: cache the result of this function? + + let is_svcb = target.starts_with('_'); + + let mut step = 0; + let mut svcb: Option = None; + + loop { + let current = svcb.clone().map_or(target.to_string(), |s| s.target); + if let Ok(tld) = PublicKey::try_from(current.clone()) { + if let Ok(Some(signed_packet)) = self.pkarr.resolve(&tld).await { + if step >= self.max_chain_length { + break; + }; + step += 1; + + // Choose most prior SVCB record + svcb = Endpoint::find(&signed_packet, ¤t, is_svcb); + + // TODO: support wildcard? + } else { + break; + } + } else { + break; + } + } + + if let Some(svcb) = svcb { + if PublicKey::try_from(svcb.target.as_str()).is_err() { + return Ok(svcb); + } + } + + Err(Error::ResolveEndpoint(target.into())) + } +} + +impl Resolve for PkarrResolver { + fn resolve(&self, name: reqwest::dns::Name) -> reqwest::dns::Resolving { + let client = self.clone(); + + Box::pin(async move { + let name = name.as_str(); + + if PublicKey::try_from(name).is_ok() { + let endpoint = client.resolve_endpoint(name).await?; + + // let addrs = format!("{}:{}", x.target, x.port).to_socket_addrs()?; + + let addrs: Addrs = Box::new(endpoint.to_socket_addrs()?); + + return Ok(addrs); + }; + + Ok(Box::new(format!("{name}:0").to_socket_addrs().unwrap())) + }) + } +} + +impl From<&PkarrClientAsync> for PkarrResolver { + fn from(pkarr: &PkarrClientAsync) -> Self { + pkarr.clone().into() + } +} + +impl From for PkarrResolver { + fn from(pkarr: PkarrClientAsync) -> Self { + Self::new(pkarr, DEFAULT_MAX_CHAIN_LENGTH) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use pkarr::dns::rdata::{A, SVCB}; + use pkarr::dns::{self, rdata::RData}; + use pkarr::{mainline::Testnet, Keypair}; + use pkarr::{PkarrClient, SignedPacket}; + + use std::future::Future; + use std::pin::Pin; + + fn generate_subtree( + client: PkarrClientAsync, + depth: u8, + branching: u8, + domain: Option, + ) -> Pin>> { + Box::pin(async move { + let keypair = Keypair::random(); + + let mut packet = dns::Packet::new_reply(0); + + for _ in 0..branching { + let mut svcb = SVCB::new(0, ".".try_into().unwrap()); + + if depth == 0 { + svcb.priority = 1; + svcb.set_port((branching) as u16 * 1000); + + if let Some(target) = &domain { + let target: &'static str = Box::leak(target.clone().into_boxed_str()); + svcb.target = target.try_into().unwrap() + } + } else { + let target = + generate_subtree(client.clone(), depth - 1, branching, domain.clone()) + .await + .to_string(); + let target: &'static str = Box::leak(target.into_boxed_str()); + svcb.target = target.try_into().unwrap(); + }; + + packet.answers.push(dns::ResourceRecord::new( + dns::Name::new("@").unwrap(), + dns::CLASS::IN, + 3600, + RData::HTTPS(svcb.into()), + )); + } + + if depth == 0 { + packet.answers.push(dns::ResourceRecord::new( + dns::Name::new("@").unwrap(), + dns::CLASS::IN, + 3600, + RData::A(A { address: 10 }), + )); + } + + let signed_packet = SignedPacket::from_packet(&keypair, &packet).unwrap(); + client.publish(&signed_packet).await.unwrap(); + + keypair.public_key() + }) + } + + fn generate( + client: PkarrClientAsync, + depth: u8, + branching: u8, + domain: Option, + ) -> Pin>> { + generate_subtree(client, depth - 1, branching, domain) + } + + #[tokio::test] + async fn resolve_endpoints() { + let testnet = Testnet::new(3); + let pkarr = PkarrClient::builder() + .testnet(&testnet) + .build() + .unwrap() + .as_async(); + + let resolver: PkarrResolver = (&pkarr).into(); + let tld = generate(pkarr, 3, 3, Some("example.com".to_string())).await; + + let endpoint = resolver.resolve_endpoint(&tld.to_string()).await.unwrap(); + assert_eq!(endpoint.target, "example.com"); + } + + #[tokio::test] + async fn max_chain_exceeded() { + let testnet = Testnet::new(3); + let pkarr = PkarrClient::builder() + .testnet(&testnet) + .build() + .unwrap() + .as_async(); + + let resolver: PkarrResolver = (&pkarr).into(); + + let tld = generate(pkarr, 4, 3, Some("example.com".to_string())).await; + + let endpoint = resolver.resolve_endpoint(&tld.to_string()).await; + + assert_eq!( + match endpoint { + Err(error) => error.to_string(), + _ => "".to_string(), + }, + crate::Error::ResolveEndpoint(tld.to_string()).to_string() + ) + } + + #[tokio::test] + async fn resolve_addresses() { + let testnet = Testnet::new(3); + let pkarr = PkarrClient::builder() + .testnet(&testnet) + .build() + .unwrap() + .as_async(); + + let resolver: PkarrResolver = (&pkarr).into(); + let tld = generate(pkarr, 3, 3, None).await; + + let endpoint = resolver.resolve_endpoint(&tld.to_string()).await.unwrap(); + assert_eq!(endpoint.target, "."); + assert_eq!(endpoint.port, 3000); + assert_eq!( + endpoint + .to_socket_addrs() + .unwrap() + .into_iter() + .map(|s| s.to_string()) + .collect::>(), + vec!["0.0.0.10:3000"] + ); + dbg!(&endpoint); + } +} diff --git a/pubky/src/shared/public.rs b/pubky/src/shared/public.rs index 366f78c..62f8144 100644 --- a/pubky/src/shared/public.rs +++ b/pubky/src/shared/public.rs @@ -762,8 +762,9 @@ mod tests { ); } - let get = client.get(url.as_str()).await.unwrap(); - dbg!(get); + let get = client.get(url.as_str()).await.unwrap().unwrap(); + + assert_eq!(get.as_ref(), &[0]); } #[tokio::test] From 98aa046ff049b369f99d87bd9cb41d8b99d5b671 Mon Sep 17 00:00:00 2001 From: nazeh Date: Sun, 22 Sep 2024 12:00:57 +0300 Subject: [PATCH 07/14] feat(pubky): update pkarr v3 --- Cargo.lock | 341 ++++++++++++++++++++++--- Cargo.toml | 2 +- pubky-homeserver/src/pkarr.rs | 4 +- pubky-homeserver/src/server.rs | 9 +- pubky/Cargo.toml | 2 +- pubky/src/lib.rs | 5 +- pubky/src/native.rs | 4 +- pubky/src/native/api/http.rs | 2 +- pubky/src/native/internals/resolver.rs | 38 +-- pubky/src/shared/pkarr.rs | 24 +- 10 files changed, 339 insertions(+), 92 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7093643..a170572 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -135,6 +135,12 @@ dependencies = [ "critical-section", ] +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "authenticator" version = "0.1.0" @@ -157,9 +163,9 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "axum" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" +checksum = "8f43644eed690f5374f1af436ecd6aea01cd201f6fbdf0178adaf6907afb2cec" dependencies = [ "async-trait", "axum-core", @@ -184,7 +190,7 @@ dependencies = [ "serde_urlencoded", "sync_wrapper 1.0.1", "tokio", - "tower", + "tower 0.5.1", "tower-layer", "tower-service", "tracing", @@ -192,9 +198,9 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" +checksum = "5e6b8ba012a258d63c9adfa28b9ddcf66149da6f986c5b5452e629d5ee64bf00" dependencies = [ "async-trait", "bytes", @@ -205,7 +211,7 @@ dependencies = [ "mime", "pin-project-lite", "rustversion", - "sync_wrapper 0.1.2", + "sync_wrapper 1.0.1", "tower-layer", "tower-service", "tracing", @@ -213,9 +219,9 @@ dependencies = [ [[package]] name = "axum-extra" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0be6ea09c9b96cb5076af0de2e383bd2bc0c18f827cf1967bdd353e0b910d733" +checksum = "73c3220b188aea709cf1b6c5f9b01c3bd936bb08bd2b5184a12b35ac8131b1f9" dependencies = [ "axum", "axum-core", @@ -230,7 +236,7 @@ dependencies = [ "serde", "tokio", "tokio-util", - "tower", + "tower 0.5.1", "tower-layer", "tower-service", "tracing", @@ -238,11 +244,10 @@ dependencies = [ [[package]] name = "axum-macros" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00c055ee2d014ae5981ce1016374e8213682aa14d9bf40e48ab48b5f3ef20eaa" +checksum = "57d123550fa8d071b7255cb0cc04dc302baa6c8c4a79f55701552684d8399bce" dependencies = [ - "heck 0.4.1", "proc-macro2", "quote", "syn", @@ -382,9 +387,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.17" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac" +checksum = "b0956a43b323ac1afaffc053ed5c4b7c1f1800bacd1683c353aabbb752515dd3" dependencies = [ "clap_builder", "clap_derive", @@ -392,9 +397,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.17" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73" +checksum = "4d72166dd41634086d5803a47eb71ae740e61d84709c36f3c34110173db3961b" dependencies = [ "anstream", "anstyle", @@ -404,11 +409,11 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.13" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" dependencies = [ - "heck 0.5.0", + "heck", "proc-macro2", "quote", "syn", @@ -472,6 +477,22 @@ dependencies = [ "url", ] +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + [[package]] name = "cpufeatures" version = "0.2.14" @@ -683,12 +704,37 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" +[[package]] +name = "encoding_rs" +version = "0.8.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +dependencies = [ + "cfg-if", +] + [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fastrand" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" + [[package]] name = "fiat-crypto" version = "0.2.9" @@ -713,6 +759,21 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -841,6 +902,25 @@ version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" +[[package]] +name = "h2" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hash32" version = "0.2.1" @@ -894,12 +974,6 @@ dependencies = [ "stable_deref_trait", ] -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - [[package]] name = "heck" version = "0.5.0" @@ -1011,6 +1085,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", + "h2", "http", "http-body", "httparse", @@ -1040,6 +1115,22 @@ dependencies = [ "webpki-roots", ] +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + [[package]] name = "hyper-util" version = "0.1.8" @@ -1054,7 +1145,7 @@ dependencies = [ "pin-project-lite", "socket2", "tokio", - "tower", + "tower 0.4.13", "tower-service", "tracing", ] @@ -1147,6 +1238,12 @@ dependencies = [ "libc", ] +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + [[package]] name = "litrs" version = "0.4.1" @@ -1263,6 +1360,23 @@ dependencies = [ "getrandom", ] +[[package]] +name = "native-tls" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -1300,6 +1414,50 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +[[package]] +name = "openssl" +version = "0.10.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "overload" version = "0.1.1" @@ -1432,8 +1590,8 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkarr" -version = "2.2.0" -source = "git+https://github.com/Pubky/pkarr?branch=v3#5b2f8e53735f63521b0d587167609a2cb9b8f2ab" +version = "3.0.0" +source = "git+https://github.com/Pubky/pkarr?branch=v3#d0be23324bcf706f5a359236d2945944abbb40f8" dependencies = [ "base32", "bytes", @@ -1442,18 +1600,17 @@ dependencies = [ "ed25519-dalek", "flume", "futures", + "getrandom", "js-sys", "lru", "mainline", "rand", + "reqwest", "self_cell", "serde", "simple-dns", "thiserror", "tracing", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", ] [[package]] @@ -1466,6 +1623,12 @@ dependencies = [ "spki", ] +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + [[package]] name = "poly1305" version = "0.8.0" @@ -1755,18 +1918,22 @@ dependencies = [ "bytes", "cookie", "cookie_store", + "encoding_rs", "futures-core", "futures-util", + "h2", "http", "http-body", "http-body-util", "hyper", "hyper-rustls", + "hyper-tls", "hyper-util", "ipnet", "js-sys", "log", "mime", + "native-tls", "once_cell", "percent-encoding", "pin-project-lite", @@ -1778,7 +1945,9 @@ dependencies = [ "serde_json", "serde_urlencoded", "sync_wrapper 1.0.1", + "system-configuration", "tokio", + "tokio-native-tls", "tokio-rustls", "tower-service", "url", @@ -1846,6 +2015,19 @@ dependencies = [ "semver", ] +[[package]] +name = "rustix" +version = "0.38.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + [[package]] name = "rustls" version = "0.23.13" @@ -1908,12 +2090,44 @@ dependencies = [ "cipher", ] +[[package]] +name = "schannel" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "self_cell" version = "1.0.4" @@ -2181,6 +2395,40 @@ dependencies = [ "crossbeam-queue", ] +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tempfile" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + [[package]] name = "thiserror" version = "1.0.63" @@ -2286,6 +2534,16 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.26.0" @@ -2357,6 +2615,21 @@ dependencies = [ "tokio", "tower-layer", "tower-service", +] + +[[package]] +name = "tower" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper 0.1.2", + "tokio", + "tower-layer", + "tower-service", "tracing", ] @@ -2540,6 +2813,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.5" diff --git a/Cargo.toml b/Cargo.toml index d5a1fc7..03ee202 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ members = [ resolver = "2" [workspace.dependencies] -pkarr = { git = "https://github.com/Pubky/pkarr", branch = "v3", package = "pkarr", features = ["async"] } +pkarr = { git = "https://github.com/Pubky/pkarr", branch = "v3", package = "pkarr" } serde = { version = "^1.0.209", features = ["derive"] } [profile.release] diff --git a/pubky-homeserver/src/pkarr.rs b/pubky-homeserver/src/pkarr.rs index a4dac80..01cb56b 100644 --- a/pubky-homeserver/src/pkarr.rs +++ b/pubky-homeserver/src/pkarr.rs @@ -7,11 +7,11 @@ use pkarr::{ rdata::{RData, A, SVCB}, Packet, }, - Keypair, PkarrClientAsync, SignedPacket, + Keypair, SignedPacket, }; pub async fn publish_server_packet( - pkarr_client: PkarrClientAsync, + pkarr_client: pkarr::Client, keypair: &Keypair, domain: Option<&String>, port: u16, diff --git a/pubky-homeserver/src/server.rs b/pubky-homeserver/src/server.rs index 9558780..7866c32 100644 --- a/pubky-homeserver/src/server.rs +++ b/pubky-homeserver/src/server.rs @@ -7,7 +7,7 @@ use tracing::{debug, info, warn}; use pkarr::{ mainline::dht::{DhtSettings, Testnet}, - PkarrClient, PkarrClientAsync, PublicKey, Settings, + PublicKey, Settings, }; use crate::{config::Config, database::DB, pkarr::publish_server_packet}; @@ -23,7 +23,7 @@ pub struct Homeserver { pub(crate) struct AppState { pub verifier: AuthVerifier, pub db: DB, - pub pkarr_client: PkarrClientAsync, + pub pkarr_client: pkarr::Client, } impl Homeserver { @@ -34,15 +34,14 @@ impl Homeserver { let db = DB::open(&config.storage()?)?; - let pkarr_client = PkarrClient::new(Settings { + let pkarr_client = pkarr::Client::new(Settings { dht: DhtSettings { bootstrap: config.bootstsrap(), request_timeout: config.dht_request_timeout(), ..Default::default() }, ..Default::default() - })? - .as_async(); + })?; let state = AppState { verifier: AuthVerifier::default(), diff --git a/pubky/Cargo.toml b/pubky/Cargo.toml index 84f8bba..6905920 100644 --- a/pubky/Cargo.toml +++ b/pubky/Cargo.toml @@ -18,7 +18,7 @@ bytes = "^1.7.1" base64 = "0.22.1" pubky-common = { version = "0.1.0", path = "../pubky-common" } -pkarr = { workspace = true, features = ["async"] } +pkarr = { workspace = true } # Native dependencies [target.'cfg(not(target_arch = "wasm32"))'.dependencies] diff --git a/pubky/src/lib.rs b/pubky/src/lib.rs index 2b6cf42..9e22a3b 100644 --- a/pubky/src/lib.rs +++ b/pubky/src/lib.rs @@ -14,9 +14,6 @@ use std::{ use wasm_bindgen::prelude::*; -#[cfg(not(target_arch = "wasm32"))] -use ::pkarr::PkarrClientAsync; - pub use error::Error; #[cfg(not(target_arch = "wasm32"))] @@ -27,7 +24,7 @@ pub use crate::shared::list_builder::ListBuilder; pub struct PubkyClient { http: reqwest::Client, #[cfg(not(target_arch = "wasm32"))] - pub(crate) pkarr: PkarrClientAsync, + pub(crate) pkarr: pkarr::Client, /// A cookie jar for nodejs fetch. #[cfg(target_arch = "wasm32")] pub(crate) session_cookies: Arc>>, diff --git a/pubky/src/native.rs b/pubky/src/native.rs index e5406de..2aa8b9e 100644 --- a/pubky/src/native.rs +++ b/pubky/src/native.rs @@ -1,7 +1,7 @@ use std::time::Duration; use std::{net::ToSocketAddrs, sync::Arc}; -use ::pkarr::{mainline::dht::Testnet, PkarrClient}; +use ::pkarr::mainline::dht::Testnet; use crate::PubkyClient; @@ -54,7 +54,7 @@ impl PubkyClientBuilder { pub fn build(self) -> PubkyClient { // TODO: convert to Result - let pkarr = PkarrClient::new(self.pkarr_settings).unwrap().as_async(); + let pkarr = pkarr::Client::new(self.pkarr_settings).unwrap(); let dns_resolver: PkarrResolver = pkarr.clone().into(); PubkyClient { diff --git a/pubky/src/native/api/http.rs b/pubky/src/native/api/http.rs index ade2c8e..4d762b3 100644 --- a/pubky/src/native/api/http.rs +++ b/pubky/src/native/api/http.rs @@ -33,7 +33,7 @@ mod tests { let homeserver = Homeserver::start_test(&testnet).await.unwrap(); - let client = PubkyClient::testnet(); + let client = PubkyClient::builder().testnet(&testnet).build(); let url = format!("http://{}/", homeserver.public_key()); diff --git a/pubky/src/native/internals/resolver.rs b/pubky/src/native/internals/resolver.rs index ff2b2bb..b04ac09 100644 --- a/pubky/src/native/internals/resolver.rs +++ b/pubky/src/native/internals/resolver.rs @@ -1,6 +1,6 @@ use std::net::ToSocketAddrs; -use pkarr::{PkarrClientAsync, PublicKey}; +use pkarr::PublicKey; use reqwest::dns::{Addrs, Resolve}; use crate::error::{Error, Result}; @@ -11,12 +11,12 @@ const DEFAULT_MAX_CHAIN_LENGTH: u8 = 3; #[derive(Debug, Clone)] pub struct PkarrResolver { - pkarr: PkarrClientAsync, + pkarr: pkarr::Client, max_chain_length: u8, } impl PkarrResolver { - pub fn new(pkarr: PkarrClientAsync, max_chain_length: u8) -> Self { + pub fn new(pkarr: pkarr::Client, max_chain_length: u8) -> Self { PkarrResolver { pkarr, max_chain_length, @@ -90,14 +90,14 @@ impl Resolve for PkarrResolver { } } -impl From<&PkarrClientAsync> for PkarrResolver { - fn from(pkarr: &PkarrClientAsync) -> Self { +impl From<&pkarr::Client> for PkarrResolver { + fn from(pkarr: &pkarr::Client) -> Self { pkarr.clone().into() } } -impl From for PkarrResolver { - fn from(pkarr: PkarrClientAsync) -> Self { +impl From for PkarrResolver { + fn from(pkarr: pkarr::Client) -> Self { Self::new(pkarr, DEFAULT_MAX_CHAIN_LENGTH) } } @@ -107,14 +107,14 @@ mod tests { use super::*; use pkarr::dns::rdata::{A, SVCB}; use pkarr::dns::{self, rdata::RData}; + use pkarr::SignedPacket; use pkarr::{mainline::Testnet, Keypair}; - use pkarr::{PkarrClient, SignedPacket}; use std::future::Future; use std::pin::Pin; fn generate_subtree( - client: PkarrClientAsync, + client: pkarr::Client, depth: u8, branching: u8, domain: Option, @@ -169,7 +169,7 @@ mod tests { } fn generate( - client: PkarrClientAsync, + client: pkarr::Client, depth: u8, branching: u8, domain: Option, @@ -180,11 +180,7 @@ mod tests { #[tokio::test] async fn resolve_endpoints() { let testnet = Testnet::new(3); - let pkarr = PkarrClient::builder() - .testnet(&testnet) - .build() - .unwrap() - .as_async(); + let pkarr = pkarr::Client::builder().testnet(&testnet).build().unwrap(); let resolver: PkarrResolver = (&pkarr).into(); let tld = generate(pkarr, 3, 3, Some("example.com".to_string())).await; @@ -196,11 +192,7 @@ mod tests { #[tokio::test] async fn max_chain_exceeded() { let testnet = Testnet::new(3); - let pkarr = PkarrClient::builder() - .testnet(&testnet) - .build() - .unwrap() - .as_async(); + let pkarr = pkarr::Client::builder().testnet(&testnet).build().unwrap(); let resolver: PkarrResolver = (&pkarr).into(); @@ -220,11 +212,7 @@ mod tests { #[tokio::test] async fn resolve_addresses() { let testnet = Testnet::new(3); - let pkarr = PkarrClient::builder() - .testnet(&testnet) - .build() - .unwrap() - .as_async(); + let pkarr = pkarr::Client::builder().testnet(&testnet).build().unwrap(); let resolver: PkarrResolver = (&pkarr).into(); let tld = generate(pkarr, 3, 3, None).await; diff --git a/pubky/src/shared/pkarr.rs b/pubky/src/shared/pkarr.rs index 85055ef..b3027a5 100644 --- a/pubky/src/shared/pkarr.rs +++ b/pubky/src/shared/pkarr.rs @@ -185,8 +185,8 @@ mod tests { rdata::{HTTPS, SVCB}, Packet, }, - mainline::{dht::DhtSettings, Testnet}, - Keypair, PkarrClient, Settings, SignedPacket, + mainline::Testnet, + Keypair, SignedPacket, }; use pubky_homeserver::Homeserver; @@ -194,15 +194,7 @@ mod tests { async fn resolve_endpoint_https() { let testnet = Testnet::new(10); - let pkarr_client = PkarrClient::new(Settings { - dht: DhtSettings { - bootstrap: Some(testnet.bootstrap.clone()), - ..Default::default() - }, - ..Default::default() - }) - .unwrap() - .as_async(); + let pkarr_client = pkarr::Client::builder().testnet(&testnet).build().unwrap(); let domain = "example.com"; let mut target; @@ -285,15 +277,7 @@ mod tests { let server = Homeserver::start_test(&testnet).await.unwrap(); // Publish an intermediate controller of the homeserver - let pkarr_client = PkarrClient::new(Settings { - dht: DhtSettings { - bootstrap: Some(testnet.bootstrap.clone()), - ..Default::default() - }, - ..Default::default() - }) - .unwrap() - .as_async(); + let pkarr_client = pkarr::Client::builder().testnet(&testnet).build().unwrap(); let intermediate = Keypair::random(); From 966ccc11e1f03be80d3c5d00785c911ec927d956 Mon Sep 17 00:00:00 2001 From: nazeh Date: Sun, 22 Sep 2024 18:29:58 +0300 Subject: [PATCH 08/14] refactor(pubky): simplify session management using 'fetch-cookie' dependency --- pubky/pkg/.gitignore | 1 - pubky/pkg/index.cjs | 6 ++++++ pubky/pkg/package.json | 3 +++ pubky/src/bin/patch.mjs | 9 --------- pubky/src/native/internals.rs | 5 +---- pubky/src/shared/auth.rs | 6 ------ pubky/src/wasm/internals/http.rs | 26 +------------------------- 7 files changed, 11 insertions(+), 45 deletions(-) create mode 100644 pubky/pkg/index.cjs diff --git a/pubky/pkg/.gitignore b/pubky/pkg/.gitignore index 7355b75..d2a005f 100644 --- a/pubky/pkg/.gitignore +++ b/pubky/pkg/.gitignore @@ -1,4 +1,3 @@ -index.cjs browser.js coverage node_modules diff --git a/pubky/pkg/index.cjs b/pubky/pkg/index.cjs new file mode 100644 index 0000000..bd8a6a2 --- /dev/null +++ b/pubky/pkg/index.cjs @@ -0,0 +1,6 @@ +const makeFetchCookie = require("fetch-cookie").default; + +let originalFetch = globalThis.fetch; +globalThis.fetch = makeFetchCookie(originalFetch); + +module.exports = require('./nodejs/pubky') diff --git a/pubky/pkg/package.json b/pubky/pkg/package.json index be3e6df..1c08264 100644 --- a/pubky/pkg/package.json +++ b/pubky/pkg/package.json @@ -37,5 +37,8 @@ "esmify": "^2.1.1", "tape": "^5.8.1", "tape-run": "^11.0.0" + }, + "dependencies": { + "fetch-cookie": "^3.0.1" } } diff --git a/pubky/src/bin/patch.mjs b/pubky/src/bin/patch.mjs index a8ed503..86983b6 100644 --- a/pubky/src/bin/patch.mjs +++ b/pubky/src/bin/patch.mjs @@ -55,12 +55,3 @@ const bytes = __toBinary(${JSON.stringify(await readFile(path.join(__dirname, `. ); await writeFile(path.join(__dirname, `../../pkg/browser.js`), patched + "\nglobalThis['pubky'] = imports"); - -// Move outside of nodejs - -await Promise.all([".js", ".d.ts", "_bg.wasm"].map(suffix => - rename( - path.join(__dirname, `../../pkg/nodejs/${name}${suffix}`), - path.join(__dirname, `../../pkg/${suffix === '.js' ? "index.cjs" : (name + suffix)}`), - )) -) diff --git a/pubky/src/native/internals.rs b/pubky/src/native/internals.rs index 9f3ae40..c40ba77 100644 --- a/pubky/src/native/internals.rs +++ b/pubky/src/native/internals.rs @@ -1,6 +1,6 @@ use pkarr::SignedPacket; use pubky_common::crypto::PublicKey; -use reqwest::{RequestBuilder, Response}; +use reqwest::RequestBuilder; use url::Url; use crate::error::Result; @@ -28,7 +28,4 @@ impl PubkyClient { pub(crate) fn inner_request(&self, method: reqwest::Method, url: Url) -> RequestBuilder { self.http.request(method, url) } - - pub(crate) fn store_session(&self, _: &Response) {} - pub(crate) fn remove_session(&self, _: &PublicKey) {} } diff --git a/pubky/src/shared/auth.rs b/pubky/src/shared/auth.rs index bdb8473..7c3d9e1 100644 --- a/pubky/src/shared/auth.rs +++ b/pubky/src/shared/auth.rs @@ -43,8 +43,6 @@ impl PubkyClient { .send() .await?; - self.store_session(&response); - self.publish_pubky_homeserver(keypair, &homeserver).await?; let bytes = response.bytes().await?; @@ -84,8 +82,6 @@ impl PubkyClient { self.inner_request(Method::DELETE, url).send().await?; - self.remove_session(pubky); - Ok(()) } @@ -175,8 +171,6 @@ impl PubkyClient { .send() .await?; - self.store_session(&response); - let bytes = response.bytes().await?; Ok(Session::deserialize(&bytes)?) diff --git a/pubky/src/wasm/internals/http.rs b/pubky/src/wasm/internals/http.rs index b28d9f9..83c7821 100644 --- a/pubky/src/wasm/internals/http.rs +++ b/pubky/src/wasm/internals/http.rs @@ -1,6 +1,6 @@ use crate::PubkyClient; -use reqwest::{Method, RequestBuilder, Response}; +use reqwest::{Method, RequestBuilder}; use url::Url; impl PubkyClient { @@ -13,28 +13,4 @@ impl PubkyClient { request } - - // Support cookies for nodejs - - pub(crate) fn store_session(&self, response: &Response) { - if let Some(cookie) = response - .headers() - .get("set-cookie") - .and_then(|h| h.to_str().ok()) - .and_then(|s| s.split(';').next()) - { - self.session_cookies - .write() - .unwrap() - .insert(cookie.to_string()); - } - } - pub(crate) fn remove_session(&self, pubky: &pkarr::PublicKey) { - let key = pubky.to_string(); - - self.session_cookies - .write() - .unwrap() - .retain(|cookie| !cookie.starts_with(&key)); - } } From 1f73b72371f6417fc42257eb10aa0cdd0fdc2418 Mon Sep 17 00:00:00 2001 From: nazeh Date: Sun, 22 Sep 2024 18:33:47 +0300 Subject: [PATCH 09/14] refactor(pubky): remove unused session_cookies in PubkyClient --- pubky/src/lib.rs | 8 -------- pubky/src/wasm.rs | 7 ------- pubky/src/wasm/internals/http.rs | 8 +------- 3 files changed, 1 insertion(+), 22 deletions(-) diff --git a/pubky/src/lib.rs b/pubky/src/lib.rs index 9e22a3b..56caada 100644 --- a/pubky/src/lib.rs +++ b/pubky/src/lib.rs @@ -6,11 +6,6 @@ mod native; #[cfg(target_arch = "wasm32")] mod wasm; -#[cfg(target_arch = "wasm32")] -use std::{ - collections::HashSet, - sync::{Arc, RwLock}, -}; use wasm_bindgen::prelude::*; @@ -25,9 +20,6 @@ pub struct PubkyClient { http: reqwest::Client, #[cfg(not(target_arch = "wasm32"))] pub(crate) pkarr: pkarr::Client, - /// A cookie jar for nodejs fetch. - #[cfg(target_arch = "wasm32")] - pub(crate) session_cookies: Arc>>, #[cfg(target_arch = "wasm32")] pub(crate) pkarr_relays: Vec, } diff --git a/pubky/src/wasm.rs b/pubky/src/wasm.rs index be36a0e..7cda1c5 100644 --- a/pubky/src/wasm.rs +++ b/pubky/src/wasm.rs @@ -1,8 +1,3 @@ -use std::{ - collections::HashSet, - sync::{Arc, RwLock}, -}; - use wasm_bindgen::prelude::*; use crate::PubkyClient; @@ -26,7 +21,6 @@ impl PubkyClient { pub fn new() -> Self { Self { http: reqwest::Client::builder().build().unwrap(), - session_cookies: Arc::new(RwLock::new(HashSet::new())), pkarr_relays: DEFAULT_RELAYS.into_iter().map(|s| s.to_string()).collect(), } } @@ -37,7 +31,6 @@ impl PubkyClient { pub fn testnet() -> Self { Self { http: reqwest::Client::builder().build().unwrap(), - session_cookies: Arc::new(RwLock::new(HashSet::new())), pkarr_relays: TESTNET_RELAYS.into_iter().map(|s| s.to_string()).collect(), } } diff --git a/pubky/src/wasm/internals/http.rs b/pubky/src/wasm/internals/http.rs index 83c7821..6973130 100644 --- a/pubky/src/wasm/internals/http.rs +++ b/pubky/src/wasm/internals/http.rs @@ -5,12 +5,6 @@ use url::Url; impl PubkyClient { pub(crate) fn inner_request(&self, method: Method, url: Url) -> RequestBuilder { - let mut request = self.http.request(method, url).fetch_credentials_include(); - - for cookie in self.session_cookies.read().unwrap().iter() { - request = request.header("Cookie", cookie); - } - - request + self.http.request(method, url).fetch_credentials_include() } } From fff7b6af3917f2f2c2f2fef0bbd466f2410dd55d Mon Sep 17 00:00:00 2001 From: nazeh Date: Sun, 22 Sep 2024 18:50:57 +0300 Subject: [PATCH 10/14] feat(js): use pkarr relay Client instead of inline impl --- Cargo.lock | 2 +- pubky/src/lib.rs | 1 - pubky/src/wasm.rs | 8 +++++-- pubky/src/wasm/internals/pkarr.rs | 40 +++++-------------------------- 4 files changed, 13 insertions(+), 38 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a170572..71da5f4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1591,7 +1591,7 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkarr" version = "3.0.0" -source = "git+https://github.com/Pubky/pkarr?branch=v3#d0be23324bcf706f5a359236d2945944abbb40f8" +source = "git+https://github.com/Pubky/pkarr?branch=v3#445c0db448b9e70f2a63a5b91e6d84f1ef466aeb" dependencies = [ "base32", "bytes", diff --git a/pubky/src/lib.rs b/pubky/src/lib.rs index 56caada..36357ff 100644 --- a/pubky/src/lib.rs +++ b/pubky/src/lib.rs @@ -18,7 +18,6 @@ pub use crate::shared::list_builder::ListBuilder; #[wasm_bindgen] pub struct PubkyClient { http: reqwest::Client, - #[cfg(not(target_arch = "wasm32"))] pub(crate) pkarr: pkarr::Client, #[cfg(target_arch = "wasm32")] pub(crate) pkarr_relays: Vec, diff --git a/pubky/src/wasm.rs b/pubky/src/wasm.rs index 7cda1c5..990058c 100644 --- a/pubky/src/wasm.rs +++ b/pubky/src/wasm.rs @@ -12,7 +12,6 @@ impl Default for PubkyClient { } } -static DEFAULT_RELAYS: [&str; 1] = ["https://relay.pkarr.org"]; static TESTNET_RELAYS: [&str; 1] = ["http://localhost:15411/pkarr"]; #[wasm_bindgen] @@ -21,7 +20,8 @@ impl PubkyClient { pub fn new() -> Self { Self { http: reqwest::Client::builder().build().unwrap(), - pkarr_relays: DEFAULT_RELAYS.into_iter().map(|s| s.to_string()).collect(), + pkarr: pkarr::Client::builder().build().unwrap(), + pkarr_relays: vec!["https://relay.pkarr.org".to_string()], } } @@ -31,6 +31,10 @@ impl PubkyClient { pub fn testnet() -> Self { Self { http: reqwest::Client::builder().build().unwrap(), + pkarr: pkarr::Client::builder() + .relays(TESTNET_RELAYS.into_iter().map(|s| s.to_string()).collect()) + .build() + .unwrap(), pkarr_relays: TESTNET_RELAYS.into_iter().map(|s| s.to_string()).collect(), } } diff --git a/pubky/src/wasm/internals/pkarr.rs b/pubky/src/wasm/internals/pkarr.rs index 49726f6..7ada26d 100644 --- a/pubky/src/wasm/internals/pkarr.rs +++ b/pubky/src/wasm/internals/pkarr.rs @@ -1,48 +1,20 @@ -use reqwest::StatusCode; - -pub use pkarr::{PublicKey, SignedPacket}; +use pkarr::SignedPacket; +use pubky_common::crypto::PublicKey; use crate::error::Result; use crate::PubkyClient; -// TODO: Add an in memory cache of packets - impl PubkyClient { - //TODO: migrate to pkarr::PkarrRelayClient + // === Pkarr === + pub(crate) async fn pkarr_resolve( &self, public_key: &PublicKey, ) -> Result> { - //TODO: Allow multiple relays in parallel - let relay = self.pkarr_relays.first().expect("initialized with relays"); - - let res = self - .http - .get(format!("{relay}/{}", public_key)) - .send() - .await?; - - if res.status() == StatusCode::NOT_FOUND { - return Ok(None); - }; - - // TODO: guard against too large responses. - let bytes = res.bytes().await?; - - let existing = SignedPacket::from_relay_payload(public_key, &bytes)?; - - Ok(Some(existing)) + Ok(self.pkarr.resolve(public_key).await?) } pub(crate) async fn pkarr_publish(&self, signed_packet: &SignedPacket) -> Result<()> { - let relay = self.pkarr_relays.first().expect("initialized with relays"); - - self.http - .put(format!("{relay}/{}", signed_packet.public_key())) - .body(signed_packet.to_relay_payload()) - .send() - .await?; - - Ok(()) + Ok(self.pkarr.publish(signed_packet).await?) } } From 5aa53f4873368f59a9279d8a604541cb56438e7e Mon Sep 17 00:00:00 2001 From: nazeh Date: Sun, 22 Sep 2024 19:06:48 +0300 Subject: [PATCH 11/14] refactor(pubky): remove unused pkarr_relays from PubkyClient --- pubky/src/lib.rs | 2 -- pubky/src/wasm.rs | 17 ----------------- 2 files changed, 19 deletions(-) diff --git a/pubky/src/lib.rs b/pubky/src/lib.rs index 36357ff..b251d3e 100644 --- a/pubky/src/lib.rs +++ b/pubky/src/lib.rs @@ -19,6 +19,4 @@ pub use crate::shared::list_builder::ListBuilder; pub struct PubkyClient { http: reqwest::Client, pub(crate) pkarr: pkarr::Client, - #[cfg(target_arch = "wasm32")] - pub(crate) pkarr_relays: Vec, } diff --git a/pubky/src/wasm.rs b/pubky/src/wasm.rs index 990058c..e983700 100644 --- a/pubky/src/wasm.rs +++ b/pubky/src/wasm.rs @@ -21,7 +21,6 @@ impl PubkyClient { Self { http: reqwest::Client::builder().build().unwrap(), pkarr: pkarr::Client::builder().build().unwrap(), - pkarr_relays: vec!["https://relay.pkarr.org".to_string()], } } @@ -35,22 +34,6 @@ impl PubkyClient { .relays(TESTNET_RELAYS.into_iter().map(|s| s.to_string()).collect()) .build() .unwrap(), - pkarr_relays: TESTNET_RELAYS.into_iter().map(|s| s.to_string()).collect(), } } - - /// Set Pkarr relays used for publishing and resolving Pkarr packets. - /// - /// By default, [PubkyClient] will use `["https://relay.pkarr.org"]` - #[wasm_bindgen(js_name = "setPkarrRelays")] - pub fn set_pkarr_relays(mut self, relays: Vec) -> Self { - self.pkarr_relays = relays; - self - } - - // Read the set of pkarr relays used by this client. - #[wasm_bindgen(js_name = "getPkarrRelays")] - pub fn get_pkarr_relays(&self) -> Vec { - self.pkarr_relays.clone() - } } From ad9db81b00951587c5c10b02f94d293bbf553947 Mon Sep 17 00:00:00 2001 From: nazeh Date: Sun, 22 Sep 2024 19:09:17 +0300 Subject: [PATCH 12/14] refactor(pubky): replace pkarr_publish/pkarr_resolve with pkarr.publish/pkarr.resolve --- pubky/src/shared/pkarr.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pubky/src/shared/pkarr.rs b/pubky/src/shared/pkarr.rs index b3027a5..f099932 100644 --- a/pubky/src/shared/pkarr.rs +++ b/pubky/src/shared/pkarr.rs @@ -19,7 +19,7 @@ impl PubkyClient { keypair: &Keypair, host: &str, ) -> Result<()> { - let existing = self.pkarr_resolve(&keypair.public_key()).await?; + let existing = self.pkarr.resolve(&keypair.public_key()).await?; let mut packet = Packet::new_reply(0); @@ -42,7 +42,7 @@ impl PubkyClient { let signed_packet = SignedPacket::from_packet(keypair, &packet)?; - self.pkarr_publish(&signed_packet).await?; + self.pkarr.publish(&signed_packet).await?; Ok(()) } @@ -81,7 +81,8 @@ impl PubkyClient { step += 1; if let Some(signed_packet) = self - .pkarr_resolve(&public_key) + .pkarr + .resolve(&public_key) .await .map_err(|_| Error::ResolveEndpoint(original_target.into()))? { From 9fd85017182f217549a1c6dda6ecc36043388e41 Mon Sep 17 00:00:00 2001 From: nazeh Date: Sun, 22 Sep 2024 19:12:07 +0300 Subject: [PATCH 13/14] refactor(pubky): simplify internals mod after using pkarr client --- pubky/src/native/internals.rs | 16 --------------- .../wasm/{internals/http.rs => internals.rs} | 0 pubky/src/wasm/internals/mod.rs | 2 -- pubky/src/wasm/internals/pkarr.rs | 20 ------------------- 4 files changed, 38 deletions(-) rename pubky/src/wasm/{internals/http.rs => internals.rs} (100%) delete mode 100644 pubky/src/wasm/internals/mod.rs delete mode 100644 pubky/src/wasm/internals/pkarr.rs diff --git a/pubky/src/native/internals.rs b/pubky/src/native/internals.rs index c40ba77..8674b68 100644 --- a/pubky/src/native/internals.rs +++ b/pubky/src/native/internals.rs @@ -1,28 +1,12 @@ -use pkarr::SignedPacket; -use pubky_common::crypto::PublicKey; use reqwest::RequestBuilder; use url::Url; -use crate::error::Result; use crate::PubkyClient; mod endpoints; pub mod resolver; impl PubkyClient { - // === Pkarr === - - pub(crate) async fn pkarr_resolve( - &self, - public_key: &PublicKey, - ) -> Result> { - Ok(self.pkarr.resolve(public_key).await?) - } - - pub(crate) async fn pkarr_publish(&self, signed_packet: &SignedPacket) -> Result<()> { - Ok(self.pkarr.publish(signed_packet).await?) - } - // === HTTP === pub(crate) fn inner_request(&self, method: reqwest::Method, url: Url) -> RequestBuilder { diff --git a/pubky/src/wasm/internals/http.rs b/pubky/src/wasm/internals.rs similarity index 100% rename from pubky/src/wasm/internals/http.rs rename to pubky/src/wasm/internals.rs diff --git a/pubky/src/wasm/internals/mod.rs b/pubky/src/wasm/internals/mod.rs deleted file mode 100644 index 53e5f4b..0000000 --- a/pubky/src/wasm/internals/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod http; -pub mod pkarr; diff --git a/pubky/src/wasm/internals/pkarr.rs b/pubky/src/wasm/internals/pkarr.rs deleted file mode 100644 index 7ada26d..0000000 --- a/pubky/src/wasm/internals/pkarr.rs +++ /dev/null @@ -1,20 +0,0 @@ -use pkarr::SignedPacket; -use pubky_common::crypto::PublicKey; - -use crate::error::Result; -use crate::PubkyClient; - -impl PubkyClient { - // === Pkarr === - - pub(crate) async fn pkarr_resolve( - &self, - public_key: &PublicKey, - ) -> Result> { - Ok(self.pkarr.resolve(public_key).await?) - } - - pub(crate) async fn pkarr_publish(&self, signed_packet: &SignedPacket) -> Result<()> { - Ok(self.pkarr.publish(signed_packet).await?) - } -} From 2ae54359bfa70bce1958705c58a2f3eebe8aa636 Mon Sep 17 00:00:00 2001 From: nazeh Date: Wed, 25 Sep 2024 22:50:52 +0300 Subject: [PATCH 14/14] feat(pubky): use resolve_endpoint from pkarr --- Cargo.lock | 81 +++----- Cargo.toml | 3 - pubky/pkg/test/http.js | 14 +- pubky/src/native.rs | 4 +- pubky/src/native/api/http.rs | 19 +- pubky/src/native/{api.rs => api/mod.rs} | 2 - pubky/src/native/internals.rs | 36 +++- pubky/src/native/internals/endpoints.rs | 213 --------------------- pubky/src/native/internals/resolver.rs | 234 ------------------------ pubky/src/shared/auth.rs | 7 +- pubky/src/shared/list_builder.rs | 7 +- pubky/src/shared/public.rs | 28 +-- pubky/src/wasm/api/http.rs | 6 +- pubky/src/wasm/{api.rs => api/mod.rs} | 8 +- pubky/src/wasm/internals.rs | 29 ++- 15 files changed, 142 insertions(+), 549 deletions(-) rename pubky/src/native/{api.rs => api/mod.rs} (83%) delete mode 100644 pubky/src/native/internals/endpoints.rs delete mode 100644 pubky/src/native/internals/resolver.rs rename pubky/src/wasm/{api.rs => api/mod.rs} (55%) diff --git a/Cargo.lock b/Cargo.lock index 71da5f4..0a0eebb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -117,9 +117,9 @@ checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "async-trait" -version = "0.1.82" +version = "0.1.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", @@ -190,7 +190,7 @@ dependencies = [ "serde_urlencoded", "sync_wrapper 1.0.1", "tokio", - "tower 0.5.1", + "tower", "tower-layer", "tower-service", "tracing", @@ -236,7 +236,7 @@ dependencies = [ "serde", "tokio", "tokio-util", - "tower 0.5.1", + "tower", "tower-layer", "tower-service", "tracing", @@ -1133,8 +1133,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.8" -source = "git+https://github.com/hyperium/hyper-util.git#2639193e9134a235db42cca16c8cff7f21f61661" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b" dependencies = [ "bytes", "futures-channel", @@ -1145,7 +1146,6 @@ dependencies = [ "pin-project-lite", "socket2", "tokio", - "tower 0.4.13", "tower-service", "tracing", ] @@ -1224,9 +1224,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.158" +version = "0.2.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" [[package]] name = "libredox" @@ -1556,26 +1556,6 @@ dependencies = [ "siphasher", ] -[[package]] -name = "pin-project" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "pin-project-lite" version = "0.2.14" @@ -1591,7 +1571,6 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkarr" version = "3.0.0" -source = "git+https://github.com/Pubky/pkarr?branch=v3#445c0db448b9e70f2a63a5b91e6d84f1ef466aeb" dependencies = [ "base32", "bytes", @@ -1604,12 +1583,14 @@ dependencies = [ "js-sys", "lru", "mainline", + "once_cell", "rand", "reqwest", "self_cell", "serde", "simple-dns", "thiserror", + "tokio", "tracing", ] @@ -1625,9 +1606,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "poly1305" @@ -1689,6 +1670,7 @@ version = "0.1.0" dependencies = [ "base64 0.22.1", "bytes", + "hyper-util", "js-sys", "pkarr", "pubky-common", @@ -1846,9 +1828,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853" +checksum = "62871f2d65009c0256aed1b9cfeeb8ac272833c404e13d53d400cd0dad7a2ac0" dependencies = [ "bitflags", ] @@ -2431,18 +2413,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", @@ -2591,9 +2573,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.21" +version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ "indexmap", "serde", @@ -2602,21 +2584,6 @@ dependencies = [ "winnow", ] -[[package]] -name = "tower" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" -dependencies = [ - "futures-core", - "futures-util", - "pin-project", - "pin-project-lite", - "tokio", - "tower-layer", - "tower-service", -] - [[package]] name = "tower" version = "0.5.1" @@ -3128,9 +3095,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.18" +version = "0.6.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +checksum = "c52ac009d615e79296318c1bcce2d422aaca15ad08515e344feeda07df67a587" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index 03ee202..017397d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,3 @@ serde = { version = "^1.0.209", features = ["derive"] } [profile.release] lto = true opt-level = 'z' - -[patch.crates-io] -hyper-util = { git = "https://github.com/hyperium/hyper-util.git" } diff --git a/pubky/pkg/test/http.js b/pubky/pkg/test/http.js index 5348d27..a11a63d 100644 --- a/pubky/pkg/test/http.js +++ b/pubky/pkg/test/http.js @@ -6,10 +6,20 @@ const TLD = '8pinxxgqs41n4aididenw5apqp1urfmzdztr8jt4abrkdn435ewo'; // TODO: test HTTPs too somehow. -test.skip("basic fetch", async (t) => { +test("basic fetch", async (t) => { let client = PubkyClient.testnet(); - let response = await client.fetch(`http://${TLD}/`, new Uint8Array([])); + // Normal TLD + { + + let response = await client.fetch(`http://relay.pkarr.org/`); + + t.equal(response.status, 200); + } + + + // Pubky + let response = await client.fetch(`http://${TLD}/`); t.equal(response.status, 200); }) diff --git a/pubky/src/native.rs b/pubky/src/native.rs index 2aa8b9e..83406f6 100644 --- a/pubky/src/native.rs +++ b/pubky/src/native.rs @@ -8,7 +8,7 @@ use crate::PubkyClient; mod api; mod internals; -use internals::resolver::PkarrResolver; +use internals::PkarrResolver; static DEFAULT_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"),); @@ -55,7 +55,7 @@ impl PubkyClientBuilder { // TODO: convert to Result let pkarr = pkarr::Client::new(self.pkarr_settings).unwrap(); - let dns_resolver: PkarrResolver = pkarr.clone().into(); + let dns_resolver: PkarrResolver = (&pkarr).into(); PubkyClient { http: reqwest::Client::builder() diff --git a/pubky/src/native/api/http.rs b/pubky/src/native/api/http.rs index 4d762b3..ada724b 100644 --- a/pubky/src/native/api/http.rs +++ b/pubky/src/native/api/http.rs @@ -28,7 +28,7 @@ mod tests { use crate::*; #[tokio::test] - async fn http_get() { + async fn http_get_pubky() { let testnet = Testnet::new(10); let homeserver = Homeserver::start_test(&testnet).await.unwrap(); @@ -45,4 +45,21 @@ mod tests { assert_eq!(response.status(), 200) } + + #[tokio::test] + async fn http_get_icann() { + let testnet = Testnet::new(10); + + let client = PubkyClient::builder().testnet(&testnet).build(); + + let url = format!("http://example.com/"); + + let response = client + .request(Default::default(), url) + .send() + .await + .unwrap(); + + assert_eq!(response.status(), 200); + } } diff --git a/pubky/src/native/api.rs b/pubky/src/native/api/mod.rs similarity index 83% rename from pubky/src/native/api.rs rename to pubky/src/native/api/mod.rs index a506a10..a502667 100644 --- a/pubky/src/native/api.rs +++ b/pubky/src/native/api/mod.rs @@ -1,5 +1,3 @@ -//! Public API modules - pub mod http; pub mod recovery_file; diff --git a/pubky/src/native/internals.rs b/pubky/src/native/internals.rs index 8674b68..4e9f593 100644 --- a/pubky/src/native/internals.rs +++ b/pubky/src/native/internals.rs @@ -3,13 +3,43 @@ use url::Url; use crate::PubkyClient; -mod endpoints; -pub mod resolver; +use std::net::ToSocketAddrs; + +use pkarr::{Client, EndpointResolver, PublicKey}; +use reqwest::dns::{Addrs, Resolve}; + +pub struct PkarrResolver(Client); + +impl Resolve for PkarrResolver { + fn resolve(&self, name: reqwest::dns::Name) -> reqwest::dns::Resolving { + let client = self.0.clone(); + + Box::pin(async move { + let name = name.as_str(); + + if PublicKey::try_from(name).is_ok() { + let endpoint = client.resolve_endpoint(name).await?; + + let addrs: Addrs = Box::new(endpoint.to_socket_addrs().into_iter()); + return Ok(addrs); + }; + + Ok(Box::new(format!("{name}:0").to_socket_addrs().unwrap())) + }) + } +} + +impl From<&pkarr::Client> for PkarrResolver { + fn from(pkarr: &pkarr::Client) -> Self { + PkarrResolver(pkarr.clone()) + } +} impl PubkyClient { // === HTTP === - pub(crate) fn inner_request(&self, method: reqwest::Method, url: Url) -> RequestBuilder { + /// A wrapper around [reqwest::Client::request], with the same signature between native and wasm. + pub(crate) async fn inner_request(&self, method: reqwest::Method, url: Url) -> RequestBuilder { self.http.request(method, url) } } diff --git a/pubky/src/native/internals/endpoints.rs b/pubky/src/native/internals/endpoints.rs deleted file mode 100644 index 8d30d30..0000000 --- a/pubky/src/native/internals/endpoints.rs +++ /dev/null @@ -1,213 +0,0 @@ -use std::net::{IpAddr, SocketAddr, ToSocketAddrs}; - -use pkarr::dns::rdata::{RData, SVCB}; -use pkarr::dns::ResourceRecord; -use pkarr::SignedPacket; -use pubky_common::timestamp::Timestamp; - -#[derive(Debug, Clone)] -pub struct Endpoint { - pub(crate) target: String, - // public_key: PublicKey, - pub(crate) port: u16, - pub(crate) addrs: Vec, -} - -impl Endpoint { - /// 1. Find the SVCB or HTTPS records with the lowest priority - /// 2. Choose a random one of the list of the above - /// 3. If the target is `.`, check A and AAAA records (https://www.rfc-editor.org/rfc/rfc9460#name-special-handling-of-in-targ) - pub(crate) fn find( - signed_packet: &SignedPacket, - target: &str, - is_svcb: bool, - ) -> Option { - let mut lowest_priority = u16::MAX; - let mut lowest_priority_index = 0; - let mut records = vec![]; - - for record in signed_packet.resource_records(target) { - if let Some(svcb) = get_svcb(record, is_svcb) { - match svcb.priority.cmp(&lowest_priority) { - std::cmp::Ordering::Equal => records.push(svcb), - std::cmp::Ordering::Less => { - lowest_priority_index = records.len(); - lowest_priority = svcb.priority; - records.push(svcb) - } - _ => {} - } - } - } - - // Good enough random selection - let now = Timestamp::now(); - let slice = &records[lowest_priority_index..]; - let index = if slice.is_empty() { - 0 - } else { - (now.into_inner() as usize) % slice.len() - }; - - slice.get(index).map(|s| { - let target = s.target.to_string(); - - let mut addrs: Vec = vec![]; - - if &target == "." { - for record in signed_packet.resource_records("@") { - match &record.rdata { - RData::A(ip) => addrs.push(IpAddr::V4(ip.address.into())), - RData::AAAA(ip) => addrs.push(IpAddr::V6(ip.address.into())), - _ => {} - } - } - } - - Endpoint { - target, - // public_key: signed_packet.public_key(), - port: u16::from_be_bytes( - s.get_param(SVCB::PORT) - .unwrap_or_default() - .try_into() - .unwrap_or([0, 0]), - ), - addrs, - } - }) - } - - pub fn to_socket_addrs(&self) -> std::io::Result> { - if self.target == "." { - let port = self.port; - return Ok(self - .addrs - .iter() - .map(|addr| SocketAddr::from((*addr, port))) - .collect::>() - .into_iter()); - } - - format!("{}:{}", self.target, self.port).to_socket_addrs() - } -} - -fn get_svcb<'a>(record: &'a ResourceRecord, is_svcb: bool) -> Option<&'a SVCB<'a>> { - match &record.rdata { - RData::SVCB(svcb) => { - if is_svcb { - Some(svcb) - } else { - None - } - } - - RData::HTTPS(curr) => { - if is_svcb { - None - } else { - Some(&curr.0) - } - } - _ => None, - } -} - -#[cfg(test)] -mod tests { - use std::net::{Ipv4Addr, Ipv6Addr}; - use std::str::FromStr; - - use super::*; - - use pkarr::{dns, Keypair}; - - #[tokio::test] - async fn endpoint_target() { - let mut packet = dns::Packet::new_reply(0); - packet.answers.push(dns::ResourceRecord::new( - dns::Name::new("foo").unwrap(), - dns::CLASS::IN, - 3600, - RData::HTTPS(SVCB::new(0, "https.example.com".try_into().unwrap()).into()), - )); - // Make sure HTTPS only follows HTTPs - packet.answers.push(dns::ResourceRecord::new( - dns::Name::new("foo").unwrap(), - dns::CLASS::IN, - 3600, - RData::SVCB(SVCB::new(0, "protocol.example.com".try_into().unwrap())), - )); - // Make sure SVCB only follows SVCB - packet.answers.push(dns::ResourceRecord::new( - dns::Name::new("foo").unwrap(), - dns::CLASS::IN, - 3600, - RData::HTTPS(SVCB::new(0, "https.example.com".try_into().unwrap()).into()), - )); - packet.answers.push(dns::ResourceRecord::new( - dns::Name::new("_foo").unwrap(), - dns::CLASS::IN, - 3600, - RData::SVCB(SVCB::new(0, "protocol.example.com".try_into().unwrap())), - )); - let keypair = Keypair::random(); - let signed_packet = SignedPacket::from_packet(&keypair, &packet).unwrap(); - - let tld = keypair.public_key(); - - // Follow foo.tld HTTPS records - let endpoint = Endpoint::find(&signed_packet, &format!("foo.{tld}"), false).unwrap(); - assert_eq!(endpoint.target, "https.example.com"); - - // Follow _foo.tld SVCB records - let endpoint = Endpoint::find(&signed_packet, &format!("_foo.{tld}"), true).unwrap(); - assert_eq!(endpoint.target, "protocol.example.com"); - } - - #[test] - fn endpoint_to_socket_addrs() { - let mut packet = dns::Packet::new_reply(0); - packet.answers.push(dns::ResourceRecord::new( - dns::Name::new("@").unwrap(), - dns::CLASS::IN, - 3600, - RData::A(Ipv4Addr::from_str("209.151.148.15").unwrap().into()), - )); - packet.answers.push(dns::ResourceRecord::new( - dns::Name::new("@").unwrap(), - dns::CLASS::IN, - 3600, - RData::AAAA(Ipv6Addr::from_str("2a05:d014:275:6201::64").unwrap().into()), - )); - - let mut svcb = SVCB::new(1, ".".try_into().unwrap()); - svcb.set_port(6881); - - packet.answers.push(dns::ResourceRecord::new( - dns::Name::new("@").unwrap(), - dns::CLASS::IN, - 3600, - RData::HTTPS(svcb.into()), - )); - let keypair = Keypair::random(); - let signed_packet = SignedPacket::from_packet(&keypair, &packet).unwrap(); - - // Follow foo.tld HTTPS records - let endpoint = Endpoint::find( - &signed_packet, - &signed_packet.public_key().to_string(), - false, - ) - .unwrap(); - - assert_eq!(endpoint.target, "."); - - let addrs = endpoint.to_socket_addrs().unwrap(); - assert_eq!( - addrs.map(|s| s.to_string()).collect::>(), - vec!["209.151.148.15:6881", "[2a05:d014:275:6201::64]:6881"] - ) - } -} diff --git a/pubky/src/native/internals/resolver.rs b/pubky/src/native/internals/resolver.rs deleted file mode 100644 index b04ac09..0000000 --- a/pubky/src/native/internals/resolver.rs +++ /dev/null @@ -1,234 +0,0 @@ -use std::net::ToSocketAddrs; - -use pkarr::PublicKey; -use reqwest::dns::{Addrs, Resolve}; - -use crate::error::{Error, Result}; - -use super::endpoints::Endpoint; - -const DEFAULT_MAX_CHAIN_LENGTH: u8 = 3; - -#[derive(Debug, Clone)] -pub struct PkarrResolver { - pkarr: pkarr::Client, - max_chain_length: u8, -} - -impl PkarrResolver { - pub fn new(pkarr: pkarr::Client, max_chain_length: u8) -> Self { - PkarrResolver { - pkarr, - max_chain_length, - } - } - - /// Resolve a `qname` to an alternative [Endpoint] as defined in [RFC9460](https://www.rfc-editor.org/rfc/rfc9460#name-terminology). - /// - /// A `qname` is can be either a regular domain name for HTTPS endpoints, - /// or it could use Attrleaf naming pattern for cusotm protcol. For example: - /// `_foo.example.com` for `foo://example.com`. - async fn resolve_endpoint(&self, qname: &str) -> Result { - let target = qname; - // TODO: cache the result of this function? - - let is_svcb = target.starts_with('_'); - - let mut step = 0; - let mut svcb: Option = None; - - loop { - let current = svcb.clone().map_or(target.to_string(), |s| s.target); - if let Ok(tld) = PublicKey::try_from(current.clone()) { - if let Ok(Some(signed_packet)) = self.pkarr.resolve(&tld).await { - if step >= self.max_chain_length { - break; - }; - step += 1; - - // Choose most prior SVCB record - svcb = Endpoint::find(&signed_packet, ¤t, is_svcb); - - // TODO: support wildcard? - } else { - break; - } - } else { - break; - } - } - - if let Some(svcb) = svcb { - if PublicKey::try_from(svcb.target.as_str()).is_err() { - return Ok(svcb); - } - } - - Err(Error::ResolveEndpoint(target.into())) - } -} - -impl Resolve for PkarrResolver { - fn resolve(&self, name: reqwest::dns::Name) -> reqwest::dns::Resolving { - let client = self.clone(); - - Box::pin(async move { - let name = name.as_str(); - - if PublicKey::try_from(name).is_ok() { - let endpoint = client.resolve_endpoint(name).await?; - - // let addrs = format!("{}:{}", x.target, x.port).to_socket_addrs()?; - - let addrs: Addrs = Box::new(endpoint.to_socket_addrs()?); - - return Ok(addrs); - }; - - Ok(Box::new(format!("{name}:0").to_socket_addrs().unwrap())) - }) - } -} - -impl From<&pkarr::Client> for PkarrResolver { - fn from(pkarr: &pkarr::Client) -> Self { - pkarr.clone().into() - } -} - -impl From for PkarrResolver { - fn from(pkarr: pkarr::Client) -> Self { - Self::new(pkarr, DEFAULT_MAX_CHAIN_LENGTH) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use pkarr::dns::rdata::{A, SVCB}; - use pkarr::dns::{self, rdata::RData}; - use pkarr::SignedPacket; - use pkarr::{mainline::Testnet, Keypair}; - - use std::future::Future; - use std::pin::Pin; - - fn generate_subtree( - client: pkarr::Client, - depth: u8, - branching: u8, - domain: Option, - ) -> Pin>> { - Box::pin(async move { - let keypair = Keypair::random(); - - let mut packet = dns::Packet::new_reply(0); - - for _ in 0..branching { - let mut svcb = SVCB::new(0, ".".try_into().unwrap()); - - if depth == 0 { - svcb.priority = 1; - svcb.set_port((branching) as u16 * 1000); - - if let Some(target) = &domain { - let target: &'static str = Box::leak(target.clone().into_boxed_str()); - svcb.target = target.try_into().unwrap() - } - } else { - let target = - generate_subtree(client.clone(), depth - 1, branching, domain.clone()) - .await - .to_string(); - let target: &'static str = Box::leak(target.into_boxed_str()); - svcb.target = target.try_into().unwrap(); - }; - - packet.answers.push(dns::ResourceRecord::new( - dns::Name::new("@").unwrap(), - dns::CLASS::IN, - 3600, - RData::HTTPS(svcb.into()), - )); - } - - if depth == 0 { - packet.answers.push(dns::ResourceRecord::new( - dns::Name::new("@").unwrap(), - dns::CLASS::IN, - 3600, - RData::A(A { address: 10 }), - )); - } - - let signed_packet = SignedPacket::from_packet(&keypair, &packet).unwrap(); - client.publish(&signed_packet).await.unwrap(); - - keypair.public_key() - }) - } - - fn generate( - client: pkarr::Client, - depth: u8, - branching: u8, - domain: Option, - ) -> Pin>> { - generate_subtree(client, depth - 1, branching, domain) - } - - #[tokio::test] - async fn resolve_endpoints() { - let testnet = Testnet::new(3); - let pkarr = pkarr::Client::builder().testnet(&testnet).build().unwrap(); - - let resolver: PkarrResolver = (&pkarr).into(); - let tld = generate(pkarr, 3, 3, Some("example.com".to_string())).await; - - let endpoint = resolver.resolve_endpoint(&tld.to_string()).await.unwrap(); - assert_eq!(endpoint.target, "example.com"); - } - - #[tokio::test] - async fn max_chain_exceeded() { - let testnet = Testnet::new(3); - let pkarr = pkarr::Client::builder().testnet(&testnet).build().unwrap(); - - let resolver: PkarrResolver = (&pkarr).into(); - - let tld = generate(pkarr, 4, 3, Some("example.com".to_string())).await; - - let endpoint = resolver.resolve_endpoint(&tld.to_string()).await; - - assert_eq!( - match endpoint { - Err(error) => error.to_string(), - _ => "".to_string(), - }, - crate::Error::ResolveEndpoint(tld.to_string()).to_string() - ) - } - - #[tokio::test] - async fn resolve_addresses() { - let testnet = Testnet::new(3); - let pkarr = pkarr::Client::builder().testnet(&testnet).build().unwrap(); - - let resolver: PkarrResolver = (&pkarr).into(); - let tld = generate(pkarr, 3, 3, None).await; - - let endpoint = resolver.resolve_endpoint(&tld.to_string()).await.unwrap(); - assert_eq!(endpoint.target, "."); - assert_eq!(endpoint.port, 3000); - assert_eq!( - endpoint - .to_socket_addrs() - .unwrap() - .into_iter() - .map(|s| s.to_string()) - .collect::>(), - vec!["0.0.0.10:3000"] - ); - dbg!(&endpoint); - } -} diff --git a/pubky/src/shared/auth.rs b/pubky/src/shared/auth.rs index 7c3d9e1..5be14d9 100644 --- a/pubky/src/shared/auth.rs +++ b/pubky/src/shared/auth.rs @@ -39,6 +39,7 @@ impl PubkyClient { let response = self .inner_request(Method::POST, url.clone()) + .await .body(body) .send() .await?; @@ -59,7 +60,7 @@ impl PubkyClient { url.set_path(&format!("/{}/session", pubky)); - let res = self.inner_request(Method::GET, url).send().await?; + let res = self.inner_request(Method::GET, url).await.send().await?; if res.status() == StatusCode::NOT_FOUND { return Ok(None); @@ -80,7 +81,7 @@ impl PubkyClient { url.set_path(&format!("/{}/session", pubky)); - self.inner_request(Method::DELETE, url).send().await?; + self.inner_request(Method::DELETE, url).await.send().await?; Ok(()) } @@ -140,6 +141,7 @@ impl PubkyClient { drop(path_segments); self.inner_request(Method::POST, callback) + .await .body(encrypted_token) .send() .await?; @@ -167,6 +169,7 @@ impl PubkyClient { let response = self .inner_request(Method::POST, url) + .await .body(token.serialize()) .send() .await?; diff --git a/pubky/src/shared/list_builder.rs b/pubky/src/shared/list_builder.rs index 2087274..b76fee4 100644 --- a/pubky/src/shared/list_builder.rs +++ b/pubky/src/shared/list_builder.rs @@ -90,7 +90,12 @@ impl<'a> ListBuilder<'a> { drop(query); - let response = self.client.inner_request(Method::GET, url).send().await?; + let response = self + .client + .inner_request(Method::GET, url) + .await + .send() + .await?; response.error_for_status_ref()?; diff --git a/pubky/src/shared/public.rs b/pubky/src/shared/public.rs index 62f8144..ada987d 100644 --- a/pubky/src/shared/public.rs +++ b/pubky/src/shared/public.rs @@ -17,6 +17,7 @@ impl PubkyClient { let response = self .inner_request(Method::PUT, url) + .await .body(content.to_owned()) .send() .await?; @@ -29,7 +30,7 @@ impl PubkyClient { pub(crate) async fn inner_get>(&self, url: T) -> Result> { let url = self.pubky_to_http(url).await?; - let response = self.inner_request(Method::GET, url).send().await?; + let response = self.inner_request(Method::GET, url).await.send().await?; if response.status() == StatusCode::NOT_FOUND { return Ok(None); @@ -46,7 +47,7 @@ impl PubkyClient { pub(crate) async fn inner_delete>(&self, url: T) -> Result<()> { let url = self.pubky_to_http(url).await?; - let response = self.inner_request(Method::DELETE, url).send().await?; + let response = self.inner_request(Method::DELETE, url).await.send().await?; response.error_for_status_ref()?; @@ -650,10 +651,7 @@ mod tests { { let response = client - .inner_request( - Method::GET, - format!("{feed_url}?limit=10").as_str().try_into().unwrap(), - ) + .request(Method::GET, format!("{feed_url}?limit=10")) .send() .await .unwrap(); @@ -683,13 +681,7 @@ mod tests { { let response = client - .inner_request( - Method::GET, - format!("{feed_url}?limit=10&cursor={cursor}") - .as_str() - .try_into() - .unwrap(), - ) + .request(Method::GET, format!("{feed_url}?limit=10&cursor={cursor}")) .send() .await .unwrap(); @@ -740,10 +732,7 @@ mod tests { { let response = client - .inner_request( - Method::GET, - format!("{feed_url}?limit=10").as_str().try_into().unwrap(), - ) + .request(Method::GET, format!("{feed_url}?limit=10")) .send() .await .unwrap(); @@ -801,10 +790,7 @@ mod tests { let feed_url = format!("http://localhost:{}/events/", homeserver.port()); let response = client - .inner_request( - Method::GET, - format!("{feed_url}").as_str().try_into().unwrap(), - ) + .request(Method::GET, format!("{feed_url}")) .send() .await .unwrap(); diff --git a/pubky/src/wasm/api/http.rs b/pubky/src/wasm/api/http.rs index 562bf66..46c5733 100644 --- a/pubky/src/wasm/api/http.rs +++ b/pubky/src/wasm/api/http.rs @@ -7,6 +7,8 @@ use reqwest::Url; use crate::PubkyClient; +use super::super::internals::resolve; + #[wasm_bindgen] impl PubkyClient { #[wasm_bindgen] @@ -19,7 +21,9 @@ impl PubkyClient { JsValue::from_str(&format!("PubkyClient::fetch(): Invalid `url`; {:?}", err)) })?; - self.resolve_url(&mut url).await.map_err(JsValue::from)?; + resolve(&self.pkarr, &mut url) + .await + .map_err(|err| JsValue::from_str(&format!("PubkyClient::fetch(): {:?}", err)))?; let js_req = web_sys::Request::new_with_str_and_init(url.as_str(), init).map_err(|err| { diff --git a/pubky/src/wasm/api.rs b/pubky/src/wasm/api/mod.rs similarity index 55% rename from pubky/src/wasm/api.rs rename to pubky/src/wasm/api/mod.rs index e339f11..a502667 100644 --- a/pubky/src/wasm/api.rs +++ b/pubky/src/wasm/api/mod.rs @@ -1,6 +1,6 @@ -//! Public API modules - -pub mod auth; pub mod http; -pub mod public; pub mod recovery_file; + +// TODO: put the Homeserver API behind a feature flag +pub mod auth; +pub mod public; diff --git a/pubky/src/wasm/internals.rs b/pubky/src/wasm/internals.rs index 6973130..95b625d 100644 --- a/pubky/src/wasm/internals.rs +++ b/pubky/src/wasm/internals.rs @@ -1,10 +1,33 @@ -use crate::PubkyClient; - use reqwest::{Method, RequestBuilder}; use url::Url; +use pkarr::{EndpointResolver, PublicKey}; + +use crate::{error::Result, PubkyClient}; + +// TODO: remove expect +pub async fn resolve(pkarr: &pkarr::Client, url: &mut Url) -> Result<()> { + let qname = url.host_str().expect("URL TO HAVE A HOST!").to_string(); + + // If http and has a Pubky TLD, switch to socket addresses. + if url.scheme() == "http" && PublicKey::try_from(qname.as_str()).is_ok() { + let endpoint = pkarr.resolve_endpoint(&qname).await?; + + if let Some(socket_address) = endpoint.to_socket_addrs().into_iter().next() { + url.set_host(Some(&socket_address.to_string()))?; + let _ = url.set_port(Some(socket_address.port())); + } else if let Some(port) = endpoint.port() { + url.set_host(Some(endpoint.target()))?; + let _ = url.set_port(Some(port)); + } + }; + + Ok(()) +} + impl PubkyClient { - pub(crate) fn inner_request(&self, method: Method, url: Url) -> RequestBuilder { + /// A wrapper around [reqwest::Client::request], with the same signature between native and wasm. + pub(crate) async fn inner_request(&self, method: Method, url: Url) -> RequestBuilder { self.http.request(method, url).fetch_credentials_include() } }