@@ -9,9 +9,11 @@ use bitcoincore_rpc::jsonrpc::serde_json;
99use bitcoincore_rpc:: RpcApi ;
1010use clap:: ArgMatches ;
1111use config:: { Config , File , FileFormat } ;
12+ use ohttp:: ClientResponse ;
1213use payjoin:: bitcoin:: psbt:: Psbt ;
1314use payjoin:: receive:: { Error , PayjoinProposal , UncheckedProposal } ;
1415use payjoin:: { bitcoin, PjUriExt , UriExt } ;
16+ use reqwest:: Request ;
1517#[ cfg( not( feature = "v2" ) ) ]
1618use rouille:: { Request , Response } ;
1719use serde:: { Deserialize , Serialize } ;
@@ -50,30 +52,44 @@ impl App {
5052 . danger_accept_invalid_certs ( self . config . danger_accept_invalid_certs )
5153 . build ( )
5254 . with_context ( || "Failed to build reqwest http client" ) ?;
53- let _ = client
55+ dbg ! ( & req. body) ;
56+ let pre_req = client
5457 . post ( req. url . as_str ( ) )
5558 . body ( req. body )
5659 . header ( "Content-Type" , "text/plain" )
57- . send ( )
58- . await
59- . with_context ( || "HTTP request failed" ) ?;
60+ . build ( )
61+ . with_context ( || "Failed to build HTTP request" ) ?;
62+ let ( ohttp_req, _) = self . ohttp_encapsulate_req ( pre_req) ;
63+ dbg ! ( & ohttp_req) ;
64+ let _ = client. post ( & self . config . ohttp_proxy ) . body ( ohttp_req) . send ( ) . await ?;
6065
6166 log:: debug!( "Awaiting response" ) ;
62- let res = Self :: long_poll ( & client, req. url . as_str ( ) ) . await ?;
67+ let res = self . long_poll ( & client, req. url . as_str ( ) ) . await ?;
6368 let mut res = std:: io:: Cursor :: new ( & res) ;
6469 self . process_pj_response ( ctx, & mut res) ?;
6570 Ok ( ( ) )
6671 }
6772
6873 #[ cfg( feature = "v2" ) ]
69- async fn long_poll ( client : & reqwest:: Client , url : & str ) -> Result < Vec < u8 > , reqwest:: Error > {
74+ async fn long_poll (
75+ & self ,
76+ client : & reqwest:: Client ,
77+ url : & str ,
78+ ) -> Result < Vec < u8 > , reqwest:: Error > {
7079 loop {
71- let response = client. get ( url) . send ( ) . await ?;
72-
73- if response. status ( ) . is_success ( ) {
74- let body = response. bytes ( ) . await ?;
80+ let req = client. get ( url) . build ( ) ?;
81+ let ( ohttp_req, ctx) = self . ohttp_encapsulate_req ( req) ;
82+
83+ let ohttp_response =
84+ client. post ( & self . config . ohttp_proxy ) . body ( ohttp_req) . send ( ) . await ?;
85+ log:: debug!( "Response: {:?}" , ohttp_response) ;
86+ if ohttp_response. status ( ) . is_success ( ) {
87+ let body = ohttp_response. bytes ( ) . await ?;
7588 if !body. is_empty ( ) {
76- return Ok ( body. to_vec ( ) ) ;
89+ let bhttp_response = ctx. decapsulate ( & body) . unwrap ( ) ;
90+ let mut r = std:: io:: Cursor :: new ( bhttp_response) ;
91+ let response = bhttp:: Message :: read_bhttp ( & mut r) . unwrap ( ) ;
92+ return Ok ( response. content ( ) . to_vec ( ) ) ;
7793 } else {
7894 log:: info!( "No response yet for payjoin request, retrying in 5 seconds" ) ;
7995 }
@@ -83,6 +99,33 @@ impl App {
8399 }
84100 }
85101
102+ fn ohttp_encapsulate_req ( & self , req : Request ) -> ( Vec < u8 > , ClientResponse ) {
103+ let ohttp_config = payjoin:: bitcoin:: base64:: decode_config (
104+ & self . config . ohttp_config ,
105+ payjoin:: bitcoin:: base64:: URL_SAFE ,
106+ )
107+ . unwrap ( ) ;
108+ let ctx = ohttp:: ClientRequest :: from_encoded_config ( & ohttp_config) . unwrap ( ) ;
109+
110+ let mut bhttp_message = bhttp:: Message :: request (
111+ req. method ( ) . as_str ( ) . as_bytes ( ) . to_vec ( ) ,
112+ req. url ( ) . scheme ( ) . as_bytes ( ) . to_vec ( ) ,
113+ req. url ( ) . authority ( ) . as_bytes ( ) . to_vec ( ) ,
114+ req. url ( ) . path ( ) . as_bytes ( ) . to_vec ( ) ,
115+ ) ;
116+ match req. body ( ) {
117+ Some ( body) => {
118+ bhttp_message. write_content ( body. as_bytes ( ) . unwrap ( ) ) ;
119+ }
120+ None => ( ) ,
121+ }
122+ // let req = serialize_request_to_bytes(req);
123+ // let http_message = bhttp::Message::read_http(&mut std::io::Cursor::new(&req)).unwrap();
124+ let mut bhttp_req = Vec :: new ( ) ;
125+ let _ = bhttp_message. write_bhttp ( bhttp:: Mode :: KnownLength , & mut bhttp_req) ;
126+ ctx. encapsulate ( & bhttp_req) . unwrap ( )
127+ }
128+
86129 #[ cfg( not( feature = "v2" ) ) ]
87130 pub fn send_payjoin ( & self , bip21 : & str ) -> Result < ( ) > {
88131 let ( req, ctx) = self . create_pj_request ( bip21) ?;
@@ -102,6 +145,23 @@ impl App {
102145 Ok ( ( ) )
103146 }
104147
148+ // fn create_v2_pj_request(&self, bip21: &str,
149+ // ) -> Result<(payjoin::send::Request, payjoin::send::Context)> {
150+ // let (req, ctx) = self.create_pj_request(bip21)?;
151+ // let config = base64::decode(&self.config.ohttp_config)?;
152+ // let req_ctx = ohttp::ClientRequest::from_encoded_config(&config)
153+ // .with_context(|| "Failed to decode ohttp config")?;
154+ // let (enc_req, req_ctx) = req_ctx.encapsulate(&req.body).with_context(|| "Failed to encapsulate request")?;
155+
156+ // Ok((payjoin::send::Request {
157+ // url: req.url,
158+ // body: enc_req,
159+ // }, payjoin::send::Context {
160+ // ohttp_ctx: req_ctx,
161+ // ..ctx
162+ // }))
163+ // }
164+
105165 fn create_pj_request (
106166 & self ,
107167 bip21 : & str ,
@@ -221,7 +281,7 @@ impl App {
221281 . with_context ( || "Failed to build reqwest http client" ) ?;
222282 log:: debug!( "Awaiting request" ) ;
223283 let receive_endpoint = format ! ( "{}/{}" , self . config. pj_endpoint, context. receive_subdir( ) ) ;
224- let mut buffer = Self :: long_poll ( & client, & receive_endpoint) . await ?;
284+ let mut buffer = self . long_poll ( & client, & receive_endpoint) . await ?;
225285
226286 log:: debug!( "Received request" ) ;
227287 let ( proposal, e) = context
@@ -232,9 +292,15 @@ impl App {
232292 . map_err ( |e| anyhow ! ( "Failed to process UncheckedProposal {}" , e) ) ?;
233293 let mut payjoin_bytes = payjoin_psbt. serialize ( ) ;
234294 let payjoin = payjoin:: v2:: encrypt_message_b ( & mut payjoin_bytes, e) ;
235- let _ = client
295+ let req = client
236296 . post ( receive_endpoint)
237297 . body ( payjoin)
298+ . build ( )
299+ . with_context ( || "Failed to build HTTP request" ) ?;
300+ let ( req, _) = self . ohttp_encapsulate_req ( req) ;
301+ let _ = client
302+ . post ( & self . config . ohttp_proxy )
303+ . body ( req)
238304 . send ( )
239305 . await
240306 . with_context ( || "HTTP request failed" ) ?;
@@ -274,14 +340,15 @@ impl App {
274340 let amount = Amount :: from_sat ( amount_arg. parse ( ) ?) ;
275341 //let subdir = self.config.pj_endpoint + pubkey.map_or(&String::from(""), |s| &format!("/{}", s));
276342 let pj_uri_string = format ! (
277- "{}?amount={}&pj={}" ,
343+ "{}?amount={}&pj={}&ohttp={} " ,
278344 pj_receiver_address. to_qr_uri( ) ,
279345 amount. to_btc( ) ,
280346 format!(
281347 "{}{}" ,
282348 self . config. pj_endpoint,
283349 pubkey. map_or( String :: from( "" ) , |s| format!( "/{}" , s) )
284- )
350+ ) ,
351+ self . config. ohttp_config,
285352 ) ;
286353 let pj_uri = payjoin:: Uri :: from_str ( & pj_uri_string)
287354 . map_err ( |e| anyhow ! ( "Constructed a bad URI string from args: {}" , e) ) ?;
@@ -465,6 +532,19 @@ impl App {
465532 }
466533}
467534
535+ fn serialize_request_to_bytes ( req : reqwest:: Request ) -> Vec < u8 > {
536+ let mut serialized_request =
537+ format ! ( "{} {} HTTP/1.1\r \n " , req. method( ) , req. url( ) ) . into_bytes ( ) ;
538+
539+ for ( name, value) in req. headers ( ) . iter ( ) {
540+ let header_line = format ! ( "{}: {}\r \n " , name. as_str( ) , value. to_str( ) . unwrap( ) ) ;
541+ serialized_request. extend ( header_line. as_bytes ( ) ) ;
542+ }
543+
544+ serialized_request. extend ( b"\r \n " ) ;
545+ serialized_request
546+ }
547+
468548struct SeenInputs {
469549 set : OutPointSet ,
470550 file : std:: fs:: File ,
@@ -504,6 +584,8 @@ pub(crate) struct AppConfig {
504584 pub bitcoind_cookie : Option < String > ,
505585 pub bitcoind_rpcuser : String ,
506586 pub bitcoind_rpcpass : String ,
587+ pub ohttp_config : String ,
588+ pub ohttp_proxy : String ,
507589
508590 // send-only
509591 pub danger_accept_invalid_certs : bool ,
@@ -537,6 +619,16 @@ impl AppConfig {
537619 "bitcoind_rpcpass" ,
538620 matches. get_one :: < String > ( "rpcpass" ) . map ( |s| s. as_str ( ) ) ,
539621 ) ?
622+ . set_default ( "ohttp_config" , "" ) ?
623+ . set_override_option (
624+ "ohttp_config" ,
625+ matches. get_one :: < String > ( "ohttp_config" ) . map ( |s| s. as_str ( ) ) ,
626+ ) ?
627+ . set_default ( "ohttp_proxy" , "" ) ?
628+ . set_override_option (
629+ "ohttp_proxy" ,
630+ matches. get_one :: < String > ( "ohttp_proxy" ) . map ( |s| s. as_str ( ) ) ,
631+ ) ?
540632 // Subcommand defaults without which file serialization fails.
541633 . set_default ( "danger_accept_invalid_certs" , false ) ?
542634 . set_default ( "pj_host" , "0.0.0.0:3000" ) ?
0 commit comments