aws_runtime/
endpoint_discovery.rs

1/*
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6//! Endpoint override detection for business metrics tracking
7
8use aws_smithy_runtime_api::box_error::BoxError;
9use aws_smithy_runtime_api::client::interceptors::Intercept;
10use aws_smithy_types::config_bag::ConfigBag;
11
12use crate::sdk_feature::AwsSdkFeature;
13
14/// Interceptor that detects when a custom endpoint URL is being used
15/// and tracks it for business metrics.
16#[derive(Debug, Default)]
17#[non_exhaustive]
18pub struct EndpointOverrideInterceptor;
19
20impl EndpointOverrideInterceptor {
21    /// Creates a new `EndpointOverrideInterceptor`
22    pub fn new() -> Self {
23        Self
24    }
25}
26
27impl Intercept for EndpointOverrideInterceptor {
28    fn name(&self) -> &'static str {
29        "EndpointOverrideInterceptor"
30    }
31
32    fn read_before_execution(
33        &self,
34        _context: &aws_smithy_runtime_api::client::interceptors::context::BeforeSerializationInterceptorContextRef<'_>,
35        cfg: &mut ConfigBag,
36    ) -> Result<(), BoxError> {
37        // Check if a custom endpoint URL was configured
38        // This is stored early in the config bag before endpoint resolution
39        if let Some(endpoint_url) = cfg.load::<aws_types::endpoint_config::EndpointUrl>() {
40            let url_str = endpoint_url.0.as_str();
41
42            // Standard AWS endpoints follow patterns like:
43            // - *.amazonaws.com
44            // - *.amazonaws.com.cn (China)
45            // If the endpoint doesn't match these patterns, it's a custom endpoint
46            if !url_str.contains(".amazonaws.com") && !url_str.contains(".amazonaws.com.cn") {
47                cfg.interceptor_state()
48                    .store_append(AwsSdkFeature::EndpointOverride);
49            }
50        }
51
52        Ok(())
53    }
54}
55
56#[cfg(test)]
57mod tests {
58    use super::*;
59    use aws_smithy_runtime_api::client::interceptors::context::{Input, InterceptorContext};
60    use aws_smithy_types::config_bag::ConfigBag;
61
62    #[test]
63    fn test_detects_custom_endpoint() {
64        let context = InterceptorContext::new(Input::doesnt_matter());
65
66        let mut cfg = ConfigBag::base();
67        cfg.interceptor_state()
68            .store_put(aws_types::endpoint_config::EndpointUrl(
69                "https://custom.example.com".to_string(),
70            ));
71
72        let interceptor = EndpointOverrideInterceptor::new();
73        let ctx = Into::into(&context);
74        interceptor.read_before_execution(&ctx, &mut cfg).unwrap();
75
76        let features: Vec<_> = cfg.load::<AwsSdkFeature>().collect();
77        assert_eq!(features.len(), 1);
78        assert!(features
79            .iter()
80            .any(|f| matches!(f, AwsSdkFeature::EndpointOverride)));
81    }
82
83    #[test]
84    fn test_ignores_default_endpoint() {
85        let context = InterceptorContext::new(Input::doesnt_matter());
86
87        let mut cfg = ConfigBag::base();
88        cfg.interceptor_state()
89            .store_put(aws_types::endpoint_config::EndpointUrl(
90                "https://service.amazonaws.com".to_string(),
91            ));
92
93        let interceptor = EndpointOverrideInterceptor::new();
94        let ctx = Into::into(&context);
95        interceptor.read_before_execution(&ctx, &mut cfg).unwrap();
96
97        let features: Vec<_> = cfg.load::<AwsSdkFeature>().collect();
98        assert_eq!(features.len(), 0);
99    }
100
101    #[test]
102    fn test_no_endpoint_url_configured() {
103        let context = InterceptorContext::new(Input::doesnt_matter());
104
105        let mut cfg = ConfigBag::base();
106        // No endpoint URL configured
107
108        let interceptor = EndpointOverrideInterceptor::new();
109        let ctx = Into::into(&context);
110        interceptor.read_before_execution(&ctx, &mut cfg).unwrap();
111
112        let features: Vec<_> = cfg.load::<AwsSdkFeature>().collect();
113        assert_eq!(features.len(), 0);
114    }
115}