libsubconverter/utils/
http.rs

1use case_insensitive_string::CaseInsensitiveString;
2use std::collections::HashMap;
3
4// Import platform-specific implementations
5#[cfg(not(target_arch = "wasm32"))]
6mod platform {
7    pub use crate::utils::http_std::{
8        get_sub_info_from_header, get_sub_info_from_response, parse_proxy, web_get, web_get_async,
9        web_patch_async, web_post_async, HttpError, HttpResponse, ProxyConfig,
10    };
11}
12
13#[cfg(target_arch = "wasm32")]
14mod platform {
15    pub use crate::utils::http_wasm::{
16        get_sub_info_from_header, get_sub_info_from_response, parse_proxy, web_get, web_get_async,
17        web_patch_async, web_post_async, HttpError, HttpResponse, ProxyConfig,
18    };
19}
20
21// Re-export platform-specific implementations
22pub use platform::*;
23
24/// Asynchronous function that returns only the body content if status is 2xx,
25/// otherwise treats as error
26/// This provides backward compatibility with code expecting only successful
27/// responses
28pub async fn web_get_content_async(
29    url: &str,
30    proxy_config: &ProxyConfig,
31    headers: Option<&HashMap<CaseInsensitiveString, String>>,
32) -> Result<String, String> {
33    match web_get_async(url, proxy_config, headers).await {
34        Ok(response) => {
35            if (200..300).contains(&response.status) {
36                Ok(response.body)
37            } else {
38                Err(format!("HTTP error {}: {}", response.status, response.body))
39            }
40        }
41        Err(e) => Err(e.message),
42    }
43}
44
45/// Extract subscription info from HTTP headers
46///
47/// # Arguments
48/// * `headers` - HTTP response headers
49///
50/// # Returns
51/// * Subscription info string with key-value pairs
52pub fn get_sub_info_from_header(headers: &HashMap<String, String>) -> String {
53    let mut sub_info = String::new();
54
55    // Extract upload and download
56    let mut upload: u64 = 0;
57    let mut download: u64 = 0;
58    let mut total: u64 = 0;
59    let mut expire: String = String::new();
60
61    // Look for subscription-userinfo header
62    if let Some(userinfo) = headers.get("subscription-userinfo") {
63        for info_item in userinfo.split(';') {
64            let info_item = info_item.trim();
65            if info_item.starts_with("upload=") {
66                if let Ok(value) = info_item[7..].parse::<u64>() {
67                    upload = value;
68                }
69            } else if info_item.starts_with("download=") {
70                if let Ok(value) = info_item[9..].parse::<u64>() {
71                    download = value;
72                }
73            } else if info_item.starts_with("total=") {
74                if let Ok(value) = info_item[6..].parse::<u64>() {
75                    total = value;
76                }
77            } else if info_item.starts_with("expire=") {
78                expire = info_item[7..].to_string();
79            }
80        }
81    }
82
83    // Add traffic info
84    if upload > 0 || download > 0 {
85        sub_info.push_str(&format!("upload={}, download={}", upload, download));
86    }
87
88    // Add total traffic
89    if total > 0 {
90        if !sub_info.is_empty() {
91            sub_info.push_str(", ");
92        }
93        sub_info.push_str(&format!("total={}", total));
94    }
95
96    // Add expiry info
97    if !expire.is_empty() {
98        if !sub_info.is_empty() {
99            sub_info.push_str(", ");
100        }
101        sub_info.push_str(&format!("expire={}", expire));
102    }
103
104    sub_info
105}
106
107/// Get subscription info from response headers with additional formatting
108///
109/// # Arguments
110/// * `headers` - HTTP response headers
111/// * `sub_info` - Mutable string to append info to
112///
113/// # Returns
114/// * `true` if info was extracted, `false` otherwise
115pub fn get_sub_info_from_response(
116    headers: &HashMap<String, String>,
117    sub_info: &mut String,
118) -> bool {
119    let header_info = get_sub_info_from_header(headers);
120    if !header_info.is_empty() {
121        if !sub_info.is_empty() {
122            sub_info.push_str(", ");
123        }
124        sub_info.push_str(&header_info);
125        true
126    } else {
127        false
128    }
129}