Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for endpoints.json #468

Merged
merged 10 commits into from
Jun 8, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions aws/rust-runtime/aws-endpoint/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ description = "AWS Endpoint Support"
smithy-http = { path = "../../../rust-runtime/smithy-http"}
aws-types = { path = "../aws-types" }
http = "0.2.3"
regex = { version = "1", default-features = false, features = ["std"]}
97 changes: 80 additions & 17 deletions aws/rust-runtime/aws-endpoint/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@
* SPDX-License-Identifier: Apache-2.0.
*/

#[doc(hidden)]
pub mod partition;

#[doc(hidden)]
pub use partition::Partition;
#[doc(hidden)]
pub use partition::PartitionResolver;

use std::error::Error;
use std::fmt;
use std::fmt::{Debug, Display, Formatter};
Expand All @@ -29,8 +37,7 @@ use std::convert::TryFrom;
#[derive(Clone)]
pub struct AwsEndpoint {
endpoint: Endpoint,
signing_service: Option<SigningService>,
signing_region: Option<SigningRegion>,
credential_scope: CredentialScope,
}

impl AwsEndpoint {
Expand Down Expand Up @@ -73,7 +80,7 @@ pub type BoxError = Box<dyn Error + Send + Sync + 'static>;
/// will be codegenerated from `endpoints.json`.
pub trait ResolveAwsEndpoint: Send + Sync {
// TODO: consider if we want modeled error variants here
fn endpoint(&self, region: &Region) -> Result<AwsEndpoint, BoxError>;
fn resolve_endpoint(&self, region: &Region) -> Result<AwsEndpoint, BoxError>;
}

/// Default AWS Endpoint Implementation
Expand All @@ -91,28 +98,81 @@ impl DefaultAwsEndpointResolver {
}
}

#[derive(Clone, Default, Debug)]
pub struct CredentialScope {
region: Option<SigningRegion>,
service: Option<SigningService>,
}

impl CredentialScope {
pub fn builder() -> credential_scope::Builder {
credential_scope::Builder::default()
}
}

pub mod credential_scope {
use crate::CredentialScope;
use aws_types::region::SigningRegion;
use aws_types::SigningService;

#[derive(Debug, Default)]
pub struct Builder {
region: Option<SigningRegion>,
service: Option<SigningService>,
}

impl Builder {
pub fn region(mut self, region: &'static str) -> Self {
self.region = Some(SigningRegion::from_static(region));
self
}

pub fn service(mut self, service: &'static str) -> Self {
self.service = Some(SigningService::from_static(service));
self
}

pub fn build(self) -> CredentialScope {
CredentialScope {
region: self.region,
service: self.service,
}
}
}
}

impl CredentialScope {
pub fn merge(&self, other: &CredentialScope) -> CredentialScope {
CredentialScope {
region: self.region.clone().or_else(|| other.region.clone()),
service: self.service.clone().or_else(|| other.service.clone()),
}
}
}

