fix(s3): accept empty put without content length (#6849)

Co-authored-by: heihutu <heihutu@gmail.com>
This commit is contained in:
houseme
2026-08-29 19:31:33 +08:00
committed by GitHub
parent 5fa3d2a682
commit 78cb142c91
6 changed files with 250 additions and 62 deletions
Generated
+87 -56
View File
@@ -56,9 +56,9 @@ dependencies = [
[[package]]
name = "aes"
version = "0.9.2"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8eb277bec05f56a0e0591f155a484cbd0f4f07ff2905051a48c72f004f7ed58"
checksum = "35f0f96ce78e38c3dc6d8948aa8163d06385be74000f3c7a95bf1eef35d3ea32"
dependencies = [
"cipher 0.5.2",
"cpubits",
@@ -73,7 +73,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f2b8006a0c83f52b62ba44a97b58bf76fe2f70a329e588f67f89691d93d498f"
dependencies = [
"aead",
"aes 0.9.2",
"aes 0.9.3",
"cipher 0.5.2",
"ctr",
"ctutils",
@@ -491,7 +491,7 @@ dependencies = [
"arrow-select",
"chrono",
"half",
"indexmap 2.14.0",
"indexmap 2.14.1",
"itoa",
"lexical-core",
"memchr",
@@ -626,9 +626,9 @@ dependencies = [
[[package]]
name = "astral-tokio-tar"
version = "0.6.4"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b18457efd137254e016bbde5e1d88df61c4e1a5ae2223746e56123bac6af2463"
checksum = "6f2e989b33246fe9240d39accf4dd9a01e0b6c1f3ce9dd095e0a47fa02505523"
dependencies = [
"futures-core",
"libc",
@@ -638,6 +638,7 @@ dependencies = [
"tokio",
"tokio-stream",
"xattr",
"zerocopy",
]
[[package]]
@@ -1220,7 +1221,7 @@ dependencies = [
"hyper",
"hyper-rustls",
"hyper-util",
"indexmap 2.14.0",
"indexmap 2.14.1",
"pin-project-lite",
"rustls",
"rustls-native-certs",
@@ -2831,7 +2832,7 @@ dependencies = [
"datafusion-session",
"datafusion-sql",
"futures",
"indexmap 2.14.0",
"indexmap 2.14.1",
"itertools 0.15.0",
"log",
"object_store",
@@ -2906,7 +2907,7 @@ dependencies = [
"foldhash 0.2.0",
"half",
"hashbrown 0.17.1",
"indexmap 2.14.0",
"indexmap 2.14.1",
"itertools 0.15.0",
"libc",
"log",
@@ -3111,7 +3112,7 @@ dependencies = [
"datafusion-functions-aggregate-common",
"datafusion-functions-window-common",
"datafusion-physical-expr-common",
"indexmap 2.14.0",
"indexmap 2.14.1",
"itertools 0.15.0",
"recursive",
"serde_json",
@@ -3126,7 +3127,7 @@ checksum = "2604994999d5aeca1d1df645ffc98bc787447aaff05dde27aad0342b48fc1fe0"
dependencies = [
"arrow",
"datafusion-common",
"indexmap 2.14.0",
"indexmap 2.14.1",
"itertools 0.15.0",
]
@@ -3282,7 +3283,7 @@ dependencies = [
"datafusion-expr",
"datafusion-expr-common",
"datafusion-physical-expr",
"indexmap 2.14.0",
"indexmap 2.14.1",
"itertools 0.15.0",
"log",
"recursive",
@@ -3304,7 +3305,7 @@ dependencies = [
"datafusion-physical-expr-common",
"half",
"hashbrown 0.17.1",
"indexmap 2.14.0",
"indexmap 2.14.1",
"itertools 0.15.0",
"parking_lot",
"petgraph 0.8.3",
@@ -3338,7 +3339,7 @@ dependencies = [
"datafusion-common",
"datafusion-expr-common",
"hashbrown 0.17.1",
"indexmap 2.14.0",
"indexmap 2.14.1",
"itertools 0.15.0",
"parking_lot",
"pin-project",
@@ -3389,7 +3390,7 @@ dependencies = [
"futures",
"half",
"hashbrown 0.17.1",
"indexmap 2.14.0",
"indexmap 2.14.1",
"itertools 0.15.0",
"log",
"num-traits",
@@ -3442,7 +3443,7 @@ dependencies = [
"datafusion-common",
"datafusion-expr",
"datafusion-functions-nested",
"indexmap 2.14.0",
"indexmap 2.14.1",
"log",
"recursive",
"regex",
@@ -4207,7 +4208,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]
@@ -4342,12 +4343,12 @@ dependencies = [
[[package]]
name = "flate2"
version = "1.1.9"
version = "1.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c"
checksum = "6e634e2e0ebac1ee034020da1ca582e17ffe4e0f5e985823721e168928136dcb"
dependencies = [
"crc32fast",
"miniz_oxide 0.8.9",
"miniz_oxide 0.9.1",
"zlib-rs",
]
@@ -4617,7 +4618,7 @@ checksum = "0bf7f043f89559805f8c7cacc432749b2fa0d0a0a9ee46ce47164ed5ba7f126c"
dependencies = [
"fnv",
"hashbrown 0.16.1",
"indexmap 2.14.0",
"indexmap 2.14.1",
"stable_deref_trait",
]
@@ -4895,7 +4896,7 @@ dependencies = [
"futures-core",
"futures-sink",
"http 1.5.0",
"indexmap 2.14.0",
"indexmap 2.14.1",
"slab",
"tokio",
"tokio-util",
@@ -5338,9 +5339,9 @@ dependencies = [
[[package]]
name = "hyper"
version = "1.11.0"
version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d22053281f852e11534f5198498373cbb59295120a20771d90f7ed1897490a72"
checksum = "27b501faa50e7a26c3d3560ca625132f4078a17771f4810baf70475ae48cbe43"
dependencies = [
"atomic-waker",
"bytes",
@@ -5560,9 +5561,9 @@ dependencies = [
[[package]]
name = "indexmap"
version = "2.14.0"
version = "2.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
checksum = "07aa2048142242915a31d35844fb311e0e53fcca590c3a0a40dcf1b841fa09eb"
dependencies = [
"equivalent",
"hashbrown 0.17.1",
@@ -5665,7 +5666,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46"
dependencies = [
"hermit-abi",
"libc",
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]
@@ -5855,7 +5856,7 @@ dependencies = [
"base64 0.22.1",
"getrandom 0.2.17",
"js-sys",
"pem",
"pem 3.0.6",
"serde",
"serde_json",
"signature 2.2.0",
@@ -5873,7 +5874,7 @@ dependencies = [
"bytes",
"crc",
"crc32c",
"indexmap 2.14.0",
"indexmap 2.14.1",
"paste",
"uuid",
]
@@ -5904,7 +5905,7 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ee7893dab2e44ae5f9d0173f26ff4aa327c10b01b06a72b52dd9405b628640d"
dependencies = [
"indexmap 2.14.0",
"indexmap 2.14.1",
]
[[package]]
@@ -6384,7 +6385,7 @@ dependencies = [
"crossbeam-epoch",
"crossbeam-utils",
"hashbrown 0.16.1",
"indexmap 2.14.0",
"indexmap 2.14.1",
"metrics",
"ordered-float 5.5.0",
"quanta",
@@ -6528,7 +6529,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
dependencies = [
"adler2",
"simd-adler32",
]
[[package]]
@@ -6538,6 +6538,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b63fbc4a50860e98e7b2aa7804ded1db5cbc3aff9193adaff57a6931bf7c4b4c"
dependencies = [
"adler2",
"simd-adler32",
]
[[package]]
@@ -7630,6 +7631,16 @@ dependencies = [
"serde_core",
]
[[package]]
name = "pem"
version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d354a98a3d1251555de99e8fdd8afda05573c31b82f59063a7b0a29b5527f120"
dependencies = [
"base64 0.23.1",
"serde_core",
]
[[package]]
name = "pem-rfc7468"
version = "0.7.0"
@@ -7682,7 +7693,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772"
dependencies = [
"fixedbitset",
"indexmap 2.14.0",
"indexmap 2.14.1",
]
[[package]]
@@ -7693,7 +7704,7 @@ checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455"
dependencies = [
"fixedbitset",
"hashbrown 0.15.5",
"indexmap 2.14.0",
"indexmap 2.14.1",
"serde",
]
@@ -7727,6 +7738,16 @@ dependencies = [
"serde",
]
[[package]]
name = "phf"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "010378780309880b08997fae13be7834dba947d36393bd372f2b1556deb2a2f6"
dependencies = [
"phf_shared 0.14.0",
"serde",
]
[[package]]
name = "phf_shared"
version = "0.12.1"
@@ -7745,6 +7766,15 @@ dependencies = [
"siphasher",
]
[[package]]
name = "phf_shared"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6fd9027e2d9319be6349febd1db4e8d02aa544921200c9b777720ac34a3aa89"
dependencies = [
"siphasher",
]
[[package]]
name = "pin-project"
version = "1.1.13"
@@ -7845,7 +7875,7 @@ version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63d440a804ec8d6fafbb6b84471e013286658d373248927692ab3366686220ca"
dependencies = [
"aes 0.9.2",
"aes 0.9.3",
"aes-gcm",
"cbc 0.2.1",
"der 0.8.1",
@@ -8343,7 +8373,7 @@ dependencies = [
"log",
"murmur3",
"nom 7.1.3",
"pem",
"pem 3.0.6",
"prost 0.13.5",
"prost-build 0.13.5",
"prost-derive 0.13.5",
@@ -8578,7 +8608,7 @@ dependencies = [
"once_cell",
"socket2",
"tracing",
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]
@@ -8774,12 +8804,12 @@ dependencies = [
[[package]]
name = "rcgen"
version = "0.14.9"
version = "0.14.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "091e7a8e7d86e6feb87a27ce8e2cba29d49eff9507afeebefab7eeb2ca667fb4"
checksum = "8774e05a7d0de114588e6a28fe7e71694b82614ed569d86d8b389dfbc98b8ad8"
dependencies = [
"aws-lc-rs",
"pem",
"pem 4.0.0",
"rustls-pki-types",
"time",
"x509-parser",
@@ -9185,7 +9215,7 @@ version = "0.63.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35bab1b87d915817d5d9cc352637cd40d5f0b298a48c6309af9156a4addc3031"
dependencies = [
"aes 0.9.2",
"aes 0.9.3",
"aws-lc-rs",
"bitflags 2.13.1",
"block-padding 0.4.2",
@@ -9916,7 +9946,7 @@ dependencies = [
"bytes",
"fnv",
"hmac 0.13.0",
"indexmap 2.14.0",
"indexmap 2.14.1",
"kafka-protocol",
"metrics",
"pbkdf2 0.13.0",
@@ -10904,7 +10934,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]
@@ -10977,7 +11007,7 @@ dependencies = [
"security-framework",
"security-framework-sys",
"webpki-root-certs",
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]
@@ -11034,7 +11064,7 @@ checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
[[package]]
name = "s3s"
version = "0.15.0"
source = "git+https://github.com/rustfs/s3s.git?rev=6e7b41252c7ba218a90886f58d297716ddf68acf#6e7b41252c7ba218a90886f58d297716ddf68acf"
source = "git+https://github.com/rustfs/s3s.git?rev=9c4690d8e73fc8d184031a19b2c4539ebc77d180#9c4690d8e73fc8d184031a19b2c4539ebc77d180"
dependencies = [
"arc-swap",
"arrayvec",
@@ -11061,6 +11091,7 @@ dependencies = [
"mime",
"nom 8.0.0",
"numeric_cast",
"phf 0.14.0",
"pin-project-lite",
"quick-xml",
"regex",
@@ -11090,7 +11121,7 @@ dependencies = [
[[package]]
name = "s3s-sigv2"
version = "0.16.0-alpha.1"
source = "git+https://github.com/rustfs/s3s.git?rev=6e7b41252c7ba218a90886f58d297716ddf68acf#6e7b41252c7ba218a90886f58d297716ddf68acf"
source = "git+https://github.com/rustfs/s3s.git?rev=9c4690d8e73fc8d184031a19b2c4539ebc77d180#9c4690d8e73fc8d184031a19b2c4539ebc77d180"
dependencies = [
"jiff",
"thiserror 2.0.20",
@@ -11099,7 +11130,7 @@ dependencies = [
[[package]]
name = "s3s-sigv4"
version = "0.16.0-alpha.1"
source = "git+https://github.com/rustfs/s3s.git?rev=6e7b41252c7ba218a90886f58d297716ddf68acf#6e7b41252c7ba218a90886f58d297716ddf68acf"
source = "git+https://github.com/rustfs/s3s.git?rev=9c4690d8e73fc8d184031a19b2c4539ebc77d180#9c4690d8e73fc8d184031a19b2c4539ebc77d180"
dependencies = [
"arrayvec",
"base64-simd",
@@ -11346,7 +11377,7 @@ version = "1.0.151"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14"
dependencies = [
"indexmap 2.14.0",
"indexmap 2.14.1",
"itoa",
"memchr",
"serde",
@@ -11417,7 +11448,7 @@ dependencies = [
"chrono",
"hex",
"indexmap 1.9.3",
"indexmap 2.14.0",
"indexmap 2.14.1",
"jiff",
"schemars 0.9.0",
"schemars 1.2.2",
@@ -11841,7 +11872,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d801accda99469cde6d73da741422610fdf6508a72d9a69d1b55cb241c720597"
dependencies = [
"aead",
"aes 0.9.2",
"aes 0.9.3",
"aes-gcm",
"chacha20",
"cipher 0.5.2",
@@ -12196,7 +12227,7 @@ dependencies = [
"getrandom 0.3.4",
"once_cell",
"rustix",
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]
@@ -12564,7 +12595,7 @@ version = "0.25.13+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6975367e4d2ef766d86af01ffad14b622fecc8d4357a998fbc4deb6e9bacaf9b"
dependencies = [
"indexmap 2.14.0",
"indexmap 2.14.1",
"toml_datetime",
"toml_parser",
"winnow",
@@ -12658,7 +12689,7 @@ checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4"
dependencies = [
"futures-core",
"futures-util",
"indexmap 2.14.0",
"indexmap 2.14.1",
"pin-project-lite",
"slab",
"sync_wrapper",
@@ -13315,7 +13346,7 @@ version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]
@@ -13786,7 +13817,7 @@ version = "8.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d04a6b5381502aa6087c94c669499eb1602eb9c5e8198e534de571f7154809b"
dependencies = [
"aes 0.9.2",
"aes 0.9.3",
"bzip2",
"constant_time_eq",
"crc32fast",
@@ -13794,7 +13825,7 @@ dependencies = [
"flate2",
"getrandom 0.4.3",
"hmac 0.13.0",
"indexmap 2.14.0",
"indexmap 2.14.1",
"lzma-rust2",
"memchr",
"pbkdf2 0.13.0",
+5 -5
View File
@@ -155,7 +155,7 @@ futures-util = "0.3.34"
pollster = "1.0.1"
pulsar = { default-features = false, version = "6.9.0" }
lapin = { default-features = false, version = "4.10.0" }
hyper = { version = "1.11.0" }
hyper = { version = "1.11.1" }
hyper-rustls = { default-features = false, version = "0.27.9" }
hyper-util = { version = "0.1.20" }
http = "1.5.0"
@@ -232,7 +232,7 @@ tokio-postgres-rustls = "0.14.0"
# Utilities and Tools
anyhow = "1.0.104"
arc-swap = "1.9.2"
astral-tokio-tar = "0.6.4"
astral-tokio-tar = "0.7.0"
atoi = "3.1.0"
atomic_enum = "0.3.0"
aws-config = { version = "1.11.0" }
@@ -257,7 +257,7 @@ datafusion = { default-features = false, version = "55.0.0" }
derive_builder = "0.20.2"
enumset = "1.1.14"
faster-hex = "0.10.0"
flate2 = "1.1.9"
flate2 = "1.1.10"
glob = "0.3.4"
google-cloud-storage = "1.18.0"
google-cloud-auth = "1.16.0"
@@ -304,7 +304,7 @@ rustify = { version = "0.7", default-features = false }
rustix = { version = "1.1.4" }
rust-embed = { version = "8.12.0" }
rustc-hash = { version = "2.1.3" }
s3s = { git = "https://github.com/rustfs/s3s.git", rev = "6e7b41252c7ba218a90886f58d297716ddf68acf", version = "0.15.0", features = ["minio"] }
s3s = { git = "https://github.com/rustfs/s3s.git", rev = "9c4690d8e73fc8d184031a19b2c4539ebc77d180", version = "0.15.0", features = ["minio"] }
serial_test = "4.0.1"
shadow-rs = { default-features = false, version = "2.0.0" }
siphasher = "1.0.3"
@@ -355,7 +355,7 @@ pyroscope = { version = "2.1.1" }
libunftp = { version = "0.23.0" }
unftp-core = "0.1.0"
suppaftp = { version = "10.0.2" }
rcgen = { version = "0.14.9", default-features = false, features = ["aws_lc_rs", "crypto", "pem"] }
rcgen = { version = "0.14.10", default-features = false, features = ["aws_lc_rs", "crypto", "pem"] }
russh = { version = "0.63.1" }
russh-sftp = "2.4.0"
+4
View File
@@ -164,6 +164,10 @@ mod delete_objects_versioning_test;
#[cfg(test)]
mod delete_object_no_content_length_test;
// Regression test for signed empty PutObject requests without Content-Length.
#[cfg(test)]
mod put_object_no_content_length_test;
// Delete-marker visibility baseline for data-movement migration proof.
#[cfg(test)]
mod delete_marker_migration_semantics_test;
@@ -0,0 +1,152 @@
// Copyright 2024 RustFS Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Regression coverage for rustfs#6830: a signed empty `PutObject` request
//! without `Content-Length` and without `Transfer-Encoding` is still a
//! zero-length object upload.
#[cfg(test)]
mod tests {
use crate::common::{RustFSTestEnvironment, init_logging};
use http::header::{CONTENT_LENGTH, HOST, TRANSFER_ENCODING};
use rustfs_signer::sign_v4;
use rustfs_utils::hash::EMPTY_STRING_SHA256_HASH;
use s3s::Body;
use std::error::Error;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::TcpStream;
use tokio::time::{Duration, timeout};
use tracing::info;
const RAW_RESPONSE_TIMEOUT: Duration = Duration::from_secs(10);
fn parse_status(raw_response: &str) -> Option<u16> {
raw_response.lines().next()?.split_whitespace().nth(1)?.parse().ok()
}
async fn send_raw_signed_put(
url: &str,
access_key: &str,
secret_key: &str,
transfer_encoding: Option<&str>,
raw_body: &[u8],
) -> Result<String, Box<dyn Error + Send + Sync>> {
let uri = url.parse::<http::Uri>()?;
let authority = uri.authority().ok_or("request URL missing authority")?.to_string();
let path_and_query = uri.path_and_query().ok_or("request URL missing path")?.as_str().to_string();
let mut request = http::Request::builder()
.method(http::Method::PUT)
.uri(uri)
.header(HOST, authority.clone())
.header("x-amz-content-sha256", EMPTY_STRING_SHA256_HASH);
if let Some(value) = transfer_encoding {
request = request.header(TRANSFER_ENCODING, value);
}
let signed = sign_v4(request.body(Body::empty())?, 0, access_key, secret_key, "", "us-east-1");
let mut raw_request = format!("PUT {path_and_query} HTTP/1.1\r\nHost: {authority}\r\nConnection: close\r\n");
for (name, value) in signed.headers() {
if name == HOST || name == CONTENT_LENGTH {
continue;
}
raw_request.push_str(name.as_str());
raw_request.push_str(": ");
raw_request.push_str(value.to_str()?);
raw_request.push_str("\r\n");
}
raw_request.push_str("\r\n");
assert!(
!raw_request.to_ascii_lowercase().contains("\r\ncontent-length:"),
"raw regression request must omit Content-Length; request was:\n{raw_request}"
);
let mut stream = TcpStream::connect(&authority).await?;
stream.write_all(raw_request.as_bytes()).await?;
stream.write_all(raw_body).await?;
stream.flush().await?;
let mut response = Vec::new();
timeout(RAW_RESPONSE_TIMEOUT, stream.read_to_end(&mut response))
.await
.map_err(|_| std::io::Error::new(std::io::ErrorKind::TimedOut, "timed out reading raw PUT response"))??;
Ok(String::from_utf8_lossy(&response).into_owned())
}
#[tokio::test]
async fn test_put_object_without_content_length_boundaries() -> Result<(), Box<dyn Error + Send + Sync>> {
init_logging();
info!("TEST: PutObject without Content-Length boundaries");
let mut env = RustFSTestEnvironment::new().await?;
env.start_rustfs_server(vec![]).await?;
let client = env.create_s3_client();
let empty_bucket = "put-no-content-length";
let empty_key = "empty.bin";
let chunked_bucket = "put-chunked-no-length";
let chunked_key = "chunked.bin";
client.create_bucket().bucket(empty_bucket).send().await?;
client.create_bucket().bucket(chunked_bucket).send().await?;
let url = format!("{}/{}/{}", env.url, empty_bucket, empty_key);
let raw_response = send_raw_signed_put(&url, &env.access_key, &env.secret_key, None, b"").await?;
info!("raw empty PUT response:\n{}", raw_response);
assert_eq!(
parse_status(&raw_response),
Some(200),
"empty PutObject without Content-Length should succeed, got:\n{raw_response}"
);
assert!(
raw_response.to_ascii_lowercase().contains("\r\netag:"),
"successful PutObject should return an ETag header: {raw_response}"
);
let head = client.head_object().bucket(empty_bucket).key(empty_key).send().await?;
assert_eq!(head.content_length(), Some(0), "stored object must be zero length");
let url = format!("{}/{}/{}", env.url, chunked_bucket, chunked_key);
let raw_response = send_raw_signed_put(&url, &env.access_key, &env.secret_key, Some("chunked"), b"0\r\n\r\n").await?;
info!("raw chunked PUT response:\n{}", raw_response);
assert_eq!(
parse_status(&raw_response),
Some(411),
"unknown-length chunked PutObject must stay rejected, got:\n{raw_response}"
);
assert!(
raw_response.contains("<Code>MissingContentLength</Code>"),
"expected MissingContentLength, got:\n{raw_response}"
);
let missing = client
.head_object()
.bucket(chunked_bucket)
.key(chunked_key)
.send()
.await
.expect_err("rejected unknown-length PUT must not create an object");
assert_eq!(
missing.raw_response().map(|response| response.status().as_u16()),
Some(404),
"rejected unknown-length PUT absence probe must return HTTP 404, got {missing:?}"
);
Ok(())
}
}
+1 -1
View File
@@ -656,7 +656,7 @@ impl DefaultObjectUsecase {
req_info.object = Some(fpath.clone());
req_info.version_id = None;
}
let entry_size = f.header().size().unwrap_or_default();
let entry_size = f.effective_size();
validate_put_object_extract_entry_size(&fpath, entry_size, extract_limits)?;
total_unpacked_size = total_unpacked_size
.checked_add(entry_size)
+1
View File
@@ -2273,6 +2273,7 @@ mod tests {
let s3_config = rustfs_s3_config();
assert!(s3_config.normalize_forward_slash_path);
assert!(s3_config.normalize_content_length);
assert!(s3_config.enable_sig_v2);
assert!(s3_config.sig_v4_allowed_services.iter().any(|service| service == "s3"));
assert!(s3_config.sig_v4_allowed_services.iter().any(|service| service == "sts"));