Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
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
25 changes: 19 additions & 6 deletions crates/core/src/api/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,19 @@ fn parse_copy_source(raw: &str) -> Result<(String, String, Option<String>), Prox
Ok((bucket.to_string(), key, version))
}

/// Extract a non-empty `versionId` query parameter.
///
/// An empty `?versionId=` is treated as absent rather than as a version named
/// "": S3 has no such version, and reading it as one would turn a malformed
/// request into a versioned read that can never succeed.
fn version_id(query_params: &[(String, String)]) -> Option<String> {
query_params
.iter()
.find(|(k, _)| k == "versionId")
.map(|(_, v)| v.clone())
.filter(|v| !v.is_empty())
}

/// Build an [`S3Operation`] from an already-extracted bucket, key, and query.
///
/// This is used by both [`parse_s3_request`] and custom resolvers that parse
Expand Down Expand Up @@ -160,18 +173,18 @@ pub fn build_s3_operation(
raw_query: query.map(|q| q.to_string()),
})
} else {
// `?versionId=` is deliberately not carried here: the read
// path does not address versions, so the backend returns the
// current object and authorizing a version would describe a
// read that never happens. See `S3Operation::GetObject::version`.
Ok(S3Operation::GetObject {
bucket,
key,
version: None,
version: version_id(&query_params),
})
}
}
Method::HEAD => Ok(S3Operation::HeadObject { bucket, key }),
Method::HEAD => Ok(S3Operation::HeadObject {
bucket,
key,
version: version_id(&query_params),
}),
Method::PUT => {
if let Some(upload_id) = upload_id {
let part_number = query_params
Expand Down
14 changes: 14 additions & 0 deletions crates/core/src/backend/multipart.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,20 @@ pub fn build_backend_url(
url.push('?');
url.push_str(&qs);
}
S3Operation::GetObject {
version: Some(version),
..
}
| S3Operation::HeadObject {
version: Some(version),
..
} => {
let qs = url::form_urlencoded::Serializer::new(String::new())
.append_pair("versionId", version)
.finish();
url.push('?');
url.push_str(&qs);
}
S3Operation::CompleteMultipartUpload { upload_id, .. }
| S3Operation::AbortMultipartUpload { upload_id, .. } => {
let qs = url::form_urlencoded::Serializer::new(String::new())
Expand Down
Loading
Loading