open_feature_flagd/resolver/common/
upstream.rs

1use 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>, // Only set for custom name resolution (envoy://)
9}
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, // Standard HTTP doesn't need custom authority
22            });
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); // Use Envoy port directly
38
39            (
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) // Standard resolution doesn't need custom authority
53        };
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}