소스 검색

feat: replace async header with prefer in body (#1618)

User the async header caused issues with cors.
To fix this we are changing it so the prefer is in the body.
To support both for now the mint will accept either the header or the body.
tsk 1 주 전
부모
커밋
fb3973750f

+ 17 - 0
crates/cashu/src/nuts/nut05.rs

@@ -92,6 +92,10 @@ pub struct MeltRequest<Q> {
     /// Blinded Message that can be used to return change [NUT-08]
     /// Amount field of BlindedMessages `SHOULD` be set to zero
     outputs: Option<Vec<BlindedMessage>>,
+    /// Whether the client prefers asynchronous processing
+    #[serde(default)]
+    #[cfg_attr(feature = "swagger", schema(value_type = bool))]
+    prefer_async: bool,
 }
 
 #[cfg(feature = "mint")]
@@ -103,6 +107,7 @@ impl TryFrom<MeltRequest<String>> for MeltRequest<QuoteId> {
             quote: QuoteId::from_str(&value.quote).map_err(|_e| Error::InvalidQuote)?,
             inputs: value.inputs,
             outputs: value.outputs,
+            prefer_async: value.prefer_async,
         })
     }
 }
@@ -140,9 +145,21 @@ where
             quote,
             inputs: inputs.without_dleqs(),
             outputs,
+            prefer_async: false,
         }
     }
 
