open_feature_flagd/resolver/common/
upstream.rs1use crate::error::FlagdError;
2use std::str::FromStr;
3use tonic::transport::{Endpoint, Uri};
4use tracing::debug;
5
6pub struct UpstreamConfig {
7 endpoint: Endpoint,
8 authority: Option<String>, }
10
11impl UpstreamConfig {
12 pub fn new(target: String, is_in_process: bool) -> Result<Self, FlagdError> {
13 debug!("Creating upstream config for target: {}", target);
14
15 if target.starts_with("http://") {
16 debug!("Target is already an HTTP endpoint");
17 let endpoint = Endpoint::from_shared(target)
18 .map_err(|e| FlagdError::Config(format!("Invalid endpoint: {}", e)))?;
19 return Ok(Self {
20 endpoint,
21 authority: None, });
23 }
24
25 let (endpoint_str, authority) = if target.starts_with("envoy://") {
26 let uri = Uri::from_str(&target)
27 .map_err(|e| FlagdError::Config(format!("Failed to parse target URI: {}", e)))?;
28 let authority = uri.path().trim_start_matches('/');
29
30 if authority.is_empty() {
31 return Err(FlagdError::Config(
32 "Service name (authority) cannot be empty".to_string(),
33 ));
34 }
35
36 let host = uri.host().unwrap_or("localhost");
37 let port = uri.port_u16().unwrap_or(9211); (
40 format!("http://{}:{}", host, port),
41 Some(authority.to_string()),
42 )
43 } else {
44 let parts: Vec<&str> = target.split(':').collect();
45 let host = parts.first().unwrap_or(&"localhost").to_string();
46 let port = parts
47 .get(1)
48 .and_then(|p| p.parse().ok())
49 .unwrap_or(if is_in_process { 8015 } else { 8013 });
50
51 debug!("Using standard resolution with {}:{}", host, port);
52 (format!("http://{}:{}", host, port), None) };
54
55 let endpoint = Endpoint::from_shared(endpoint_str)
56 .map_err(|e| FlagdError::Config(format!("Invalid endpoint: {}", e)))?;
57
58 Ok(Self {
59 endpoint,
60 authority,
61 })
62 }
63
64 pub fn endpoint(&self) -> &Endpoint {
65 &self.endpoint
66 }
67
68 pub fn authority(&self) -> Option<String> {
69 self.authority.clone()
70 }
71}