mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-27 15:37:02 +00:00
fix:use RFC1123 format for last-modified header in 304 responses (#1627)
Co-authored-by: houseme <housemecn@gmail.com>
This commit is contained in:
@@ -47,10 +47,14 @@ use std::collections::HashMap;
|
|||||||
use std::ops::Add;
|
use std::ops::Add;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
use time::format_description::well_known::{Rfc2822, Rfc3339};
|
use time::format_description::well_known::Rfc3339;
|
||||||
|
use time::{format_description::FormatItem, macros::format_description};
|
||||||
use tokio::io::AsyncRead;
|
use tokio::io::AsyncRead;
|
||||||
use tracing::{debug, warn};
|
use tracing::{debug, warn};
|
||||||
|
|
||||||
|
const RFC1123: &[FormatItem<'_>] =
|
||||||
|
format_description!("[weekday repr:short], [day] [month repr:short] [year] [hour]:[minute]:[second] GMT");
|
||||||
|
|
||||||
/// =======================
|
/// =======================
|
||||||
/// Presigned POST helpers
|
/// Presigned POST helpers
|
||||||
/// =======================
|
/// =======================
|
||||||
@@ -614,7 +618,7 @@ pub(crate) async fn validate_bucket_object_lock_enabled(bucket: &str) -> S3Resul
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Validates HTTP conditional request headers for a single object according to
|
/// Validates HTTP conditional request headers for a single object according to
|
||||||
/// RFC 7232 and S3 API semantics.
|
/// RFC 7232 (HTTP/1.1 conditional requests) and S3 API semantics.
|
||||||
///
|
///
|
||||||
/// This function evaluates the following headers, if present, in the standard
|
/// This function evaluates the following headers, if present, in the standard
|
||||||
/// conditional request order:
|
/// conditional request order:
|
||||||
@@ -654,7 +658,7 @@ pub(crate) fn check_preconditions(headers: &HeaderMap, info: &ObjectInfo) -> S3R
|
|||||||
if headers.get("if-match").is_none()
|
if headers.get("if-match").is_none()
|
||||||
&& let Some(t) = mod_time
|
&& let Some(t) = mod_time
|
||||||
&& let Some(if_unmodified_since) = headers.get("if-unmodified-since").and_then(|v| v.to_str().ok())
|
&& let Some(if_unmodified_since) = headers.get("if-unmodified-since").and_then(|v| v.to_str().ok())
|
||||||
&& let Ok(given_time) = OffsetDateTime::parse(if_unmodified_since, &Rfc2822)
|
&& let Ok(given_time) = time::PrimitiveDateTime::parse(if_unmodified_since, &RFC1123).map(|dt| dt.assume_utc())
|
||||||
&& t > given_time.add(time::Duration::seconds(1))
|
&& t > given_time.add(time::Duration::seconds(1))
|
||||||
{
|
{
|
||||||
return Err(S3Error::new(S3ErrorCode::PreconditionFailed));
|
return Err(S3Error::new(S3ErrorCode::PreconditionFailed));
|
||||||
@@ -670,7 +674,7 @@ pub(crate) fn check_preconditions(headers: &HeaderMap, info: &ObjectInfo) -> S3R
|
|||||||
error_headers.insert("etag", etag_header);
|
error_headers.insert("etag", etag_header);
|
||||||
}
|
}
|
||||||
if let Some(t) = mod_time
|
if let Some(t) = mod_time
|
||||||
&& let Ok(last_modified_str) = t.format(&Rfc2822)
|
&& let Ok(last_modified_str) = t.format(&RFC1123)
|
||||||
&& let Ok(last_modified_header) = HeaderValue::from_str(&last_modified_str)
|
&& let Ok(last_modified_header) = HeaderValue::from_str(&last_modified_str)
|
||||||
{
|
{
|
||||||
error_headers.insert("last-modified", last_modified_header);
|
error_headers.insert("last-modified", last_modified_header);
|
||||||
@@ -683,11 +687,11 @@ pub(crate) fn check_preconditions(headers: &HeaderMap, info: &ObjectInfo) -> S3R
|
|||||||
return Err(s3_error);
|
return Err(s3_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If-Modified-Since (only when If-None-Match is absent - RFC 7232)
|
// If-Modified-Since (only when If-None-Match is absent — semantics per RFC 7232; dates use RFC 1123 format)
|
||||||
if headers.get("if-none-match").is_none()
|
if headers.get("if-none-match").is_none()
|
||||||
&& let Some(t) = mod_time
|
&& let Some(t) = mod_time
|
||||||
&& let Some(if_modified_since) = headers.get("if-modified-since").and_then(|v| v.to_str().ok())
|
&& let Some(if_modified_since) = headers.get("if-modified-since").and_then(|v| v.to_str().ok())
|
||||||
&& let Ok(given_time) = OffsetDateTime::parse(if_modified_since, &Rfc2822)
|
&& let Ok(given_time) = time::PrimitiveDateTime::parse(if_modified_since, &RFC1123).map(|dt| dt.assume_utc())
|
||||||
&& t < given_time.add(time::Duration::seconds(1))
|
&& t < given_time.add(time::Duration::seconds(1))
|
||||||
{
|
{
|
||||||
let mut error_headers = HeaderMap::new();
|
let mut error_headers = HeaderMap::new();
|
||||||
@@ -696,7 +700,7 @@ pub(crate) fn check_preconditions(headers: &HeaderMap, info: &ObjectInfo) -> S3R
|
|||||||
{
|
{
|
||||||
error_headers.insert("etag", etag_header);
|
error_headers.insert("etag", etag_header);
|
||||||
}
|
}
|
||||||
if let Ok(last_modified_str) = t.format(&Rfc2822)
|
if let Ok(last_modified_str) = t.format(&RFC1123)
|
||||||
&& let Ok(last_modified_header) = HeaderValue::from_str(&last_modified_str)
|
&& let Ok(last_modified_header) = HeaderValue::from_str(&last_modified_str)
|
||||||
{
|
{
|
||||||
error_headers.insert("last-modified", last_modified_header);
|
error_headers.insert("last-modified", last_modified_header);
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ mod tests {
|
|||||||
};
|
};
|
||||||
use s3s::{S3Error, S3ErrorCode, s3_error};
|
use s3s::{S3Error, S3ErrorCode, s3_error};
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
use time::format_description::well_known::Rfc2822;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_fs_creation() {
|
fn test_fs_creation() {
|
||||||
@@ -558,6 +557,9 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_check_preconditions() {
|
fn test_check_preconditions() {
|
||||||
|
use time::{format_description::FormatItem, macros::format_description};
|
||||||
|
const RFC1123: &[FormatItem<'_>] =
|
||||||
|
format_description!("[weekday repr:short], [day] [month repr:short] [year] [hour]:[minute]:[second] GMT");
|
||||||
let valid_mod_time = OffsetDateTime::from_unix_timestamp(1700000000).unwrap();
|
let valid_mod_time = OffsetDateTime::from_unix_timestamp(1700000000).unwrap();
|
||||||
let valid_etag = "\"d41d8cd98f00b204e9800998ecf8427e\"";
|
let valid_etag = "\"d41d8cd98f00b204e9800998ecf8427e\"";
|
||||||
let wrong_etag = "\"wrong-etag-123456\"";
|
let wrong_etag = "\"wrong-etag-123456\"";
|
||||||
@@ -605,7 +607,7 @@ mod tests {
|
|||||||
let mut headers5 = HeaderMap::new();
|
let mut headers5 = HeaderMap::new();
|
||||||
headers5.insert(
|
headers5.insert(
|
||||||
"if-modified-since",
|
"if-modified-since",
|
||||||
HeaderValue::from_str(&valid_mod_time.format(&Rfc2822).unwrap()).unwrap(),
|
HeaderValue::from_str(&valid_mod_time.format(&RFC1123).unwrap()).unwrap(),
|
||||||
);
|
);
|
||||||
let info5 = info3.clone();
|
let info5 = info3.clone();
|
||||||
let result5 = check_preconditions(&headers5, &info5);
|
let result5 = check_preconditions(&headers5, &info5);
|
||||||
@@ -618,7 +620,7 @@ mod tests {
|
|||||||
let mut headers6 = HeaderMap::new();
|
let mut headers6 = HeaderMap::new();
|
||||||
headers6.insert(
|
headers6.insert(
|
||||||
"if-modified-since",
|
"if-modified-since",
|
||||||
HeaderValue::from_str(&earlier_time.format(&Rfc2822).unwrap()).unwrap(),
|
HeaderValue::from_str(&earlier_time.format(&RFC1123).unwrap()).unwrap(),
|
||||||
);
|
);
|
||||||
let info6 = info3.clone();
|
let info6 = info3.clone();
|
||||||
assert!(check_preconditions(&headers6, &info6).is_ok());
|
assert!(check_preconditions(&headers6, &info6).is_ok());
|
||||||
@@ -641,7 +643,7 @@ mod tests {
|
|||||||
let mut headers9 = HeaderMap::new();
|
let mut headers9 = HeaderMap::new();
|
||||||
headers9.insert(
|
headers9.insert(
|
||||||
"if-unmodified-since",
|
"if-unmodified-since",
|
||||||
HeaderValue::from_str(&earlier_time.format(&Rfc2822).unwrap()).unwrap(),
|
HeaderValue::from_str(&earlier_time.format(&RFC1123).unwrap()).unwrap(),
|
||||||
);
|
);
|
||||||
let info9 = info3.clone();
|
let info9 = info3.clone();
|
||||||
let result9 = check_preconditions(&headers9, &info9);
|
let result9 = check_preconditions(&headers9, &info9);
|
||||||
@@ -652,7 +654,7 @@ mod tests {
|
|||||||
let mut headers10 = HeaderMap::new();
|
let mut headers10 = HeaderMap::new();
|
||||||
headers10.insert(
|
headers10.insert(
|
||||||
"if-unmodified-since",
|
"if-unmodified-since",
|
||||||
HeaderValue::from_str(&valid_mod_time.format(&Rfc2822).unwrap()).unwrap(),
|
HeaderValue::from_str(&valid_mod_time.format(&RFC1123).unwrap()).unwrap(),
|
||||||
);
|
);
|
||||||
let info10 = info3.clone();
|
let info10 = info3.clone();
|
||||||
assert!(check_preconditions(&headers10, &info10).is_ok());
|
assert!(check_preconditions(&headers10, &info10).is_ok());
|
||||||
@@ -684,7 +686,7 @@ mod tests {
|
|||||||
headers13.insert("if-none-match", HeaderValue::from_str(valid_etag).unwrap());
|
headers13.insert("if-none-match", HeaderValue::from_str(valid_etag).unwrap());
|
||||||
headers13.insert(
|
headers13.insert(
|
||||||
"if-modified-since",
|
"if-modified-since",
|
||||||
HeaderValue::from_str(&earlier_time.format(&Rfc2822).unwrap()).unwrap(),
|
HeaderValue::from_str(&earlier_time.format(&RFC1123).unwrap()).unwrap(),
|
||||||
);
|
);
|
||||||
let info13 = info3.clone();
|
let info13 = info3.clone();
|
||||||
let result13 = check_preconditions(&headers13, &info13);
|
let result13 = check_preconditions(&headers13, &info13);
|
||||||
@@ -696,7 +698,7 @@ mod tests {
|
|||||||
headers14.insert("if-none-match", HeaderValue::from_str("\"wrong-etag\"").unwrap());
|
headers14.insert("if-none-match", HeaderValue::from_str("\"wrong-etag\"").unwrap());
|
||||||
headers14.insert(
|
headers14.insert(
|
||||||
"if-modified-since",
|
"if-modified-since",
|
||||||
HeaderValue::from_str(&valid_mod_time.format(&Rfc2822).unwrap()).unwrap(),
|
HeaderValue::from_str(&valid_mod_time.format(&RFC1123).unwrap()).unwrap(),
|
||||||
);
|
);
|
||||||
let info14 = info3.clone();
|
let info14 = info3.clone();
|
||||||
assert!(check_preconditions(&headers14, &info14).is_ok());
|
assert!(check_preconditions(&headers14, &info14).is_ok());
|
||||||
|
|||||||
Reference in New Issue
Block a user