mirror of
https://github.com/EasyTier/EasyTier.git
synced 2026-05-07 02:09:06 +00:00
feat: add X-Network-Name header to HTTP connector requests (#1839)
This allows HTTP redirect servers to provide network-specific node lists based on the client's network identity. Updated unit tests to verify the header is correctly sent.
This commit is contained in:
@@ -169,13 +169,19 @@ impl HttpTunnelConnector {
|
|||||||
|
|
||||||
let original_url_clone = original_url.to_string();
|
let original_url_clone = original_url.to_string();
|
||||||
let body_clone = body.clone();
|
let body_clone = body.clone();
|
||||||
|
let network_name = self.global_ctx.network.network_name.clone();
|
||||||
let res = tokio::task::spawn_blocking(move || {
|
let res = tokio::task::spawn_blocking(move || {
|
||||||
let uri = http_req::uri::Uri::try_from(original_url_clone.as_ref())
|
let uri = http_req::uri::Uri::try_from(original_url_clone.as_ref())
|
||||||
.with_context(|| format!("parsing url failed. url: {}", original_url_clone))?;
|
.with_context(|| format!("parsing url failed. url: {}", original_url_clone))?;
|
||||||
|
|
||||||
tracing::info!("sending http request to {}", uri);
|
tracing::info!(
|
||||||
|
"sending http request to {}, network_name: {}",
|
||||||
|
uri,
|
||||||
|
network_name
|
||||||
|
);
|
||||||
|
|
||||||
Request::new(&uri)
|
Request::new(&uri)
|
||||||
|
.header("X-Network-Name", &network_name)
|
||||||
.redirect_policy(RedirectPolicy::Limit(0))
|
.redirect_policy(RedirectPolicy::Limit(0))
|
||||||
.timeout(std::time::Duration::from_secs(20))
|
.timeout(std::time::Duration::from_secs(20))
|
||||||
.send(&mut *body_clone.write().unwrap())
|
.send(&mut *body_clone.write().unwrap())
|
||||||
@@ -244,19 +250,39 @@ impl super::TunnelConnector for HttpTunnelConnector {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use tokio::{io::AsyncWriteExt as _, net::TcpListener};
|
use tokio::{io::AsyncReadExt as _, io::AsyncWriteExt as _, net::TcpListener};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
common::global_ctx::tests::get_mock_global_ctx,
|
common::global_ctx::tests::get_mock_global_ctx_with_network,
|
||||||
tunnel::{tcp::TcpTunnelListener, TunnelConnector, TunnelListener},
|
tunnel::{tcp::TcpTunnelListener, TunnelConnector, TunnelListener},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
async fn run_http_redirect_server(port: u16, test_type: HttpRedirectType) -> Result<(), Error> {
|
async fn run_http_redirect_server(
|
||||||
let listener = TcpListener::bind(format!("0.0.0.0:{}", port)).await?;
|
port: u16,
|
||||||
|
test_type: HttpRedirectType,
|
||||||
|
) -> Result<String, Error> {
|
||||||
|
let listener = TcpListener::bind(format!("127.0.0.1:{}", port)).await?;
|
||||||
let (mut stream, _) = listener.accept().await?;
|
let (mut stream, _) = listener.accept().await?;
|
||||||
|
|
||||||
|
let mut buf = [0u8; 4096];
|
||||||
|
let n = stream.read(&mut buf).await?;
|
||||||
|
let req = String::from_utf8_lossy(&buf[..n]);
|
||||||
|
|
||||||
|
let mut captured_network_name = String::new();
|
||||||
|
for line in req.lines() {
|
||||||
|
if line.to_lowercase().starts_with("x-network-name:") {
|
||||||
|
captured_network_name = line
|
||||||
|
.split_once(':')
|
||||||
|
.map(|x| x.1)
|
||||||
|
.unwrap_or_default()
|
||||||
|
.trim()
|
||||||
|
.to_string();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
match test_type {
|
match test_type {
|
||||||
HttpRedirectType::RedirectToQuery => {
|
HttpRedirectType::RedirectToQuery => {
|
||||||
let resp = "HTTP/1.1 301 Moved Permanently\r\nLocation: http://test.com/?url=tcp://127.0.0.1:25888\r\n\r\n";
|
let resp = "HTTP/1.1 301 Moved Permanently\r\nLocation: http://test.com/?url=tcp://127.0.0.1:25888\r\n\r\n";
|
||||||
@@ -276,7 +302,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(captured_network_name)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest::rstest]
|
#[rstest::rstest]
|
||||||
@@ -292,10 +318,17 @@ mod tests {
|
|||||||
)]
|
)]
|
||||||
test_type: HttpRedirectType,
|
test_type: HttpRedirectType,
|
||||||
) {
|
) {
|
||||||
|
let network_name = format!("net_{}", rand::random::<u32>());
|
||||||
let http_task = tokio::spawn(run_http_redirect_server(35888, test_type));
|
let http_task = tokio::spawn(run_http_redirect_server(35888, test_type));
|
||||||
tokio::time::sleep(std::time::Duration::from_millis(10)).await;
|
tokio::time::sleep(std::time::Duration::from_millis(10)).await;
|
||||||
let test_url: url::Url = "http://127.0.0.1:35888".parse().unwrap();
|
let test_url: url::Url = "http://127.0.0.1:35888".parse().unwrap();
|
||||||
let global_ctx = get_mock_global_ctx();
|
|
||||||
|
let identity = crate::common::config::NetworkIdentity {
|
||||||
|
network_name: network_name.clone(),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let global_ctx = get_mock_global_ctx_with_network(Some(identity));
|
||||||
|
|
||||||
let mut flags = global_ctx.config.get_flags();
|
let mut flags = global_ctx.config.get_flags();
|
||||||
flags.bind_device = false;
|
flags.bind_device = false;
|
||||||
global_ctx.config.set_flags(flags);
|
global_ctx.config.set_flags(flags);
|
||||||
@@ -310,11 +343,14 @@ mod tests {
|
|||||||
|
|
||||||
let t = connector.connect().await.unwrap();
|
let t = connector.connect().await.unwrap();
|
||||||
assert_eq!(connector.redirect_type, test_type);
|
assert_eq!(connector.redirect_type, test_type);
|
||||||
|
|
||||||
|
let captured_name = http_task.await.unwrap().unwrap();
|
||||||
|
assert_eq!(captured_name, network_name);
|
||||||
|
|
||||||
let info = t.info().unwrap();
|
let info = t.info().unwrap();
|
||||||
let remote_addr = info.remote_addr.unwrap();
|
let remote_addr = info.remote_addr.unwrap();
|
||||||
assert_eq!(remote_addr, test_url.into());
|
assert_eq!(remote_addr, test_url.into());
|
||||||
|
|
||||||
tokio::join!(task).0.unwrap();
|
tokio::join!(task).0.unwrap();
|
||||||
tokio::join!(http_task).0.unwrap().unwrap();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user