+    /// Set the prefer_async flag for asynchronous processing
+    pub fn prefer_async(mut self, prefer_async: bool) -> Self {
+        self.prefer_async = prefer_async;
+        self
+    }
+
+    /// Get the prefer_async flag
+    pub fn is_prefer_async(&self) -> bool {
+        self.prefer_async
+    }
+
     /// Get quote
     pub fn quote(&self) -> &Q {
         &self.quote

+ 4 - 1
crates/cdk-axum/src/custom_handlers.rs

@@ -338,7 +338,10 @@ pub async fn post_melt_custom(
         .await
         .map_err(into_response)?;
 
-    let res = if prefer.respond_async {
+    // Check for async preference in either the Prefer header or the request body
+    let respond_async = prefer.respond_async || payload.is_prefer_async();
+
+    let res = if respond_async {
         // Asynchronous processing - return immediately after setup
         state
             .mint

+ 1 - 2
crates/cdk-integration-tests/src/init_pure_tests.rs

@@ -139,11 +139,10 @@ impl MintConnector for DirectMintConnection {
             .map(Into::into)
     }
 
-    async fn post_melt_with_options(
+    async fn post_melt(
         &self,
         _method: &PaymentMethod,
         request: MeltRequest<String>,
-        _options: cdk::wallet::MeltOptions,
     ) -> Result<MeltQuoteBolt11Response<String>, Error> {
         let request_uuid = request.try_into().unwrap();
         self.mint.melt(&request_uuid).await.map(Into::into)

+ 6 - 23
crates/cdk/examples/mint-token-bolt12-with-custom-http.rs

@@ -56,22 +56,12 @@ impl HttpTransport for CustomHttp {
         panic!("Not supported");
     }
 
-    async fn http_get_with_headers<R>(
-        &self,
-        url: Url,
-        _auth: Option<AuthToken>,
-        custom_headers: &[(&str, &str)],
-    ) -> Result<R, Error>
+    async fn http_get<R>(&self, url: Url, _auth: Option<AuthToken>) -> Result<R, Error>
     where
         R: DeserializeOwned,
     {
-        let mut request = self.agent.get(url.as_str());
-
-        for (key, value) in custom_headers {
-            request = request.header(*key, *value);
-        }
-
-        request
+        self.agent
+            .get(url.as_str())
             .call()
             .map_err(|e| Error::HttpError(None, e.to_string()))?
             .body_mut()
@@ -79,25 +69,18 @@ impl HttpTransport for CustomHttp {
             .map_err(|e| Error::HttpError(None, e.to_string()))
     }
 
-    /// HTTP Post request
-    async fn http_post_with_headers<P, R>(
+    async fn http_post<P, R>(
         &self,
         url: Url,
         _auth_token: Option<AuthToken>,
-        custom_headers: &[(&str, &str)],
         payload: &P,
     ) -> Result<R, Error>
     where
         P: Serialize + ?Sized + Send + Sync,
         R: DeserializeOwned,
     {
-        let mut request = self.agent.post(url.as_str());
-
-        for (key, value) in custom_headers {
-            request = request.header(*key, *value);
-        }
-
-        request
+        self.agent
+            .post(url.as_str())
             .send_json(payload)
             .map_err(|e| Error::HttpError(None, e.to_string()))?
             .body_mut()

+ 3 - 7
crates/cdk/src/wallet/melt/saga/mod.rs

@@ -50,7 +50,6 @@ use super::MeltConfirmOptions;
 use crate::nuts::nut00::ProofsMethods;
 use crate::nuts::{MeltRequest, PreMintSecrets, Proofs, State};
 use crate::util::unix_time;
-use crate::wallet::mint_connector::MeltOptions;
 use crate::wallet::saga::{add_compensation, new_compensations, Compensations};
 use crate::{ensure_cdk, Amount, Error, Wallet};
 
@@ -872,16 +871,13 @@ impl<'a> MeltSaga<'a, MeltRequested> {
             quote_info.id.clone(),
             self.state_data.final_proofs.clone(),
             Some(self.state_data.premint_secrets.blinded_messages()),
-        );
+        )
+        .prefer_async(true);
 
         let melt_result = self
             .wallet
             .client
-            .post_melt_with_options(
-                &quote_info.payment_method,
-                request,
-                MeltOptions { async_melt: true },
-            )
+            .post_melt(&quote_info.payment_method, request)
             .await;
 
         let melt_response = match melt_result {

+ 7 - 30
crates/cdk/src/wallet/mint_connector/http_client.rs

@@ -15,7 +15,7 @@ use url::Url;
 use web_time::{Duration, Instant};
 
 use super::transport::Transport;
-use super::{Error, MeltOptions, MintConnector};
+use super::{Error, MintConnector};
 use crate::mint_url::MintUrl;
 use crate::nuts::nut00::{KnownMethod, PaymentMethod};
 use crate::nuts::nut22::MintAuthRequest;
@@ -116,7 +116,6 @@ where
         method: nut19::Method,
         path: nut19::Path,
         auth_token: Option<AuthToken>,
-        custom_headers: &[(&str, &str)],
         payload: &P,
     ) -> Result<R, Error>
     where
@@ -152,16 +151,8 @@ where
             };
 
             let result = match method {
-                nut19::Method::Get => {
-                    transport
-                        .http_get_with_headers(url, auth_token.clone(), custom_headers)
-                        .await
-                }
-                nut19::Method::Post => {
-                    transport
-                        .http_post_with_headers(url, auth_token.clone(), custom_headers, payload)
-                        .await
-                }
+                nut19::Method::Get => transport.http_get(url, auth_token.clone()).await,
+                nut19::Method::Post => transport.http_post(url, auth_token.clone(), payload).await,
             };
 
             if result.is_ok() {
@@ -319,7 +310,7 @@ where
             PaymentMethod::Custom(m) => nut19::Path::custom_mint(m),
         };
 
-        self.retriable_http_request(nut19::Method::Post, path, auth_token, &[], &request)
+        self.retriable_http_request(nut19::Method::Post, path, auth_token, &request)
             .await
     }
 
@@ -365,11 +356,10 @@ where
     /// Melt [NUT-05]
     /// [Nut-08] Lightning fee return if outputs defined
     #[instrument(skip(self, request), fields(mint_url = %self.mint_url))]
-    async fn post_melt_with_options(
+    async fn post_melt(
         &self,
         method: &PaymentMethod,
         request: MeltRequest<String>,
-        options: MeltOptions,
     ) -> Result<MeltQuoteBolt11Response<String>, Error> {
         let auth_token = self
             .get_auth_token(Method::Post, RoutePath::Melt(method.to_string()))
@@ -385,20 +375,8 @@ where
             PaymentMethod::Custom(m) => nut19::Path::custom_melt(m),
         };
 
-        let custom_headers = if options.async_melt {
-            vec![("Prefer", "respond-async")]
-        } else {
-            vec![]
-        };
-
-        self.retriable_http_request(
-            nut19::Method::Post,
-            path,
-            auth_token,
-            &custom_headers,
-            &request,
-        )
-        .await
+        self.retriable_http_request(nut19::Method::Post, path, auth_token, &request)
+            .await
     }
 
     /// Swap Token [NUT-03]
@@ -410,7 +388,6 @@ where
             nut19::Method::Post,
             nut19::Path::Swap,
             auth_token,
-            &[],
             &swap_request,
         )
         .await

+ 0 - 18
crates/cdk/src/wallet/mint_connector/mod.rs

@@ -22,13 +22,6 @@ use crate::wallet::AuthWallet;
 pub mod http_client;
 pub mod transport;
 
-/// Melt Options
-#[derive(Debug, Clone, Default)]
-pub struct MeltOptions {
-    /// Prefer respond-async
-    pub async_melt: bool,
-}
-
 /// Auth HTTP Client with async transport
 pub type AuthHttpClient = http_client::AuthHttpClient<transport::Async>;
 /// Default Http Client with async transport (non-Tor)
@@ -98,17 +91,6 @@ pub trait MintConnector: Debug {
         &self,
         method: &PaymentMethod,
         request: MeltRequest<String>,
-    ) -> Result<MeltQuoteBolt11Response<String>, Error> {
-        self.post_melt_with_options(method, request, MeltOptions::default())
-            .await
-    }
-
-    /// Melt [NUT-05] with options (e.g. async)
-    async fn post_melt_with_options(
-        &self,
-        method: &PaymentMethod,
-        request: MeltRequest<String>,
-        options: MeltOptions,
     ) -> Result<MeltQuoteBolt11Response<String>, Error>;
 
     /// Split Token [NUT-06]

+ 2 - 45
crates/cdk/src/wallet/mint_connector/transport.rs

@@ -38,19 +38,6 @@ pub trait Transport: Default + Send + Sync + Debug + Clone {
         auth: Option<cdk_common::AuthToken>,
     ) -> Result<R, super::Error>
     where
-        R: serde::de::DeserializeOwned,
-    {
-        self.http_get_with_headers(url, auth, &[]).await
-    }
-
-    /// HTTP Get request with custom headers
-    async fn http_get_with_headers<R>(
-        &self,
-        url: url::Url,
-        auth: Option<cdk_common::AuthToken>,
-        custom_headers: &[(&str, &str)],
-    ) -> Result<R, super::Error>
-    where
         R: serde::de::DeserializeOwned;
 
     /// HTTP Post request
@@ -62,22 +49,6 @@ pub trait Transport: Default + Send + Sync + Debug + Clone {
     ) -> Result<R, super::Error>
     where
         P: serde::Serialize + ?Sized + Send + Sync,
-        R: serde::de::DeserializeOwned,
-    {
-        self.http_post_with_headers(url, auth_token, &[], payload)
-            .await
-    }
-
-    /// HTTP Post request with custom headers
-    async fn http_post_with_headers<P, R>(
-        &self,
-        url: url::Url,
-        auth_token: Option<cdk_common::AuthToken>,
-        custom_headers: &[(&str, &str)],
-        payload: &P,
-    ) -> Result<R, super::Error>
-    where
-        P: serde::Serialize + ?Sized + Send + Sync,
         R: serde::de::DeserializeOwned;
 }
 
@@ -164,12 +135,7 @@ impl Transport for Async {
             .collect::<Vec<_>>())
     }
 
-    async fn http_get_with_headers<R>(
-        &self,
-        url: Url,
-        auth: Option<AuthToken>,
-        custom_headers: &[(&str, &str)],
-    ) -> Result<R, Error>
+    async fn http_get<R>(&self, url: Url, auth: Option<AuthToken>) -> Result<R, Error>
     where
         R: DeserializeOwned,
     {
@@ -180,10 +146,6 @@ impl Transport for Async {
             request = request.header(auth.header_key(), auth.to_string());
         }
 
-        for (key, value) in custom_headers {
-            request = request.header(*key, *value);
-        }
-
         let response = request
             .send()
             .await
@@ -201,11 +163,10 @@ impl Transport for Async {
         })
     }
 
-    async fn http_post_with_headers<P, R>(
+    async fn http_post<P, R>(
         &self,
         url: Url,
         auth_token: Option<AuthToken>,
-        custom_headers: &[(&str, &str)],
         payload: &P,
     ) -> Result<R, Error>
     where
@@ -219,10 +180,6 @@ impl Transport for Async {
             request = request.header(auth.header_key(), auth.to_string());
         }
 
-        for (key, value) in custom_headers {
-            request = request.header(*key, *value);
-        }
-
         let response = request
             .send()
             .await

+ 5 - 13
crates/cdk/src/wallet/mint_connector/transport/tor_transport.rs

@@ -149,7 +149,6 @@ impl TorAsync {
         method: http::Method,
         url: Url,
         auth: Option<AuthToken>,
-        custom_headers: &[(&str, &str)],
         mut body: Option<Vec<u8>>,
     ) -> Result<R, Error>
     where
@@ -176,10 +175,6 @@ impl TorAsync {
         let mut builder = Request::builder().method(method).uri(uri);
         builder = builder.header(header::ACCEPT, "application/json");
 
-        for (key, value) in custom_headers {
-            builder = builder.header(*key, *value);
-        }
-
         let mut req = if let Some(b) = body.take() {
             builder
                 .header(http::header::CONTENT_TYPE, "application/json")
@@ -237,24 +232,21 @@ impl Transport for TorAsync {
         panic!("not supported with TorAsync transport");
     }
 
-    async fn http_get_with_headers<R>(
+    async fn http_get<R>(
         &self,
         url: url::Url,
         auth: Option<cdk_common::AuthToken>,
-        custom_headers: &[(&str, &str)],
     ) -> Result<R, super::super::Error>
     where
         R: serde::de::DeserializeOwned,
     {
-        self.request::<R>(Method::GET, url, auth, custom_headers, None)
-            .await
+        self.request::<R>(Method::GET, url, auth, None).await
     }
 
-    async fn http_post_with_headers<P, R>(
+    async fn http_post<P, R>(
         &self,
         url: url::Url,
         auth_token: Option<cdk_common::AuthToken>,
-        custom_headers: &[(&str, &str)],
         payload: &P,
     ) -> Result<R, super::super::Error>
     where
@@ -262,7 +254,7 @@ impl Transport for TorAsync {
         R: serde::de::DeserializeOwned,
     {
         let body = serde_json::to_vec(payload).map_err(|e| Error::Custom(e.to_string()))?;
-        self.request::<R>(Method::POST, url, auth_token, custom_headers, Some(body))
+        self.request::<R>(Method::POST, url, auth_token, Some(body))
             .await
     }
 
@@ -323,7 +315,7 @@ impl Transport for TorAsync {
         }
 
         let resp: DnsResp = self
-            .request::<DnsResp>(Method::GET, url, None, &[], None::<Vec<u8>>)
+            .request::<DnsResp>(Method::GET, url, None, None::<Vec<u8>>)
             .await?;
 
         let answers = resp.Answer.unwrap_or_default();

+ 1 - 2
crates/cdk/src/wallet/mod.rs

@@ -71,8 +71,7 @@ pub use cdk_common::wallet as types;
 pub use melt::{MeltConfirmOptions, MeltOutcome, PendingMelt, PreparedMelt};
 pub use mint_connector::transport::Transport as HttpTransport;
 pub use mint_connector::{
-    AuthHttpClient, HttpClient, LnurlPayInvoiceResponse, LnurlPayResponse, MeltOptions,
-    MintConnector,
+    AuthHttpClient, HttpClient, LnurlPayInvoiceResponse, LnurlPayResponse, MintConnector,
 };
 pub use multi_mint_wallet::{MultiMintReceiveOptions, MultiMintSendOptions, MultiMintWallet};
 #[cfg(feature = "nostr")]

+ 1 - 2
crates/cdk/src/wallet/test_utils.rs

@@ -349,11 +349,10 @@ impl MintConnector for MockMintConnector {
         unimplemented!()
     }
 
-    async fn post_melt_with_options(
+    async fn post_melt(
         &self,
         _method: &PaymentMethod,
         _request: MeltRequest<String>,
-        _options: crate::wallet::MeltOptions,
     ) -> Result<MeltQuoteBolt11Response<String>, Error> {
         unimplemented!()
     }