Skip to content

Commit

Permalink
Added support for R2 Checksums to GET and PUT. (#460)
Browse files Browse the repository at this point in the history
* Added support for R2 Checksums to GET and PUT.

* Fixed some clippy warnings.
  • Loading branch information
spigaz authored Mar 8, 2024
1 parent 9328b29 commit 3f1f783
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 8 deletions.
2 changes: 2 additions & 0 deletions worker-sys/src/types/r2.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
mod bucket;
mod checksums;
mod http_metadata;
mod multipart_upload;
mod object;
Expand All @@ -8,6 +9,7 @@ mod range;
mod uploaded_part;

pub use bucket::*;
pub use checksums::*;
pub use http_metadata::*;
pub use multipart_upload::*;
pub use object::*;
Expand Down
60 changes: 60 additions & 0 deletions worker-sys/src/types/r2/checksums.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use js_sys::{ArrayBuffer, Object, Reflect, Uint8Array};
use wasm_bindgen::JsCast;

#[derive(Debug, Clone)]
pub struct R2Checksums {
pub md5: Option<Vec<u8>>,
pub sha1: Option<Vec<u8>>,
pub sha256: Option<Vec<u8>>,
pub sha384: Option<Vec<u8>>,
pub sha512: Option<Vec<u8>>,
}

impl R2Checksums {
pub fn new() -> Self {
Self {
md5: None,
sha1: None,
sha256: None,
sha384: None,
sha512: None,
}
}
}

fn get(obj: &Object, key: &str) -> Option<Vec<u8>> {
let value = Reflect::get(obj, &key.into());
if value.is_err() {
return None;
}

let value = value.unwrap().dyn_into::<ArrayBuffer>();
if value.is_err() {
return None;
}

let array_buffer: ArrayBuffer = value.unwrap();

let uint8_array = Uint8Array::new(&array_buffer);
let mut vec = vec![0; uint8_array.length() as usize];
uint8_array.copy_to(&mut vec);
Some(vec)
}

impl From<Object> for R2Checksums {
fn from(obj: Object) -> Self {
Self {
md5: get(&obj, "md5"),
sha1: get(&obj, "sha1"),
sha256: get(&obj, "sha256"),
sha384: get(&obj, "sha384"),
sha512: get(&obj, "sha512"),
}
}
}

impl Default for R2Checksums {
fn default() -> Self {
Self::new()
}
}
3 changes: 3 additions & 0 deletions worker-sys/src/types/r2/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ extern "C" {
#[wasm_bindgen(method, getter, js_name=httpMetadata)]
pub fn http_metadata(this: &R2Object) -> R2HttpMetadata;

#[wasm_bindgen(method, getter)]
pub fn checksums(this: &R2Object) -> js_sys::Object;

#[wasm_bindgen(method, getter, js_name=customMetadata)]
pub fn custom_metadata(this: &R2Object) -> js_sys::Object;

Expand Down
38 changes: 32 additions & 6 deletions worker/src/r2/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,8 @@ pub struct PutOptionsBuilder<'bucket> {
pub(crate) value: Data,
pub(crate) http_metadata: Option<HttpMetadata>,
pub(crate) custom_metadata: Option<HashMap<String, String>>,
pub(crate) md5: Option<Vec<u8>>,
pub(crate) checksum: Option<Vec<u8>>,
pub(crate) checksum_algorithm: String,
}

impl<'bucket> PutOptionsBuilder<'bucket> {
Expand All @@ -169,12 +170,37 @@ impl<'bucket> PutOptionsBuilder<'bucket> {
self
}

/// A md5 hash to use to check the received object’s integrity.
pub fn md5(mut self, bytes: impl Into<Vec<u8>>) -> Self {
self.md5 = Some(bytes.into());
fn checksum_set(mut self, algorithm: &str, checksum: impl Into<Vec<u8>>) -> Self {
self.checksum_algorithm = algorithm.into();
self.checksum = Some(checksum.into());
self
}

/// A md5 hash to use to check the received object’s integrity.
pub fn md5(self, bytes: impl Into<Vec<u8>>) -> Self {
self.checksum_set("md5", bytes)
}

/// A sha1 hash to use to check the received object’s integrity.
pub fn sha1(self, bytes: impl Into<Vec<u8>>) -> Self {
self.checksum_set("sha1", bytes)
}

/// A sha256 hash to use to check the received object’s integrity.
pub fn sha256(self, bytes: impl Into<Vec<u8>>) -> Self {
self.checksum_set("sha256", bytes)
}

/// A sha384 hash to use to check the received object’s integrity.
pub fn sha384(self, bytes: impl Into<Vec<u8>>) -> Self {
self.checksum_set("sha384", bytes)
}

/// A sha512 hash to use to check the received object’s integrity.
pub fn sha512(self, bytes: impl Into<Vec<u8>>) -> Self {
self.checksum_set("sha512", bytes)
}

/// Executes the PUT operation on the R2 bucket.
pub async fn execute(self) -> Result<Object> {
let value: JsValue = self.value.into();
Expand All @@ -195,11 +221,11 @@ impl<'bucket> PutOptionsBuilder<'bucket> {
}
None => JsValue::UNDEFINED,
},
"md5" => self.md5.map(|bytes| {
self.checksum_algorithm => self.checksum.map(|bytes| {
let arr = Uint8Array::new_with_length(bytes.len() as _);
arr.copy_from(&bytes);
arr.buffer()
})
}),
}
.into(),
);
Expand Down
14 changes: 12 additions & 2 deletions worker/src/r2/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use js_sys::{JsString, Reflect, Uint8Array};
use wasm_bindgen::{JsCast, JsValue};
use wasm_bindgen_futures::JsFuture;
use worker_sys::{
FixedLengthStream as EdgeFixedLengthStream, R2Bucket as EdgeR2Bucket,
FixedLengthStream as EdgeFixedLengthStream, R2Bucket as EdgeR2Bucket, R2Checksums,
R2MultipartUpload as EdgeR2MultipartUpload, R2Object as EdgeR2Object,
R2ObjectBody as EdgeR2ObjectBody, R2Objects as EdgeR2Objects,
R2UploadedPart as EdgeR2UploadedPart,
Expand All @@ -19,6 +19,7 @@ use crate::{
mod builder;

/// An instance of the R2 bucket binding.
#[derive(Clone)]
pub struct Bucket {
inner: EdgeR2Bucket,
}
Expand Down Expand Up @@ -62,7 +63,8 @@ impl Bucket {
value: value.into(),
http_metadata: None,
custom_metadata: None,
md5: None,
checksum: None,
checksum_algorithm: "md5".into(),
}
}

Expand Down Expand Up @@ -215,6 +217,14 @@ impl Object {
.into()
}

pub fn checksum(&self) -> R2Checksums {
match &self.inner {
ObjectInner::NoBody(inner) => inner.checksums(),
ObjectInner::Body(inner) => inner.checksums(),
}
.into()
}

pub fn custom_metadata(&self) -> Result<HashMap<String, String>> {
let metadata = match &self.inner {
ObjectInner::NoBody(inner) => inner.custom_metadata(),
Expand Down

0 comments on commit 3f1f783

Please sign in to comment.