Browse Source

fix: client join urls without trailing '/'

thesimplekid 1 năm trước cách đây
mục cha
commit
e1264d3ee0

+ 19 - 18
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<Keys, Error> {
-        let url = mint_url.join("keys")?;
+    async fn get_mint_keys(&self, mint_url: Url) -> Result<Keys, Error> {
+        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<nut02::Response, Error> {
-        let url = mint_url.join("keysets")?;
+    async fn get_mint_keysets(&self, mint_url: Url) -> Result<nut02::Response, Error> {
+        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<RequestMintResponse, Error> {
-        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<PostMintResponse, Error> {
-        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<CheckFeesResponse, Error> {
-        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<Proof>,
         invoice: Bolt11Invoice,
         outputs: Option<Vec<BlindedMessage>>,
     ) -> Result<MeltResponse, Error> {
-        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<SplitResponse, Error> {
-        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<nut00::mint::Proof>,
     ) -> Result<CheckSpendableResponse, Error> {
-        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<MintInfo, Error> {
-        let url = mint_url.join("info")?;
+    async fn get_mint_info(&self, mint_url: Url) -> Result<MintInfo, Error> {
+        let url = join_url(mint_url, "info")?;
         let res = Request::get(url.as_str())
             .send()
             .await

+ 20 - 18
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<Keys, Error> {
-        let url = mint_url.join("keys")?;
+    async fn get_mint_keys(&self, mint_url: Url) -> Result<Keys, Error> {
+        let url = join_url(mint_url, "keys")?;
         let keys = minreq::get(url).send()?.json::<Value>()?;
 
         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<nut02::Response, Error> {
-        let url = mint_url.join("keysets")?;
+    async fn get_mint_keysets(&self, mint_url: Url) -> Result<nut02::Response, Error> {
+        let url = join_url(mint_url, "keysets")?;
         let res = minreq::get(url).send()?.json::<Value>()?;
 
         let response: Result<nut02::Response, serde_json::Error> =
@@ -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<RequestMintResponse, Error> {
-        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<PostMintResponse, Error> {
-        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<CheckFeesResponse, Error> {
-        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<Proof>,
         invoice: Bolt11Invoice,
         outputs: Option<Vec<BlindedMessage>>,
     ) -> Result<MeltResponse, Error> {
-        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<SplitResponse, Error> {
-        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<nut00::mint::Proof>,
     ) -> Result<CheckSpendableResponse, Error> {
-        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<MintInfo, Error> {
-        let url = mint_url.join("info")?;
+    async fn get_mint_info(&self, mint_url: Url) -> Result<MintInfo, Error> {
+        let url = join_url(mint_url, "info")?;
+
         let res = minreq::get(url).send()?.json::<Value>()?;
 
         let response: Result<MintInfo, serde_json::Error> = serde_json::from_value(res.clone());

+ 26 - 9
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<Keys, Error>;
+    async fn get_mint_keys(&self, mint_url: Url) -> Result<Keys, Error>;
 
-    async fn get_mint_keysets(&self, mint_url: &Url) -> Result<nut02::Response, Error>;
+    async fn get_mint_keysets(&self, mint_url: Url) -> Result<nut02::Response, Error>;
 
     async fn get_request_mint(
         &self,
-        mint_url: &Url,
+        mint_url: Url,
         amount: Amount,
     ) -> Result<RequestMintResponse, Error>;
 
     // TODO: Hash should have a type
     async fn post_mint(
         &self,
-        mint_url: &Url,
+        mint_url: Url,
         blinded_messages: BlindedMessages,
         hash: &str,
     ) -> Result<PostMintResponse, Error>;
 
     async fn post_check_fees(
         &self,
-        mint_url: &Url,
+        mint_url: Url,
         invoice: Bolt11Invoice,
     ) -> Result<CheckFeesResponse, Error>;
 
     async fn post_melt(
         &self,
-        mint_url: &Url,
+        mint_url: Url,
         proofs: Vec<Proof>,
         invoice: Bolt11Invoice,
         outputs: Option<Vec<BlindedMessage>>,
@@ -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<SplitResponse, Error>;
 
     #[cfg(feature = "nut07")]
     async fn post_check_spendable(
         &self,
-        mint_url: &Url,
+        mint_url: Url,
         proofs: Vec<nut00::mint::Proof>,
     ) -> Result<CheckSpendableResponse, Error>;
 
     #[cfg(feature = "nut09")]
-    async fn get_mint_info(&self, mint_url: &Url) -> Result<MintInfo, Error>;
+    async fn get_mint_info(&self, mint_url: Url) -> Result<MintInfo, Error>;
+}
+
+#[cfg(any(not(target_arch = "wasm32"), feature = "gloo"))]
+fn join_url(url: Url, path: &str) -> Result<Url, Error> {
+    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)]

+ 8 - 8
crates/cashu-sdk/src/wallet.rs

@@ -62,7 +62,7 @@ impl<C: Client> Wallet<C> {
     ) -> Result<ProofsStatus, Error> {
         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<C: Client> Wallet<C> {
     pub async fn request_mint(&self, amount: Amount) -> Result<RequestMintResponse, Error> {
         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<C: Client> Wallet<C> {
         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<C: Client> Wallet<C> {
     pub async fn check_fee(&self, invoice: Bolt11Invoice) -> Result<Amount, Error> {
         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<C: Client> Wallet<C> {
             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<C: Client> Wallet<C> {
             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<C: Client> Wallet<C> {
         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<C: Client> Wallet<C> {
         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),