@@ -52,30 +52,50 @@ impl App {
5252 . danger_accept_invalid_certs ( self . config . danger_accept_invalid_certs )
5353 . build ( )
5454 . with_context ( || "Failed to build reqwest http client" ) ?;
55- let _ = client
56- . post ( req . url . as_str ( ) )
57- . body ( req . body )
58- . header ( "Content-Type" , "text/plain" )
59- . send ( )
60- . await
61- . with_context ( || "HTTP request failed" ) ?;
55+ let ohttp_config =
56+ payjoin :: bitcoin :: base64 :: decode_config ( & self . config . ohttp_config , base64 :: URL_SAFE )
57+ . unwrap ( ) ;
58+ let ( ohttp_req , _ ) =
59+ payjoin :: v2 :: ohttp_encapsulate ( ohttp_config , "POST" , req . url . as_str ( ) , Some ( & req . body ) ) ;
60+ dbg ! ( & ohttp_req ) ;
61+ let _ = client . post ( & self . config . ohttp_proxy ) . body ( ohttp_req ) . send ( ) . await ?;
6262
6363 log:: debug!( "Awaiting response" ) ;
64- let res = Self :: long_poll ( & client, req. url . as_str ( ) ) . await ?;
64+ let res = self . long_poll ( & client, req. url . as_str ( ) ) . await ?;
6565 let mut res = std:: io:: Cursor :: new ( & res) ;
6666 self . process_pj_response ( ctx, & mut res) ?;
6767 Ok ( ( ) )
6868 }
6969
7070 #[ cfg( feature = "v2" ) ]
71- async fn long_poll ( client : & reqwest:: Client , url : & str ) -> Result < Vec < u8 > , reqwest:: Error > {
71+ async fn long_poll (
72+ & self ,
73+ client : & reqwest:: Client ,
74+ url : & str ,
75+ ) -> Result < Vec < u8 > , reqwest:: Error > {
7276 loop {
73- let response = client. get ( url) . send ( ) . await ?;
74-
75- if response. status ( ) . is_success ( ) {
76- let body = response. bytes ( ) . await ?;
77+ let req = client. get ( url) . build ( ) ?;
78+ let ohttp_config = payjoin:: bitcoin:: base64:: decode_config (
79+ & self . config . ohttp_config ,
80+ payjoin:: bitcoin:: base64:: URL_SAFE ,
81+ )
82+ . unwrap ( ) ;
83+ let body = req. body ( ) . and_then ( |b| b. as_bytes ( ) ) ;
84+ let ( ohttp_req, ctx) = payjoin:: v2:: ohttp_encapsulate (
85+ ohttp_config,
86+ req. method ( ) . as_str ( ) ,
87+ req. url ( ) . as_str ( ) ,
88+ body,
89+ ) ;
90+
91+ let ohttp_response =
92+ client. post ( & self . config . ohttp_proxy ) . body ( ohttp_req) . send ( ) . await ?;
93+ log:: debug!( "Response: {:?}" , ohttp_response) ;
94+ if ohttp_response. status ( ) . is_success ( ) {
95+ let body = ohttp_response. bytes ( ) . await ?;
7796 if !body. is_empty ( ) {
78- return Ok ( body. to_vec ( ) ) ;
97+ let res_body = payjoin:: v2:: ohttp_decapsulate ( ctx, & body) ;
98+ return Ok ( res_body) ;
7999 } else {
80100 log:: info!( "No response yet for payjoin request, retrying in 5 seconds" ) ;
81101 }
@@ -215,7 +235,7 @@ impl App {
215235 . with_context ( || "Failed to build reqwest http client" ) ?;
216236 log:: debug!( "Awaiting request" ) ;
217237 let receive_endpoint = format ! ( "{}/{}" , self . config. pj_endpoint, context. receive_subdir( ) ) ;
218- let mut buffer = Self :: long_poll ( & client, & receive_endpoint) . await ?;
238+ let mut buffer = self . long_poll ( & client, & receive_endpoint) . await ?;
219239
220240 log:: debug!( "Received request" ) ;
221241 let proposal = context
@@ -226,9 +246,14 @@ impl App {
226246 . map_err ( |e| anyhow ! ( "Failed to process UncheckedProposal {}" , e) ) ?;
227247
228248 let body = payjoin_proposal. serialize_body ( ) ;
249+ let ohttp_config =
250+ payjoin:: bitcoin:: base64:: decode_config ( & self . config . ohttp_config , base64:: URL_SAFE )
251+ . unwrap ( ) ;
252+ let ( req, _) =
253+ payjoin:: v2:: ohttp_encapsulate ( ohttp_config, "POST" , & receive_endpoint, Some ( & body) ) ;
229254 let _ = client
230- . post ( receive_endpoint )
231- . body ( body )
255+ . post ( & self . config . ohttp_proxy )
256+ . body ( req )
232257 . send ( )
233258 . await
234259 . with_context ( || "HTTP request failed" ) ?;
@@ -268,14 +293,15 @@ impl App {
268293 let amount = Amount :: from_sat ( amount_arg. parse ( ) ?) ;
269294 //let subdir = self.config.pj_endpoint + pubkey.map_or(&String::from(""), |s| &format!("/{}", s));
270295 let pj_uri_string = format ! (
271- "{}?amount={}&pj={}" ,
296+ "{}?amount={}&pj={}&ohttp={} " ,
272297 pj_receiver_address. to_qr_uri( ) ,
273298 amount. to_btc( ) ,
274299 format!(
275300 "{}{}" ,
276301 self . config. pj_endpoint,
277302 pubkey. map_or( String :: from( "" ) , |s| format!( "/{}" , s) )
278- )
303+ ) ,
304+ self . config. ohttp_config,
279305 ) ;
280306
281307 // check validity
@@ -459,6 +485,19 @@ impl App {
459485 }
460486}
461487
488+ fn serialize_request_to_bytes ( req : reqwest:: Request ) -> Vec < u8 > {
489+ let mut serialized_request =
490+ format ! ( "{} {} HTTP/1.1\r \n " , req. method( ) , req. url( ) ) . into_bytes ( ) ;
491+
492+ for ( name, value) in req. headers ( ) . iter ( ) {
493+ let header_line = format ! ( "{}: {}\r \n " , name. as_str( ) , value. to_str( ) . unwrap( ) ) ;
494+ serialized_request. extend ( header_line. as_bytes ( ) ) ;
495+ }
496+
497+ serialized_request. extend ( b"\r \n " ) ;
498+ serialized_request
499+ }
500+
462501struct SeenInputs {
463502 set : OutPointSet ,
464503 file : std:: fs:: File ,
@@ -498,6 +537,8 @@ pub(crate) struct AppConfig {
498537 pub bitcoind_cookie : Option < String > ,
499538 pub bitcoind_rpcuser : String ,
500539 pub bitcoind_rpcpass : String ,
540+ pub ohttp_config : String ,
541+ pub ohttp_proxy : String ,
501542
502543 // send-only
503544 pub danger_accept_invalid_certs : bool ,
@@ -531,6 +572,16 @@ impl AppConfig {
531572 "bitcoind_rpcpass" ,
532573 matches. get_one :: < String > ( "rpcpass" ) . map ( |s| s. as_str ( ) ) ,
533574 ) ?
575+ . set_default ( "ohttp_config" , "" ) ?
576+ . set_override_option (
577+ "ohttp_config" ,
578+ matches. get_one :: < String > ( "ohttp_config" ) . map ( |s| s. as_str ( ) ) ,
579+ ) ?
580+ . set_default ( "ohttp_proxy" , "" ) ?
581+ . set_override_option (
582+ "ohttp_proxy" ,
583+ matches. get_one :: < String > ( "ohttp_proxy" ) . map ( |s| s. as_str ( ) ) ,
584+ ) ?
534585 // Subcommand defaults without which file serialization fails.
535586 . set_default ( "danger_accept_invalid_certs" , false ) ?
536587 . set_default ( "pj_host" , "0.0.0.0:3000" ) ?
0 commit comments