Expose dev subscription headers without HWID

This commit is contained in:
2026-07-08 21:51:45 +03:00
parent a2581187da
commit 4d859dc0a6
7 changed files with 143 additions and 21 deletions
+34
View File
@@ -210,6 +210,8 @@ pub struct LocalSingBoxStatusResponse {
pub component: ComponentStatusDto,
pub generated_config_path: String,
pub lan_listen_host: Option<String>,
#[cfg(debug_assertions)]
pub subscription_identity: SubscriptionRequestIdentityDto,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
@@ -243,6 +245,21 @@ pub struct SubscriptionServerDto {
pub server_port: u16,
}
#[cfg(debug_assertions)]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SubscriptionRequestIdentityDto {
pub headers: Vec<SubscriptionRequestHeaderDto>,
}
#[cfg(debug_assertions)]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SubscriptionRequestHeaderDto {
pub name: String,
pub value: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SaveSingBoxSubscriptionInputDto {
@@ -492,6 +509,21 @@ impl SubscriptionFetcher for SystemSubscriptionFetcher {
}
}
#[cfg(debug_assertions)]
fn subscription_request_identity_for_display() -> SubscriptionRequestIdentityDto {
let identity = subscription::SubscriptionFetchIdentity::default();
let headers = identity
.request_headers_without_device_hwid()
.into_iter()
.map(|(name, value)| SubscriptionRequestHeaderDto {
name: name.to_string(),
value,
})
.collect();
SubscriptionRequestIdentityDto { headers }
}
pub trait Clock {
fn now(&self) -> String;
}
@@ -1404,6 +1436,8 @@ fn read_singbox_status_with_detection(
.display()
.to_string(),
lan_listen_host: local_lan_ipv4(),
#[cfg(debug_assertions)]
subscription_identity: subscription_request_identity_for_display(),
})
}
+27 -16
View File
@@ -54,6 +54,30 @@ impl SubscriptionFetchIdentity {
..Self::default()
}
}
pub fn request_headers_without_device_hwid(&self) -> Vec<(&'static str, String)> {
let mut headers = vec![
("User-Agent", self.user_agent.clone()),
("X-App-Name", self.app_name.clone()),
("X-Device-OS", self.device_os.clone()),
("X-Device-Model", self.device_model.clone()),
];
if let Some(device_os_version) = self
.device_os_version
.as_deref()
.map(str::trim)
.filter(|value| !value.is_empty())
{
let header_value = sanitize_header_value(device_os_version);
if !header_value.is_empty() {
headers.push(("X-Device-OS-Version", header_value.clone()));
headers.push(("X-Ver-OS", header_value));
}
}
headers
}
}
impl Default for SubscriptionFetchIdentity {
@@ -119,23 +143,10 @@ pub fn fetch_subscription_with_identity(
));
}
let mut request = reqwest::blocking::Client::new()
.get(parsed_url)
.header("user-agent", identity.user_agent.as_str())
.header("x-app-name", identity.app_name.as_str())
.header("x-device-os", identity.device_os.as_str())
.header("x-device-model", identity.device_model.as_str());
let mut request = reqwest::blocking::Client::new().get(parsed_url);
if let Some(device_os_version) = identity
.device_os_version
.as_deref()
.map(str::trim)
.filter(|value| !value.is_empty())
{
let header_value = sanitize_header_value(device_os_version);
if !header_value.is_empty() {
request = request.header("x-device-os-version", header_value);
}
for (name, value) in identity.request_headers_without_device_hwid() {
request = request.header(name, value);
}
if let Some(device_hwid) = identity