mirror of
https://github.com/telemt/telemt.git
synced 2026-05-22 19:51:43 +03:00
Compare commits
9 Commits
37b6f7b985
...
f1bf95a7de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f1bf95a7de | ||
|
|
959a16af88 | ||
|
|
a54f9ba719 | ||
|
|
2d5cd9c8e1 | ||
|
|
d72cfd6bc4 | ||
|
|
fa3566a9cb | ||
|
|
4e59e52454 | ||
|
|
7b9b46291d | ||
|
|
2a168b2600 |
@@ -255,13 +255,22 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||
```
|
||||
## proxy_secret_path
|
||||
- **Constraints / validation**: `String`. When omitted, the default path is `"proxy-secret"`. Empty values are accepted by TOML/serde but will likely fail at runtime (invalid file path).
|
||||
- **Description**: Path to Telegram infrastructure `proxy-secret` cache file used by ME handshake/RPC auth. Telemt always tries a fresh download from `https://core.telegram.org/getProxySecret` first, caches it to this path on success, and falls back to reading the cached file (any age) on download failure.
|
||||
- **Description**: Path to Telegram infrastructure `proxy-secret` cache file used by ME handshake/RPC auth. Telemt always tries a fresh download from `https://core.telegram.org/getProxySecret` first (unless `proxy_secret_url` is set) , caches it to this path on success, and falls back to reading the cached file (any age) on download failure.
|
||||
- **Example**:
|
||||
|
||||
```toml
|
||||
[general]
|
||||
proxy_secret_path = "proxy-secret"
|
||||
```
|
||||
## proxy_secret_url
|
||||
- **Constraints / validation**: `String`. When omitted, the `"https://core.telegram.org/getProxySecret"` is used.
|
||||
- **Description**: Optional URL to obtain `proxy-secret` file used by ME handshake/RPC auth. Telemt always tries a fresh download from this URL first (with fallback to `https://core.telegram.org/getProxySecret` if absent).
|
||||
- **Example**:
|
||||
|
||||
```toml
|
||||
[general]
|
||||
proxy_secret_url = "https://core.telegram.org/getProxySecret"
|
||||
```
|
||||
## proxy_config_v4_cache_path
|
||||
- **Constraints / validation**: `String`. When set, must not be empty/whitespace-only.
|
||||
- **Description**: Optional disk cache path for raw `getProxyConfig` (IPv4) snapshot. At startup Telemt tries to fetch a fresh snapshot first; on fetch failure or empty snapshot it falls back to this cache file when present and non-empty.
|
||||
@@ -271,6 +280,15 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||
[general]
|
||||
proxy_config_v4_cache_path = "cache/proxy-config-v4.txt"
|
||||
```
|
||||
## proxy_config_v4_url
|
||||
- **Constraints / validation**: `String`. When omitted, the `"https://core.telegram.org/getProxyConfig"` is used.
|
||||
- **Description**: Optional URL to obtain raw `getProxyConfig` (IPv4). Telemt always tries a fresh download from this URL first (with fallback to `https://core.telegram.org/getProxyConfig` if absent).
|
||||
- **Example**:
|
||||
|
||||
```toml
|
||||
[general]
|
||||
proxy_config_v4_url = "https://core.telegram.org/getProxyConfig"
|
||||
```
|
||||
## proxy_config_v6_cache_path
|
||||
- **Constraints / validation**: `String`. When set, must not be empty/whitespace-only.
|
||||
- **Description**: Optional disk cache path for raw `getProxyConfigV6` (IPv6) snapshot. At startup Telemt tries to fetch a fresh snapshot first; on fetch failure or empty snapshot it falls back to this cache file when present and non-empty.
|
||||
@@ -280,6 +298,15 @@ This document lists all configuration keys accepted by `config.toml`.
|
||||
[general]
|
||||
proxy_config_v6_cache_path = "cache/proxy-config-v6.txt"
|
||||
```
|
||||
## proxy_config_v6_url
|
||||
- **Constraints / validation**: `String`. When omitted, the `"https://core.telegram.org/getProxyConfigV6"` is used.
|
||||
- **Description**: Optional URL to obtain raw `getProxyConfigV6` (IPv6). Telemt always tries a fresh download from this URL first (with fallback to `https://core.telegram.org/getProxyConfigV6` if absent).
|
||||
- **Example**:
|
||||
|
||||
```toml
|
||||
[general]
|
||||
proxy_config_v6_url = "https://core.telegram.org/getProxyConfigV6"
|
||||
```
|
||||
## ad_tag
|
||||
- **Constraints / validation**: `String` (optional). When set, must be exactly 32 hex characters; invalid values are disabled during config load.
|
||||
- **Description**: Global fallback sponsored-channel `ad_tag` (used when user has no override in `access.user_ad_tags`). An all-zero tag is accepted but has no effect (and is warned about) until replaced with a real tag from `@MTProxybot`.
|
||||
|
||||
@@ -255,13 +255,22 @@
|
||||
```
|
||||
## proxy_secret_path
|
||||
- **Ограничения / валидация**: `String`. Если этот параметр не указан, используется путь по умолчанию — «proxy-secret». Пустые значения принимаются TOML/serde, но во время выполнения произойдет ошибка (invalid file path).
|
||||
- **Описание**: Путь к файлу кэша `proxy-secret` инфраструктуры Telegram, используемому ME-handshake/аутентификацией RPC. Telemt всегда сначала пытается выполнить новую загрузку с https://core.telegram.org/getProxySecret, в случае успеха кэширует ее по этому пути и возвращается к чтению кэшированного файла в случае сбоя загрузки.
|
||||
- **Описание**: Путь к файлу кэша `proxy-secret` инфраструктуры Telegram, используемому ME-handshake/аутентификацией RPC. Telemt всегда сначала пытается выполнить новую загрузку с https://core.telegram.org/getProxySecret (если не установлен `proxy_secret_url`), в случае успеха кэширует ее по этому пути и возвращается к чтению кэшированного файла в случае сбоя загрузки.
|
||||
- **Пример**:
|
||||
|
||||
```toml
|
||||
[general]
|
||||
proxy_secret_path = "proxy-secret"
|
||||
```
|
||||
## proxy_secret_url
|
||||
- **Ограничения / валидация**: `String`. Если не указан, используется `"https://core.telegram.org/getProxySecret"`.
|
||||
- **Описание**: Необязательный URL для получения файла `proxy-secret` используемого ME-handshake/аутентификацией RPC. Telemt всегда сначала пытается выполнить новую загрузку с этого URL (если не задан, используется https://core.telegram.org/getProxySecret).
|
||||
- **Пример**:
|
||||
|
||||
```toml
|
||||
[general]
|
||||
proxy_secret_url = "https://core.telegram.org/getProxySecret"
|
||||
```
|
||||
## proxy_config_v4_cache_path
|
||||
- **Ограничения / валидация**: `String`. Если используется, значение не должно быть пустым или содержать только пробелы.
|
||||
- **Описание**: Необязательный путь к кэшу для необработанного (raw) снимка getProxyConfig (IPv4). При запуске Telemt сначала пытается получить свежий снимок; в случае сбоя выборки или пустого снимка он возвращается к этому файлу кэша, если он присутствует и не пуст.
|
||||
@@ -271,6 +280,15 @@
|
||||
[general]
|
||||
proxy_config_v4_cache_path = "cache/proxy-config-v4.txt"
|
||||
```
|
||||
## proxy_config_v4_url
|
||||
- **Ограничения / валидация**: `String`. Если не указан, используется `"https://core.telegram.org/getProxyConfig"`.
|
||||
- **Описание**: Необязательный URL для получения `getProxyConfig` (IPv4). Telemt при всегда пытается выполнить новую загрузку с этого URL (и если не задан, использует `https://core.telegram.org/getProxyConfig`).
|
||||
- **Example**:
|
||||
|
||||
```toml
|
||||
[general]
|
||||
proxy_config_v4_url = "https://core.telegram.org/getProxyConfig"
|
||||
```
|
||||
## proxy_config_v6_cache_path
|
||||
- **Ограничения / валидация**: `String`. Если используется, значение не должно быть пустым или содержать только пробелы.
|
||||
- **Описание**: Необязательный путь к кэшу для необработанного (raw) снимка getProxyConfigV6 (IPv6). При запуске Telemt сначала пытается получить свежий снимок; в случае сбоя выборки или пустого снимка он возвращается к этому файлу кэша, если он присутствует и не пуст.
|
||||
@@ -280,6 +298,15 @@
|
||||
[general]
|
||||
proxy_config_v6_cache_path = "cache/proxy-config-v6.txt"
|
||||
```
|
||||
## proxy_config_v6_url
|
||||
- **Ограничения / валидация**: `String`. Если не указан, используется `"https://core.telegram.org/getProxyConfigV6"`.
|
||||
- **Описание**: Необязательный URL для получения `getProxyConfigV6` (IPv6). Telemt при всегда пытается выполнить новую загрузку с этого URL (и если не задан, использует `https://core.telegram.org/getProxyConfigV6`).
|
||||
- **Example**:
|
||||
|
||||
```toml
|
||||
[general]
|
||||
proxy_config_v6_url = "https://core.telegram.org/getProxyConfigV6"
|
||||
```
|
||||
## ad_tag
|
||||
- **Ограничения / валидация**: `String` (необязательный параметр). Если используется, значение должно быть ровно 32 символа в шестнадцатеричной системе; недопустимые значения отключаются во время загрузки конфигурации.
|
||||
- **Описание**: Глобальный резервный спонсируемый канал `ad_tag` (используется, когда у пользователя нет переопределения в `access.user_ad_tags`). Тег со всеми нулями принимается, но не имеет никакого эффекта, пока не будет заменен реальным тегом от `@MTProxybot`.
|
||||
|
||||
@@ -392,14 +392,26 @@ pub struct GeneralConfig {
|
||||
#[serde(default = "default_proxy_secret_path")]
|
||||
pub proxy_secret_path: Option<String>,
|
||||
|
||||
/// Optional custom URL for infrastructure secret (https://core.telegram.org/getProxySecret if absent).
|
||||
#[serde(default)]
|
||||
pub proxy_secret_url: Option<String>,
|
||||
|
||||
/// Optional path to cache raw getProxyConfig (IPv4) snapshot for startup fallback.
|
||||
#[serde(default = "default_proxy_config_v4_cache_path")]
|
||||
pub proxy_config_v4_cache_path: Option<String>,
|
||||
|
||||
/// Optional custom URL for getProxyConfig (https://core.telegram.org/getProxyConfig if absent).
|
||||
#[serde(default)]
|
||||
pub proxy_config_v4_url: Option<String>,
|
||||
|
||||
/// Optional path to cache raw getProxyConfigV6 snapshot for startup fallback.
|
||||
#[serde(default = "default_proxy_config_v6_cache_path")]
|
||||
pub proxy_config_v6_cache_path: Option<String>,
|
||||
|
||||
/// Optional custom URL for getProxyConfigV6 (https://core.telegram.org/getProxyConfigV6 if absent).
|
||||
#[serde(default)]
|
||||
pub proxy_config_v6_url: Option<String>,
|
||||
|
||||
/// Global ad_tag (32 hex chars from @MTProxybot). Fallback when user has no per-user tag in access.user_ad_tags.
|
||||
#[serde(default)]
|
||||
pub ad_tag: Option<String>,
|
||||
@@ -960,8 +972,11 @@ impl Default for GeneralConfig {
|
||||
use_middle_proxy: default_true(),
|
||||
ad_tag: None,
|
||||
proxy_secret_path: default_proxy_secret_path(),
|
||||
proxy_secret_url: None,
|
||||
proxy_config_v4_cache_path: default_proxy_config_v4_cache_path(),
|
||||
proxy_config_v4_url: None,
|
||||
proxy_config_v6_cache_path: default_proxy_config_v6_cache_path(),
|
||||
proxy_config_v6_url: None,
|
||||
middle_proxy_nat_ip: None,
|
||||
middle_proxy_nat_probe: default_true(),
|
||||
middle_proxy_nat_stun: default_middle_proxy_nat_stun(),
|
||||
|
||||
@@ -66,6 +66,7 @@ pub(crate) async fn initialize_me_pool(
|
||||
match crate::transport::middle_proxy::fetch_proxy_secret_with_upstream(
|
||||
proxy_secret_path,
|
||||
config.general.proxy_secret_len_max,
|
||||
config.general.proxy_secret_url.as_deref(),
|
||||
Some(upstream_manager.clone()),
|
||||
)
|
||||
.await
|
||||
@@ -126,7 +127,11 @@ pub(crate) async fn initialize_me_pool(
|
||||
.set_me_status(StartupMeStatus::Initializing, COMPONENT_ME_PROXY_CONFIG_V4)
|
||||
.await;
|
||||
let cfg_v4 = load_startup_proxy_config_snapshot(
|
||||
"https://core.telegram.org/getProxyConfig",
|
||||
config
|
||||
.general
|
||||
.proxy_config_v4_url
|
||||
.as_deref()
|
||||
.unwrap_or("https://core.telegram.org/getProxyConfig"),
|
||||
config.general.proxy_config_v4_cache_path.as_deref(),
|
||||
me2dc_fallback,
|
||||
"getProxyConfig",
|
||||
@@ -158,7 +163,11 @@ pub(crate) async fn initialize_me_pool(
|
||||
.set_me_status(StartupMeStatus::Initializing, COMPONENT_ME_PROXY_CONFIG_V6)
|
||||
.await;
|
||||
let cfg_v6 = load_startup_proxy_config_snapshot(
|
||||
"https://core.telegram.org/getProxyConfigV6",
|
||||
config
|
||||
.general
|
||||
.proxy_config_v6_url
|
||||
.as_deref()
|
||||
.unwrap_or("https://core.telegram.org/getProxyConfigV6"),
|
||||
config.general.proxy_config_v6_cache_path.as_deref(),
|
||||
me2dc_fallback,
|
||||
"getProxyConfigV6",
|
||||
|
||||
@@ -321,7 +321,14 @@ async fn run_update_cycle(
|
||||
let mut maps_changed = false;
|
||||
|
||||
let mut ready_v4: Option<(ProxyConfigData, u64)> = None;
|
||||
let cfg_v4 = retry_fetch("https://core.telegram.org/getProxyConfig", upstream.clone()).await;
|
||||
let cfg_v4 = retry_fetch(
|
||||
cfg.general
|
||||
.proxy_config_v4_url
|
||||
.as_deref()
|
||||
.unwrap_or("https://core.telegram.org/getProxyConfig"),
|
||||
upstream.clone(),
|
||||
)
|
||||
.await;
|
||||
if let Some(cfg_v4) = cfg_v4
|
||||
&& snapshot_passes_guards(cfg, &cfg_v4, "getProxyConfig")
|
||||
{
|
||||
@@ -346,7 +353,10 @@ async fn run_update_cycle(
|
||||
|
||||
let mut ready_v6: Option<(ProxyConfigData, u64)> = None;
|
||||
let cfg_v6 = retry_fetch(
|
||||
"https://core.telegram.org/getProxyConfigV6",
|
||||
cfg.general
|
||||
.proxy_config_v6_url
|
||||
.as_deref()
|
||||
.unwrap_or("https://core.telegram.org/getProxyConfigV6"),
|
||||
upstream.clone(),
|
||||
)
|
||||
.await;
|
||||
@@ -430,6 +440,7 @@ async fn run_update_cycle(
|
||||
match download_proxy_secret_with_max_len_via_upstream(
|
||||
cfg.general.proxy_secret_len_max,
|
||||
upstream,
|
||||
cfg.general.proxy_secret_url.as_deref(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
|
||||
@@ -7,7 +7,6 @@ mod model;
|
||||
mod pressure;
|
||||
mod scheduler;
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) use model::PressureState;
|
||||
pub(crate) use model::{AdmissionDecision, DispatchAction, DispatchFeedback, SchedulerDecision};
|
||||
pub(crate) use scheduler::{WorkerFairnessConfig, WorkerFairnessSnapshot, WorkerFairnessState};
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::collections::HashMap;
|
||||
use std::io::ErrorKind;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, AtomicU32, AtomicU64, Ordering};
|
||||
use std::time::Instant;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use bytes::{Bytes, BytesMut};
|
||||
use tokio::io::AsyncReadExt;
|
||||
@@ -21,8 +21,8 @@ use crate::stats::Stats;
|
||||
|
||||
use super::codec::{RpcChecksumMode, WriterCommand, rpc_crc};
|
||||
use super::fairness::{
|
||||
AdmissionDecision, DispatchAction, DispatchFeedback, SchedulerDecision, WorkerFairnessConfig,
|
||||
WorkerFairnessSnapshot, WorkerFairnessState,
|
||||
AdmissionDecision, DispatchAction, DispatchFeedback, PressureState, SchedulerDecision,
|
||||
WorkerFairnessConfig, WorkerFairnessSnapshot, WorkerFairnessState,
|
||||
};
|
||||
use super::registry::RouteResult;
|
||||
use super::{ConnRegistry, MeResponse};
|
||||
@@ -45,10 +45,22 @@ fn is_data_route_queue_full(result: RouteResult) -> bool {
|
||||
)
|
||||
}
|
||||
|
||||
fn should_close_on_queue_full_streak(streak: u8) -> bool {
|
||||
fn should_close_on_queue_full_streak(streak: u8, pressure_state: PressureState) -> bool {
|
||||
if pressure_state < PressureState::Shedding {
|
||||
return false;
|
||||
}
|
||||
|
||||
streak >= DATA_ROUTE_QUEUE_FULL_STARVATION_THRESHOLD
|
||||
}
|
||||
|
||||
fn should_schedule_fairness_retry(snapshot: &WorkerFairnessSnapshot) -> bool {
|
||||
snapshot.total_queued_bytes > 0
|
||||
}
|
||||
|
||||
fn fairness_retry_delay(route_wait_ms: u64) -> Duration {
|
||||
Duration::from_millis(route_wait_ms.max(1))
|
||||
}
|
||||
|
||||
async fn route_data_with_retry(
|
||||
reg: &ConnRegistry,
|
||||
conn_id: u64,
|
||||
@@ -157,7 +169,7 @@ async fn drain_fairness_scheduler(
|
||||
break;
|
||||
};
|
||||
let cid = candidate.frame.conn_id;
|
||||
let _pressure_state = candidate.pressure_state;
|
||||
let pressure_state = candidate.pressure_state;
|
||||
let _flow_class = candidate.flow_class;
|
||||
let routed = route_data_with_retry(
|
||||
reg,
|
||||
@@ -176,7 +188,7 @@ async fn drain_fairness_scheduler(
|
||||
if is_data_route_queue_full(routed) {
|
||||
let streak = data_route_queue_full_streak.entry(cid).or_insert(0);
|
||||
*streak = streak.saturating_add(1);
|
||||
if should_close_on_queue_full_streak(*streak) {
|
||||
if should_close_on_queue_full_streak(*streak, pressure_state) {
|
||||
fairness.remove_flow(cid);
|
||||
data_route_queue_full_streak.remove(&cid);
|
||||
reg.unregister(cid).await;
|
||||
@@ -231,10 +243,33 @@ pub(crate) async fn reader_loop(
|
||||
let mut fairness_snapshot = fairness.snapshot();
|
||||
loop {
|
||||
let mut tmp = [0u8; 65_536];
|
||||
let backlog_retry_enabled = should_schedule_fairness_retry(&fairness_snapshot);
|
||||
let backlog_retry_delay =
|
||||
fairness_retry_delay(reader_route_data_wait_ms.load(Ordering::Relaxed));
|
||||
let mut retry_only = false;
|
||||
let n = tokio::select! {
|
||||
res = rd.read(&mut tmp) => res.map_err(ProxyError::Io)?,
|
||||
_ = tokio::time::sleep(backlog_retry_delay), if backlog_retry_enabled => {
|
||||
retry_only = true;
|
||||
0usize
|
||||
},
|
||||
_ = cancel.cancelled() => return Ok(()),
|
||||
};
|
||||
if retry_only {
|
||||
let route_wait_ms = reader_route_data_wait_ms.load(Ordering::Relaxed);
|
||||
drain_fairness_scheduler(
|
||||
&mut fairness,
|
||||
reg.as_ref(),
|
||||
&tx,
|
||||
&mut data_route_queue_full_streak,
|
||||
route_wait_ms,
|
||||
stats.as_ref(),
|
||||
)
|
||||
.await;
|
||||
let current_snapshot = fairness.snapshot();
|
||||
apply_fairness_metrics_delta(stats.as_ref(), &mut fairness_snapshot, current_snapshot);
|
||||
continue;
|
||||
}
|
||||
if n == 0 {
|
||||
stats.increment_me_reader_eof_total();
|
||||
return Err(ProxyError::Io(std::io::Error::new(
|
||||
@@ -317,12 +352,9 @@ pub(crate) async fn reader_loop(
|
||||
stats.increment_me_route_drop_queue_full_high();
|
||||
let streak = data_route_queue_full_streak.entry(cid).or_insert(0);
|
||||
*streak = streak.saturating_add(1);
|
||||
if should_close_on_queue_full_streak(*streak)
|
||||
|| matches!(
|
||||
admission,
|
||||
AdmissionDecision::RejectSaturated
|
||||
| AdmissionDecision::RejectStandingFlow
|
||||
)
|
||||
let pressure_state = fairness.pressure_state();
|
||||
if should_close_on_queue_full_streak(*streak, pressure_state)
|
||||
|| matches!(admission, AdmissionDecision::RejectSaturated)
|
||||
{
|
||||
fairness.remove_flow(cid);
|
||||
data_route_queue_full_streak.remove(&cid);
|
||||
@@ -445,14 +477,18 @@ pub(crate) async fn reader_loop(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::time::Duration;
|
||||
|
||||
use bytes::Bytes;
|
||||
|
||||
use super::PressureState;
|
||||
use crate::transport::middle_proxy::ConnRegistry;
|
||||
|
||||
use super::{
|
||||
MeResponse, RouteResult, is_data_route_queue_full, route_data_with_retry,
|
||||
should_close_on_queue_full_streak, should_close_on_route_result_for_ack,
|
||||
should_close_on_route_result_for_data,
|
||||
MeResponse, RouteResult, WorkerFairnessSnapshot, fairness_retry_delay,
|
||||
is_data_route_queue_full, route_data_with_retry, should_close_on_queue_full_streak,
|
||||
should_close_on_route_result_for_ack, should_close_on_route_result_for_data,
|
||||
should_schedule_fairness_retry,
|
||||
};
|
||||
|
||||
#[test]
|
||||
@@ -475,10 +511,29 @@ mod tests {
|
||||
assert!(is_data_route_queue_full(RouteResult::QueueFullBase));
|
||||
assert!(is_data_route_queue_full(RouteResult::QueueFullHigh));
|
||||
assert!(!is_data_route_queue_full(RouteResult::NoConn));
|
||||
assert!(!should_close_on_queue_full_streak(1));
|
||||
assert!(!should_close_on_queue_full_streak(2));
|
||||
assert!(should_close_on_queue_full_streak(3));
|
||||
assert!(should_close_on_queue_full_streak(u8::MAX));
|
||||
assert!(!should_close_on_queue_full_streak(1, PressureState::Normal));
|
||||
assert!(!should_close_on_queue_full_streak(2, PressureState::Pressured));
|
||||
assert!(!should_close_on_queue_full_streak(3, PressureState::Pressured));
|
||||
assert!(should_close_on_queue_full_streak(3, PressureState::Shedding));
|
||||
assert!(should_close_on_queue_full_streak(
|
||||
u8::MAX,
|
||||
PressureState::Saturated
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fairness_retry_is_scheduled_only_when_queue_has_pending_bytes() {
|
||||
let mut snapshot = WorkerFairnessSnapshot::default();
|
||||
assert!(!should_schedule_fairness_retry(&snapshot));
|
||||
|
||||
snapshot.total_queued_bytes = 1;
|
||||
assert!(should_schedule_fairness_retry(&snapshot));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fairness_retry_delay_never_drops_below_one_millisecond() {
|
||||
assert_eq!(fairness_retry_delay(0), Duration::from_millis(1));
|
||||
assert_eq!(fairness_retry_delay(2), Duration::from_millis(2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -37,20 +37,26 @@ pub(super) fn validate_proxy_secret_len(data_len: usize, max_len: usize) -> Resu
|
||||
|
||||
/// Fetch Telegram proxy-secret binary.
|
||||
#[allow(dead_code)]
|
||||
pub async fn fetch_proxy_secret(cache_path: Option<&str>, max_len: usize) -> Result<Vec<u8>> {
|
||||
fetch_proxy_secret_with_upstream(cache_path, max_len, None).await
|
||||
pub async fn fetch_proxy_secret(
|
||||
cache_path: Option<&str>,
|
||||
max_len: usize,
|
||||
proxy_secret_url: Option<&str>,
|
||||
) -> Result<Vec<u8>> {
|
||||
fetch_proxy_secret_with_upstream(cache_path, max_len, proxy_secret_url, None).await
|
||||
}
|
||||
|
||||
/// Fetch Telegram proxy-secret binary, optionally through upstream routing.
|
||||
pub async fn fetch_proxy_secret_with_upstream(
|
||||
cache_path: Option<&str>,
|
||||
max_len: usize,
|
||||
proxy_secret_url: Option<&str>,
|
||||
upstream: Option<Arc<UpstreamManager>>,
|
||||
) -> Result<Vec<u8>> {
|
||||
let cache = cache_path.unwrap_or("proxy-secret");
|
||||
|
||||
// 1) Try fresh download first.
|
||||
match download_proxy_secret_with_max_len_via_upstream(max_len, upstream).await {
|
||||
match download_proxy_secret_with_max_len_via_upstream(max_len, upstream, proxy_secret_url).await
|
||||
{
|
||||
Ok(data) => {
|
||||
if let Err(e) = tokio::fs::write(cache, &data).await {
|
||||
warn!(error = %e, "Failed to cache proxy-secret (non-fatal)");
|
||||
@@ -91,14 +97,19 @@ pub async fn fetch_proxy_secret_with_upstream(
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub async fn download_proxy_secret_with_max_len(max_len: usize) -> Result<Vec<u8>> {
|
||||
download_proxy_secret_with_max_len_via_upstream(max_len, None).await
|
||||
download_proxy_secret_with_max_len_via_upstream(max_len, None, None).await
|
||||
}
|
||||
|
||||
pub async fn download_proxy_secret_with_max_len_via_upstream(
|
||||
max_len: usize,
|
||||
upstream: Option<Arc<UpstreamManager>>,
|
||||
proxy_secret_url: Option<&str>,
|
||||
) -> Result<Vec<u8>> {
|
||||
let resp = https_get("https://core.telegram.org/getProxySecret", upstream).await?;
|
||||
let resp = https_get(
|
||||
proxy_secret_url.unwrap_or("https://core.telegram.org/getProxySecret"),
|
||||
upstream,
|
||||
)
|
||||
.await?;
|
||||
|
||||
if !(200..=299).contains(&resp.status) {
|
||||
return Err(ProxyError::Proxy(format!(
|
||||
|
||||
Reference in New Issue
Block a user