Skip to content

Commit c7f9fa0

Browse files
committed
禁用本地网络代理
1 parent 450261c commit c7f9fa0

File tree

6 files changed

+240
-150
lines changed

6 files changed

+240
-150
lines changed

src-tauri/src/platforms/common/http_client.rs

Lines changed: 115 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@ use std::time::Duration;
66
pub const DEFAULT_USER_AGENT: &str = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36";
77
const DEFAULT_TIMEOUT_SECONDS: u64 = 20;
88

9-
#[derive(Debug)]
9+
#[derive(Debug, Clone)]
1010
pub struct HttpClient {
1111
pub inner: Client,
1212
headers: ReqwestHeaderMap,
1313
}
1414

15+
#[allow(dead_code)]
1516
impl HttpClient {
1617
pub fn new() -> Result<Self, String> {
1718
let mut default_headers = ReqwestHeaderMap::new();
@@ -37,6 +38,33 @@ impl HttpClient {
3738
})
3839
}
3940

41+
/// 创建一个绕过所有代理的直连HTTP客户端
42+
/// 这个客户端将忽略系统代理设置,直接连接到目标服务器
43+
pub fn new_direct_connection() -> Result<Self, String> {
44+
let mut default_headers = ReqwestHeaderMap::new();
45+
default_headers.insert(
46+
USER_AGENT,
47+
HeaderValue::from_str(DEFAULT_USER_AGENT)
48+
.map_err(|e| format!("Invalid default user agent: {}", e))?,
49+
);
50+
51+
let cookie_jar = Arc::new(Jar::default());
52+
53+
let client_builder = Client::builder()
54+
.timeout(Duration::from_secs(DEFAULT_TIMEOUT_SECONDS))
55+
.cookie_provider(cookie_jar)
56+
.no_proxy(); // 关键:禁用所有代理设置
57+
58+
let inner_client = client_builder
59+
.build()
60+
.map_err(|e| format!("Failed to build direct connection reqwest client: {}", e))?;
61+
62+
Ok(HttpClient {
63+
inner: inner_client,
64+
headers: default_headers,
65+
})
66+
}
67+
4068
// Method to add or update a header for subsequent requests made with this client instance
4169
pub fn insert_header(&mut self, name: HeaderName, value: &str) -> Result<(), String> {
4270
let header_value = HeaderValue::from_str(value)
@@ -97,5 +125,90 @@ impl HttpClient {
97125
Ok(json_response)
98126
}
99127

100-
// Add post, post_json etc. from the demo if needed in the future
128+
pub async fn post_form(&self, url: &str, form_data: &str) -> Result<Response, String> {
129+
let response = self.send_request(
130+
self.inner
131+
.post(url)
132+
.header("Content-Type", "application/x-www-form-urlencoded")
133+
.body(form_data.to_string())
134+
).await?;
135+
Ok(response)
136+
}
137+
138+
pub async fn post_form_json<T: serde::de::DeserializeOwned>(&self, url: &str, form_data: &str) -> Result<T, String> {
139+
let response = self.post_form(url, form_data).await?;
140+
let status = response.status();
141+
if !status.is_success() {
142+
let err_text = response
143+
.text()
144+
.await
145+
.unwrap_or_else(|_| "Failed to read error body".to_string());
146+
return Err(format!(
147+
"POST FORM {} failed with status {}: {}",
148+
url, status, err_text
149+
));
150+
}
151+
let json_response = response
152+
.json::<T>()
153+
.await
154+
.map_err(|e| format!("Failed to parse JSON response from {}: {}", url, e))?;
155+
Ok(json_response)
156+
}
157+
158+
pub async fn get_json_with_headers<T: serde::de::DeserializeOwned>(&self, url: &str, headers: Option<ReqwestHeaderMap>) -> Result<T, String> {
159+
let mut request_builder = self.inner.get(url);
160+
161+
if let Some(additional_headers) = headers {
162+
request_builder = request_builder.headers(additional_headers);
163+
}
164+
165+
let response = self.send_request(request_builder).await?;
166+
let status = response.status();
167+
if !status.is_success() {
168+
let err_text = response
169+
.text()
170+
.await
171+
.unwrap_or_else(|_| "Failed to read error body".to_string());
172+
return Err(format!(
173+
"GET JSON {} failed with status {}: {}",
174+
url, status, err_text
175+
));
176+
}
177+
let json_response = response
178+
.json::<T>()
179+
.await
180+
.map_err(|e| format!("Failed to parse JSON response from {}: {}", url, e))?;
181+
Ok(json_response)
182+
}
183+
184+
pub async fn get_with_cookies(&self, url: &str) -> Result<Response, String> {
185+
let request_builder = self.inner.get(url).headers(self.headers.clone());
186+
self.send_request(request_builder).await
187+
}
188+
189+
pub async fn get_text_with_headers(&self, url: &str, headers: Option<ReqwestHeaderMap>) -> Result<String, String> {
190+
let mut request_builder = self.inner.get(url).headers(self.headers.clone());
191+
192+
if let Some(additional_headers) = headers {
193+
request_builder = request_builder.headers(additional_headers);
194+
}
195+
196+
let response = self.send_request(request_builder).await?;
197+
let status = response.status();
198+
if !status.is_success() {
199+
let err_text = response
200+
.text()
201+
.await
202+
.unwrap_or_else(|_| "Failed to read error body".to_string());
203+
return Err(format!(
204+
"GET {} failed with status {}: {}",
205+
url, status, err_text
206+
));
207+
}
208+
let text_response = response
209+
.text()
210+
.await
211+
.map_err(|e| format!("Failed to read text response from {}: {}", url, e))?;
212+
Ok(text_response)
213+
}
101214
}