/// An `Endpoint` can be its own resolver to support static endpoints
impl ResolveAwsEndpoint for Endpoint {
fn endpoint(&self, _region: &Region) -> Result<AwsEndpoint, BoxError> {
fn resolve_endpoint(&self, _region: &Region) -> Result<AwsEndpoint, BoxError> {
Ok(AwsEndpoint {
endpoint: self.clone(),
signing_service: None,
signing_region: None,
credential_scope: Default::default(),
})
}
}

impl ResolveAwsEndpoint for DefaultAwsEndpointResolver {
fn endpoint(&self, region: &Region) -> Result<AwsEndpoint, BoxError> {
fn resolve_endpoint(&self, region: &Region) -> Result<AwsEndpoint, BoxError> {
let uri = Uri::from_str(&format!(
"https://{}.{}.amazonaws.com",
self.service,
region.as_ref(),
))?;
Ok(AwsEndpoint {
endpoint: Endpoint::mutable(uri),
signing_region: Some(region.clone().into()),
signing_service: None,
credential_scope: CredentialScope {
region: Some(region.clone().into()),
service: None,
},
})
}
}
Expand Down Expand Up @@ -162,13 +222,14 @@ impl MapRequest for AwsEndpointStage {
.get::<Region>()
.ok_or(AwsEndpointStageError::NoRegion)?;
let endpoint = provider
.endpoint(region)
.resolve_endpoint(region)
.map_err(AwsEndpointStageError::EndpointResolutionError)?;
let signing_region = endpoint
.signing_region
.credential_scope
.region
.unwrap_or_else(|| region.clone().into());
config.insert::<SigningRegion>(signing_region);
if let Some(signing_service) = endpoint.signing_service {
if let Some(signing_service) = endpoint.credential_scope.service {
config.insert::<SigningService>(signing_service);
}
endpoint
Expand Down Expand Up @@ -200,8 +261,8 @@ mod test {
use smithy_http::operation;

use crate::{
set_endpoint_resolver, AwsEndpoint, AwsEndpointStage, BoxError, DefaultAwsEndpointResolver,
ResolveAwsEndpoint,
set_endpoint_resolver, AwsEndpoint, AwsEndpointStage, BoxError, CredentialScope,
DefaultAwsEndpointResolver, ResolveAwsEndpoint,
};
use http::header::HOST;
use smithy_http::endpoint::Endpoint;
Expand Down Expand Up @@ -243,11 +304,13 @@ mod test {
fn sets_service_override_when_set() {
struct ServiceOverrideResolver;
impl ResolveAwsEndpoint for ServiceOverrideResolver {
fn endpoint(&self, _region: &Region) -> Result<AwsEndpoint, BoxError> {
fn resolve_endpoint(&self, _region: &Region) -> Result<AwsEndpoint, BoxError> {
Ok(AwsEndpoint {
endpoint: Endpoint::immutable(Uri::from_static("http://www.service.com")),
signing_service: Some(SigningService::from_static("qldb-override")),
signing_region: Some(SigningRegion::from(Region::new("us-east-override"))),
credential_scope: CredentialScope {
service: Some(SigningService::from_static("qldb-override")),
region: Some(SigningRegion::from(Region::new("us-east-override"))),
},
})
}
}
Expand Down
73 changes: 73 additions & 0 deletions aws/rust-runtime/aws-endpoint/src/partition/endpoint.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/

use crate::{AwsEndpoint, BoxError, CredentialScope, ResolveAwsEndpoint};
use aws_types::region::Region;
use smithy_http::endpoint::Endpoint;

pub type CompleteEndpoint = Definition;

/// Endpoint metadata
///
/// T & P exist to support optional vs. non optional values for Protocol and URI template during
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Outdated comment?

/// merging
#[derive(Debug)]
pub struct Definition {
/// URI for the endpoint.
///
/// May contain `{region}` which will replaced with the region during endpoint construction
pub uri_template: &'static str,

/// Protocol to use for this endpoint
pub protocol: Protocol,

/// Credential scope to set for requests to this endpoint
pub credential_scope: CredentialScope,

/// Signature versions supported by this endpoint.
///
/// Currently unused since the SDK only supports SigV4
pub signature_versions: SignatureVersion,
}

#[derive(Eq, PartialEq, Copy, Clone, Debug)]
pub enum Protocol {
Http,
Https,
}

impl Protocol {
fn as_str(&self) -> &'static str {
match self {
Protocol::Http => "http",
Protocol::Https => "https",
}
}
}

#[derive(Eq, PartialEq, Copy, Clone, Debug)]
pub enum SignatureVersion {
V4,
}

impl ResolveAwsEndpoint for CompleteEndpoint {
fn resolve_endpoint(&self, region: &Region) -> Result<AwsEndpoint, BoxError> {
let uri = self.uri_template.replace("{region}", region.as_ref());
let uri = format!("{}://{}", self.protocol.as_str(), uri);
let endpoint = Endpoint::mutable(uri.parse()?);
let ep = AwsEndpoint {
endpoint,
credential_scope: CredentialScope {
service: self.credential_scope.service.clone(),
region: self
.credential_scope
.region
.clone()
.or_else(|| Some(region.clone().into())),
},
};
Ok(ep)
}
}
Loading