use std::fmt::Debug;
use async_trait::async_trait;
use cashu::nuts::nut22::AuthToken;
use serde::de::DeserializeOwned;
use serde::Serialize;
use url::Url;
#[cfg(any(target_arch = "wasm32", feature = "bitreq", feature = "reqwest"))]
use crate::{HttpClient, HttpClientBuilder};
use crate::{HttpError, RawResponse};
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait Transport: Send + Sync + Debug + Clone {
async fn ws_connect(
&self,
url: &str,
headers: &[(&str, &str)],
) -> Result<(crate::ws::WsSender, crate::ws::WsReceiver), crate::ws::WsError> {
crate::ws::connect(url, headers).await
}
fn with_proxy(
&mut self,
proxy: Url,
host_matcher: Option<&str>,
accept_invalid_certs: bool,
) -> Result<(), HttpError>;
async fn resolve_dns_txt(&self, _domain: &str) -> Result<Vec<String>, HttpError> {
Err(HttpError::Other(
"DNS TXT resolution is not enabled for this transport".to_owned(),
))
}
async fn http_get<R>(&self, url: Url, auth: Option<AuthToken>) -> Result<R, HttpError>
where
R: DeserializeOwned;
async fn http_get_raw(
&self,
url: Url,
auth: Option<AuthToken>,
) -> Result<RawResponse, HttpError>;
async fn http_post<P, R>(
&self,
url: Url,
auth_token: Option<AuthToken>,
payload: &P,
) -> Result<R, HttpError>
where
P: Serialize + Send + Sync,
R: DeserializeOwned;
async fn http_post_form_raw<P>(
&self,
url: Url,
auth_token: Option<AuthToken>,
payload: &P,
) -> Result<RawResponse, HttpError>
where
P: Serialize + Send + Sync;
}
#[cfg(any(target_arch = "wasm32", feature = "bitreq", feature = "reqwest"))]
#[derive(Debug, Clone)]
pub struct Async {
inner: HttpClient,
}
#[cfg(any(target_arch = "wasm32", feature = "bitreq", feature = "reqwest"))]
impl Default for Async {
fn default() -> Self {
Self {
inner: HttpClient::builder()
.no_redirects()
.build()
.expect("default no-redirect client"),
}
}
}
#[cfg(any(target_arch = "wasm32", feature = "bitreq", feature = "reqwest"))]
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl Transport for Async {
fn with_proxy(
&mut self,
proxy: Url,
host_matcher: Option<&str>,
accept_invalid_certs: bool,
) -> Result<(), HttpError> {
let builder = HttpClientBuilder::default()
.no_redirects()
.danger_accept_invalid_certs(accept_invalid_certs);
let builder = match host_matcher {
Some(pattern) => builder.proxy_with_matcher(proxy, pattern)?,
None => builder.proxy(proxy),
};
self.inner = builder.build()?;
Ok(())
}
#[cfg(all(feature = "bip353", not(target_arch = "wasm32")))]
async fn resolve_dns_txt(&self, domain: &str) -> Result<Vec<String>, HttpError> {
crate::dns::resolve_dns_txt(domain).await
}
async fn http_get<R>(&self, url: Url, auth: Option<AuthToken>) -> Result<R, HttpError>
where
R: DeserializeOwned,
{
self.http_get_raw(url, auth).await?.json_or_status_error()
}
async fn http_get_raw(
&self,
url: Url,
auth: Option<AuthToken>,
) -> Result<RawResponse, HttpError> {
let url_str = url.to_string();
let mut request = self.inner.get(&url_str);
if let Some(auth) = auth {
request = request.header(auth.header_key(), auth.to_string());
}
request.send().await
}
async fn http_post<P, R>(
&self,
url: Url,
auth_token: Option<AuthToken>,
payload: &P,
) -> Result<R, HttpError>
where
P: Serialize + Send + Sync,
R: DeserializeOwned,
{
let url_str = url.to_string();
let mut request = self.inner.post(&url_str).json(payload);
if let Some(auth) = auth_token {
request = request.header(auth.header_key(), auth.to_string());
}
request.send_json::<R>().await
}
async fn http_post_form_raw<P>(
&self,
url: Url,
auth_token: Option<AuthToken>,
payload: &P,
) -> Result<RawResponse, HttpError>
where
P: Serialize + Send + Sync,
{
let url_str = url.to_string();
let mut request = self.inner.post(&url_str).form(payload);
if let Some(auth) = auth_token {
request = request.header(auth.header_key(), auth.to_string());
}
request.send().await
}
}
#[cfg(all(
feature = "bitreq",
not(feature = "reqwest"),
not(target_arch = "wasm32")
))]
pub type BitreqTransport = Async;
#[cfg(all(feature = "reqwest", not(target_arch = "wasm32")))]
pub type ReqwestTransport = Async;
#[cfg(all(feature = "tor", not(target_arch = "wasm32")))]
mod tor_transport;
#[cfg(all(feature = "tor", not(target_arch = "wasm32")))]
pub use self::tor_transport::TorAsync;