diff --git a/Cargo.lock b/Cargo.lock index 3437609..2b41cc5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1914,6 +1914,7 @@ dependencies = [ "bytes", "cookie", "cookie_store", + "futures-lite", "js-sys", "pkarr", "pubky-common", diff --git a/pubky/Cargo.toml b/pubky/Cargo.toml index b7ac704..dd56d49 100644 --- a/pubky/Cargo.toml +++ b/pubky/Cargo.toml @@ -31,7 +31,7 @@ tokio = { version = "1.37.0", features = ["full"] } # Wasm dependencies [target.'cfg(target_arch = "wasm32")'.dependencies] reqwest = { version = "0.12.5", default-features = false } - +futures-lite = { version = "2.5.0", default-features = false } wasm-bindgen = "0.2.92" wasm-bindgen-futures = "0.4.42" @@ -43,8 +43,6 @@ anyhow = "1.0.93" pubky-homeserver = { path = "../pubky-homeserver" } tokio = "1.37.0" -[features] - [package.metadata.docs.rs] all-features = true diff --git a/pubky/src/error.rs b/pubky/src/error.rs index 1d5c170..0704065 100644 --- a/pubky/src/error.rs +++ b/pubky/src/error.rs @@ -22,12 +22,14 @@ pub enum Error { #[error(transparent)] PublicKeyError(#[from] pkarr::errors::PublicKeyError), + #[cfg(not(target_arch = "wasm32"))] #[error(transparent)] PkarrPublishError(#[from] pkarr::errors::PublishError), #[error(transparent)] SignedPacketError(#[from] pkarr::errors::SignedPacketError), + #[cfg(not(target_arch = "wasm32"))] #[error(transparent)] PkarrClientWasShutdown(#[from] pkarr::errors::ClientWasShutdown), diff --git a/pubky/src/lib.rs b/pubky/src/lib.rs index 4392ff2..04b1b3c 100644 --- a/pubky/src/lib.rs +++ b/pubky/src/lib.rs @@ -23,11 +23,13 @@ pub use crate::shared::list_builder::ListBuilder; #[derive(Clone)] #[wasm_bindgen] pub struct Client { - #[cfg(not(target_arch = "wasm32"))] - pub(crate) cookie_store: Arc, http: reqwest::Client, + pkarr: pkarr::Client, + + #[cfg(not(target_arch = "wasm32"))] + cookie_store: Arc, + #[cfg(not(target_arch = "wasm32"))] icann_http: reqwest::Client, - pub(crate) pkarr: pkarr::Client, } impl Debug for Client { diff --git a/pubky/src/native.rs b/pubky/src/native.rs index 5fe3846..956f0d5 100644 --- a/pubky/src/native.rs +++ b/pubky/src/native.rs @@ -7,6 +7,7 @@ use crate::Client; mod api; mod cookies; mod http; +mod internal; pub(crate) use cookies::CookieJar; diff --git a/pubky/src/native/internal.rs b/pubky/src/native/internal.rs new file mode 100644 index 0000000..b6d1990 --- /dev/null +++ b/pubky/src/native/internal.rs @@ -0,0 +1,12 @@ +//! Native specific implementation of methods used in the shared module +//! + +use reqwest::{IntoUrl, Method, RequestBuilder}; + +use crate::Client; + +impl Client { + pub(crate) async fn inner_request(&self, method: Method, url: T) -> RequestBuilder { + self.request(method, url) + } +} diff --git a/pubky/src/shared/auth.rs b/pubky/src/shared/auth.rs index 3fb1ae4..7b4bfcc 100644 --- a/pubky/src/shared/auth.rs +++ b/pubky/src/shared/auth.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use base64::{alphabet::URL_SAFE, engine::general_purpose::NO_PAD, Engine}; -use reqwest::StatusCode; +use reqwest::{Method, StatusCode}; use url::Url; use pkarr::{Keypair, PublicKey}; @@ -28,7 +28,8 @@ impl Client { homeserver: &PublicKey, ) -> Result { let response = self - .post(format!("https://{}/signup", homeserver)) + .inner_request(Method::POST, format!("https://{}/signup", homeserver)) + .await .body(AuthToken::sign(keypair, vec![Capability::root()]).serialize()) .send() .await? @@ -38,6 +39,7 @@ impl Client { .await?; // Store the cookie to the correct URL. + #[cfg(not(target_arch = "wasm32"))] self.cookie_store .store_session_after_signup(&response, &keypair.public_key()); @@ -52,7 +54,8 @@ impl Client { /// if the response has any other `>=404` status code. pub(crate) async fn inner_session(&self, pubky: &PublicKey) -> Result> { let res = self - .get(format!("pubky://{}/session", pubky)) + .inner_request(Method::GET, format!("pubky://{}/session", pubky)) + .await .send() .await?; @@ -71,7 +74,8 @@ impl Client { /// Signout from a homeserver. pub(crate) async fn inner_signout(&self, pubky: &PublicKey) -> Result<()> { - self.delete(format!("pubky://{}/session", pubky)) + self.inner_request(Method::DELETE, format!("pubky://{}/session", pubky)) + .await .send() .await? .error_for_status()?; @@ -135,7 +139,8 @@ impl Client { path_segments.push(&channel_id); drop(path_segments); - self.post(callback) + self.inner_request(Method::POST, callback) + .await .body(encrypted_token) .send() .await? @@ -146,7 +151,8 @@ impl Client { pub(crate) async fn signin_with_authtoken(&self, token: &AuthToken) -> Result { let response = self - .post(format!("pubky://{}/session", token.pubky())) + .inner_request(Method::POST, format!("pubky://{}/session", token.pubky())) + .await .body(token.serialize()) .send() .await? diff --git a/pubky/src/shared/list_builder.rs b/pubky/src/shared/list_builder.rs index 0f4352d..2f6e10b 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.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/wasm.rs b/pubky/src/wasm.rs index a9c63e8..c7abc9c 100644 --- a/pubky/src/wasm.rs +++ b/pubky/src/wasm.rs @@ -3,6 +3,7 @@ use wasm_bindgen::prelude::*; use crate::Client; mod api; +mod http; mod internals; mod wrappers; diff --git a/pubky/src/wasm/api/auth.rs b/pubky/src/wasm/api/auth.rs index deaa14d..f277035 100644 --- a/pubky/src/wasm/api/auth.rs +++ b/pubky/src/wasm/api/auth.rs @@ -1,8 +1,11 @@ +//! Wasm bindings for the Auth api + use url::Url; use pubky_common::capabilities::Capabilities; use crate::Client; + use crate::Error; use crate::wasm::wrappers::keys::{Keypair, PublicKey}; diff --git a/pubky/src/wasm/api/mod.rs b/pubky/src/wasm/api/mod.rs index a502667..f43316f 100644 --- a/pubky/src/wasm/api/mod.rs +++ b/pubky/src/wasm/api/mod.rs @@ -1,4 +1,3 @@ -pub mod http; pub mod recovery_file; // TODO: put the Homeserver API behind a feature flag diff --git a/pubky/src/wasm/api/public.rs b/pubky/src/wasm/api/public.rs index 9b7f03d..fc62b8e 100644 --- a/pubky/src/wasm/api/public.rs +++ b/pubky/src/wasm/api/public.rs @@ -1,5 +1,9 @@ +//! Wasm bindings for the /pub/ api + use wasm_bindgen::prelude::*; +use reqwest::{Method, StatusCode}; + use js_sys::{Array, Uint8Array}; use crate::Client; @@ -24,7 +28,11 @@ impl Client { /// 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()) + self.inner_request(Method::DELETE, url) + .await + .send() + .await + .map_err(|e| e.into()) } /// Returns a list of Pubky urls (as strings). diff --git a/pubky/src/wasm/api/http.rs b/pubky/src/wasm/http.rs similarity index 87% rename from pubky/src/wasm/api/http.rs rename to pubky/src/wasm/http.rs index 5b7d54e..e1abdd8 100644 --- a/pubky/src/wasm/api/http.rs +++ b/pubky/src/wasm/http.rs @@ -7,8 +7,6 @@ use reqwest::Url; use crate::Client; -use super::super::internals::resolve; - #[wasm_bindgen] impl Client { #[wasm_bindgen] @@ -21,10 +19,6 @@ impl Client { JsValue::from_str(&format!("pubky::Client::fetch(): Invalid `url`; {:?}", err)) })?; - resolve(&self.pkarr, &mut url) - .await - .map_err(|err| JsValue::from_str(&format!("pubky::Client::fetch(): {:?}", err)))?; - let js_req = web_sys::Request::new_with_str_and_init(url.as_str(), init).map_err(|err| { JsValue::from_str(&format!( diff --git a/pubky/src/wasm/internals.rs b/pubky/src/wasm/internals.rs index 0d387a5..272cab8 100644 --- a/pubky/src/wasm/internals.rs +++ b/pubky/src/wasm/internals.rs @@ -1,33 +1,53 @@ -use reqwest::{Method, RequestBuilder}; +//! Wasm specific implementation of methods used in the shared module +//! + +use reqwest::{IntoUrl, Method, RequestBuilder}; use url::Url; +use futures_lite::{pin, Stream, StreamExt}; + +use pkarr::extra::endpoints::EndpointsResolver; use pkarr::PublicKey; use crate::{error::Result, Client}; -// 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 Client { /// 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 { + pub(crate) async fn inner_request(&self, method: Method, url: T) -> RequestBuilder { + let original_url = url.as_str(); + let mut url = Url::parse(original_url).expect("Invalid url in inner_request"); + + if url.scheme() == "pubky" { + url.set_scheme("https"); + } + + if PublicKey::try_from(original_url).is_ok() { + let stream = self + .pkarr + .resolve_https_endpoints(url.host_str().unwrap_or("")); + + let mut so_far = None; + + while let Some(endpoint) = stream.next().await { + if let Some(e) = so_far { + if e.domain() == "." && endpoint.domain() != "." { + so_far = Some(endpoint); + } + } else { + so_far = Some(endpoint) + } + } + + if let Some(e) = so_far { + url.set_host(Some(e.domain())); + url.set_port(Some(e.port())); + + return self.http.request(method, url).fetch_credentials_include(); + } else { + // TODO: didn't find any domain, what to do? + } + } + self.http.request(method, url).fetch_credentials_include() } } diff --git a/pubky/src/wasm/wrappers/keys.rs b/pubky/src/wasm/wrappers/keys.rs index 3b27045..29ac3d3 100644 --- a/pubky/src/wasm/wrappers/keys.rs +++ b/pubky/src/wasm/wrappers/keys.rs @@ -81,7 +81,7 @@ impl PublicKey { .ok_or("Couldn't create a PublicKey from this type of value")?; Ok(PublicKey( - pkarr::PublicKey::try_from(string).map_err(Error::Pkarr)?, + pkarr::PublicKey::try_from(string).map_err(Error::PublicKeyError)?, )) } }