| 
					
				 | 
			
			
				@@ -1,3 +1,4 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//! HTTP Mint client with pluggable transport 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 use std::collections::HashSet; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 use std::sync::{Arc, RwLock as StdRwLock}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 use std::time::{Duration, Instant}; 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -6,17 +7,15 @@ use async_trait::async_trait; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 use cdk_common::{nut19, MeltQuoteBolt12Request, MintQuoteBolt12Request, MintQuoteBolt12Response}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 #[cfg(feature = "auth")] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 use cdk_common::{Method, ProtectedEndpoint, RoutePath}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-use reqwest::{Client, IntoUrl}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 use serde::de::DeserializeOwned; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 use serde::Serialize; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 #[cfg(feature = "auth")] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 use tokio::sync::RwLock; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 use tracing::instrument; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-#[cfg(not(target_arch = "wasm32"))] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 use url::Url; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+use super::transport::Transport; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 use super::{Error, MintConnector}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-use crate::error::ErrorResponse; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 use crate::mint_url::MintUrl; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 #[cfg(feature = "auth")] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 use crate::nuts::nut22::MintAuthRequest; 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -29,119 +28,30 @@ use crate::nuts::{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 #[cfg(feature = "auth")] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 use crate::wallet::auth::{AuthMintConnector, AuthWallet}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-#[derive(Debug, Clone)] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-struct HttpClientCore { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    inner: Client, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-impl HttpClientCore { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    fn new() -> Self { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        #[cfg(not(target_arch = "wasm32"))] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        if rustls::crypto::CryptoProvider::get_default().is_none() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            let _ = rustls::crypto::ring::default_provider().install_default(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        Self { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            inner: Client::new(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    fn client(&self) -> &Client { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        &self.inner 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    async fn http_get<U: IntoUrl + Send, R: DeserializeOwned>( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        &self, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        url: U, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        auth: Option<AuthToken>, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    ) -> Result<R, Error> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        let mut request = self.client().get(url); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        if let Some(auth) = auth { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            request = request.header(auth.header_key(), auth.to_string()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        let response = request 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            .send() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            .await 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            .map_err(|e| { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                Error::HttpError( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    e.status().map(|status_code| status_code.as_u16()), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    e.to_string(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            })? 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            .text() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            .await 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            .map_err(|e| { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                Error::HttpError( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    e.status().map(|status_code| status_code.as_u16()), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    e.to_string(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            })?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        serde_json::from_str::<R>(&response).map_err(|err| { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            tracing::warn!("Http Response error: {}", err); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            match ErrorResponse::from_json(&response) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                Ok(ok) => <ErrorResponse as Into<Error>>::into(ok), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                Err(err) => err.into(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        }) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    async fn http_post<U: IntoUrl + Send, P: Serialize + ?Sized, R: DeserializeOwned>( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        &self, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        url: U, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        auth_token: Option<AuthToken>, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        payload: &P, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    ) -> Result<R, Error> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        let mut request = self.client().post(url).json(&payload); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        if let Some(auth) = auth_token { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            request = request.header(auth.header_key(), auth.to_string()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        let response = request.send().await.map_err(|e| { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            Error::HttpError( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                e.status().map(|status_code| status_code.as_u16()), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                e.to_string(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        })?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        let response = response.text().await.map_err(|e| { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            Error::HttpError( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                e.status().map(|status_code| status_code.as_u16()), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                e.to_string(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        })?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        serde_json::from_str::<R>(&response).map_err(|err| { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            tracing::warn!("Http Response error: {}", err); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            match ErrorResponse::from_json(&response) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                Ok(ok) => <ErrorResponse as Into<Error>>::into(ok), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                Err(err) => err.into(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        }) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 type Cache = (u64, HashSet<(nut19::Method, nut19::Path)>); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 /// Http Client 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 #[derive(Debug, Clone)] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-pub struct HttpClient { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    core: HttpClientCore, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+pub struct HttpClient<T> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+where 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    T: Transport + Send + Sync + 'static, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    transport: Arc<T>, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     mint_url: MintUrl, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     cache_support: Arc<StdRwLock<Cache>>, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     #[cfg(feature = "auth")] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     auth_wallet: Arc<RwLock<Option<AuthWallet>>>, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-impl HttpClient { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+impl<T> HttpClient<T> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+where 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    T: Transport + Send + Sync + 'static, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /// Create new [`HttpClient`] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     #[cfg(feature = "auth")] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     pub fn new(mint_url: MintUrl, auth_wallet: Option<AuthWallet>) -> Self { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         Self { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            core: HttpClientCore::new(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            transport: T::default().into(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             mint_url, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             auth_wallet: Arc::new(RwLock::new(auth_wallet)), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             cache_support: Default::default(), 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -152,7 +62,7 @@ impl HttpClient { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /// Create new [`HttpClient`] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     pub fn new(mint_url: MintUrl) -> Self { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         Self { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            core: HttpClientCore::new(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            transport: T::default().into(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             cache_support: Default::default(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             mint_url, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -176,7 +86,6 @@ impl HttpClient { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    #[cfg(not(target_arch = "wasm32"))] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /// Create new [`HttpClient`] with a proxy for specific TLDs. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /// Specifying `None` for `host_matcher` will use the proxy for all 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /// requests. 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -186,32 +95,11 @@ impl HttpClient { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         host_matcher: Option<&str>, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         accept_invalid_certs: bool, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     ) -> Result<Self, Error> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        let regex = host_matcher 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            .map(regex::Regex::new) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            .transpose() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            .map_err(|e| Error::Custom(e.to_string()))?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        let client = reqwest::Client::builder() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            .proxy(reqwest::Proxy::custom(move |url| { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if let Some(matcher) = regex.as_ref() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    if let Some(host) = url.host_str() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        if matcher.is_match(host) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            return Some(proxy.clone()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            })) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            .danger_accept_invalid_certs(accept_invalid_certs) // Allow self-signed certs 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            .build() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            .map_err(|e| { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                Error::HttpError( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    e.status().map(|status_code| status_code.as_u16()), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    e.to_string(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            })?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        let mut transport = T::default(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        transport.with_proxy(proxy, host_matcher, accept_invalid_certs)?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         Ok(Self { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            core: HttpClientCore { inner: client }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            transport: transport.into(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             mint_url, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             #[cfg(feature = "auth")] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             auth_wallet: Arc::new(RwLock::new(None)), 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -231,7 +119,7 @@ impl HttpClient { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         payload: &P, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     ) -> Result<R, Error> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     where 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        P: Serialize + ?Sized, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        P: Serialize + ?Sized + Send + Sync, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         R: DeserializeOwned, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         let started = Instant::now(); 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -259,8 +147,12 @@ impl HttpClient { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             })?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             let result = match method { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                nut19::Method::Get => self.core.http_get(url, auth_token.clone()).await, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                nut19::Method::Post => self.core.http_post(url, auth_token.clone(), payload).await, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                nut19::Method::Get => self.transport.http_get(url, auth_token.clone()).await, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                nut19::Method::Post => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    self.transport 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        .http_post(url, auth_token.clone(), payload) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        .await 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             if result.is_ok() { 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -291,15 +183,18 @@ impl HttpClient { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 #[cfg_attr(not(target_arch = "wasm32"), async_trait)] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-impl MintConnector for HttpClient { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+impl<T> MintConnector for HttpClient<T> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+where 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    T: Transport + Send + Sync + 'static, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /// Get Active Mint Keys [NUT-01] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     #[instrument(skip(self), fields(mint_url = %self.mint_url))] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     async fn get_mint_keys(&self) -> Result<Vec<KeySet>, Error> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         let url = self.mint_url.join_paths(&["v1", "keys"])?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         Ok(self 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            .core 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            .http_get::<_, KeysResponse>(url, None) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            .transport 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            .http_get::<KeysResponse>(url, None) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             .await? 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             .keysets) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -311,7 +206,7 @@ impl MintConnector for HttpClient { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             .mint_url 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             .join_paths(&["v1", "keys", &keyset_id.to_string()])?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        let keys_response = self.core.http_get::<_, KeysResponse>(url, None).await?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        let keys_response = self.transport.http_get::<KeysResponse>(url, None).await?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         Ok(keys_response.keysets.first().unwrap().clone()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -320,7 +215,7 @@ impl MintConnector for HttpClient { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     #[instrument(skip(self), fields(mint_url = %self.mint_url))] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     async fn get_mint_keysets(&self) -> Result<KeysetResponse, Error> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         let url = self.mint_url.join_paths(&["v1", "keysets"])?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        self.core.http_get(url, None).await 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.transport.http_get(url, None).await 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /// Mint Quote [NUT-04] 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -341,7 +236,7 @@ impl MintConnector for HttpClient { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         #[cfg(not(feature = "auth"))] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         let auth_token = None; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        self.core.http_post(url, auth_token, &request).await 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.transport.http_post(url, auth_token, &request).await 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /// Mint Quote status 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -361,7 +256,7 @@ impl MintConnector for HttpClient { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         #[cfg(not(feature = "auth"))] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         let auth_token = None; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        self.core.http_get(url, auth_token).await 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.transport.http_get(url, auth_token).await 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /// Mint Tokens [NUT-04] 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -399,7 +294,7 @@ impl MintConnector for HttpClient { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         #[cfg(not(feature = "auth"))] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         let auth_token = None; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        self.core.http_post(url, auth_token, &request).await 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.transport.http_post(url, auth_token, &request).await 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /// Melt Quote Status 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -419,7 +314,7 @@ impl MintConnector for HttpClient { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         #[cfg(not(feature = "auth"))] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         let auth_token = None; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        self.core.http_get(url, auth_token).await 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.transport.http_get(url, auth_token).await 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /// Melt [NUT-05] 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -467,7 +362,7 @@ impl MintConnector for HttpClient { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /// Helper to get mint info 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     async fn get_mint_info(&self) -> Result<MintInfo, Error> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         let url = self.mint_url.join_paths(&["v1", "info"])?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        let info: MintInfo = self.core.http_get(url, None).await?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        let info: MintInfo = self.transport.http_get(url, None).await?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         if let Ok(mut cache_support) = self.cache_support.write() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             *cache_support = ( 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -509,7 +404,7 @@ impl MintConnector for HttpClient { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         #[cfg(not(feature = "auth"))] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         let auth_token = None; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        self.core.http_post(url, auth_token, &request).await 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.transport.http_post(url, auth_token, &request).await 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /// Restore request [NUT-13] 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -523,7 +418,7 @@ impl MintConnector for HttpClient { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         #[cfg(not(feature = "auth"))] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         let auth_token = None; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        self.core.http_post(url, auth_token, &request).await 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.transport.http_post(url, auth_token, &request).await 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /// Mint Quote Bolt12 [NUT-23] 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -544,7 +439,7 @@ impl MintConnector for HttpClient { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         #[cfg(not(feature = "auth"))] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         let auth_token = None; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        self.core.http_post(url, auth_token, &request).await 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.transport.http_post(url, auth_token, &request).await 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /// Mint Quote Bolt12 status 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -564,7 +459,7 @@ impl MintConnector for HttpClient { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         #[cfg(not(feature = "auth"))] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         let auth_token = None; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        self.core.http_get(url, auth_token).await 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.transport.http_get(url, auth_token).await 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /// Melt Quote Bolt12 [NUT-23] 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -583,7 +478,7 @@ impl MintConnector for HttpClient { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         #[cfg(not(feature = "auth"))] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         let auth_token = None; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        self.core.http_post(url, auth_token, &request).await 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.transport.http_post(url, auth_token, &request).await 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /// Melt Quote Bolt12 Status [NUT-23] 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -603,7 +498,7 @@ impl MintConnector for HttpClient { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         #[cfg(not(feature = "auth"))] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         let auth_token = None; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        self.core.http_get(url, auth_token).await 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.transport.http_get(url, auth_token).await 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /// Melt Bolt12 [NUT-23] 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -632,18 +527,24 @@ impl MintConnector for HttpClient { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 /// Http Client 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 #[derive(Debug, Clone)] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 #[cfg(feature = "auth")] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-pub struct AuthHttpClient { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    core: HttpClientCore, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+pub struct AuthHttpClient<T> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+where 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    T: Transport + Send + Sync + 'static, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    transport: Arc<T>, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     mint_url: MintUrl, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     cat: Arc<RwLock<AuthToken>>, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 #[cfg(feature = "auth")] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-impl AuthHttpClient { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+impl<T> AuthHttpClient<T> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+where 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    T: Transport + Send + Sync + 'static, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /// Create new [`AuthHttpClient`] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     pub fn new(mint_url: MintUrl, cat: Option<AuthToken>) -> Self { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         Self { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            core: HttpClientCore::new(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            transport: T::default().into(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             mint_url, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             cat: Arc::new(RwLock::new( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 cat.unwrap_or(AuthToken::ClearAuth("".to_string())), 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -655,7 +556,10 @@ impl AuthHttpClient { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 #[cfg_attr(not(target_arch = "wasm32"), async_trait)] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 #[cfg(feature = "auth")] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-impl AuthMintConnector for AuthHttpClient { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+impl<T> AuthMintConnector for AuthHttpClient<T> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+where 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    T: Transport + Send + Sync + 'static, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     async fn get_auth_token(&self) -> Result<AuthToken, Error> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         Ok(self.cat.read().await.clone()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -668,7 +572,7 @@ impl AuthMintConnector for AuthHttpClient { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /// Get Mint Info [NUT-06] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     async fn get_mint_info(&self) -> Result<MintInfo, Error> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         let url = self.mint_url.join_paths(&["v1", "info"])?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        let mint_info: MintInfo = self.core.http_get::<_, MintInfo>(url, None).await?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        let mint_info: MintInfo = self.transport.http_get::<MintInfo>(url, None).await?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         Ok(mint_info) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -680,7 +584,7 @@ impl AuthMintConnector for AuthHttpClient { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             self.mint_url 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 .join_paths(&["v1", "auth", "blind", "keys", &keyset_id.to_string()])?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        let mut keys_response = self.core.http_get::<_, KeysResponse>(url, None).await?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        let mut keys_response = self.transport.http_get::<KeysResponse>(url, None).await?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         let keyset = keys_response 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             .keysets 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -698,14 +602,14 @@ impl AuthMintConnector for AuthHttpClient { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             .mint_url 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             .join_paths(&["v1", "auth", "blind", "keysets"])?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        self.core.http_get(url, None).await 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.transport.http_get(url, None).await 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /// Mint Tokens [NUT-22] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     #[instrument(skip(self, request), fields(mint_url = %self.mint_url))] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     async fn post_mint_blind_auth(&self, request: MintAuthRequest) -> Result<MintResponse, Error> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         let url = self.mint_url.join_paths(&["v1", "auth", "blind", "mint"])?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        self.core 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.transport 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             .http_post(url, Some(self.cat.read().await.clone()), &request) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             .await 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 |