From ae36a180f79fcbd2e1922f6d014f376cc4f5983e Mon Sep 17 00:00:00 2001 From: Keivan-sf Date: Sat, 26 Jul 2025 16:32:53 +0330 Subject: [PATCH] Support xhttp --- src/config_models/mod.rs | 10 ++++++++++ src/parser/vless/mod.rs | 24 ++++++++++++++++++++++-- src/parser/vless/models.rs | 1 + 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/config_models/mod.rs b/src/config_models/mod.rs index 7a4acae..ce2ad9c 100644 --- a/src/config_models/mod.rs +++ b/src/config_models/mod.rs @@ -60,6 +60,15 @@ pub struct KCPSettings { pub seed: Option, } +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize)] +pub struct XHTTPSettings { + pub host: Option, + pub path: Option, + pub mode: Option, + pub extra: Option, +} + #[allow(non_snake_case)] #[derive(Serialize, Deserialize)] pub struct RealitySettings { @@ -124,6 +133,7 @@ pub struct StreamSettings { pub grpcSettings: Option, pub quicSettings: Option, pub kcpSettings: Option, + pub xhttpSettings: Option, } #[allow(non_snake_case)] diff --git a/src/parser/vless/mod.rs b/src/parser/vless/mod.rs index 1e1355a..51daefa 100644 --- a/src/parser/vless/mod.rs +++ b/src/parser/vless/mod.rs @@ -31,8 +31,8 @@ pub fn create_outbound_object(data: models::VlessData) -> Outbound { }, wsSettings: if network_type == String::from("ws") { Some(WsSettings { - Host: data.query.host, - path: data.query.path, + Host: data.query.host.clone(), + path: data.query.path.clone(), acceptProxyProtocol: None, }) } else { @@ -93,6 +93,16 @@ pub fn create_outbound_object(data: models::VlessData) -> Outbound { } else { None }, + xhttpSettings: if network_type == String::from("xhttp") { + Some(XHTTPSettings { + host: data.query.host.clone(), + path: data.query.path.clone(), + mode: data.query.mode, + extra: data.query.extra.and_then(|e| parse_raw_json(e.as_str())), + }) + } else { + None + }, }, settings: OutboundSettings::Vless(VlessOutboundSettings { vnext: vec![VlessServerObject { @@ -167,6 +177,7 @@ fn parse_vless_query(raw_query: &str) -> models::VlessQuery { service_name: url_decode(get_parameter_value(&query, "serviceName")), slpn: get_parameter_value(&query, "slpn"), spx: url_decode(get_parameter_value(&query, "spx")), + extra: url_decode(get_parameter_value(&query, "extra")), }; return a; } @@ -179,6 +190,15 @@ fn url_decode(value: Option) -> Option { }); } +fn parse_raw_json(input: &str) -> Option { + serde_json::from_str::(input) + .ok() + .and_then(|v| match v { + serde_json::Value::Object(_) => Some(v), + _ => None, + }) +} + fn get_parameter_value(query: &Vec<(&str, &str)>, param: &str) -> Option { return query .iter() diff --git a/src/parser/vless/models.rs b/src/parser/vless/models.rs index 767cd3b..c287a3f 100644 --- a/src/parser/vless/models.rs +++ b/src/parser/vless/models.rs @@ -19,6 +19,7 @@ pub struct VlessQuery { pub slpn: Option, pub spx: Option, pub alpn: Option, + pub extra: Option, } pub struct VlessAddress {