Add device HWID support to sing-box subscriptions

This commit is contained in:
2026-07-08 20:31:21 +03:00
parent 7316e932f0
commit 42b85cc8fa
12 changed files with 450 additions and 19 deletions

View File

@@ -5,7 +5,10 @@ mod subscription;
use base64::{engine::general_purpose, Engine};
use models::redact_subscription_url;
use subscription::{parse_subscription_body, parse_user_info};
use std::io::{Read, Write};
use std::net::TcpListener;
use std::time::Duration;
use subscription::{parse_subscription_body, parse_user_info, SubscriptionFetchIdentity};
#[test]
fn parses_singbox_json_config_servers() {
@@ -78,6 +81,55 @@ fn rejects_invalid_or_non_http_subscription_url_before_network() {
assert!(unsupported.message.contains("http or https"));
}
#[test]
fn fetch_subscription_sends_device_hwid_header_when_identity_is_set() {
let listener = TcpListener::bind("127.0.0.1:0").expect("bind local test listener");
let url = format!("http://{}/subscription", listener.local_addr().unwrap());
let request_thread = std::thread::spawn(move || {
let (mut stream, _) = listener.accept().expect("accept test request");
stream
.set_read_timeout(Some(Duration::from_secs(2)))
.expect("set read timeout");
let mut request = Vec::new();
let mut buffer = [0_u8; 512];
loop {
let bytes_read = stream.read(&mut buffer).expect("read request");
if bytes_read == 0 {
break;
}
request.extend_from_slice(&buffer[..bytes_read]);
if request.windows(4).any(|window| window == b"\r\n\r\n") {
break;
}
}
let body = r#"{"outbounds":[{"type":"vless","tag":"nl-1","server":"nl.example.test","server_port":443}]}"#;
let response = format!(
"HTTP/1.1 200 OK\r\ncontent-type: application/json\r\ncontent-length: {}\r\n\r\n{}",
body.len(),
body
);
stream
.write_all(response.as_bytes())
.expect("write response");
String::from_utf8_lossy(&request).to_ascii_lowercase()
});
let identity = SubscriptionFetchIdentity::with_device_hwid(Some("hwid-abc123"));
let cache = subscription::fetch_subscription_with_identity(&url, &identity)
.expect("fetch subscription through local test server");
let request = request_thread.join().expect("request thread");
assert_eq!(cache.servers[0].tag, "nl-1");
assert!(request.contains("x-hwid: hwid-abc123"));
assert!(request.contains("user-agent: proxywarden/"));
assert!(request.contains("x-app-name: proxywarden"));
assert!(request.contains("x-device-os:"));
assert!(request.contains("x-device-model: proxywarden"));
}
#[test]
fn redacts_subscription_url_for_display() {
assert_eq!(