Loading rust-runtime/inlineable/Cargo.toml +1 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ repository = "https://github.com/awslabs/smithy-rs" "pin-project-lite" = "0.2" "tower" = { version = "0.4.11", default_features = false } "async-trait" = "0.1" "url" = "2.2.2" [dev-dependencies] proptest = "1" Loading rust-runtime/inlineable/src/endpoint_lib.rs 0 → 100644 +10 −0 Original line number Diff line number Diff line /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ mod arn; mod diagnostic; mod host; mod parse_url; mod substring; rust-runtime/inlineable/src/endpoint_lib/arn.rs 0 → 100644 +153 −0 Original line number Diff line number Diff line /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ use crate::endpoint_lib::diagnostic::DiagnosticCollector; use std::borrow::Cow; use std::error::Error; use std::fmt::{Display, Formatter}; #[derive(Debug, Eq, PartialEq)] pub(crate) struct Arn<'a> { partition: &'a str, service: &'a str, region: &'a str, account_id: &'a str, resource_id: Vec<&'a str>, } #[allow(unused)] impl<'a> Arn<'a> { pub(crate) fn partition(&self) -> &'a str { self.partition } pub(crate) fn service(&self) -> &'a str { self.service } pub(crate) fn region(&self) -> &'a str { self.region } pub(crate) fn account_id(&self) -> &'a str { self.account_id } pub(crate) fn resource_id(&self) -> &Vec<&'a str> { &self.resource_id } } #[derive(Debug, PartialEq)] pub(crate) struct InvalidArn { message: Cow<'static, str>, } impl InvalidArn { fn from_static(message: &'static str) -> InvalidArn { Self { message: Cow::Borrowed(message), } } } impl Display for InvalidArn { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.message) } } impl Error for InvalidArn {} impl<'a> Arn<'a> { pub(crate) fn parse(arn: &'a str) -> Result<Self, InvalidArn> { let mut split = arn.splitn(6, ':'); let invalid_format = || InvalidArn::from_static("ARN must have 6 components delimited by `:`"); let arn = split.next().ok_or_else(invalid_format)?; let partition = split.next().ok_or_else(invalid_format)?; let service = split.next().ok_or_else(invalid_format)?; let region = split.next().ok_or_else(invalid_format)?; let account_id = split.next().ok_or_else(invalid_format)?; let resource_id = split.next().ok_or_else(invalid_format)?; if arn != "arn" { return Err(InvalidArn::from_static( "first component of the ARN must be `arn`", )); } if partition.is_empty() || service.is_empty() || resource_id.is_empty() { return Err(InvalidArn::from_static( "partition, service, and resource id must all be non-empty", )); } let resource_id = resource_id.split([':', '/']).collect::<Vec<_>>(); Ok(Self { partition, service, region, account_id, resource_id, }) } } pub(crate) fn parse_arn<'a, 'b>(input: &'a str, e: &'b mut DiagnosticCollector) -> Option<Arn<'a>> { e.capture(Arn::parse(input)) } #[cfg(test)] mod test { use super::Arn; use crate::endpoint_lib::diagnostic::DiagnosticCollector; #[test] fn arn_parser() { let arn = "arn:aws:s3:us-east-2:012345678:outpost:op-1234"; let parsed = Arn::parse(arn).expect("valid ARN"); assert_eq!( parsed, Arn { partition: "aws", service: "s3", region: "us-east-2", account_id: "012345678", resource_id: vec!["outpost", "op-1234"] } ); } #[test] fn allow_slash_arns() { let arn = "arn:aws:s3:us-east-2:012345678:outpost/op-1234"; let parsed = Arn::parse(arn).expect("valid ARN"); assert_eq!( parsed, Arn { partition: "aws", service: "s3", region: "us-east-2", account_id: "012345678", resource_id: vec!["outpost", "op-1234"] } ); } #[test] fn resource_id_must_be_nonempty() { let arn = "arn:aws:s3:us-east-2:012345678:"; Arn::parse(arn).expect_err("empty resource"); } #[test] fn arns_with_empty_parts() { let arn = "arn:aws:s3:::my_corporate_bucket/Development/*"; assert_eq!( Arn::parse(arn).expect("valid arn"), Arn { partition: "aws", service: "s3", region: "", account_id: "", resource_id: vec!["my_corporate_bucket", "Development", "*"] } ); } } rust-runtime/inlineable/src/endpoint_lib/diagnostic.rs 0 → 100644 +45 −0 Original line number Diff line number Diff line /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ use std::error::Error; /// Diagnostic collector for endpoint resolution /// /// Endpoint functions return `Option<T>`—to enable diagnostic information to flow, we capture the /// last error that occurred. #[derive(Debug, Default)] pub(crate) struct DiagnosticCollector { last_error: Option<Box<dyn Error + Send + Sync>>, } impl DiagnosticCollector { /// Report an error to the collector pub(crate) fn report_error(&mut self, err: impl Into<Box<dyn Error + Send + Sync>>) { self.last_error = Some(err.into()); } /// Capture a result, returning Some(t) when the input was `Ok` and `None` otherwise pub(crate) fn capture<T, E: Into<Box<dyn Error + Send + Sync>>>( &mut self, err: Result<T, E>, ) -> Option<T> { match err { Ok(res) => Some(res), Err(e) => { self.report_error(e); None } } } pub(crate) fn take_last_error(&mut self) -> Option<Box<dyn Error + Send + Sync>> { self.last_error.take() } /// Create a new diagnostic collector pub(crate) fn new() -> Self { Self { last_error: None } } } rust-runtime/inlineable/src/endpoint_lib/host.rs 0 → 100644 +76 −0 Original line number Diff line number Diff line /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ use crate::endpoint_lib::diagnostic::DiagnosticCollector; pub(crate) fn is_valid_host_label( label: &str, allow_dots: bool, e: &mut DiagnosticCollector, ) -> bool { if allow_dots { for part in label.split('.') { if !is_valid_host_label(part, false, e) { return false; } } true } else { if label.is_empty() || label.len() > 63 { e.report_error("host was too short or too long"); return false; } label.chars().enumerate().all(|(idx, ch)| match (ch, idx) { ('-', 0) => { e.report_error("cannot start with `-`"); false } _ => ch.is_alphanumeric() || ch == '-', }) } } #[cfg(test)] mod test { use proptest::proptest; fn is_valid_host_label(label: &str, allow_dots: bool) -> bool { super::is_valid_host_label(label, allow_dots, &mut DiagnosticCollector::new()) } #[test] fn basic_cases() { assert_eq!(is_valid_host_label("", false), false); assert_eq!(is_valid_host_label("", true), false); assert_eq!(is_valid_host_label(".", true), false); assert_eq!(is_valid_host_label("a.b", true), true); assert_eq!(is_valid_host_label("a.b", false), false); assert_eq!(is_valid_host_label("a.b.", true), false); assert_eq!(is_valid_host_label("a.b.c", true), true); assert_eq!(is_valid_host_label("a_b", true), false); assert_eq!(is_valid_host_label(&"a".repeat(64), false), false); assert_eq!( is_valid_host_label(&format!("{}.{}", "a".repeat(63), "a".repeat(63)), true), true ); } #[test] fn start_bounds() { assert_eq!(is_valid_host_label("-foo", false), false); assert_eq!(is_valid_host_label("-foo", true), false); assert_eq!(is_valid_host_label(".foo", true), false); assert_eq!(is_valid_host_label("a-b.foo", true), true); } use crate::endpoint_lib::diagnostic::DiagnosticCollector; use proptest::prelude::*; proptest! { #[test] fn no_panics(s in any::<String>(), dots in any::<bool>()) { is_valid_host_label(&s, dots); } } } Loading
rust-runtime/inlineable/Cargo.toml +1 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ repository = "https://github.com/awslabs/smithy-rs" "pin-project-lite" = "0.2" "tower" = { version = "0.4.11", default_features = false } "async-trait" = "0.1" "url" = "2.2.2" [dev-dependencies] proptest = "1" Loading
rust-runtime/inlineable/src/endpoint_lib.rs 0 → 100644 +10 −0 Original line number Diff line number Diff line /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ mod arn; mod diagnostic; mod host; mod parse_url; mod substring;
rust-runtime/inlineable/src/endpoint_lib/arn.rs 0 → 100644 +153 −0 Original line number Diff line number Diff line /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ use crate::endpoint_lib::diagnostic::DiagnosticCollector; use std::borrow::Cow; use std::error::Error; use std::fmt::{Display, Formatter}; #[derive(Debug, Eq, PartialEq)] pub(crate) struct Arn<'a> { partition: &'a str, service: &'a str, region: &'a str, account_id: &'a str, resource_id: Vec<&'a str>, } #[allow(unused)] impl<'a> Arn<'a> { pub(crate) fn partition(&self) -> &'a str { self.partition } pub(crate) fn service(&self) -> &'a str { self.service } pub(crate) fn region(&self) -> &'a str { self.region } pub(crate) fn account_id(&self) -> &'a str { self.account_id } pub(crate) fn resource_id(&self) -> &Vec<&'a str> { &self.resource_id } } #[derive(Debug, PartialEq)] pub(crate) struct InvalidArn { message: Cow<'static, str>, } impl InvalidArn { fn from_static(message: &'static str) -> InvalidArn { Self { message: Cow::Borrowed(message), } } } impl Display for InvalidArn { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.message) } } impl Error for InvalidArn {} impl<'a> Arn<'a> { pub(crate) fn parse(arn: &'a str) -> Result<Self, InvalidArn> { let mut split = arn.splitn(6, ':'); let invalid_format = || InvalidArn::from_static("ARN must have 6 components delimited by `:`"); let arn = split.next().ok_or_else(invalid_format)?; let partition = split.next().ok_or_else(invalid_format)?; let service = split.next().ok_or_else(invalid_format)?; let region = split.next().ok_or_else(invalid_format)?; let account_id = split.next().ok_or_else(invalid_format)?; let resource_id = split.next().ok_or_else(invalid_format)?; if arn != "arn" { return Err(InvalidArn::from_static( "first component of the ARN must be `arn`", )); } if partition.is_empty() || service.is_empty() || resource_id.is_empty() { return Err(InvalidArn::from_static( "partition, service, and resource id must all be non-empty", )); } let resource_id = resource_id.split([':', '/']).collect::<Vec<_>>(); Ok(Self { partition, service, region, account_id, resource_id, }) } } pub(crate) fn parse_arn<'a, 'b>(input: &'a str, e: &'b mut DiagnosticCollector) -> Option<Arn<'a>> { e.capture(Arn::parse(input)) } #[cfg(test)] mod test { use super::Arn; use crate::endpoint_lib::diagnostic::DiagnosticCollector; #[test] fn arn_parser() { let arn = "arn:aws:s3:us-east-2:012345678:outpost:op-1234"; let parsed = Arn::parse(arn).expect("valid ARN"); assert_eq!( parsed, Arn { partition: "aws", service: "s3", region: "us-east-2", account_id: "012345678", resource_id: vec!["outpost", "op-1234"] } ); } #[test] fn allow_slash_arns() { let arn = "arn:aws:s3:us-east-2:012345678:outpost/op-1234"; let parsed = Arn::parse(arn).expect("valid ARN"); assert_eq!( parsed, Arn { partition: "aws", service: "s3", region: "us-east-2", account_id: "012345678", resource_id: vec!["outpost", "op-1234"] } ); } #[test] fn resource_id_must_be_nonempty() { let arn = "arn:aws:s3:us-east-2:012345678:"; Arn::parse(arn).expect_err("empty resource"); } #[test] fn arns_with_empty_parts() { let arn = "arn:aws:s3:::my_corporate_bucket/Development/*"; assert_eq!( Arn::parse(arn).expect("valid arn"), Arn { partition: "aws", service: "s3", region: "", account_id: "", resource_id: vec!["my_corporate_bucket", "Development", "*"] } ); } }
rust-runtime/inlineable/src/endpoint_lib/diagnostic.rs 0 → 100644 +45 −0 Original line number Diff line number Diff line /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ use std::error::Error; /// Diagnostic collector for endpoint resolution /// /// Endpoint functions return `Option<T>`—to enable diagnostic information to flow, we capture the /// last error that occurred. #[derive(Debug, Default)] pub(crate) struct DiagnosticCollector { last_error: Option<Box<dyn Error + Send + Sync>>, } impl DiagnosticCollector { /// Report an error to the collector pub(crate) fn report_error(&mut self, err: impl Into<Box<dyn Error + Send + Sync>>) { self.last_error = Some(err.into()); } /// Capture a result, returning Some(t) when the input was `Ok` and `None` otherwise pub(crate) fn capture<T, E: Into<Box<dyn Error + Send + Sync>>>( &mut self, err: Result<T, E>, ) -> Option<T> { match err { Ok(res) => Some(res), Err(e) => { self.report_error(e); None } } } pub(crate) fn take_last_error(&mut self) -> Option<Box<dyn Error + Send + Sync>> { self.last_error.take() } /// Create a new diagnostic collector pub(crate) fn new() -> Self { Self { last_error: None } } }
rust-runtime/inlineable/src/endpoint_lib/host.rs 0 → 100644 +76 −0 Original line number Diff line number Diff line /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ use crate::endpoint_lib::diagnostic::DiagnosticCollector; pub(crate) fn is_valid_host_label( label: &str, allow_dots: bool, e: &mut DiagnosticCollector, ) -> bool { if allow_dots { for part in label.split('.') { if !is_valid_host_label(part, false, e) { return false; } } true } else { if label.is_empty() || label.len() > 63 { e.report_error("host was too short or too long"); return false; } label.chars().enumerate().all(|(idx, ch)| match (ch, idx) { ('-', 0) => { e.report_error("cannot start with `-`"); false } _ => ch.is_alphanumeric() || ch == '-', }) } } #[cfg(test)] mod test { use proptest::proptest; fn is_valid_host_label(label: &str, allow_dots: bool) -> bool { super::is_valid_host_label(label, allow_dots, &mut DiagnosticCollector::new()) } #[test] fn basic_cases() { assert_eq!(is_valid_host_label("", false), false); assert_eq!(is_valid_host_label("", true), false); assert_eq!(is_valid_host_label(".", true), false); assert_eq!(is_valid_host_label("a.b", true), true); assert_eq!(is_valid_host_label("a.b", false), false); assert_eq!(is_valid_host_label("a.b.", true), false); assert_eq!(is_valid_host_label("a.b.c", true), true); assert_eq!(is_valid_host_label("a_b", true), false); assert_eq!(is_valid_host_label(&"a".repeat(64), false), false); assert_eq!( is_valid_host_label(&format!("{}.{}", "a".repeat(63), "a".repeat(63)), true), true ); } #[test] fn start_bounds() { assert_eq!(is_valid_host_label("-foo", false), false); assert_eq!(is_valid_host_label("-foo", true), false); assert_eq!(is_valid_host_label(".foo", true), false); assert_eq!(is_valid_host_label("a-b.foo", true), true); } use crate::endpoint_lib::diagnostic::DiagnosticCollector; use proptest::prelude::*; proptest! { #[test] fn no_panics(s in any::<String>(), dots in any::<bool>()) { is_valid_host_label(&s, dots); } } }