src-tauri/src/platforms/douyin/danmu/web_fetcher.rs

Lines changed: 45 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1+
use crate::platforms::common::http_client::HttpClient;
12
use regex::Regex;
2-
use reqwest::cookie::Jar;
3-
use reqwest::Client;
43
use serde::{Deserialize, Serialize};
54
use serde_json;
65
use std::sync::Arc;
@@ -25,17 +24,15 @@ pub struct DouyinLiveWebFetcher {
2524
pub ttwid: Option<String>,
2625
pub room_id: Option<String>,
2726
pub user_agent: String,
28-
pub http_client: Client,
27+
pub http_client: HttpClient,
2928
pub(crate) _ws_stream: Option<Arc<Mutex<WebSocketStream<MaybeTlsStream<TcpStream>>>>>,
3029
}
3130

3231
impl DouyinLiveWebFetcher {
3332
pub fn new(live_id: &str) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
34-
let cookie_jar = Arc::new(Jar::default());
35-
let http_client = Client::builder()
36-
.cookie_provider(cookie_jar)
37-
.user_agent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36")
38-
.build()?;
33+
// 使用直连HTTP客户端,绕过所有代理设置
34+
let http_client = HttpClient::new_direct_connection()
35+
.map_err(|e| format!("Failed to create direct connection HttpClient: {}", e))?;
3936

4037
Ok(DouyinLiveWebFetcher {
4138
live_id: live_id.to_string(),
@@ -53,7 +50,8 @@ impl DouyinLiveWebFetcher {
5350
}
5451

5552
let live_url = "https://live.douyin.com/";
56-
let response = self.http_client.get(live_url).send().await?;
53+
let response = self.http_client.get_with_cookies(live_url).await
54+
.map_err(|e| format!("Failed to get response from {}: {}", live_url, e))?;
5755

5856
let ttwid_val = response
5957
.cookies()
@@ -85,14 +83,16 @@ impl DouyinLiveWebFetcher {
8583
);
8684

8785
let url = format!("https://live.douyin.com/{}", self.live_id);
88-
let response = self
89-
.http_client
90-
.get(&url)
91-
.header("Cookie", cookie_header)
92-
.send()
93-
.await?;
94-
95-
let text = response.text().await?;
86+
87+
let mut headers = reqwest::header::HeaderMap::new();
88+
headers.insert(
89+
reqwest::header::COOKIE,
90+
reqwest::header::HeaderValue::from_str(&cookie_header)
91+
.map_err(|e| format!("Failed to create cookie header: {}", e))?
92+
);
93+
94+
let text = self.http_client.get_text_with_headers(&url, Some(headers)).await
95+
.map_err(|e| format!("Failed to get room page: {}", e))?;
9696
println!(
9797
"HTML Response (first 500 chars): {}",
9898
&text[..std::cmp::min(500, text.len())]
@@ -123,15 +123,20 @@ impl DouyinLiveWebFetcher {
123123
room_id_val // room_id_str is the actual numerical room ID
124124
);
125125

126-
let response = self
127-
.http_client
128-
.get(&url)
129-
.header("User-Agent", &self.user_agent)
130-
.header("Cookie", format!("ttwid={};", ttwid_val))
131-
.send()
132-
.await?;
133-
134-
let data: serde_json::Value = response.json().await?;
126+
let mut headers = reqwest::header::HeaderMap::new();
127+
headers.insert(
128+
reqwest::header::USER_AGENT,
129+
reqwest::header::HeaderValue::from_str(&self.user_agent)
130+
.map_err(|e| format!("Failed to create user-agent header: {}", e))?
131+
);
132+
headers.insert(
133+
reqwest::header::COOKIE,
134+
reqwest::header::HeaderValue::from_str(&format!("ttwid={};", ttwid_val))
135+
.map_err(|e| format!("Failed to create cookie header: {}", e))?
136+
);
137+
138+
let data: serde_json::Value = self.http_client.get_json_with_headers(&url, Some(headers)).await
139+
.map_err(|e| format!("Failed to get room status: {}", e))?;
135140

136141
// This part is mostly for printing/debugging in the original code
137142
if let Some(room_data_top) = data.get("data") {
@@ -209,26 +214,20 @@ pub async fn fetch_douyin_room_info(live_id: String) -> Result<DouyinFollowListR
209214
room_id_str // room_id_str (the numerical one we fetched)
210215
);
211216

212-
let response = fetcher
213-
.http_client
214-
.get(&url)
215-
.header("User-Agent", &fetcher.user_agent)
216-
.header("Cookie", format!("ttwid={};", ttwid))
217-
.send()
218-
.await
219-
.map_err(|e| format!("Failed to send request to web/enter: {}", e))?;
220-
221-
let response_text = response
222-
.text()
223-
.await
224-
.map_err(|e| format!("Failed to get response text: {}", e))?;
225-
// println!("[fetch_douyin_room_info] Response text: {}", response_text);
226-
let data: serde_json::Value = serde_json::from_str(&response_text).map_err(|e| {
227-
format!(
228-
"Failed to parse JSON from web/enter: {}. Response text: {}",
229-
e, response_text
230-
)
231-
})?;
217+
let mut headers = reqwest::header::HeaderMap::new();
218+
headers.insert(
219+
reqwest::header::USER_AGENT,
220+
reqwest::header::HeaderValue::from_str(&fetcher.user_agent)
221+
.map_err(|e| format!("Failed to create user-agent header: {}", e))?
222+
);
223+
headers.insert(
224+
reqwest::header::COOKIE,
225+
reqwest::header::HeaderValue::from_str(&format!("ttwid={};", ttwid))
226+
.map_err(|e| format!("Failed to create cookie header: {}", e))?
227+
);
228+
229+
let data: serde_json::Value = fetcher.http_client.get_json_with_headers(&url, Some(headers)).await
230+
.map_err(|e| format!("Failed to get room info from web/enter: {}", e))?;
232231

233232
// Parse data based on typical Douyin API structure
234233
let room_data_top = data

src-tauri/src/platforms/douyin/douyin_streamer_detail.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,9 @@ pub async fn get_douyin_live_stream_url_with_quality(
4545
});
4646
}
4747

48+
// 使用直连HTTP客户端,绕过所有代理设置
4849
let mut http_client =
49-
HttpClient::new().map_err(|e| format!("Failed to create HttpClient: {}", e))?;
50+
HttpClient::new_direct_connection().map_err(|e| format!("Failed to create direct connection HttpClient: {}", e))?;
5051

5152
if let Err(e) = setup_douyin_cookies(&mut http_client, &room_id_str).await {
5253
return Ok(crate::platforms::common::LiveStreamInfo {

src-tauri/src/platforms/douyin/douyin_streamer_info.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@ pub async fn fetch_douyin_streamer_info(
2424
});
2525
}
2626

27+
// 使用直连HTTP客户端,绕过所有代理设置
2728
let mut http_client =
28-
HttpClient::new().map_err(|e| format!("Failed to create HttpClient: {}", e))?;
29+
HttpClient::new_direct_connection().map_err(|e| format!("Failed to create direct connection HttpClient: {}", e))?;
2930

3031
if let Err(e) = setup_douyin_cookies(&mut http_client, &room_id_str).await {
3132
return Ok(crate::platforms::common::LiveStreamInfo {

src-tauri/src/platforms/douyin/douyin_streamer_list.rs

Lines changed: 9 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::platforms::common::http_client::HttpClient;
12
use reqwest::header::{HeaderMap, HeaderValue, COOKIE};
23
use serde::{Deserialize, Serialize};
34
use tauri::State; // Removed SET_COOKIE
@@ -83,10 +84,9 @@ pub async fn fetch_douyin_partition_rooms(
8384
) -> Result<DouyinLiveListResponse, String> {
8485
let count: i32 = 15; // Number of items requested per page, explicitly typed as i32
8586

86-
// For this test, create a new client, similar to the user's test snippet
87-
let local_client = reqwest::Client::builder()
88-
.build()
89-
.map_err(|e| format!("Failed to build local reqwest client: {}", e))?;
87+
// 使用直连HTTP客户端,绕过所有代理设置
88+
let local_client = HttpClient::new_direct_connection()
89+
.map_err(|e| format!("Failed to create direct connection HttpClient: {}", e))?;
9090

9191
// Use hardcoded ttwid and odin_tt from the user's working test for now
9292
let hardcoded_odin_tt = "54c68ba8fa8ce792ad017c55272d171c283baedc87b2f6282ca8706df295cbd89c5d55449b587b7ebe0a2e352e394a86975955c9ed7f98f209996bdca2749479619aceecc7b75c2374e146b5a722b2e1";
@@ -106,21 +106,9 @@ pub async fn fetch_douyin_partition_rooms(
106106
count, offset, partition, partition_type, ms_token
107107
);
108108

109-
let request_builder = local_client
110-
.request(reqwest::Method::GET, &url)
111-
.headers(headers);
112-
113-
match request_builder.send().await {
114-
Ok(response) => {
115-
let initial_status = response.status();
116-
if initial_status.is_success() {
117-
let response_text = response
118-
.text()
119-
.await
120-
.map_err(|e| format!("Failed to read response text: {}", e))?;
121-
return match serde_json::from_str::<DouyinPartitionApiResponse>(&response_text) {
122-
Ok(api_response) => {
123-
if api_response.status_code == 0 {
109+
match local_client.get_json_with_headers::<DouyinPartitionApiResponse>(&url, Some(headers)).await {
110+
Ok(api_response) => {
111+
if api_response.status_code == 0 {
124112
let mut frontend_rooms = Vec::new();
125113
let received_rooms_count = api_response.data.data.len(); // Number of rooms actually received from this API call
126114

@@ -168,26 +156,10 @@ pub async fn fetch_douyin_partition_rooms(
168156
})
169157
} else {
170158
Err(format!(
171-
"Douyin API returned non-zero status code: {}. Response: {}",
172-
api_response.status_code, response_text
159+
"Douyin API returned non-zero status code: {}",
160+
api_response.status_code
173161
))
174162
}
175-
}
176-
Err(e) => Err(format!(
177-
"Failed to parse Douyin room list JSON: {}. Response: {}",
178-
e, response_text
179-
)),
180-
};
181-
} else {
182-
let error_body = response
183-
.text()
184-
.await
185-
.unwrap_or_else(|_| "Failed to read error body".to_string());
186-
Err(format!(
187-
"Failed to fetch Douyin room list: HTTP Status {}. Body: {}",
188-
initial_status, error_body
189-
))
190-
}
191163
}
192164
Err(e) => Err(format!("Network error fetching Douyin room list: {}", e)),
193165
}

0 commit comments

Comments
 (0)