1use std::{
6 borrow::Cow,
7 fmt,
8 net::{IpAddr, SocketAddr},
9};
10
11use faststr::FastStr;
12use http::uri::{Scheme, Uri};
13use volo::{client::Apply, context::Context, net::Address};
14
15use super::utils::{get_default_port, is_default_port};
16use crate::{
17 client::dns::Port,
18 context::ClientContext,
19 error::{
20 ClientError,
21 client::{Result, bad_scheme, no_address, port_unavailable, scheme_unavailable},
22 },
23 utils::consts,
24};
25
26#[derive(Clone, Debug, Default)]
28pub enum Target {
29 #[default]
30 None,
32 Remote(RemoteTarget),
34 #[cfg(target_family = "unix")]
36 Local(std::os::unix::net::SocketAddr),
37}
38
39impl fmt::Display for Target {
40 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41 match self {
42 Target::None => f.write_str("none"),
43 Target::Remote(rt) => write!(f, "{rt}"),
44 #[cfg(target_family = "unix")]
45 Target::Local(sa) => {
46 if let Some(path) = sa.as_pathname().and_then(std::path::Path::to_str) {
47 f.write_str(path)
48 } else {
49 f.write_str("[unnamed]")
50 }
51 }
52 }
53 }
54}
55
56#[derive(Clone, Debug)]
58pub struct RemoteTarget {
59 pub scheme: Scheme,
61 pub host: RemoteHost,
63 pub port: u16,
65}
66
67impl fmt::Display for RemoteTarget {
68 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69 f.write_str(self.scheme.as_str())?;
70 f.write_str("://")?;
71 write!(f, "{}", self.host)?;
72 if !is_default_port(&self.scheme, self.port) {
73 write!(f, ":{}", self.port)?;
74 }
75 Ok(())
76 }
77}
78
79#[derive(Clone, Debug)]
81pub enum RemoteHost {
82 Ip(IpAddr),
84 Name(FastStr),
86}
87
88impl fmt::Display for RemoteHost {
89 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90 match self {
91 Self::Ip(ip) => {
92 if ip.is_ipv4() {
93 write!(f, "{ip}")
94 } else {
95 write!(f, "[{ip}]")
96 }
97 }
98 Self::Name(name) => f.write_str(name),
99 }
100 }
101}
102
103fn check_scheme(scheme: &Scheme) -> Result<()> {
104 if scheme == &Scheme::HTTPS {
105 #[cfg(not(feature = "__tls"))]
106 {
107 tracing::error!("[Volo-HTTP] https is not allowed when feature `tls` is not enabled");
108 return Err(bad_scheme(scheme.clone()));
109 }
110 #[cfg(feature = "__tls")]
111 return Ok(());
112 }
113 if scheme == &Scheme::HTTP {
114 return Ok(());
115 }
116 tracing::error!("[Volo-HTTP] scheme '{scheme}' is unsupported");
117 Err(bad_scheme(scheme.clone()))
118}
119
120impl Target {
121 pub const unsafe fn new_host_unchecked(scheme: Scheme, host: FastStr, port: u16) -> Self {
131 Self::Remote(RemoteTarget {
132 scheme,
133 host: RemoteHost::Name(host),
134 port,
135 })
136 }
137
138 pub const unsafe fn new_addr_unchecked(scheme: Scheme, ip: IpAddr, port: u16) -> Self {
148 Self::Remote(RemoteTarget {
149 scheme,
150 host: RemoteHost::Ip(ip),
151 port,
152 })
153 }
154
155 pub fn new_host<S>(scheme: Option<Scheme>, host: S, port: Option<u16>) -> Result<Self>
157 where
158 S: Into<Cow<'static, str>>,
159 {
160 let scheme = scheme.unwrap_or(Scheme::HTTP);
161 check_scheme(&scheme)?;
162 let host = FastStr::from(host.into());
163 let port = match port {
164 Some(p) => p,
165 None => get_default_port(&scheme),
166 };
167 Ok(unsafe { Self::new_host_unchecked(scheme, host, port) })
169 }
170
171 pub fn new_addr(scheme: Option<Scheme>, ip: IpAddr, port: Option<u16>) -> Result<Self> {
173 let scheme = scheme.unwrap_or(Scheme::HTTP);
174 check_scheme(&scheme)?;
175 let port = match port {
176 Some(p) => p,
177 None => get_default_port(&scheme),
178 };
179 Ok(unsafe { Self::new_addr_unchecked(scheme, ip, port) })
181 }
182
183 pub fn from_host<S>(host: S) -> Self
185 where
186 S: Into<Cow<'static, str>>,
187 {
188 let host = FastStr::from(host.into());
189 unsafe { Self::new_host_unchecked(Scheme::HTTP, host, consts::HTTP_DEFAULT_PORT) }
191 }
192
193 pub fn from_uri(uri: &Uri) -> Result<Self> {
195 let scheme = uri.scheme().cloned().unwrap_or(Scheme::HTTP);
196 check_scheme(&scheme)?;
197 let host = uri.host().ok_or_else(no_address)?;
198 let port = match uri.port_u16() {
199 Some(p) => p,
200 None => get_default_port(&scheme),
201 };
202
203 Ok(unsafe {
205 match host.parse::<IpAddr>() {
206 Ok(ip) => Self::new_addr_unchecked(scheme, ip, port),
207 Err(_) => {
208 Self::new_host_unchecked(scheme, FastStr::from_string(host.to_owned()), port)
209 }
210 }
211 })
212 }
213
214 pub fn set_scheme(&mut self, scheme: Scheme) -> Result<()> {
219 let rt = match self.remote_mut() {
220 Some(rt) => rt,
221 None => {
222 tracing::warn!("[Volo-HTTP] set scheme to an empty target or uds is invalid");
223 return Err(scheme_unavailable());
224 }
225 };
226 check_scheme(&scheme)?;
227 if is_default_port(&rt.scheme, rt.port) {
228 rt.port = get_default_port(&scheme);
229 }
230 rt.scheme = scheme;
231 Ok(())
232 }
233
234 pub fn set_port(&mut self, port: u16) -> Result<()> {
236 let rt = match self.remote_mut() {
237 Some(rt) => rt,
238 None => {
239 tracing::warn!("[Volo-HTTP] set port to an empty target or uds is invalid");
240 return Err(port_unavailable());
241 }
242 };
243 rt.port = port;
244 Ok(())
245 }
246
247 pub fn is_none(&self) -> bool {
249 matches!(self, Target::None)
250 }
251
252 pub fn remote_ref(&self) -> Option<&RemoteTarget> {
254 match self {
255 Self::Remote(remote) => Some(remote),
256 _ => None,
257 }
258 }
259
260 pub fn remote_mut(&mut self) -> Option<&mut RemoteTarget> {
262 match self {
263 Self::Remote(remote) => Some(remote),
264 _ => None,
265 }
266 }
267
268 pub fn remote_ip(&self) -> Option<&IpAddr> {
270 if let Self::Remote(rt) = &self {
271 if let RemoteHost::Ip(ip) = &rt.host {
272 return Some(ip);
273 }
274 }
275 None
276 }
277
278 pub fn remote_host(&self) -> Option<&FastStr> {
280 if let Self::Remote(rt) = &self {
281 if let RemoteHost::Name(name) = &rt.host {
282 return Some(name);
283 }
284 }
285 None
286 }
287
288 #[cfg(target_family = "unix")]
290 pub fn unix_socket_addr(&self) -> Option<&std::os::unix::net::SocketAddr> {
291 if let Self::Local(sa) = &self {
292 Some(sa)
293 } else {
294 None
295 }
296 }
297
298 pub fn scheme(&self) -> Option<&Scheme> {
300 if let Self::Remote(rt) = self {
301 Some(&rt.scheme)
302 } else {
303 None
304 }
305 }
306
307 pub fn port(&self) -> Option<u16> {
309 if let Self::Remote(rt) = self {
310 Some(rt.port)
311 } else {
312 None
313 }
314 }
315}
316
317impl From<Address> for Target {
318 fn from(value: Address) -> Self {
319 match value {
320 Address::Ip(sa) => {
321 unsafe { Target::new_addr_unchecked(Scheme::HTTP, sa.ip(), sa.port()) }
323 }
324 #[cfg(target_family = "unix")]
325 Address::Unix(uds) => Target::Local(uds),
326 #[allow(unreachable_patterns)]
327 _ => unimplemented!("unsupported type of address"),
328 }
329 }
330}
331
332fn ipv6_strip_brackets(src: FastStr) -> FastStr {
333 let bytes = src.as_bytes();
334 match (bytes.first(), bytes.last()) {
335 (Some(b'['), Some(b']')) => unsafe { src.index(1, src.len() - 1) },
337 _ => src,
338 }
339}
340
341impl Apply<ClientContext> for Target {
342 type Error = ClientError;
343
344 fn apply(self, cx: &mut ClientContext) -> Result<(), Self::Error> {
345 cx.set_target(self.clone());
346
347 match self {
348 Self::Remote(rt) => {
349 match rt.host {
350 RemoteHost::Ip(ip) => {
351 let sa = SocketAddr::new(ip, rt.port);
352 let callee = cx.rpc_info_mut().callee_mut();
353 callee.set_service_name(FastStr::from_string(format!("{}", sa.ip())));
354 callee.set_address(Address::Ip(sa));
355 }
356 RemoteHost::Name(host) => {
357 let port = rt.port;
358 let callee = cx.rpc_info_mut().callee_mut();
359 callee.set_service_name(ipv6_strip_brackets(host));
360 callee.insert(Port(port));
364 }
365 }
366 }
367 #[cfg(target_family = "unix")]
368 Self::Local(uds) => {
369 let callee = cx.rpc_info_mut().callee_mut();
370 callee.set_address(Address::Unix(uds));
371 callee.set_service_name(FastStr::from_static_str("unix-domain-socket"));
372 }
373 Self::None => {}
374 }
375
376 Ok(())
377 }
378}
379
380#[cfg(test)]
381mod target_tests {
382 use faststr::FastStr;
383
384 use super::ipv6_strip_brackets;
385
386 #[test]
387 fn ipv6_strip_test() {
388 {
389 let s = FastStr::from_static_str("foo");
390 assert_eq!(ipv6_strip_brackets(s.clone()), s);
391 }
392 {
393 let s = FastStr::from_static_str("127.0.0.1");
394 assert_eq!(ipv6_strip_brackets(s.clone()), s);
395 }
396 {
397 let s = FastStr::from_static_str("[[[");
398 assert_eq!(ipv6_strip_brackets(s.clone()), s);
399 }
400 {
401 let s = FastStr::from_static_str("]]]");
402 assert_eq!(ipv6_strip_brackets(s.clone()), s);
403 }
404 {
405 let s = FastStr::from_static_str("(::1)");
406 assert_eq!(ipv6_strip_brackets(s.clone()), s);
407 }
408 {
409 let s = FastStr::from_static_str("[::1]");
410 assert_eq!(ipv6_strip_brackets(s), "::1");
411 }
412 }
413}