|
@@ -4,48 +4,93 @@ use rand::Rng;
|
|
|
use std::time::Duration;
|
|
use std::time::Duration;
|
|
|
use tokio::{
|
|
use tokio::{
|
|
|
io::{AsyncReadExt, AsyncWriteExt},
|
|
io::{AsyncReadExt, AsyncWriteExt},
|
|
|
- net::TcpStream,
|
|
|
|
|
|
|
+ net::{TcpStream, UdpSocket},
|
|
|
sync::broadcast,
|
|
sync::broadcast,
|
|
|
time,
|
|
time,
|
|
|
};
|
|
};
|
|
|
|
|
+use tracing::{debug, info, instrument};
|
|
|
|
|
+use tracing_subscriber::EnvFilter;
|
|
|
|
|
|
|
|
use crate::common::run_rathole_server;
|
|
use crate::common::run_rathole_server;
|
|
|
|
|
|
|
|
mod common;
|
|
mod common;
|
|
|
|
|
|
|
|
-const ECHO_SERVER_ADDR: &str = "localhost:8080";
|
|
|
|
|
-const PINGPONG_SERVER_ADDR: &str = "localhost:8081";
|
|
|
|
|
-const ECHO_SERVER_ADDR_EXPOSED: &str = "localhost:2334";
|
|
|
|
|
-const PINGPONG_SERVER_ADDR_EXPOSED: &str = "localhost:2335";
|
|
|
|
|
|
|
+const ECHO_SERVER_ADDR: &str = "0.0.0.0:8080";
|
|
|
|
|
+const PINGPONG_SERVER_ADDR: &str = "0.0.0.0:8081";
|
|
|
|
|
+const ECHO_SERVER_ADDR_EXPOSED: &str = "0.0.0.0:2334";
|
|
|
|
|
+const PINGPONG_SERVER_ADDR_EXPOSED: &str = "0.0.0.0:2335";
|
|
|
const HITTER_NUM: usize = 4;
|
|
const HITTER_NUM: usize = 4;
|
|
|
|
|
|
|
|
|
|
+#[derive(Clone, Copy, Debug)]
|
|
|
|
|
+enum Type {
|
|
|
|
|
+ Tcp,
|
|
|
|
|
+ Udp,
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+fn init() {
|
|
|
|
|
+ let level = "info";
|
|
|
|
|
+ let _ = tracing_subscriber::fmt()
|
|
|
|
|
+ .with_env_filter(
|
|
|
|
|
+ EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::from(level)),
|
|
|
|
|
+ )
|
|
|
|
|
+ .try_init();
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+#[tokio::test]
|
|
|
|
|
+async fn tcp() -> Result<()> {
|
|
|
|
|
+ init();
|
|
|
|
|
+
|
|
|
|
|
+ // Spawn a echo server
|
|
|
|
|
+ tokio::spawn(async move {
|
|
|
|
|
+ if let Err(e) = common::tcp::echo_server(ECHO_SERVER_ADDR).await {
|
|
|
|
|
+ panic!("Failed to run the echo server for testing: {:?}", e);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // Spawn a pingpong server
|
|
|
|
|
+ tokio::spawn(async move {
|
|
|
|
|
+ if let Err(e) = common::tcp::pingpong_server(PINGPONG_SERVER_ADDR).await {
|
|
|
|
|
+ panic!("Failed to run the pingpong server for testing: {:?}", e);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ test("tests/for_tcp/tcp_transport.toml", Type::Tcp).await?;
|
|
|
|
|
+ test("tests/for_tcp/tls_transport.toml", Type::Tcp).await?;
|
|
|
|
|
+
|
|
|
|
|
+ Ok(())
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
#[tokio::test]
|
|
#[tokio::test]
|
|
|
-async fn main() -> Result<()> {
|
|
|
|
|
|
|
+async fn udp() -> Result<()> {
|
|
|
|
|
+ init();
|
|
|
|
|
+
|
|
|
// Spawn a echo server
|
|
// Spawn a echo server
|
|
|
tokio::spawn(async move {
|
|
tokio::spawn(async move {
|
|
|
- if let Err(e) = common::echo_server(ECHO_SERVER_ADDR).await {
|
|
|
|
|
|
|
+ if let Err(e) = common::udp::echo_server(ECHO_SERVER_ADDR).await {
|
|
|
panic!("Failed to run the echo server for testing: {:?}", e);
|
|
panic!("Failed to run the echo server for testing: {:?}", e);
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
// Spawn a pingpong server
|
|
// Spawn a pingpong server
|
|
|
tokio::spawn(async move {
|
|
tokio::spawn(async move {
|
|
|
- if let Err(e) = common::pingpong_server(PINGPONG_SERVER_ADDR).await {
|
|
|
|
|
|
|
+ if let Err(e) = common::udp::pingpong_server(PINGPONG_SERVER_ADDR).await {
|
|
|
panic!("Failed to run the pingpong server for testing: {:?}", e);
|
|
panic!("Failed to run the pingpong server for testing: {:?}", e);
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
- test("tests/tcp_transport.toml").await?;
|
|
|
|
|
- test("tests/tls_transport.toml").await?;
|
|
|
|
|
|
|
+ test("tests/for_udp/tcp_transport.toml", Type::Udp).await?;
|
|
|
|
|
+ test("tests/for_udp/tls_transport.toml", Type::Udp).await?;
|
|
|
|
|
|
|
|
Ok(())
|
|
Ok(())
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-async fn test(config_path: &'static str) -> Result<()> {
|
|
|
|
|
|
|
+#[instrument]
|
|
|
|
|
+async fn test(config_path: &'static str, t: Type) -> Result<()> {
|
|
|
let (client_shutdown_tx, client_shutdown_rx) = broadcast::channel(1);
|
|
let (client_shutdown_tx, client_shutdown_rx) = broadcast::channel(1);
|
|
|
let (server_shutdown_tx, server_shutdown_rx) = broadcast::channel(1);
|
|
let (server_shutdown_tx, server_shutdown_rx) = broadcast::channel(1);
|
|
|
|
|
|
|
|
// Start the client
|
|
// Start the client
|
|
|
|
|
+ info!("start the client");
|
|
|
tokio::spawn(async move {
|
|
tokio::spawn(async move {
|
|
|
run_rathole_client(&config_path, client_shutdown_rx)
|
|
run_rathole_client(&config_path, client_shutdown_rx)
|
|
|
.await
|
|
.await
|
|
@@ -56,6 +101,7 @@ async fn test(config_path: &'static str) -> Result<()> {
|
|
|
time::sleep(Duration::from_secs(1)).await;
|
|
time::sleep(Duration::from_secs(1)).await;
|
|
|
|
|
|
|
|
// Start the server
|
|
// Start the server
|
|
|
|
|
+ info!("start the server");
|
|
|
tokio::spawn(async move {
|
|
tokio::spawn(async move {
|
|
|
run_rathole_server(&config_path, server_shutdown_rx)
|
|
run_rathole_server(&config_path, server_shutdown_rx)
|
|
|
.await
|
|
.await
|
|
@@ -63,27 +109,42 @@ async fn test(config_path: &'static str) -> Result<()> {
|
|
|
});
|
|
});
|
|
|
time::sleep(Duration::from_secs(1)).await; // Wait for the client to retry
|
|
time::sleep(Duration::from_secs(1)).await; // Wait for the client to retry
|
|
|
|
|
|
|
|
- echo_hitter(ECHO_SERVER_ADDR_EXPOSED).await.unwrap();
|
|
|
|
|
- pingpong_hitter(PINGPONG_SERVER_ADDR_EXPOSED).await.unwrap();
|
|
|
|
|
|
|
+ info!("echo");
|
|
|
|
|
+ echo_hitter(ECHO_SERVER_ADDR_EXPOSED, t).await.unwrap();
|
|
|
|
|
+ info!("pingpong");
|
|
|
|
|
+ pingpong_hitter(PINGPONG_SERVER_ADDR_EXPOSED, t)
|
|
|
|
|
+ .await
|
|
|
|
|
+ .unwrap();
|
|
|
|
|
|
|
|
// Simulate the client crash and restart
|
|
// Simulate the client crash and restart
|
|
|
|
|
+ info!("shutdown the client");
|
|
|
client_shutdown_tx.send(true)?;
|
|
client_shutdown_tx.send(true)?;
|
|
|
time::sleep(Duration::from_millis(500)).await;
|
|
time::sleep(Duration::from_millis(500)).await;
|
|
|
|
|
+
|
|
|
|
|
+ info!("restart the client");
|
|
|
let client_shutdown_rx = client_shutdown_tx.subscribe();
|
|
let client_shutdown_rx = client_shutdown_tx.subscribe();
|
|
|
- tokio::spawn(async move {
|
|
|
|
|
|
|
+ let client = tokio::spawn(async move {
|
|
|
run_rathole_client(&config_path, client_shutdown_rx)
|
|
run_rathole_client(&config_path, client_shutdown_rx)
|
|
|
.await
|
|
.await
|
|
|
.unwrap();
|
|
.unwrap();
|
|
|
});
|
|
});
|
|
|
|
|
+ time::sleep(Duration::from_secs(1)).await; // Wait for the client to start
|
|
|
|
|
|
|
|
- echo_hitter(ECHO_SERVER_ADDR_EXPOSED).await.unwrap();
|
|
|
|
|
- pingpong_hitter(PINGPONG_SERVER_ADDR_EXPOSED).await.unwrap();
|
|
|
|
|
|
|
+ info!("echo");
|
|
|
|
|
+ echo_hitter(ECHO_SERVER_ADDR_EXPOSED, t).await.unwrap();
|
|
|
|
|
+ info!("pingpong");
|
|
|
|
|
+ pingpong_hitter(PINGPONG_SERVER_ADDR_EXPOSED, t)
|
|
|
|
|
+ .await
|
|
|
|
|
+ .unwrap();
|
|
|
|
|
|
|
|
// Simulate the server crash and restart
|
|
// Simulate the server crash and restart
|
|
|
|
|
+ info!("shutdown the server");
|
|
|
server_shutdown_tx.send(true)?;
|
|
server_shutdown_tx.send(true)?;
|
|
|
time::sleep(Duration::from_millis(500)).await;
|
|
time::sleep(Duration::from_millis(500)).await;
|
|
|
|
|
+
|
|
|
|
|
+ info!("restart the server");
|
|
|
let server_shutdown_rx = server_shutdown_tx.subscribe();
|
|
let server_shutdown_rx = server_shutdown_tx.subscribe();
|
|
|
- tokio::spawn(async move {
|
|
|
|
|
|
|
+ let server = tokio::spawn(async move {
|
|
|
run_rathole_server(&config_path, server_shutdown_rx)
|
|
run_rathole_server(&config_path, server_shutdown_rx)
|
|
|
.await
|
|
.await
|
|
|
.unwrap();
|
|
.unwrap();
|
|
@@ -91,24 +152,44 @@ async fn test(config_path: &'static str) -> Result<()> {
|
|
|
time::sleep(Duration::from_secs(1)).await; // Wait for the client to retry
|
|
time::sleep(Duration::from_secs(1)).await; // Wait for the client to retry
|
|
|
|
|
|
|
|
// Simulate heavy load
|
|
// Simulate heavy load
|
|
|
|
|
+ info!("lots of echo and pingpong");
|
|
|
for _ in 0..HITTER_NUM / 2 {
|
|
for _ in 0..HITTER_NUM / 2 {
|
|
|
tokio::spawn(async move {
|
|
tokio::spawn(async move {
|
|
|
- echo_hitter(ECHO_SERVER_ADDR_EXPOSED).await.unwrap();
|
|
|
|
|
|
|
+ echo_hitter(ECHO_SERVER_ADDR_EXPOSED, t).await.unwrap();
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
tokio::spawn(async move {
|
|
tokio::spawn(async move {
|
|
|
- pingpong_hitter(PINGPONG_SERVER_ADDR_EXPOSED).await.unwrap();
|
|
|
|
|
|
|
+ pingpong_hitter(PINGPONG_SERVER_ADDR_EXPOSED, t)
|
|
|
|
|
+ .await
|
|
|
|
|
+ .unwrap();
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Shutdown
|
|
// Shutdown
|
|
|
|
|
+ info!("shutdown the server and the client");
|
|
|
server_shutdown_tx.send(true)?;
|
|
server_shutdown_tx.send(true)?;
|
|
|
client_shutdown_tx.send(true)?;
|
|
client_shutdown_tx.send(true)?;
|
|
|
|
|
|
|
|
|
|
+ let _ = tokio::join!(server, client);
|
|
|
|
|
+
|
|
|
Ok(())
|
|
Ok(())
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-async fn echo_hitter(addr: &str) -> Result<()> {
|
|
|
|
|
|
|
+async fn echo_hitter(addr: &'static str, t: Type) -> Result<()> {
|
|
|
|
|
+ match t {
|
|
|
|
|
+ Type::Tcp => tcp_echo_hitter(addr).await,
|
|
|
|
|
+ Type::Udp => udp_echo_hitter(addr).await,
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+async fn pingpong_hitter(addr: &'static str, t: Type) -> Result<()> {
|
|
|
|
|
+ match t {
|
|
|
|
|
+ Type::Tcp => tcp_pingpong_hitter(addr).await,
|
|
|
|
|
+ Type::Udp => udp_pingpong_hitter(addr).await,
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+async fn tcp_echo_hitter(addr: &'static str) -> Result<()> {
|
|
|
let mut conn = TcpStream::connect(addr).await?;
|
|
let mut conn = TcpStream::connect(addr).await?;
|
|
|
|
|
|
|
|
let mut wr = [0u8; 1024];
|
|
let mut wr = [0u8; 1024];
|
|
@@ -123,7 +204,27 @@ async fn echo_hitter(addr: &str) -> Result<()> {
|
|
|
Ok(())
|
|
Ok(())
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-async fn pingpong_hitter(addr: &str) -> Result<()> {
|
|
|
|
|
|
|
+async fn udp_echo_hitter(addr: &'static str) -> Result<()> {
|
|
|
|
|
+ let conn = UdpSocket::bind("0.0.0.0:0").await?;
|
|
|
|
|
+ conn.connect(addr).await?;
|
|
|
|
|
+
|
|
|
|
|
+ let mut wr = [0u8; 128];
|
|
|
|
|
+ let mut rd = [0u8; 128];
|
|
|
|
|
+ for _ in 0..3 {
|
|
|
|
|
+ rand::thread_rng().fill(&mut wr);
|
|
|
|
|
+
|
|
|
|
|
+ conn.send(&wr).await?;
|
|
|
|
|
+ debug!("send");
|
|
|
|
|
+
|
|
|
|
|
+ conn.recv(&mut rd).await?;
|
|
|
|
|
+ debug!("recv");
|
|
|
|
|
+
|
|
|
|
|
+ assert_eq!(wr, rd);
|
|
|
|
|
+ }
|
|
|
|
|
+ Ok(())
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+async fn tcp_pingpong_hitter(addr: &'static str) -> Result<()> {
|
|
|
let mut conn = TcpStream::connect(addr).await?;
|
|
let mut conn = TcpStream::connect(addr).await?;
|
|
|
|
|
|
|
|
let wr = PING.as_bytes();
|
|
let wr = PING.as_bytes();
|
|
@@ -137,3 +238,23 @@ async fn pingpong_hitter(addr: &str) -> Result<()> {
|
|
|
|
|
|
|
|
Ok(())
|
|
Ok(())
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+async fn udp_pingpong_hitter(addr: &'static str) -> Result<()> {
|
|
|
|
|
+ let conn = UdpSocket::bind("0.0.0.0:0").await?;
|
|
|
|
|
+ conn.connect(&addr).await?;
|
|
|
|
|
+
|
|
|
|
|
+ let wr = PING.as_bytes();
|
|
|
|
|
+ let mut rd = [0u8; PONG.len()];
|
|
|
|
|
+
|
|
|
|
|
+ for _ in 0..3 {
|
|
|
|
|
+ conn.send(wr).await?;
|
|
|
|
|
+ debug!("ping");
|
|
|
|
|
+
|
|
|
|
|
+ conn.recv(&mut rd).await?;
|
|
|
|
|
+ debug!("pong");
|
|
|
|
|
+
|
|
|
|
|
+ assert_eq!(rd, PONG.as_bytes());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Ok(())
|
|
|
|
|
+}
|