docs(replication): register the http interop duplication and pin its wire values (#5996)

backlog#1833 PR1 prescribed deduplicating crates/replication/src/http.rs onto the canonical rustfs-utils http modules via a re-export facade. That plan conflicts with a standing architecture guard the issue's review missed: check_architecture_migration_rules.sh rejects any rustfs-utils import or dependency from the replication crate ("replication crate HTTP/helper contracts must not import or depend on rustfs-utils"), the same way it bans rustfs-filemeta and rustfs-storage-api — the wire-contract crate deliberately has zero internal dependencies.

So this lands the issue's fallback shape instead (the same bidirectional do-not-merge pattern the issue itself prescribes for the policy path.rs cluster): a module doc on replication/http.rs naming the canonical owners and the guard that forces the local copy, mirror notes on utils' metadata_compat.rs and header_compat.rs, and a new test pinning every duplicated constant to its literal wire value so the two copies cannot drift silently.

No production code changed.

Ref rustfs/backlog#1833 (PR1).
This commit is contained in:
Zhengchao An
2026-08-12 22:20:11 +08:00
committed by GitHub
parent 87d47a6e5d
commit baadaccc30
3 changed files with 61 additions and 0 deletions
+47
View File
@@ -12,6 +12,26 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//! DELIBERATE DUPLICATION — do not merge these declarations into the
//! rustfs-utils http module without a maintainer decision on the crate
//! boundary.
//!
//! The canonical owners of these interop-contract values live in the
//! rustfs-utils crate: `crates/utils/src/http/metadata_compat.rs` (dual
//! x-rustfs-internal-/x-minio-internal- metadata keys),
//! `crates/utils/src/http/header_compat.rs` (x-rustfs-/x-minio- header pairs),
//! and `crates/utils/src/http/headers.rs` (standard S3 header names). This
//! crate keeps a local copy because
//! `rustfs-replication` is a wire-contract crate that must stay free of
//! internal dependencies: `scripts/check_architecture_migration_rules.sh`
//! rejects any `rustfs-utils` import or dependency here ("replication crate
//! HTTP/helper contracts must not import or depend on rustfs-utils"), and the
//! same rule bans `rustfs-filemeta` and `rustfs-storage-api`.
//!
//! Drift protection lives in the test module below: every constant's literal
//! wire value is pinned, so a change on either side that breaks interop fails
//! this crate's tests rather than silently forking the contract.
use std::collections::HashMap;
const RUSTFS_INTERNAL_PREFIX: &str = "x-rustfs-internal-";
@@ -145,4 +165,31 @@ mod tests {
assert!(has_prefix_fold("X-Amz-Meta-Foo", "x-amz-meta-"));
assert!(!has_prefix_fold("X-Amz-Meta-Foo", "amz-meta"));
}
/// Pins every duplicated interop constant to its literal wire value. The
/// canonical owner lives in the rustfs-utils crate (see the module doc);
/// an arch guard forbids depending on it from this crate, so byte-for-byte
/// pinning here is what keeps the two copies from drifting apart.
#[test]
fn duplicated_interop_constants_pin_canonical_wire_values() {
use super::*;
assert_eq!(AMZ_BUCKET_REPLICATION_STATUS, "X-Amz-Replication-Status");
assert_eq!(AMZ_OBJECT_LOCK_LEGAL_HOLD, "X-Amz-Object-Lock-Legal-Hold");
assert_eq!(AMZ_OBJECT_LOCK_MODE, "X-Amz-Object-Lock-Mode");
assert_eq!(AMZ_OBJECT_LOCK_RETAIN_UNTIL_DATE, "X-Amz-Object-Lock-Retain-Until-Date");
assert_eq!(AMZ_OBJECT_TAGGING, "X-Amz-Tagging");
assert_eq!(AMZ_WEBSITE_REDIRECT_LOCATION, "x-amz-website-redirect-location");
assert_eq!(CACHE_CONTROL, "Cache-Control");
assert_eq!(CONTENT_DISPOSITION, "Content-Disposition");
assert_eq!(CONTENT_ENCODING, "Content-Encoding");
assert_eq!(CONTENT_LANGUAGE, "Content-Language");
assert_eq!(EXPIRES, "Expires");
assert_eq!(SSEC_ALGORITHM_HEADER, "x-amz-server-side-encryption-customer-algorithm");
assert_eq!(SSEC_KEY_HEADER, "x-amz-server-side-encryption-customer-key");
assert_eq!(SSEC_KEY_MD5_HEADER, "x-amz-server-side-encryption-customer-key-md5");
assert_eq!(SUFFIX_ACTUAL_SIZE, "actual-size");
assert_eq!(SUFFIX_REPLICATION_STATUS, "replication-status");
assert_eq!(SUFFIX_REPLICATION_RESET_STATUS, "replication-reset-status");
}
}
+7
View File
@@ -17,6 +17,13 @@
//!
//! Use suffix-based API: `get_header(headers, SUFFIX_FORCE_DELETE)` queries both
//! x-rustfs-force-delete and x-minio-force-delete.
//!
//! This module is the canonical owner of these interop values. One deliberate
//! copy exists: `crates/replication/src/http.rs` re-declares the subset it
//! needs because the wire-contract crate must stay free of internal
//! dependencies (arch guard in `scripts/check_architecture_migration_rules.sh`
//! bans replication -> rustfs-utils). When changing a value here, check the
//! pinned copy there; its tests pin the shared wire values byte-for-byte.
use http::{HeaderMap, HeaderValue};
use std::borrow::Cow;
+7
View File
@@ -14,6 +14,13 @@
//! System metadata compatibility: write both x-rustfs-internal-* and x-minio-internal-*
//! for MinIO interoperability. Read prefers RustFS, fallback to MinIO.
//!
//! This module is the canonical owner of these interop values. One deliberate
//! copy exists: `crates/replication/src/http.rs` re-declares the subset it
//! needs because the wire-contract crate must stay free of internal
//! dependencies (arch guard in `scripts/check_architecture_migration_rules.sh`
//! bans replication -> rustfs-utils). When changing a value here, check the
//! pinned copy there; its tests pin the shared wire values byte-for-byte.
use std::collections::{BTreeMap, HashMap};