Skip to content

Commit 9e5e3cb

Browse files
authored
fix(API): proxy name missing in proxy chain for group outbounds (#817)
* API fix * clippy * clippy * add tests * up
1 parent 2735015 commit 9e5e3cb

File tree

13 files changed

+339
-170
lines changed

13 files changed

+339
-170
lines changed

clash_lib/src/app/outbound/manager.rs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use crate::{
2626
},
2727
print_and_exit,
2828
proxy::{
29-
OutboundType, fallback,
29+
fallback,
3030
group::smart,
3131
loadbalance, selector, socks, trojan,
3232
utils::{DirectConnector, ProxyConnector},
@@ -139,29 +139,23 @@ impl OutboundManager {
139139
}
140140

141141
for (k, v) in self.handlers.iter().chain(provider_handlers.iter()) {
142-
let mut m = v.as_map().await;
142+
let mut m = if let Some(g) = v.try_as_group_handler() {
143+
g.as_map().await
144+
} else {
145+
let mut m = HashMap::new();
146+
m.insert("type".to_string(), Box::new(v.proto()) as _);
147+
m
148+
};
143149

144150
let alive = proxy_manager.alive(k).await;
145151
let history = proxy_manager.delay_history(k).await;
146152
let support_udp = v.support_udp().await;
147-
let icon = v.icon();
148153

149154
m.insert("history".to_string(), Box::new(history));
150155
m.insert("alive".to_string(), Box::new(alive));
151156
m.insert("name".to_string(), Box::new(k.to_owned()));
152157
m.insert("udp".to_string(), Box::new(support_udp));
153158

154-
if matches!(
155-
v.proto(),
156-
OutboundType::UrlTest
157-
| OutboundType::Relay
158-
| OutboundType::Selector
159-
| OutboundType::Fallback
160-
| OutboundType::LoadBalance
161-
) {
162-
m.insert("icon".to_string(), Box::new(icon));
163-
}
164-
165159
r.insert(k.clone(), Box::new(m) as _);
166160
}
167161

@@ -172,7 +166,13 @@ impl OutboundManager {
172166
&self,
173167
proxy: &AnyOutboundHandler,
174168
) -> HashMap<String, Box<dyn Serialize + Send>> {
175-
let mut r = proxy.as_map().await;
169+
let mut r = if let Some(g) = proxy.try_as_group_handler() {
170+
g.as_map().await
171+
} else {
172+
let mut m = HashMap::new();
173+
m.insert("type".to_string(), Box::new(proxy.proto()) as _);
174+
m
175+
};
176176

177177
let proxy_manager = self.proxy_manager.clone();
178178

clash_lib/src/proxy/group/fallback/mod.rs

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
use std::{collections::HashMap, fmt::Debug, io};
1+
use async_trait::async_trait;
22

3-
use erased_serde::Serialize;
3+
use std::{fmt::Debug, io};
44
use tracing::debug;
55

66
use crate::{
@@ -14,6 +14,7 @@ use crate::{
1414
proxy::{
1515
AnyOutboundHandler, ConnectorType, DialWithConnector, HandlerCommonOptions,
1616
OutboundHandler, OutboundType,
17+
group::GroupProxyAPIResponse,
1718
utils::{RemoteConnector, provider_helper::get_proxies_from_providers},
1819
},
1920
session::Session,
@@ -71,7 +72,7 @@ impl Handler {
7172

7273
impl DialWithConnector for Handler {}
7374

74-
#[async_trait::async_trait]
75+
#[async_trait]
7576
impl OutboundHandler for Handler {
7677
/// The name of the outbound handler
7778
fn name(&self) -> &str {
@@ -96,13 +97,11 @@ impl OutboundHandler for Handler {
9697
resolver: ThreadSafeDNSResolver,
9798
) -> io::Result<BoxedChainedStream> {
9899
let proxy = self.find_alive_proxy(true).await;
99-
match proxy.connect_stream(sess, resolver).await {
100-
Ok(s) => {
101-
s.append_to_chain(self.name()).await;
102-
Ok(s)
103-
}
104-
Err(e) => Err(e),
105-
}
100+
let s = proxy.connect_stream(sess, resolver).await?;
101+
102+
s.append_to_chain(self.name()).await;
103+
104+
Ok(s)
106105
}
107106

108107
/// connect to remote target via UDP
@@ -112,7 +111,11 @@ impl OutboundHandler for Handler {
112111
resolver: ThreadSafeDNSResolver,
113112
) -> io::Result<BoxedChainedDatagram> {
114113
let proxy = self.find_alive_proxy(true).await;
115-
proxy.connect_datagram(sess, resolver).await
114+
let s = proxy.connect_datagram(sess, resolver).await?;
115+
116+
s.append_to_chain(self.name()).await;
117+
118+
Ok(s)
116119
}
117120

118121
async fn support_connector(&self) -> ConnectorType {
@@ -131,21 +134,19 @@ impl OutboundHandler for Handler {
131134
.await
132135
}
133136

134-
async fn as_map(&self) -> HashMap<String, Box<dyn Serialize + Send>> {
135-
let all = get_proxies_from_providers(&self.providers, false).await;
136-
137-
let mut m = HashMap::new();
138-
m.insert("type".to_string(), Box::new(self.proto()) as _);
139-
m.insert(
140-
"now".to_string(),
141-
Box::new(self.find_alive_proxy(false).await.name().to_owned()) as _,
142-
);
143-
m.insert(
144-
"all".to_string(),
145-
Box::new(all.iter().map(|x| x.name().to_owned()).collect::<Vec<_>>())
146-
as _,
147-
);
148-
m
137+
fn try_as_group_handler(&self) -> Option<&dyn GroupProxyAPIResponse> {
138+
Some(self as _)
139+
}
140+
}
141+
142+
#[async_trait]
143+
impl GroupProxyAPIResponse for Handler {
144+
async fn get_proxies(&self) -> Vec<AnyOutboundHandler> {
145+
Handler::get_proxies(self, false).await
146+
}
147+
148+
async fn get_active_proxy(&self) -> Option<AnyOutboundHandler> {
149+
Some(Handler::find_alive_proxy(self, false).await)
149150
}
150151

151152
fn icon(&self) -> Option<String> {

clash_lib/src/proxy/group/loadbalance/mod.rs

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
mod helpers;
22

3-
use std::{collections::HashMap, io, sync::Arc};
4-
5-
use erased_serde::Serialize;
3+
use async_trait::async_trait;
64
use helpers::strategy_sticky_session;
5+
use std::{io, sync::Arc};
76
use tokio::sync::Mutex;
87
use tracing::debug;
98

9+
use self::helpers::{StrategyFn, strategy_consistent_hashring, strategy_rr};
1010
use crate::{
1111
app::{
1212
dispatcher::{BoxedChainedDatagram, BoxedChainedStream},
@@ -19,13 +19,12 @@ use crate::{
1919
proxy::{
2020
AnyOutboundHandler, ConnectorType, DialWithConnector, HandlerCommonOptions,
2121
OutboundHandler, OutboundType,
22+
group::GroupProxyAPIResponse,
2223
utils::{RemoteConnector, provider_helper::get_proxies_from_providers},
2324
},
2425
session::Session,
2526
};
2627

27-
use self::helpers::{StrategyFn, strategy_consistent_hashring, strategy_rr};
28-
2928
#[derive(Default, Clone)]
3029
pub struct HandlerOptions {
3130
pub common_opts: HandlerCommonOptions,
@@ -82,7 +81,7 @@ impl Handler {
8281

8382
impl DialWithConnector for Handler {}
8483

85-
#[async_trait::async_trait]
84+
#[async_trait]
8685
impl OutboundHandler for Handler {
8786
/// The name of the outbound handler
8887
fn name(&self) -> &str {
@@ -109,13 +108,12 @@ impl OutboundHandler for Handler {
109108
let proxies = self.get_proxies(false).await;
110109
let proxy = (self.inner.lock().await.strategy_fn)(proxies, sess).await?;
111110
debug!("{} use proxy {}", self.name(), proxy.name());
112-
match proxy.connect_stream(sess, resolver).await {
113-
Ok(s) => {
114-
s.append_to_chain(self.name()).await;
115-
Ok(s)
116-
}
117-
Err(e) => Err(e),
118-
}
111+
112+
let s = proxy.connect_stream(sess, resolver).await?;
113+
114+
s.append_to_chain(self.name()).await;
115+
116+
Ok(s)
119117
}
120118

121119
/// connect to remote target via UDP
@@ -127,7 +125,12 @@ impl OutboundHandler for Handler {
127125
let proxies = self.get_proxies(false).await;
128126
let proxy = (self.inner.lock().await.strategy_fn)(proxies, sess).await?;
129127
debug!("{} use proxy {}", self.name(), proxy.name());
130-
proxy.connect_datagram(sess, resolver).await
128+
129+
let s = proxy.connect_datagram(sess, resolver).await?;
130+
131+
s.append_to_chain(self.name()).await;
132+
133+
Ok(s)
131134
}
132135

133136
async fn support_connector(&self) -> ConnectorType {
@@ -148,18 +151,19 @@ impl OutboundHandler for Handler {
148151
.await
149152
}
150153

151-
async fn as_map(&self) -> HashMap<String, Box<dyn Serialize + Send>> {
152-
let all = get_proxies_from_providers(&self.providers, false).await;
154+
fn try_as_group_handler(&self) -> Option<&dyn GroupProxyAPIResponse> {
155+
Some(self as _)
156+
}
157+
}
153158

154-
let mut m = HashMap::new();
155-
m.insert("type".to_string(), Box::new(self.proto()) as _);
159+
#[async_trait]
160+
impl GroupProxyAPIResponse for Handler {
161+
async fn get_proxies(&self) -> Vec<AnyOutboundHandler> {
162+
Handler::get_proxies(self, false).await
163+
}
156164

157-
m.insert(
158-
"all".to_string(),
159-
Box::new(all.iter().map(|x| x.name().to_owned()).collect::<Vec<_>>())
160-
as _,
161-
);
162-
m
165+
async fn get_active_proxy(&self) -> Option<AnyOutboundHandler> {
166+
None
163167
}
164168

165169
fn icon(&self) -> Option<String> {

clash_lib/src/proxy/group/mod.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,51 @@
1+
use crate::proxy::{AnyOutboundHandler, OutboundHandler};
2+
use async_trait::async_trait;
3+
use erased_serde::Serialize;
4+
use std::collections::HashMap;
5+
16
pub mod fallback;
27
pub mod loadbalance;
38
pub mod relay;
49
pub mod selector;
510
pub mod smart;
611
pub mod urltest;
12+
13+
/// Convenience trait for group proxy serializing API responses.
14+
#[async_trait]
15+
pub trait GroupProxyAPIResponse: OutboundHandler {
16+
/// Returns all proxies in the group, which are usually stored in a list of
17+
/// ProxyProviders.
18+
async fn get_proxies(&self) -> Vec<AnyOutboundHandler>;
19+
/// Returns the current effective proxy for the group.
20+
/// e.g. for a selector, it returns the currently selected proxy, and for
21+
/// urltest, it returns the fastest proxy, etc.
22+
async fn get_active_proxy(&self) -> Option<AnyOutboundHandler>;
23+
24+
/// used in the API responses.
25+
async fn as_map(&self) -> HashMap<String, Box<dyn Serialize + Send>> {
26+
let all = self.get_proxies().await;
27+
28+
let mut m = HashMap::new();
29+
m.insert("type".to_string(), Box::new(self.proto()) as _);
30+
31+
if let Some(active) = self.get_active_proxy().await {
32+
m.insert("now".to_string(), Box::new(active.name().to_owned()) as _);
33+
}
34+
35+
let icon = self.icon();
36+
if let Some(icon) = icon {
37+
m.insert("icon".to_string(), Box::new(icon) as _);
38+
}
39+
40+
m.insert(
41+
"all".to_string(),
42+
Box::new(all.iter().map(|x| x.name().to_owned()).collect::<Vec<_>>())
43+
as _,
44+
);
45+
m
46+
}
47+
48+
fn icon(&self) -> Option<String> {
49+
None
50+
}
51+
}

clash_lib/src/proxy/group/relay/mod.rs

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
use std::{collections::HashMap, io, sync::Arc};
1+
use std::{io, sync::Arc};
22

33
use async_trait::async_trait;
4-
use erased_serde::Serialize;
54
use futures::stream::{self, StreamExt};
65
use tracing::debug;
76

@@ -18,6 +17,7 @@ use crate::{
1817
proxy::{
1918
AnyOutboundHandler, ConnectorType, DialWithConnector, HandlerCommonOptions,
2019
OutboundHandler, OutboundType,
20+
group::GroupProxyAPIResponse,
2121
utils::{
2222
DirectConnector, ProxyConnector, RemoteConnector,
2323
provider_helper::get_proxies_from_providers,
@@ -178,18 +178,19 @@ impl OutboundHandler for Handler {
178178
ConnectorType::None
179179
}
180180

181-
async fn as_map(&self) -> HashMap<String, Box<dyn Serialize + Send>> {
182-
let all = get_proxies_from_providers(&self.providers, false).await;
181+
fn try_as_group_handler(&self) -> Option<&dyn GroupProxyAPIResponse> {
182+
Some(self as _)
183+
}
184+
}
183185

184-
let mut m = HashMap::new();
185-
m.insert("type".to_string(), Box::new(self.proto()) as _);
186-
m.insert(
187-
"all".to_string(),
188-
Box::new(all.iter().map(|x| x.name().to_owned()).collect::<Vec<_>>())
189-
as _,
190-
);
186+
#[async_trait]
187+
impl GroupProxyAPIResponse for Handler {
188+
async fn get_proxies(&self) -> Vec<AnyOutboundHandler> {
189+
Handler::get_proxies(self, false).await
190+
}
191191

192-
m
192+
async fn get_active_proxy(&self) -> Option<AnyOutboundHandler> {
193+
None
193194
}
194195

195196
fn icon(&self) -> Option<String> {

0 commit comments

Comments
 (0)