Skip to content

Commit

Permalink
Feat/udp docker test (#370)
Browse files Browse the repository at this point in the history
* enhance: support docker test of datagram

* add other udp tests

* fix, cannot support udp proxy in shadow-tls test

* fix: fix udp not flush error in vmess&trojan

* fix

* simplify latency test; rename Suite::defaults to Suite::tcps

* fmt

* use legacy image

* print out docker logs

* f

* disable wg test for now

---------

Signed-off-by: Yuwei Ba <dev@watfaq.com>
Co-authored-by: Yuwei Ba <dev@watfaq.com>
Co-authored-by: Yuwei B <i@xiaoba.me>
  • Loading branch information
3 people authored May 10, 2024
1 parent 685ae2d commit 6efcff2
Show file tree
Hide file tree
Showing 12 changed files with 283 additions and 106 deletions.
10 changes: 9 additions & 1 deletion clash/tests/data/config/vmess-ws.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,15 @@
},
"streamSettings": {
"network": "ws",
"security": "none"
"security": "tls",
"tlsSettings": {
"certificates": [
{
"certificateFile": "/etc/ssl/v2ray/fullchain.pem",
"keyFile": "/etc/ssl/v2ray/privkey.pem"
}
]
}
}
}
],
Expand Down
7 changes: 4 additions & 3 deletions clash_lib/src/proxy/relay/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,9 +157,10 @@ mod tests {
use tokio::sync::RwLock;

use crate::proxy::mocks::MockDummyProxyProvider;
use crate::proxy::utils::test_utils::Suite;
use crate::proxy::utils::test_utils::{consts::*, docker_runner::DockerTestRunner};
use crate::proxy::utils::test_utils::{
docker_runner::DockerTestRunnerBuilder, run_default_test_suites_and_cleanup,
docker_runner::DockerTestRunnerBuilder, run_test_suites_and_cleanup,
};

use super::*;
Expand Down Expand Up @@ -205,7 +206,7 @@ mod tests {
});

let handler = Handler::new(Default::default(), vec![Arc::new(RwLock::new(provider))]);
run_default_test_suites_and_cleanup(handler, get_ss_runner(port).await?).await
run_test_suites_and_cleanup(handler, get_ss_runner(port).await?, Suite::tcp_tests()).await
}

#[tokio::test]
Expand Down Expand Up @@ -237,6 +238,6 @@ mod tests {
});

