diff --git a/Cargo.toml b/Cargo.toml index 8e780acccf..32d78ed5c1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -377,8 +377,8 @@ mockall = { default-features = false, version = "0.14" } multer = { default-features = false, version = "3" } mutants = { default-features = false, version = "0.0.3" } once_cell = { default-features = false, version = "1.19.0" } -pastey = { default-features = false, version = "0.2.1" } pretty_assertions = { default-features = false, version = "1.4.1", features = ["alloc"] } +pastey = { default-features = false, version = "0.2.1" } regex = { default-features = false, version = "1" } rsa = { default-features = false, version = "0.9.8" } rustc_version = { default-features = false, version = "0.4" } diff --git a/src/storage/src/storage/signed_url.rs b/src/storage/src/storage/signed_url.rs index 5cae3f2a82..c4d429acf3 100644 --- a/src/storage/src/storage/signed_url.rs +++ b/src/storage/src/storage/signed_url.rs @@ -413,11 +413,22 @@ impl SignedUrlBuilder { fn resolve_endpoint(&self) -> String { match self.endpoint.as_ref() { - Some(e) if e.starts_with("http://") => e.clone(), - Some(e) if e.starts_with("https://") => e.clone(), - Some(e) => format!("https://{}", e), - None => format!("https://storage.{}", self.universe_domain), + Some(e) if e.starts_with("http://") => return e.clone(), + Some(e) if e.starts_with("https://") => return e.clone(), + Some(e) => return format!("https://{}", e), + None => {} } + + let emulator_host = std::env::var("STORAGE_EMULATOR_HOST").ok(); + match emulator_host { + Some(host) if host.starts_with("http://") => return host.clone(), + Some(host) if host.starts_with("https://") => return host.clone(), + Some(host) if !host.is_empty() => return format!("http://{}", host), + Some(_) => {} + None => {} + } + + format!("https://storage.{}", self.universe_domain) } fn canonicalize_header_value(value: &str) -> String { diff --git a/tests/integration/src/storage.rs b/tests/integration/src/storage.rs index 3bac55ca2d..5cd1e0ea56 100644 --- a/tests/integration/src/storage.rs +++ b/tests/integration/src/storage.rs @@ -84,6 +84,46 @@ pub async fn objects( Ok(()) } +#[cfg(google_cloud_unstable_signed_url)] +pub async fn signed_urls( + builder: storage::builder::storage::ClientBuilder, + bucket_name: &str, + prefix: &str, +) -> anyhow::Result<()> { + //let creds = auth::credentials::mds::Builder::default().build()?; + let client = builder.build().await?; + + // let signer = auth::credentials::mds::Builder::default().build_signer()?; + let signer = auth::credentials::Builder::default().build_signer()?; + + const CONTENTS: &str = "the quick brown fox jumps over the lazy dog"; + let insert = client + .write_object(bucket_name, format!("{prefix}/quick.text"), CONTENTS) + .set_content_type("text/plain") + .set_content_language("en") + .set_storage_class("STANDARD") + .send_unbuffered() + .await?; + tracing::info!("success with insert={insert:?}"); + + tracing::info!("testing signed_url()"); + let signed_url = + storage::builder::storage::SignedUrlBuilder::for_object(bucket_name, &insert.name) + .sign_with(&signer) + .await?; + + tracing::info!("signed_url={signed_url}"); + + // Download the contents of the object using the signed URL. + let client = reqwest::Client::new(); + let res = client.get(signed_url).send().await?; + let out = res.text().await?; + assert_eq!(out, CONTENTS); + tracing::info!("signed url works and can read contents={out:?}"); + + Ok(()) +} + async fn read_all(mut response: ReadObjectResponse) -> Result> { let mut contents = Vec::new(); while let Some(b) = response.next().await.transpose()? { diff --git a/tests/integration/tests/driver.rs b/tests/integration/tests/driver.rs index e49bc28fbe..dbc142b64c 100644 --- a/tests/integration/tests/driver.rs +++ b/tests/integration/tests/driver.rs @@ -201,6 +201,24 @@ mod driver { result } + #[cfg(all(test, google_cloud_unstable_signed_url))] + #[tokio::test(flavor = "multi_thread", worker_threads = 1)] + async fn run_storage_signed_urls() -> integration_tests::Result<()> { + let _guard = integration_tests::enable_tracing(); + let (control, bucket) = integration_tests::storage::create_test_bucket().await?; + + let builder = Storage::builder(); + let result = + integration_tests::storage::signed_urls(builder, &bucket.name, "default-endpoint") + .await + .map_err(integration_tests::report_error); + + if let Err(e) = storage_samples::cleanup_bucket(control, bucket.name.clone()).await { + tracing::error!("error cleaning up test bucket {}: {e:?}", bucket.name); + }; + result + } + #[test_case(Storage::builder(); "default")] #[tokio::test(flavor = "multi_thread", worker_threads = 1)] async fn run_storage_read_object(