mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-19 19:16:17 +00:00
Feature up/ilm (#305)
* fix * fix * fix * fix delete-marker expiration. add api_restore. * fix * time retry object upload * lock file * make fmt * fix * restore object * fix * fix * serde-rs-xml -> quick-xml * fix * checksum * fix * fix * fix * fix * fix * fix * fix
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
// 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.
|
||||
#![allow(dead_code)]
|
||||
|
||||
use base64_simd::STANDARD;
|
||||
use std::error::Error;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct DecodeError(base64_simd::Error);
|
||||
|
||||
impl Error for DecodeError {
|
||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
Some(&self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for DecodeError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "failed to decode base64")
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn decode(input: impl AsRef<str>) -> Result<Vec<u8>, DecodeError> {
|
||||
STANDARD.decode_to_vec(input.as_ref()).map_err(DecodeError)
|
||||
}
|
||||
|
||||
pub(crate) fn encode(input: impl AsRef<[u8]>) -> String {
|
||||
STANDARD.encode_to_string(input.as_ref())
|
||||
}
|
||||
|
||||
pub(crate) fn encoded_length(length: usize) -> usize {
|
||||
STANDARD.encoded_length(length)
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// 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.
|
||||
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UnknownChecksumAlgorithmError {
|
||||
checksum_algorithm: String,
|
||||
}
|
||||
|
||||
impl UnknownChecksumAlgorithmError {
|
||||
pub(crate) fn new(checksum_algorithm: impl Into<String>) -> Self {
|
||||
Self {
|
||||
checksum_algorithm: checksum_algorithm.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn checksum_algorithm(&self) -> &str {
|
||||
&self.checksum_algorithm
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for UnknownChecksumAlgorithmError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
r#"unknown checksum algorithm "{}", please pass a known algorithm name ("crc32", "crc32c", "sha1", "sha256", "md5")"#,
|
||||
self.checksum_algorithm
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for UnknownChecksumAlgorithmError {}
|
||||
@@ -0,0 +1,196 @@
|
||||
// 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.
|
||||
|
||||
use crate::base64;
|
||||
use http::header::{HeaderMap, HeaderValue};
|
||||
|
||||
use crate::Crc64Nvme;
|
||||
use crate::{CRC_32_C_NAME, CRC_32_NAME, CRC_64_NVME_NAME, Checksum, Crc32, Crc32c, Md5, SHA_1_NAME, SHA_256_NAME, Sha1, Sha256};
|
||||
|
||||
pub const CRC_32_HEADER_NAME: &str = "x-amz-checksum-crc32";
|
||||
pub const CRC_32_C_HEADER_NAME: &str = "x-amz-checksum-crc32c";
|
||||
pub const SHA_1_HEADER_NAME: &str = "x-amz-checksum-sha1";
|
||||
pub const SHA_256_HEADER_NAME: &str = "x-amz-checksum-sha256";
|
||||
pub const CRC_64_NVME_HEADER_NAME: &str = "x-amz-checksum-crc64nvme";
|
||||
|
||||
pub(crate) static MD5_HEADER_NAME: &str = "content-md5";
|
||||
|
||||
pub const CHECKSUM_ALGORITHMS_IN_PRIORITY_ORDER: [&str; 5] =
|
||||
[CRC_64_NVME_NAME, CRC_32_C_NAME, CRC_32_NAME, SHA_1_NAME, SHA_256_NAME];
|
||||
|
||||
pub trait HttpChecksum: Checksum + Send + Sync {
|
||||
fn headers(self: Box<Self>) -> HeaderMap<HeaderValue> {
|
||||
let mut header_map = HeaderMap::new();
|
||||
header_map.insert(self.header_name(), self.header_value());
|
||||
|
||||
header_map
|
||||
}
|
||||
|
||||
fn header_name(&self) -> &'static str;
|
||||
|
||||
fn header_value(self: Box<Self>) -> HeaderValue {
|
||||
let hash = self.finalize();
|
||||
HeaderValue::from_str(&base64::encode(&hash[..])).expect("base64 encoded bytes are always valid header values")
|
||||
}
|
||||
|
||||
fn size(&self) -> u64 {
|
||||
let trailer_name_size_in_bytes = self.header_name().len();
|
||||
let base64_encoded_checksum_size_in_bytes = base64::encoded_length(Checksum::size(self) as usize);
|
||||
|
||||
let size = trailer_name_size_in_bytes + ":".len() + base64_encoded_checksum_size_in_bytes;
|
||||
|
||||
size as u64
|
||||
}
|
||||
}
|
||||
|
||||
impl HttpChecksum for Crc32 {
|
||||
fn header_name(&self) -> &'static str {
|
||||
CRC_32_HEADER_NAME
|
||||
}
|
||||
}
|
||||
|
||||
impl HttpChecksum for Crc32c {
|
||||
fn header_name(&self) -> &'static str {
|
||||
CRC_32_C_HEADER_NAME
|
||||
}
|
||||
}
|
||||
|
||||
impl HttpChecksum for Crc64Nvme {
|
||||
fn header_name(&self) -> &'static str {
|
||||
CRC_64_NVME_HEADER_NAME
|
||||
}
|
||||
}
|
||||
|
||||
impl HttpChecksum for Sha1 {
|
||||
fn header_name(&self) -> &'static str {
|
||||
SHA_1_HEADER_NAME
|
||||
}
|
||||
}
|
||||
|
||||
impl HttpChecksum for Sha256 {
|
||||
fn header_name(&self) -> &'static str {
|
||||
SHA_256_HEADER_NAME
|
||||
}
|
||||
}
|
||||
|
||||
impl HttpChecksum for Md5 {
|
||||
fn header_name(&self) -> &'static str {
|
||||
MD5_HEADER_NAME
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::base64;
|
||||
use bytes::Bytes;
|
||||
|
||||
use crate::{CRC_32_C_NAME, CRC_32_NAME, CRC_64_NVME_NAME, ChecksumAlgorithm, SHA_1_NAME, SHA_256_NAME};
|
||||
|
||||
use super::HttpChecksum;
|
||||
|
||||
#[test]
|
||||
fn test_trailer_length_of_crc32_checksum_body() {
|
||||
let checksum = CRC_32_NAME.parse::<ChecksumAlgorithm>().unwrap().into_impl();
|
||||
let expected_size = 29;
|
||||
let actual_size = HttpChecksum::size(&*checksum);
|
||||
assert_eq!(expected_size, actual_size)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_trailer_value_of_crc32_checksum_body() {
|
||||
let checksum = CRC_32_NAME.parse::<ChecksumAlgorithm>().unwrap().into_impl();
|
||||
// The CRC32 of an empty string is all zeroes
|
||||
let expected_value = Bytes::from_static(b"\0\0\0\0");
|
||||
let expected_value = base64::encode(&expected_value);
|
||||
let actual_value = checksum.header_value();
|
||||
assert_eq!(expected_value, actual_value)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_trailer_length_of_crc32c_checksum_body() {
|
||||
let checksum = CRC_32_C_NAME.parse::<ChecksumAlgorithm>().unwrap().into_impl();
|
||||
let expected_size = 30;
|
||||
let actual_size = HttpChecksum::size(&*checksum);
|
||||
assert_eq!(expected_size, actual_size)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_trailer_value_of_crc32c_checksum_body() {
|
||||
let checksum = CRC_32_C_NAME.parse::<ChecksumAlgorithm>().unwrap().into_impl();
|
||||
// The CRC32C of an empty string is all zeroes
|
||||
let expected_value = Bytes::from_static(b"\0\0\0\0");
|
||||
let expected_value = base64::encode(&expected_value);
|
||||
let actual_value = checksum.header_value();
|
||||
assert_eq!(expected_value, actual_value)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_trailer_length_of_crc64nvme_checksum_body() {
|
||||
let checksum = CRC_64_NVME_NAME.parse::<ChecksumAlgorithm>().unwrap().into_impl();
|
||||
let expected_size = 37;
|
||||
let actual_size = HttpChecksum::size(&*checksum);
|
||||
assert_eq!(expected_size, actual_size)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_trailer_value_of_crc64nvme_checksum_body() {
|
||||
let checksum = CRC_64_NVME_NAME.parse::<ChecksumAlgorithm>().unwrap().into_impl();
|
||||
// The CRC64NVME of an empty string is all zeroes
|
||||
let expected_value = Bytes::from_static(b"\0\0\0\0\0\0\0\0");
|
||||
let expected_value = base64::encode(&expected_value);
|
||||
let actual_value = checksum.header_value();
|
||||
assert_eq!(expected_value, actual_value)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_trailer_length_of_sha1_checksum_body() {
|
||||
let checksum = SHA_1_NAME.parse::<ChecksumAlgorithm>().unwrap().into_impl();
|
||||
let expected_size = 48;
|
||||
let actual_size = HttpChecksum::size(&*checksum);
|
||||
assert_eq!(expected_size, actual_size)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_trailer_value_of_sha1_checksum_body() {
|
||||
let checksum = SHA_1_NAME.parse::<ChecksumAlgorithm>().unwrap().into_impl();
|
||||
// The SHA1 of an empty string is da39a3ee5e6b4b0d3255bfef95601890afd80709
|
||||
let expected_value = Bytes::from_static(&[
|
||||
0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55, 0xbf, 0xef, 0x95, 0x60, 0x18, 0x90, 0xaf, 0xd8, 0x07,
|
||||
0x09,
|
||||
]);
|
||||
let expected_value = base64::encode(&expected_value);
|
||||
let actual_value = checksum.header_value();
|
||||
assert_eq!(expected_value, actual_value)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_trailer_length_of_sha256_checksum_body() {
|
||||
let checksum = SHA_256_NAME.parse::<ChecksumAlgorithm>().unwrap().into_impl();
|
||||
let expected_size = 66;
|
||||
let actual_size = HttpChecksum::size(&*checksum);
|
||||
assert_eq!(expected_size, actual_size)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_trailer_value_of_sha256_checksum_body() {
|
||||
let checksum = SHA_256_NAME.parse::<ChecksumAlgorithm>().unwrap().into_impl();
|
||||
let expected_value = Bytes::from_static(&[
|
||||
0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41,
|
||||
0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55,
|
||||
]);
|
||||
let expected_value = base64::encode(&expected_value);
|
||||
let actual_value = checksum.header_value();
|
||||
assert_eq!(expected_value, actual_value)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,446 @@
|
||||
// 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.
|
||||
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
#![allow(clippy::derive_partial_eq_without_eq)]
|
||||
#![warn(
|
||||
// missing_docs,
|
||||
rustdoc::missing_crate_level_docs,
|
||||
unreachable_pub,
|
||||
rust_2018_idioms
|
||||
)]
|
||||
|
||||
use crate::error::UnknownChecksumAlgorithmError;
|
||||
|
||||
use bytes::Bytes;
|
||||
use std::{fmt::Debug, str::FromStr};
|
||||
|
||||
mod base64;
|
||||
pub mod error;
|
||||
pub mod http;
|
||||
|
||||
pub const CRC_32_NAME: &str = "crc32";
|
||||
pub const CRC_32_C_NAME: &str = "crc32c";
|
||||
pub const CRC_64_NVME_NAME: &str = "crc64nvme";
|
||||
pub const SHA_1_NAME: &str = "sha1";
|
||||
pub const SHA_256_NAME: &str = "sha256";
|
||||
pub const MD5_NAME: &str = "md5";
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
#[non_exhaustive]
|
||||
pub enum ChecksumAlgorithm {
|
||||
#[default]
|
||||
Crc32,
|
||||
Crc32c,
|
||||
#[deprecated]
|
||||
Md5,
|
||||
Sha1,
|
||||
Sha256,
|
||||
Crc64Nvme,
|
||||
}
|
||||
|
||||
impl FromStr for ChecksumAlgorithm {
|
||||
type Err = UnknownChecksumAlgorithmError;
|
||||
|
||||
fn from_str(checksum_algorithm: &str) -> Result<Self, Self::Err> {
|
||||
if checksum_algorithm.eq_ignore_ascii_case(CRC_32_NAME) {
|
||||
Ok(Self::Crc32)
|
||||
} else if checksum_algorithm.eq_ignore_ascii_case(CRC_32_C_NAME) {
|
||||
Ok(Self::Crc32c)
|
||||
} else if checksum_algorithm.eq_ignore_ascii_case(SHA_1_NAME) {
|
||||
Ok(Self::Sha1)
|
||||
} else if checksum_algorithm.eq_ignore_ascii_case(SHA_256_NAME) {
|
||||
Ok(Self::Sha256)
|
||||
} else if checksum_algorithm.eq_ignore_ascii_case(MD5_NAME) {
|
||||
// MD5 is now an alias for the default Crc32 since it is deprecated
|
||||
Ok(Self::Crc32)
|
||||
} else if checksum_algorithm.eq_ignore_ascii_case(CRC_64_NVME_NAME) {
|
||||
Ok(Self::Crc64Nvme)
|
||||
} else {
|
||||
Err(UnknownChecksumAlgorithmError::new(checksum_algorithm))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ChecksumAlgorithm {
|
||||
pub fn into_impl(self) -> Box<dyn http::HttpChecksum> {
|
||||
match self {
|
||||
Self::Crc32 => Box::<Crc32>::default(),
|
||||
Self::Crc32c => Box::<Crc32c>::default(),
|
||||
Self::Crc64Nvme => Box::<Crc64Nvme>::default(),
|
||||
#[allow(deprecated)]
|
||||
Self::Md5 => Box::<Crc32>::default(),
|
||||
Self::Sha1 => Box::<Sha1>::default(),
|
||||
Self::Sha256 => Box::<Sha256>::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Crc32 => CRC_32_NAME,
|
||||
Self::Crc32c => CRC_32_C_NAME,
|
||||
Self::Crc64Nvme => CRC_64_NVME_NAME,
|
||||
#[allow(deprecated)]
|
||||
Self::Md5 => MD5_NAME,
|
||||
Self::Sha1 => SHA_1_NAME,
|
||||
Self::Sha256 => SHA_256_NAME,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Checksum: Send + Sync {
|
||||
fn update(&mut self, bytes: &[u8]);
|
||||
fn finalize(self: Box<Self>) -> Bytes;
|
||||
fn size(&self) -> u64;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Crc32 {
|
||||
hasher: crc_fast::Digest,
|
||||
}
|
||||
|
||||
impl Default for Crc32 {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
hasher: crc_fast::Digest::new(crc_fast::CrcAlgorithm::Crc32IsoHdlc),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Crc32 {
|
||||
fn update(&mut self, bytes: &[u8]) {
|
||||
self.hasher.update(bytes);
|
||||
}
|
||||
|
||||
fn finalize(self) -> Bytes {
|
||||
let checksum = self.hasher.finalize() as u32;
|
||||
|
||||
Bytes::copy_from_slice(checksum.to_be_bytes().as_slice())
|
||||
}
|
||||
|
||||
fn size() -> u64 {
|
||||
4
|
||||
}
|
||||
}
|
||||
|
||||
impl Checksum for Crc32 {
|
||||
fn update(&mut self, bytes: &[u8]) {
|
||||
Self::update(self, bytes)
|
||||
}
|
||||
fn finalize(self: Box<Self>) -> Bytes {
|
||||
Self::finalize(*self)
|
||||
}
|
||||
fn size(&self) -> u64 {
|
||||
Self::size()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Crc32c {
|
||||
hasher: crc_fast::Digest,
|
||||
}
|
||||
|
||||
impl Default for Crc32c {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
hasher: crc_fast::Digest::new(crc_fast::CrcAlgorithm::Crc32Iscsi),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Crc32c {
|
||||
fn update(&mut self, bytes: &[u8]) {
|
||||
self.hasher.update(bytes);
|
||||
}
|
||||
|
||||
fn finalize(self) -> Bytes {
|
||||
let checksum = self.hasher.finalize() as u32;
|
||||
|
||||
Bytes::copy_from_slice(checksum.to_be_bytes().as_slice())
|
||||
}
|
||||
|
||||
fn size() -> u64 {
|
||||
4
|
||||
}
|
||||
}
|
||||
|
||||
impl Checksum for Crc32c {
|
||||
fn update(&mut self, bytes: &[u8]) {
|
||||
Self::update(self, bytes)
|
||||
}
|
||||
fn finalize(self: Box<Self>) -> Bytes {
|
||||
Self::finalize(*self)
|
||||
}
|
||||
fn size(&self) -> u64 {
|
||||
Self::size()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Crc64Nvme {
|
||||
hasher: crc_fast::Digest,
|
||||
}
|
||||
|
||||
impl Default for Crc64Nvme {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
hasher: crc_fast::Digest::new(crc_fast::CrcAlgorithm::Crc64Nvme),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Crc64Nvme {
|
||||
fn update(&mut self, bytes: &[u8]) {
|
||||
self.hasher.update(bytes);
|
||||
}
|
||||
|
||||
fn finalize(self) -> Bytes {
|
||||
Bytes::copy_from_slice(self.hasher.finalize().to_be_bytes().as_slice())
|
||||
}
|
||||
|
||||
fn size() -> u64 {
|
||||
8
|
||||
}
|
||||
}
|
||||
|
||||
impl Checksum for Crc64Nvme {
|
||||
fn update(&mut self, bytes: &[u8]) {
|
||||
Self::update(self, bytes)
|
||||
}
|
||||
fn finalize(self: Box<Self>) -> Bytes {
|
||||
Self::finalize(*self)
|
||||
}
|
||||
fn size(&self) -> u64 {
|
||||
Self::size()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct Sha1 {
|
||||
hasher: sha1::Sha1,
|
||||
}
|
||||
|
||||
impl Sha1 {
|
||||
fn update(&mut self, bytes: &[u8]) {
|
||||
use sha1::Digest;
|
||||
self.hasher.update(bytes);
|
||||
}
|
||||
|
||||
fn finalize(self) -> Bytes {
|
||||
use sha1::Digest;
|
||||
Bytes::copy_from_slice(self.hasher.finalize().as_slice())
|
||||
}
|
||||
|
||||
fn size() -> u64 {
|
||||
use sha1::Digest;
|
||||
sha1::Sha1::output_size() as u64
|
||||
}
|
||||
}
|
||||
|
||||
impl Checksum for Sha1 {
|
||||
fn update(&mut self, bytes: &[u8]) {
|
||||
Self::update(self, bytes)
|
||||
}
|
||||
|
||||
fn finalize(self: Box<Self>) -> Bytes {
|
||||
Self::finalize(*self)
|
||||
}
|
||||
fn size(&self) -> u64 {
|
||||
Self::size()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct Sha256 {
|
||||
hasher: sha2::Sha256,
|
||||
}
|
||||
|
||||
impl Sha256 {
|
||||
fn update(&mut self, bytes: &[u8]) {
|
||||
use sha2::Digest;
|
||||
self.hasher.update(bytes);
|
||||
}
|
||||
|
||||
fn finalize(self) -> Bytes {
|
||||
use sha2::Digest;
|
||||
Bytes::copy_from_slice(self.hasher.finalize().as_slice())
|
||||
}
|
||||
|
||||
fn size() -> u64 {
|
||||
use sha2::Digest;
|
||||
sha2::Sha256::output_size() as u64
|
||||
}
|
||||
}
|
||||
|
||||
impl Checksum for Sha256 {
|
||||
fn update(&mut self, bytes: &[u8]) {
|
||||
Self::update(self, bytes);
|
||||
}
|
||||
fn finalize(self: Box<Self>) -> Bytes {
|
||||
Self::finalize(*self)
|
||||
}
|
||||
fn size(&self) -> u64 {
|
||||
Self::size()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct Md5 {
|
||||
hasher: md5::Md5,
|
||||
}
|
||||
|
||||
impl Md5 {
|
||||
fn update(&mut self, bytes: &[u8]) {
|
||||
use md5::Digest;
|
||||
self.hasher.update(bytes);
|
||||
}
|
||||
|
||||
fn finalize(self) -> Bytes {
|
||||
use md5::Digest;
|
||||
Bytes::copy_from_slice(self.hasher.finalize().as_slice())
|
||||
}
|
||||
|
||||
fn size() -> u64 {
|
||||
use md5::Digest;
|
||||
md5::Md5::output_size() as u64
|
||||
}
|
||||
}
|
||||
|
||||
impl Checksum for Md5 {
|
||||
fn update(&mut self, bytes: &[u8]) {
|
||||
Self::update(self, bytes)
|
||||
}
|
||||
fn finalize(self: Box<Self>) -> Bytes {
|
||||
Self::finalize(*self)
|
||||
}
|
||||
fn size(&self) -> u64 {
|
||||
Self::size()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{
|
||||
Crc32, Crc32c, Md5, Sha1, Sha256,
|
||||
http::{CRC_32_C_HEADER_NAME, CRC_32_HEADER_NAME, MD5_HEADER_NAME, SHA_1_HEADER_NAME, SHA_256_HEADER_NAME},
|
||||
};
|
||||
|
||||
use crate::ChecksumAlgorithm;
|
||||
use crate::http::HttpChecksum;
|
||||
|
||||
use crate::base64;
|
||||
use http::HeaderValue;
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::fmt::Write;
|
||||
|
||||
const TEST_DATA: &str = r#"test data"#;
|
||||
|
||||
fn base64_encoded_checksum_to_hex_string(header_value: &HeaderValue) -> String {
|
||||
let decoded_checksum = base64::decode(header_value.to_str().unwrap()).unwrap();
|
||||
let decoded_checksum = decoded_checksum.into_iter().fold(String::new(), |mut acc, byte| {
|
||||
write!(acc, "{byte:02X?}").expect("string will always be writeable");
|
||||
acc
|
||||
});
|
||||
|
||||
format!("0x{decoded_checksum}")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_crc32_checksum() {
|
||||
let mut checksum = Crc32::default();
|
||||
checksum.update(TEST_DATA.as_bytes());
|
||||
let checksum_result = Box::new(checksum).headers();
|
||||
let encoded_checksum = checksum_result.get(CRC_32_HEADER_NAME).unwrap();
|
||||
let decoded_checksum = base64_encoded_checksum_to_hex_string(encoded_checksum);
|
||||
|
||||
let expected_checksum = "0xD308AEB2";
|
||||
|
||||
assert_eq!(decoded_checksum, expected_checksum);
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_arch = "powerpc", target_arch = "powerpc64")))]
|
||||
#[test]
|
||||
fn test_crc32c_checksum() {
|
||||
let mut checksum = Crc32c::default();
|
||||
checksum.update(TEST_DATA.as_bytes());
|
||||
let checksum_result = Box::new(checksum).headers();
|
||||
let encoded_checksum = checksum_result.get(CRC_32_C_HEADER_NAME).unwrap();
|
||||
let decoded_checksum = base64_encoded_checksum_to_hex_string(encoded_checksum);
|
||||
|
||||
let expected_checksum = "0x3379B4CA";
|
||||
|
||||
assert_eq!(decoded_checksum, expected_checksum);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_crc64nvme_checksum() {
|
||||
use crate::{Crc64Nvme, http::CRC_64_NVME_HEADER_NAME};
|
||||
let mut checksum = Crc64Nvme::default();
|
||||
checksum.update(TEST_DATA.as_bytes());
|
||||
let checksum_result = Box::new(checksum).headers();
|
||||
let encoded_checksum = checksum_result.get(CRC_64_NVME_HEADER_NAME).unwrap();
|
||||
let decoded_checksum = base64_encoded_checksum_to_hex_string(encoded_checksum);
|
||||
|
||||
let expected_checksum = "0xAECAF3AF9C98A855";
|
||||
|
||||
assert_eq!(decoded_checksum, expected_checksum);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sha1_checksum() {
|
||||
let mut checksum = Sha1::default();
|
||||
checksum.update(TEST_DATA.as_bytes());
|
||||
let checksum_result = Box::new(checksum).headers();
|
||||
let encoded_checksum = checksum_result.get(SHA_1_HEADER_NAME).unwrap();
|
||||
let decoded_checksum = base64_encoded_checksum_to_hex_string(encoded_checksum);
|
||||
|
||||
let expected_checksum = "0xF48DD853820860816C75D54D0F584DC863327A7C";
|
||||
|
||||
assert_eq!(decoded_checksum, expected_checksum);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sha256_checksum() {
|
||||
let mut checksum = Sha256::default();
|
||||
checksum.update(TEST_DATA.as_bytes());
|
||||
let checksum_result = Box::new(checksum).headers();
|
||||
let encoded_checksum = checksum_result.get(SHA_256_HEADER_NAME).unwrap();
|
||||
let decoded_checksum = base64_encoded_checksum_to_hex_string(encoded_checksum);
|
||||
|
||||
let expected_checksum = "0x916F0027A575074CE72A331777C3478D6513F786A591BD892DA1A577BF2335F9";
|
||||
|
||||
assert_eq!(decoded_checksum, expected_checksum);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_md5_checksum() {
|
||||
let mut checksum = Md5::default();
|
||||
checksum.update(TEST_DATA.as_bytes());
|
||||
let checksum_result = Box::new(checksum).headers();
|
||||
let encoded_checksum = checksum_result.get(MD5_HEADER_NAME).unwrap();
|
||||
let decoded_checksum = base64_encoded_checksum_to_hex_string(encoded_checksum);
|
||||
|
||||
let expected_checksum = "0xEB733A00C0C9D336E65691A37AB54293";
|
||||
|
||||
assert_eq!(decoded_checksum, expected_checksum);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_checksum_algorithm_returns_error_for_unknown() {
|
||||
let error = "some invalid checksum algorithm"
|
||||
.parse::<ChecksumAlgorithm>()
|
||||
.expect_err("it should error");
|
||||
assert_eq!("some invalid checksum algorithm", error.checksum_algorithm());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user