let handler = Handler::new(Default::default(), vec![Arc::new(RwLock::new(provider))]);
run_default_test_suites_and_cleanup(handler, get_ss_runner(port).await?).await
run_test_suites_and_cleanup(handler, get_ss_runner(port).await?, Suite::tcp_tests()).await
}
}
6 changes: 3 additions & 3 deletions clash_lib/src/proxy/shadowsocks/datagram.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 +105,12 @@ impl Sink<UdpPacket> for OutboundDatagramShadowsocks {

let pkt_container = pkt;

if let Some(pkt) = pkt_container.take() {
let data = pkt.data;
if let Some(pkt) = pkt_container {
let data = pkt.data.as_ref();
let addr: shadowsocks::relay::Address =
(pkt.dst_addr.host(), pkt.dst_addr.port()).into();

let n = ready!(inner.poll_send_to(dst, &addr, data.as_ref(), cx))?;
let n = ready!(inner.poll_send_to(dst, &addr, data, cx))?;

debug!(
"send udp packet to remote ss server, len: {}, remote_addr: {}, dst_addr: {}",
Expand Down
11 changes: 7 additions & 4 deletions clash_lib/src/proxy/shadowsocks/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ mod tests {
use super::super::utils::test_utils::{consts::*, docker_runner::DockerTestRunner};
use crate::proxy::utils::test_utils::{
docker_runner::{DockerTestRunnerBuilder, MultiDockerTestRunner},
run_default_test_suites_and_cleanup,
run_test_suites_and_cleanup, Suite,
};

use super::*;
Expand All @@ -379,6 +379,7 @@ mod tests {
#[tokio::test]
#[serial_test::serial]
async fn test_ss() -> anyhow::Result<()> {
let _ = tracing_subscriber::fmt().try_init();
let opts = HandlerOptions {
name: "test-ss".to_owned(),
common_opts: Default::default(),
Expand All @@ -391,7 +392,7 @@ mod tests {
};
let port = opts.port;
let handler = Handler::new(opts);
run_default_test_suites_and_cleanup(handler, get_ss_runner(port).await?).await
run_test_suites_and_cleanup(handler, get_ss_runner(port).await?, Suite::all()).await
}

async fn get_shadowtls_runner(
Expand Down Expand Up @@ -439,13 +440,15 @@ mod tests {
})),
udp: false,
};
let handler = Handler::new(opts);
let handler: Arc<dyn OutboundHandler> = Handler::new(opts);
// we need to store all the runners in a container, to make sure all of them can be destroyed after the test
let mut chained = MultiDockerTestRunner::default();
chained.add(get_ss_runner(ss_port)).await;
chained
.add(get_shadowtls_runner(ss_port, shadow_tls_port))
.await;
run_default_test_suites_and_cleanup(handler, chained).await
// currently, shadow-tls does't support udp proxy
// see: https://github.com/ihciah/shadow-tls/issues/54
run_test_suites_and_cleanup(handler, chained, Suite::tcp_tests()).await
}
}
22 changes: 17 additions & 5 deletions clash_lib/src/proxy/trojan/datagram.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub struct OutboundDatagramTrojan {
state: ReadState,
read_buf: BytesMut,

written: Option<usize>,
flushed: bool,
pkt: Option<UdpPacket>,
}
Expand All @@ -36,6 +37,7 @@ impl OutboundDatagramTrojan {
read_buf: BytesMut::new(),
state: ReadState::Atyp,

written: None,
flushed: true,
pkt: None,
}
Expand Down Expand Up @@ -77,6 +79,7 @@ impl Sink<UdpPacket> for OutboundDatagramTrojan {
let Self {
ref mut inner,
ref mut pkt,
ref mut written,
ref mut flushed,
..
} = *self;
Expand All @@ -85,18 +88,22 @@ impl Sink<UdpPacket> for OutboundDatagramTrojan {

let pkt_container = pkt;

if let Some(pkt) = pkt_container.take() {
let data = pkt.data;
if let Some(pkt) = pkt_container {
let data = &pkt.data;

let mut payload = BytesMut::new();
pkt.dst_addr.write_buf(&mut payload);
payload.put_u16(data.len() as u16);
payload.put_slice(b"\r\n");
payload.put_slice(&data);
payload.put_slice(data);

if written.is_none() {
*written = Some(0);
}

while !payload.is_empty() {
let n = ready!(inner.as_mut().poll_write(cx, payload.as_ref()))?;

*written.as_mut().unwrap() += n;
payload.advance(n);

trace!(
Expand All @@ -107,7 +114,12 @@ impl Sink<UdpPacket> for OutboundDatagramTrojan {
);
}

*flushed = true;
if !*flushed {
ready!(inner.as_mut().poll_flush(cx))?;
*flushed = true;
}
*written = None;
*pkt_container = None;

Poll::Ready(Ok(()))
} else {
Expand Down
7 changes: 4 additions & 3 deletions clash_lib/src/proxy/trojan/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ mod tests {
config_helper::test_config_base_dir,
consts::*,
docker_runner::{DockerTestRunner, DockerTestRunnerBuilder},
run_default_test_suites_and_cleanup,
run_test_suites_and_cleanup, Suite,
};

use super::*;
Expand Down Expand Up @@ -324,7 +324,8 @@ mod tests {
})),
};
let handler = Handler::new(opts);
run_default_test_suites_and_cleanup(handler, get_ws_runner().await?).await
// ignore the udp test
run_test_suites_and_cleanup(handler, get_ws_runner().await?, Suite::all()).await
}

async fn get_grpc_runner() -> anyhow::Result<DockerTestRunner> {
Expand Down Expand Up @@ -363,6 +364,6 @@ mod tests {
})),
};
let handler = Handler::new(opts);
run_default_test_suites_and_cleanup(handler, get_grpc_runner().await?).await
run_test_suites_and_cleanup(handler, get_grpc_runner().await?, Suite::all()).await
}
}
4 changes: 1 addition & 3 deletions clash_lib/src/proxy/utils/test_utils/consts.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
pub const LOCAL_ADDR: &str = "127.0.0.1";
pub const EXAMPLE_REQ: &[u8] = b"GET / HTTP/1.1\r\nHost: example.com\r\nAccept: */*\r\n\r\n";
pub const EXAMLE_RESP_200: &[u8] = b"HTTP/1.1 200";

pub const IMAGE_WG: &str = "lscr.io/linuxserver/wireguard:latest";
pub const IMAGE_WG: &str = "lscr.io/linuxserver/wireguard:1.0.20210914-legacy";
pub const IMAGE_SS_RUST: &str = "ghcr.io/shadowsocks/ssserver-rust:latest";
pub const IMAGE_SHADOW_TLS: &str = "ghcr.io/ihciah/shadow-tls:latest";
pub const IMAGE_TROJAN_GO: &str = "p4gefau1t/trojan-go:latest";
Expand Down
20 changes: 19 additions & 1 deletion clash_lib/src/proxy/utils/test_utils/docker_runner.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::collections::HashMap;

use bollard::container::{Config, RemoveContainerOptions};
use bollard::container::{Config, LogsOptions, RemoveContainerOptions};
use bollard::secret::{HostConfig, Mount, PortBinding};
use bollard::Docker;

Expand Down Expand Up @@ -41,6 +41,24 @@ impl DockerTestRunner {

// you can run the cleanup manually
pub async fn cleanup(self) -> anyhow::Result<()> {
let logs = self
.instance
.logs::<String>(
&self.id,
Some(LogsOptions {
follow: false,
stdout: true,
stderr: true,
..Default::default()
}),
)
.try_collect::<Vec<_>>()
.await?;

for log in logs {
eprintln!("{}", log);
}

self.instance
.remove_container(
&self.id,
Expand Down
Loading

0 comments on commit 6efcff2

Please sign in to comment.