wip: debug proxy now owns a filter which can drop or modify request bodies
							parent
							
								
									a980d151fc
								
							
						
					
					
						commit
						9648a9853c
					
				|  | @ -165,9 +165,9 @@ checksum = "e91831deabf0d6d7ec49552e489aed63b7456a7a3c46cff62adad428110b0af0" | |||
| 
 | ||||
| [[package]] | ||||
| name = "async-trait" | ||||
| version = "0.1.42" | ||||
| version = "0.1.45" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "8d3a45e77e34375a7923b1e8febb049bb011f064714a8e17a1a616fef01da13d" | ||||
| checksum = "d3340571769500ddef1e94b45055fabed6b08a881269b7570c830b8f32ef84ef" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  | @ -468,6 +468,7 @@ name = "debug_proxy" | |||
| version = "0.1.0" | ||||
| dependencies = [ | ||||
|  "anyhow", | ||||
|  "async-trait", | ||||
|  "futures-util", | ||||
|  "http", | ||||
|  "hyper", | ||||
|  |  | |||
|  | @ -9,6 +9,7 @@ license = "AGPL-3.0" | |||
| [dependencies] | ||||
| 
 | ||||
| anyhow = "1.0.34" | ||||
| async-trait = "0.1.45" | ||||
| futures-util = "0.3.8" | ||||
| http = "0.2.1" | ||||
| hyper = { version = "0.14.4", features = ["server", "stream"] } | ||||
|  |  | |||
|  | @ -3,12 +3,14 @@ use std::{ | |||
| 	sync::Arc, | ||||
| }; | ||||
| 
 | ||||
| use async_trait::async_trait; | ||||
| use futures_util::StreamExt; | ||||
| use hyper::{ | ||||
| 	Body, | ||||
| 	Request, | ||||
| 	Response, | ||||
| 	Server, | ||||
| 	body::Bytes, | ||||
| 	service::{ | ||||
| 		make_service_fn, | ||||
| 		service_fn, | ||||
|  | @ -25,18 +27,77 @@ use tokio::{ | |||
| use tokio_stream::wrappers::ReceiverStream; | ||||
| use ulid::Ulid; | ||||
| 
 | ||||
| struct State { | ||||
| 	client: Client, | ||||
| 	upstream_authority: String, | ||||
| #[async_trait] | ||||
| trait ProxyFilter { | ||||
| 	async fn request_body (&self, mut body: Body, tx: mpsc::Sender <Result <Bytes, hyper::Error>>) -> anyhow::Result <()>; | ||||
| } | ||||
| 
 | ||||
| async fn handle_all (req: Request <Body>, state: Arc <State>) | ||||
| struct PassthroughFilter {} | ||||
| 
 | ||||
| #[async_trait] | ||||
| impl ProxyFilter for PassthroughFilter { | ||||
| 	async fn request_body (&self, mut body: Body, tx: mpsc::Sender <Result <Bytes, hyper::Error>>) -> anyhow::Result <()> { | ||||
| 		let mut bytes_transferred = 0; | ||||
| 		
 | ||||
| 		loop { | ||||
| 			let item = body.next ().await; | ||||
| 			
 | ||||
| 			if let Some (item) = item  { | ||||
| 				if let Ok (item) = &item { | ||||
| 					bytes_transferred += item.len (); | ||||
| 				} | ||||
| 				tx.send (item).await?; | ||||
| 			} | ||||
| 			else { | ||||
| 				// Finished
 | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 		
 | ||||
| 		Ok (()) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| struct RequestBodyDropFilter {} | ||||
| 
 | ||||
| #[async_trait] | ||||
| impl ProxyFilter for RequestBodyDropFilter { | ||||
| 	async fn request_body (&self, mut body: Body, tx: mpsc::Sender <Result <Bytes, hyper::Error>>) -> anyhow::Result <()> { | ||||
| 		let mut bytes_transferred = 0; | ||||
| 		
 | ||||
| 		loop { | ||||
| 			let item = body.next ().await; | ||||
| 			
 | ||||
| 			if let Some (item) = item  { | ||||
| 				if let Ok (item) = &item { | ||||
| 					bytes_transferred += item.len (); | ||||
| 				} | ||||
| 				// tx.send (item).await?;
 | ||||
| 				tracing::debug! ("RequestBodyDropFilter dropping chunk"); | ||||
| 			} | ||||
| 			else { | ||||
| 				// Finished
 | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 		
 | ||||
| 		Ok (()) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| struct State <PF> { | ||||
| 	client: Client, | ||||
| 	upstream_authority: String, | ||||
| 	proxy_filter: Arc <PF>, | ||||
| } | ||||
| 
 | ||||
| async fn handle_all <PF: 'static + ProxyFilter + Sync + Send> (req: Request <Body>, state: Arc <State <PF>>) | ||||
| -> anyhow::Result <Response <Body>> | ||||
| { | ||||
| 	let req_id = Ulid::new ().to_string (); | ||||
| 	let (head, mut body) = req.into_parts (); | ||||
| 	
 | ||||
| 	tracing::trace! ("{} Got URI {}", req_id, head.uri); | ||||
| 	tracing::debug! ("{} Got URI {}", req_id, head.uri); | ||||
| 	
 | ||||
| 	let upstream_authority = state.upstream_authority.clone (); | ||||
| 	
 | ||||
|  | @ -55,27 +116,10 @@ async fn handle_all (req: Request <Body>, state: Arc <State>) | |||
| 	let (tx, rx) = mpsc::channel (1); | ||||
| 	spawn ({ | ||||
| 		let req_id = req_id.clone (); | ||||
| 		let proxy_filter = state.proxy_filter.clone (); | ||||
| 		
 | ||||
| 		async move { | ||||
| 			let mut bytes_transferred = 0; | ||||
| 			
 | ||||
| 			loop { | ||||
| 				let item = body.next ().await; | ||||
| 				
 | ||||
| 				if let Some (item) = item  { | ||||
| 					if let Ok (item) = &item { | ||||
| 						bytes_transferred += item.len (); | ||||
| 					} | ||||
| 					tx.send (item).await?; | ||||
| 				} | ||||
| 				else { | ||||
| 					// Finished
 | ||||
| 					break; | ||||
| 				} | ||||
| 			} | ||||
| 			
 | ||||
| 			tracing::trace! ("{} Request body bytes: {}", req_id, bytes_transferred); | ||||
| 			
 | ||||
| 			Ok::<_, anyhow::Error> (()) | ||||
| 			proxy_filter.request_body (body, tx).await | ||||
| 		} | ||||
| 	}); | ||||
| 	
 | ||||
|  | @ -122,9 +166,13 @@ pub async fn run_proxy ( | |||
| 	shutdown_oneshot: oneshot::Receiver <()>, | ||||
| ) -> anyhow::Result <()> 
 | ||||
| { | ||||
| 	let filter = PassthroughFilter {}; | ||||
| 	// let filter = RequestBodyDropFilter {};
 | ||||
| 	
 | ||||
| 	let state = Arc::new (State { | ||||
| 		client: Client::builder ().build ()?, | ||||
| 		upstream_authority, | ||||
| 		proxy_filter: Arc::new (filter), | ||||
| 	}); | ||||
| 	
 | ||||
| 	let make_svc = make_service_fn (|_conn| { | ||||
|  |  | |||
|  | @ -106,9 +106,8 @@ async fn handle_one_req ( | |||
| 			if e.is_request () { | ||||
| 				warn! ("Error while POSTing response. Client probably hung up."); | ||||
| 			} | ||||
| 			else { | ||||
| 			
 | ||||
| 			error! ("Err: {:?}", e); | ||||
| 			} | ||||
| 		}, | ||||
| 	} | ||||
| 	
 | ||||
|  |  | |||
							
								
								
									
										33
									
								
								src/tests.rs
								
								
								
								
							
							
						
						
									
										33
									
								
								src/tests.rs
								
								
								
								
							|  | @ -9,7 +9,6 @@ use std::{ | |||
| }; | ||||
| 
 | ||||
| use tokio::{ | ||||
| 	runtime::Runtime, | ||||
| 	spawn, | ||||
| 	sync::oneshot, | ||||
| }; | ||||
|  | @ -42,8 +41,9 @@ async fn testing_client_checks ( | |||
| 	
 | ||||
| 	assert_eq! (resp, "Relay is up\n"); | ||||
| 	
 | ||||
| 	let resp = client.get (&format! ("{}/frontend/servers/{}/files/COPYING", relay_url, server_name)) | ||||
| 	.send ().await.expect ("Couldn't find license").bytes ().await.expect ("Couldn't find license"); | ||||
| 	let req = client.get (&format! ("{}/frontend/servers/{}/files/COPYING", relay_url, server_name)) | ||||
| 	.send (); | ||||
| 	let resp = tokio::time::timeout (Duration::from_secs (2), req).await.expect ("Request timed out").expect ("Couldn't find license").bytes ().await.expect ("Couldn't find license"); | ||||
| 	
 | ||||
| 	if blake3::hash (&resp) != blake3::Hash::from ([ | ||||
| 		0xca, 0x02, 0x92, 0x78, | ||||
|  | @ -174,16 +174,13 @@ impl TestingServer { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| #[test] | ||||
| fn end_to_end () { | ||||
| #[tokio::test] | ||||
| async fn end_to_end () { | ||||
| 	// Prefer this form for tests, since all tests share one process
 | ||||
| 	// and we don't care if another test already installed a subscriber.
 | ||||
| 	
 | ||||
| 	//tracing_subscriber::fmt ().try_init ().ok ();
 | ||||
| 	let rt = Runtime::new ().expect ("Can't create runtime for testing"); | ||||
| 	
 | ||||
| 	// Spawn the root task
 | ||||
| 	rt.block_on (async { | ||||
| 	let relay_port = 4000; | ||||
| 	// No proxy
 | ||||
| 	let proxy_port = relay_port; | ||||
|  | @ -214,15 +211,14 @@ fn end_to_end () { | |||
| 	
 | ||||
| 	testing_server.graceful_shutdown ().await; | ||||
| 	testing_relay.graceful_shutdown ().await; | ||||
| 	}); | ||||
| } | ||||
| 
 | ||||
| #[test] | ||||
| fn debug_proxy () { | ||||
| 	tracing_subscriber::fmt ().try_init ().ok (); | ||||
| 	let rt = Runtime::new ().expect ("Can't create runtime for testing"); | ||||
| #[tokio::test] | ||||
| async fn debug_proxy () { | ||||
| 	tracing_subscriber::fmt () | ||||
| 	.with_env_filter (tracing_subscriber::EnvFilter::from_default_env ()) | ||||
| 	.try_init ().ok (); | ||||
| 	
 | ||||
| 	rt.block_on (async { | ||||
| 	let relay_port = 4002; | ||||
| 	let proxy_port = 11510; | ||||
| 	
 | ||||
|  | @ -271,14 +267,10 @@ fn debug_proxy () { | |||
| 	info! ("Proxy stopped"); | ||||
| 	
 | ||||
| 	testing_relay.graceful_shutdown ().await; | ||||
| 	}); | ||||
| } | ||||
| 
 | ||||
| #[test] | ||||
| fn scraper_endpoints () { | ||||
| 	let rt = Runtime::new ().expect ("Can't create runtime for testing"); | ||||
| 	
 | ||||
| 	rt.block_on (async { | ||||
| #[tokio::test] | ||||
| async fn scraper_endpoints () { | ||||
| 	use ptth_relay::*; | ||||
| 	
 | ||||
| 	let config_file = config::file::Config { | ||||
|  | @ -341,5 +333,4 @@ fn scraper_endpoints () { | |||
| 	
 | ||||
| 	stop_relay_tx.send (()).expect ("Couldn't shut down relay"); | ||||
| 	task_relay.await.expect ("Couldn't join relay").expect ("Relay error"); | ||||
| 	}); | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 _
						_