diff --git a/README.md b/README.md index 731bc253..11eceb6a 100644 --- a/README.md +++ b/README.md @@ -196,7 +196,7 @@ Credentials are resolved using the default AWS provider chain, including the `AW If you do not want to use credentials at all, you can set the `SCCACHE_S3_NO_CREDENTIALS` environment variable. This requires the bucket to allow public readonly access, and can be useful to implement a readonly cache for pull requests, which typically can't be given access to credentials for security reasons. -You can configure the region using the `SCCACHE_REGION` environment variable, or specify the `region` key in `~/.aws/credentials`. Alternatively you can specify the endpoint URL using the `SCCACHE_ENDPOINT` environment variable. To connect to a minio storage for example you can set `SCCACHE_ENDPOINT=:`. If your endpoint requires TLS, set `SCCACHE_S3_USE_SSL=true`. +You can configure the region using the `SCCACHE_REGION` environment variable, or specify the `region` key in `~/.aws/credentials`. Alternatively you can specify the endpoint URL using the `SCCACHE_ENDPOINT` environment variable. To connect to a minio storage for example you can set `SCCACHE_ENDPOINT=:`. If your endpoint requires TLS, set `SCCACHE_S3_USE_SSL=true`. If you don't need a secure network layer, http might be better for performance. You can also define a prefix that will be prepended to the keys of all cache objects created and read within the S3 bucket, effectively creating a scope. To do that use the `SCCACHE_S3_KEY_PREFIX` environment variable. This can be useful when sharing a bucket with another application. diff --git a/src/cache/s3.rs b/src/cache/s3.rs index 78c201ce..197b1c28 100644 --- a/src/cache/s3.rs +++ b/src/cache/s3.rs @@ -37,7 +37,7 @@ impl S3Cache { key_prefix: &str, no_credentials: bool, endpoint: Option<&str>, - use_ssl: bool, + use_ssl: Option, ) -> Result { Ok(S3Cache { key_prefix: key_prefix.to_owned(), @@ -101,6 +101,29 @@ fn normalize_key(prefix: &str, key: &str) -> String { ) } +fn endpoint_resolver(endpoint: &str, use_ssl: Option) -> Endpoint { + let endpoint_uri: http::Uri = endpoint.try_into().unwrap(); + let mut parts = endpoint_uri.into_parts(); + match use_ssl { + Some(true) => { + parts.scheme = Some(http::uri::Scheme::HTTPS); + } + Some(false) => { + parts.scheme = Some(http::uri::Scheme::HTTP); + } + None => { + if parts.scheme.is_none() { + parts.scheme = Some(http::uri::Scheme::HTTP); + } + } + } + // path_and_query is required when scheme is set + if parts.path_and_query.is_none() { + parts.path_and_query = Some(http::uri::PathAndQuery::from_static("/")); + } + Endpoint::mutable(http::Uri::from_parts(parts).unwrap()) +} + struct S3Client { bucket: String, config: Config, @@ -111,7 +134,7 @@ impl S3Client { bucket: &str, region: Option<&str>, endpoint: Option<&str>, - use_ssl: bool, + use_ssl: Option, ) -> Result { let region_provider = RegionProviderChain::first_try(region.map(|r| Region::new(r.to_owned()))) @@ -120,19 +143,7 @@ impl S3Client { let shared_config = aws_config::from_env().region(region_provider).load().await; let mut builder = aws_sdk_s3::config::Builder::from(&shared_config); if let Some(endpoint) = endpoint { - let endpoint_uri: http::Uri = endpoint.try_into().unwrap(); - let mut parts = endpoint_uri.into_parts(); - if use_ssl { - parts.scheme = Some(http::uri::Scheme::HTTPS); - } else { - parts.scheme = Some(http::uri::Scheme::HTTP); - } - // path_and_query is required when scheme is set - if parts.path_and_query.is_none() { - parts.path_and_query = Some(http::uri::PathAndQuery::from_static("/")); - } - builder = - builder.endpoint_resolver(Endpoint::mutable(http::Uri::from_parts(parts).unwrap())); + builder = builder.endpoint_resolver(endpoint_resolver(endpoint, use_ssl)); } let config = builder.build(); @@ -203,4 +214,16 @@ mod test { "prefix/0/1/2/0123456789abcdef0123456789abcdef" ); } + + #[test] + fn test_endpoint_resolver() { + let endpoint = endpoint_resolver("s3-us-east-1.amazonaws.com", None); + assert_eq!(endpoint.uri().scheme_str(), Some("http")); + + let endpoint = endpoint_resolver("s3-us-east-1.amazonaws.com", Some(true)); + assert_eq!(endpoint.uri().scheme_str(), Some("https")); + + let endpoint = endpoint_resolver("s3-us-east-1.amazonaws.com", Some(false)); + assert_eq!(endpoint.uri().scheme_str(), Some("http")); + } } diff --git a/src/config.rs b/src/config.rs index 046c3ce0..f1a69202 100644 --- a/src/config.rs +++ b/src/config.rs @@ -207,7 +207,7 @@ pub struct S3CacheConfig { pub key_prefix: String, pub no_credentials: bool, pub endpoint: Option, - pub use_ssl: bool, + pub use_ssl: Option, } #[derive(Debug, PartialEq, Eq)] @@ -460,8 +460,7 @@ fn config_from_env() -> Result { let no_credentials = env::var("SCCACHE_S3_NO_CREDENTIALS").ok().is_some(); let use_ssl = env::var("SCCACHE_S3_USE_SSL") .ok() - .filter(|value| value != "off") - .is_some(); + .map(|value| value != "off"); let endpoint = env::var("SCCACHE_ENDPOINT").ok(); let key_prefix = env::var("SCCACHE_S3_KEY_PREFIX") .ok() @@ -1061,7 +1060,7 @@ no_credentials = true bucket: "name".to_owned(), region: Some("us-east-2".to_owned()), endpoint: Some("s3-us-east-1.amazonaws.com".to_owned()), - use_ssl: true, + use_ssl: Some(true), key_prefix: "s3prefix".into(), no_credentials: true, }),