libsubconverter/parser/explodes/
hysteria.rs

1use crate::{models::HYSTERIA_DEFAULT_GROUP, utils::url_decode, Proxy};
2use std::collections::HashMap;
3use url::Url;
4
5/// Parse a Hysteria link into a Proxy object
6pub fn explode_hysteria(hysteria: &str, node: &mut Proxy) -> bool {
7    // Check if the link starts with hysteria://
8    if !hysteria.starts_with("hysteria://") {
9        return false;
10    }
11
12    // Try to parse as URL
13    let url = match Url::parse(hysteria) {
14        Ok(url) => url,
15        Err(_) => return false,
16    };
17
18    // Extract host and port
19    let host = match url.host_str() {
20        Some(host) => host,
21        None => return false,
22    };
23    let port = url.port().unwrap_or(443);
24
25    // Extract parameters from the query string
26    let mut params = HashMap::new();
27    for (key, value) in url.query_pairs() {
28        params.insert(key.to_string(), url_decode(&value));
29    }
30
31    // Extract auth string
32    let auth = params.get("auth").map(|s| s.as_str()).unwrap_or("");
33
34    // Extract protocol
35    let protocol = params.get("protocol").map(|s| s.as_str()).unwrap_or("udp");
36
37    // Extract up and down speeds
38    let up = params.get("upmbps").map(|s| s.as_str()).unwrap_or("10");
39    let down = params.get("downmbps").map(|s| s.as_str()).unwrap_or("50");
40    let up_speed = up.parse::<u32>().ok();
41    let down_speed = down.parse::<u32>().ok();
42
43    // Extract ALPN
44    let alpn_str = params.get("alpn").map(|s| s.as_str()).unwrap_or("");
45    let mut alpn = Vec::new();
46    if !alpn_str.is_empty() {
47        for a in alpn_str.split(',') {
48            alpn.push(a.trim().to_string());
49        }
50    }
51
52    // Extract obfs
53    let obfs = params.get("obfs").map(|s| s.as_str()).unwrap_or("");
54    let obfs_param = params.get("obfsParam").map(|s| s.as_str()).unwrap_or("");
55
56    // Extract SNI
57    let sni = params.get("peer").map(|s| s.as_str()).unwrap_or(host);
58
59    // Extract insecure
60    let insecure = params
61        .get("insecure")
62        .map(|s| s == "1" || s.to_lowercase() == "true")
63        .unwrap_or(false);
64
65    // Extract remark from the fragment
66    let remark = url.fragment().unwrap_or("");
67    let formatted_remark = if remark.is_empty() {
68        format!("{} ({})", host, port)
69    } else {
70        remark.to_string()
71    };
72
73    // Create the proxy object using the hysteria_construct method
74    *node = Proxy::hysteria_construct(
75        HYSTERIA_DEFAULT_GROUP.to_string(),
76        formatted_remark,
77        host.to_string(),
78        port,
79        "".to_string(), // ports
80        protocol.to_string(),
81        obfs_param.to_string(),
82        up_speed,
83        down_speed,
84        auth.to_string(),
85        obfs.to_string(),
86        sni.to_string(),
87        "".to_string(), // fingerprint
88        "".to_string(), // ca
89        "".to_string(), // ca_str
90        None,           // recv_window_conn
91        None,           // recv_window
92        None,           // disable_mtu_discovery
93        None,           // hop_interval
94        alpn,
95        None, // tcp_fast_open
96        Some(insecure),
97        None, // underlying_proxy
98    );
99
100    true
101}