From e1264d3ee0929aeb07de8ba758ae1a1a0b0e1012 Mon Sep 17 00:00:00 2001 From: thesimplekid Date: Wed, 15 Nov 2023 20:43:35 +0000 Subject: [PATCH] fix: client join urls without trailing '/' --- crates/cashu-sdk/src/client/gloo_client.rs | 37 +++++++++---------- crates/cashu-sdk/src/client/minreq_client.rs | 38 ++++++++++---------- crates/cashu-sdk/src/client/mod.rs | 35 +++++++++++++----- crates/cashu-sdk/src/wallet.rs | 16 ++++----- 4 files changed, 73 insertions(+), 53 deletions(-) diff --git a/crates/cashu-sdk/src/client/gloo_client.rs b/crates/cashu-sdk/src/client/gloo_client.rs index 81702118..84c1eced 100644 --- a/crates/cashu-sdk/src/client/gloo_client.rs +++ b/crates/cashu-sdk/src/client/gloo_client.rs @@ -19,6 +19,7 @@ use gloo::net::http::Request; use serde_json::Value; use url::Url; +use super::join_url; use crate::client::{Client, Error}; #[derive(Debug, Clone)] @@ -27,8 +28,8 @@ pub struct HttpClient {} #[async_trait(?Send)] impl Client for HttpClient { /// Get Mint Keys [NUT-01] - async fn get_mint_keys(&self, mint_url: &Url) -> Result { - let url = mint_url.join("keys")?; + async fn get_mint_keys(&self, mint_url: Url) -> Result { + let url = join_url(mint_url, "keys")?; let keys = Request::get(url.as_str()) .send() .await @@ -42,8 +43,8 @@ impl Client for HttpClient { } /// Get Keysets [NUT-02] - async fn get_mint_keysets(&self, mint_url: &Url) -> Result { - let url = mint_url.join("keysets")?; + async fn get_mint_keysets(&self, mint_url: Url) -> Result { + let url = join_url(mint_url, "keysets")?; let res = Request::get(url.as_str()) .send() .await @@ -64,10 +65,10 @@ impl Client for HttpClient { /// Request Mint [NUT-03] async fn get_request_mint( &self, - mint_url: &Url, + mint_url: Url, amount: Amount, ) -> Result { - let mut url = mint_url.join("mint")?; + let mut url = join_url(mint_url, "mint")?; url.query_pairs_mut() .append_pair("amount", &amount.to_sat().to_string()); @@ -91,11 +92,11 @@ impl Client for HttpClient { /// Mint Tokens [NUT-04] async fn post_mint( &self, - mint_url: &Url, + mint_url: Url, blinded_messages: BlindedMessages, hash: &str, ) -> Result { - let mut url = mint_url.join("mint")?; + let mut url = join_url(mint_url, "mint")?; url.query_pairs_mut().append_pair("hash", hash); let request = MintRequest { @@ -124,10 +125,10 @@ impl Client for HttpClient { /// Check Max expected fee [NUT-05] async fn post_check_fees( &self, - mint_url: &Url, + mint_url: Url, invoice: Bolt11Invoice, ) -> Result { - let url = mint_url.join("checkfees")?; + let url = join_url(mint_url, "checkfees")?; let request = CheckFeesRequest { pr: invoice }; @@ -154,12 +155,12 @@ impl Client for HttpClient { /// [Nut-08] Lightning fee return if outputs defined async fn post_melt( &self, - mint_url: &Url, + mint_url: Url, proofs: Vec, invoice: Bolt11Invoice, outputs: Option>, ) -> Result { - let url = mint_url.join("melt")?; + let url = join_url(mint_url, "melt")?; let request = MeltRequest { proofs, @@ -189,10 +190,10 @@ impl Client for HttpClient { /// Split Token [NUT-06] async fn post_split( &self, - mint_url: &Url, + mint_url: Url, split_request: SplitRequest, ) -> Result { - let url = mint_url.join("split")?; + let url = join_url(mint_url, "split")?; let res = Request::post(url.as_str()) .json(&split_request) @@ -217,10 +218,10 @@ impl Client for HttpClient { #[cfg(feature = "nut07")] async fn post_check_spendable( &self, - mint_url: &Url, + mint_url: Url, proofs: Vec, ) -> Result { - let url = mint_url.join("check")?; + let url = join_url(mint_url, "check")?; let request = CheckSpendableRequest { proofs: proofs.to_owned(), }; @@ -246,8 +247,8 @@ impl Client for HttpClient { /// Get Mint Info [NUT-09] #[cfg(feature = "nut09")] - async fn get_mint_info(&self, mint_url: &Url) -> Result { - let url = mint_url.join("info")?; + async fn get_mint_info(&self, mint_url: Url) -> Result { + let url = join_url(mint_url, "info")?; let res = Request::get(url.as_str()) .send() .await diff --git a/crates/cashu-sdk/src/client/minreq_client.rs b/crates/cashu-sdk/src/client/minreq_client.rs index 2d777f9e..1fc87f1e 100644 --- a/crates/cashu-sdk/src/client/minreq_client.rs +++ b/crates/cashu-sdk/src/client/minreq_client.rs @@ -18,6 +18,7 @@ use cashu::{Amount, Bolt11Invoice}; use serde_json::Value; use url::Url; +use super::join_url; use crate::client::{Client, Error}; #[derive(Debug, Clone)] @@ -26,8 +27,8 @@ pub struct HttpClient {} #[async_trait(?Send)] impl Client for HttpClient { /// Get Mint Keys [NUT-01] - async fn get_mint_keys(&self, mint_url: &Url) -> Result { - let url = mint_url.join("keys")?; + async fn get_mint_keys(&self, mint_url: Url) -> Result { + let url = join_url(mint_url, "keys")?; let keys = minreq::get(url).send()?.json::()?; let keys: Keys = serde_json::from_str(&keys.to_string())?; @@ -35,8 +36,8 @@ impl Client for HttpClient { } /// Get Keysets [NUT-02] - async fn get_mint_keysets(&self, mint_url: &Url) -> Result { - let url = mint_url.join("keysets")?; + async fn get_mint_keysets(&self, mint_url: Url) -> Result { + let url = join_url(mint_url, "keysets")?; let res = minreq::get(url).send()?.json::()?; let response: Result = @@ -51,10 +52,10 @@ impl Client for HttpClient { /// Request Mint [NUT-03] async fn get_request_mint( &self, - mint_url: &Url, + mint_url: Url, amount: Amount, ) -> Result { - let mut url = mint_url.join("mint")?; + let mut url = join_url(mint_url, "mint")?; url.query_pairs_mut() .append_pair("amount", &amount.to_sat().to_string()); @@ -72,11 +73,11 @@ impl Client for HttpClient { /// Mint Tokens [NUT-04] async fn post_mint( &self, - mint_url: &Url, + mint_url: Url, blinded_messages: BlindedMessages, hash: &str, ) -> Result { - let mut url = mint_url.join("mint")?; + let mut url = join_url(mint_url, "mint")?; url.query_pairs_mut().append_pair("hash", hash); let request = MintRequest { @@ -100,10 +101,10 @@ impl Client for HttpClient { /// Check Max expected fee [NUT-05] async fn post_check_fees( &self, - mint_url: &Url, + mint_url: Url, invoice: Bolt11Invoice, ) -> Result { - let url = mint_url.join("checkfees")?; + let url = join_url(mint_url, "checkfees")?; let request = CheckFeesRequest { pr: invoice }; @@ -125,12 +126,12 @@ impl Client for HttpClient { /// [Nut-08] Lightning fee return if outputs defined async fn post_melt( &self, - mint_url: &Url, + mint_url: Url, proofs: Vec, invoice: Bolt11Invoice, outputs: Option>, ) -> Result { - let url = mint_url.join("melt")?; + let url = join_url(mint_url, "melt")?; let request = MeltRequest { proofs, @@ -155,10 +156,10 @@ impl Client for HttpClient { /// Split Token [NUT-06] async fn post_split( &self, - mint_url: &Url, + mint_url: Url, split_request: SplitRequest, ) -> Result { - let url = mint_url.join("split")?; + let url = join_url(mint_url, "split")?; let res = minreq::post(url) .with_json(&split_request)? @@ -178,10 +179,10 @@ impl Client for HttpClient { #[cfg(feature = "nut07")] async fn post_check_spendable( &self, - mint_url: &Url, + mint_url: Url, proofs: Vec, ) -> Result { - let url = mint_url.join("check")?; + let url = join_url(mint_url, "check")?; let request = CheckSpendableRequest { proofs }; let res = minreq::post(url) @@ -200,8 +201,9 @@ impl Client for HttpClient { /// Get Mint Info [NUT-09] #[cfg(feature = "nut09")] - async fn get_mint_info(&self, mint_url: &Url) -> Result { - let url = mint_url.join("info")?; + async fn get_mint_info(&self, mint_url: Url) -> Result { + let url = join_url(mint_url, "info")?; + let res = minreq::get(url).send()?.json::()?; let response: Result = serde_json::from_value(res.clone()); diff --git a/crates/cashu-sdk/src/client/mod.rs b/crates/cashu-sdk/src/client/mod.rs index 77c21a9a..8280d652 100644 --- a/crates/cashu-sdk/src/client/mod.rs +++ b/crates/cashu-sdk/src/client/mod.rs @@ -84,33 +84,33 @@ pub struct MintErrorResponse { #[async_trait(?Send)] pub trait Client { - async fn get_mint_keys(&self, mint_url: &Url) -> Result; + async fn get_mint_keys(&self, mint_url: Url) -> Result; - async fn get_mint_keysets(&self, mint_url: &Url) -> Result; + async fn get_mint_keysets(&self, mint_url: Url) -> Result; async fn get_request_mint( &self, - mint_url: &Url, + mint_url: Url, amount: Amount, ) -> Result; // TODO: Hash should have a type async fn post_mint( &self, - mint_url: &Url, + mint_url: Url, blinded_messages: BlindedMessages, hash: &str, ) -> Result; async fn post_check_fees( &self, - mint_url: &Url, + mint_url: Url, invoice: Bolt11Invoice, ) -> Result; async fn post_melt( &self, - mint_url: &Url, + mint_url: Url, proofs: Vec, invoice: Bolt11Invoice, outputs: Option>, @@ -121,19 +121,36 @@ pub trait Client { // in but in check spendable and melt the compants are passed in async fn post_split( &self, - mint_url: &Url, + mint_url: Url, split_request: SplitRequest, ) -> Result; #[cfg(feature = "nut07")] async fn post_check_spendable( &self, - mint_url: &Url, + mint_url: Url, proofs: Vec, ) -> Result; #[cfg(feature = "nut09")] - async fn get_mint_info(&self, mint_url: &Url) -> Result; + async fn get_mint_info(&self, mint_url: Url) -> Result; +} + +#[cfg(any(not(target_arch = "wasm32"), feature = "gloo"))] +fn join_url(url: Url, path: &str) -> Result { + let mut url = url; + if !url.path().ends_with('/') { + url.path_segments_mut() + .map_err(|_| Error::Custom("Url Path Segmants".to_string()))? + .push(path); + } else { + url.path_segments_mut() + .map_err(|_| Error::Custom("Url Path Segmants".to_string()))? + .pop() + .push(path); + } + + Ok(url) } #[cfg(test)] diff --git a/crates/cashu-sdk/src/wallet.rs b/crates/cashu-sdk/src/wallet.rs index 5cb9676f..1a760327 100644 --- a/crates/cashu-sdk/src/wallet.rs +++ b/crates/cashu-sdk/src/wallet.rs @@ -62,7 +62,7 @@ impl Wallet { ) -> Result { let spendable = self .client - .post_check_spendable(&self.mint_url.clone().try_into()?, proofs.clone()) + .post_check_spendable(self.mint_url.clone().try_into()?, proofs.clone()) .await?; // Separate proofs in spent and unspent based on mint response @@ -81,7 +81,7 @@ impl Wallet { pub async fn request_mint(&self, amount: Amount) -> Result { Ok(self .client - .get_request_mint(&self.mint_url.clone().try_into()?, amount) + .get_request_mint(self.mint_url.clone().try_into()?, amount) .await?) } @@ -99,7 +99,7 @@ impl Wallet { let mint_res = self .client .post_mint( - &self.mint_url.clone().try_into()?, + self.mint_url.clone().try_into()?, blinded_messages.clone(), hash, ) @@ -119,7 +119,7 @@ impl Wallet { pub async fn check_fee(&self, invoice: Bolt11Invoice) -> Result { Ok(self .client - .post_check_fees(&self.mint_url.clone().try_into()?, invoice) + .post_check_fees(self.mint_url.clone().try_into()?, invoice) .await? .fee) } @@ -137,7 +137,7 @@ impl Wallet { let keys = if token.mint.to_string().eq(&self.mint_url.to_string()) { self.mint_keys.clone() } else { - self.client.get_mint_keys(&token.mint.try_into()?).await? + self.client.get_mint_keys(token.mint.try_into()?).await? }; // Sum amount of all proofs @@ -148,7 +148,7 @@ impl Wallet { let split_response = self .client .post_split( - &self.mint_url.clone().try_into()?, + self.mint_url.clone().try_into()?, split_payload.split_payload, ) .await?; @@ -255,7 +255,7 @@ impl Wallet { let split_response = self .client .post_split( - &self.mint_url.clone().try_into()?, + self.mint_url.clone().try_into()?, split_payload.split_payload, ) .await?; @@ -299,7 +299,7 @@ impl Wallet { let melt_response = self .client .post_melt( - &self.mint_url.clone().try_into()?, + self.mint_url.clone().try_into()?, proofs, invoice, Some(blinded.blinded_messages),