1//! A proxy that forwards data to another server and forwards that server's
2//! responses back to clients.
3//!
4//! Because the Tokio runtime uses a thread pool, each TCP connection is
5//! processed concurrently with all other TCP connections across multiple
6//! threads.
7//!
8//! You can showcase this by running this in one terminal:
9//!
10//! cargo run --example proxy
11//!
12//! This in another terminal
13//!
14//! cargo run --example echo
15//!
16//! And finally this in another terminal
17//!
18//! cargo run --example connect 127.0.0.1:8081
19//!
20//! This final terminal will connect to our proxy, which will in turn connect to
21//! the echo server, and you'll be able to see data flowing between them.
22
23#![warn(rust_2018_idioms)]
24
25use tokio::io::copy_bidirectional;
26use tokio::net::{TcpListener, TcpStream};
27
28use futures::FutureExt;
29use std::env;
30use std::error::Error;
31
32#[tokio::main]
33async fn main() -> Result<(), Box<dyn Error>> {
34 let listen_addr = env::args()
35 .nth(1)
36 .unwrap_or_else(|| "127.0.0.1:8081".to_string());
37 let server_addr = env::args()
38 .nth(2)
39 .unwrap_or_else(|| "127.0.0.1:8080".to_string());
40
41 println!("Listening on: {}", listen_addr);
42 println!("Proxying to: {}", server_addr);
43
44 let listener = TcpListener::bind(listen_addr).await?;
45
46 while let Ok((mut inbound, _)) = listener.accept().await {
47 let mut outbound = TcpStream::connect(server_addr.clone()).await?;
48
49 tokio::spawn(async move {
50 copy_bidirectional(&mut inbound, &mut outbound)
51 .map(|r| {
52 if let Err(e) = r {
53 println!("Failed to transfer; error={}", e);
54 }
55 })
56 .await
57 });
58 }
59
60 Ok(())
61}
62