fix(signer): address post-merge review comments (#3216)

* fix: address PR #3150 review comments

- Restore get_host_addr as best-effort wrapper (String return type)
- Replace bare expect("err") with descriptive messages in unsigned trailer
- Simplify aws-chunked header construction
- 20 signer + 96 io-core tests pass

* fix(signer): preserve host fallback

* fix(signer): avoid signer fallback panics

---------

Co-authored-by: houseme <housemecn@gmail.com>
This commit is contained in:
安正超
2026-06-05 06:45:50 +08:00
committed by GitHub
parent d6120f5788
commit 1dd3839a9f
2 changed files with 70 additions and 18 deletions
@@ -25,15 +25,47 @@ pub fn streaming_unsigned_v4(
) -> request::Request<Body> {
let headers = req.headers_mut();
let chunked_value = HeaderValue::from_str(&["aws-chunked"].join(",")).expect("err");
let chunked_value = HeaderValue::from_static("aws-chunked");
headers.insert(http::header::TRANSFER_ENCODING, chunked_value);
if !session_token.is_empty() {
headers.insert("X-Amz-Security-Token", HeaderValue::from_str(session_token).expect("err"));
if !session_token.is_empty()
&& let Ok(token_value) = HeaderValue::from_str(session_token)
{
headers.insert("X-Amz-Security-Token", token_value);
}
let format = format_description!("[year]-[month]-[day]T[hour]:[minute]:[second].[subsecond]Z");
headers.insert("X-Amz-Date", HeaderValue::from_str(&req_time.format(&format).unwrap()).expect("err"));
if let Ok(date) = req_time.format(&format)
&& let Ok(date_value) = HeaderValue::from_str(&date)
{
headers.insert("X-Amz-Date", date_value);
}
//req.content_length = 100
req
}
#[cfg(test)]
mod tests {
use super::streaming_unsigned_v4;
use http::request;
use s3s::Body;
use time::OffsetDateTime;
#[test]
fn streaming_unsigned_v4_skips_invalid_session_token_header() {
let req = request::Request::builder()
.method(http::Method::GET)
.uri("https://bucket.example.com/object")
.body(Body::empty())
.expect("request should build");
let req = streaming_unsigned_v4(req, "invalid\ntoken", 0, OffsetDateTime::UNIX_EPOCH);
assert!(req.headers().get("X-Amz-Security-Token").is_none());
assert_eq!(
req.headers().get(http::header::TRANSFER_ENCODING),
Some(&http::HeaderValue::from_static("aws-chunked"))
);
assert!(req.headers().get("X-Amz-Date").is_some());
}
}
+34 -14
View File
@@ -26,14 +26,7 @@ pub enum HostAddrError {
pub fn try_get_host_addr(req: &request::Request<Body>) -> Result<String, HostAddrError> {
let host = req.headers().get("host");
let uri = req.uri();
let uri_host = uri.host().ok_or(HostAddrError::MissingUriHost)?;
let req_host = if let Some(port) = uri.port() {
format!("{uri_host}:{port}")
} else {
uri_host.to_string()
};
let req_host = uri_host_addr(req).ok_or(HostAddrError::MissingUriHost)?;
if let Some(host) = host {
let host = host.to_str().map_err(|_| HostAddrError::InvalidHostHeader)?;
@@ -48,12 +41,24 @@ pub fn try_get_host_addr(req: &request::Request<Body>) -> Result<String, HostAdd
pub fn get_host_addr(req: &request::Request<Body>) -> String {
match try_get_host_addr(req) {
Ok(host) => host,
Err(HostAddrError::MissingUriHost) => match req.headers().get("host").map(|host| host.to_str()) {
Some(Ok(host)) => host.to_string(),
Some(Err(_)) => panic!("failed to resolve request host: invalid UTF-8 header value for `host`"),
None => panic!("failed to resolve request host: request uri has no host"),
},
Err(err) => panic!("failed to resolve request host: {err}"),
Err(HostAddrError::MissingUriHost) => req
.headers()
.get("host")
.and_then(|host| host.to_str().ok())
.unwrap_or_default()
.to_string(),
Err(HostAddrError::InvalidHostHeader) => uri_host_addr(req).unwrap_or_default(),
}
}
fn uri_host_addr(req: &request::Request<Body>) -> Option<String> {
let uri = req.uri();
let uri_host = uri.host()?;
if let Some(port) = uri.port() {
Some(format!("{uri_host}:{port}"))
} else {
Some(uri_host.to_string())
}
}
@@ -132,6 +137,21 @@ mod tests {
assert!(matches!(err, HostAddrError::InvalidHostHeader));
}
#[test]
fn get_host_addr_uses_uri_host_when_host_header_is_non_utf8() {
let mut req = request::Request::builder()
.method(http::Method::GET)
.uri("https://bucket.example.com:9443/object")
.body(Body::empty())
.expect("request should build");
req.headers_mut().insert(
"host",
HeaderValue::from_bytes(&[0xFF]).expect("invalid utf8 bytes should be accepted by HeaderValue"),
);
assert_eq!(get_host_addr(&req), "bucket.example.com:9443");
}
#[test]
fn try_get_host_addr_rejects_relative_uri_without_host() {
let req = request::Request::builder()