From 6965d158b69419618aa5582053fdc8947c4fead0 Mon Sep 17 00:00:00 2001 From: Hans Larsen Date: Wed, 26 May 2021 08:49:39 -0700 Subject: [PATCH] Fix DID and function signatures for dfx e2e (#3) --- assets.did | 17 +++++++++----- src/lib.rs | 66 +++++++++++++++++++++++++++++++++--------------------- 2 files changed, 52 insertions(+), 31 deletions(-) diff --git a/assets.did b/assets.did index abd1fbb..d11ecd9 100644 --- a/assets.did +++ b/assets.did @@ -1,6 +1,7 @@ type BatchId = nat; type ChunkId = nat; type Key = text; +type Time = int; type CreateAssetArguments = record { key: Key; @@ -57,15 +58,20 @@ type HttpResponse = record { type StreamingCallbackHttpResponse = record { body: blob; - token: opt Token; + token: opt StreamingCallbackToken; }; -type Token = record {}; +type StreamingCallbackToken = record { + key: Key; + content_encoding: text; + index: nat; + sha256: opt blob; +}; type StreamingStrategy = variant { Callback: record { - callback: func (Token) -> (StreamingCallbackHttpResponse) query; - token: Token; + callback: func (StreamingCallbackToken) -> (opt StreamingCallbackHttpResponse) query; + token: StreamingCallbackToken; }; }; @@ -98,6 +104,7 @@ service: { content_encoding: text; sha256: opt blob; // sha256 of entire asset encoding, calculated by dfx and passed in SetAssetContentArguments length: nat; // Size of this encoding's blob. Calculated when uploading assets. + modified: Time; }; }) query; @@ -127,7 +134,7 @@ service: { }) -> (); http_request: (request: HttpRequest) -> (HttpResponse) query; - http_request_stream_callback: (token: opt Token) -> (StreamingCallbackHttpResponse) query; + http_request_streaming_callback: (token: StreamingCallbackToken) -> (opt StreamingCallbackHttpResponse) query; authorize: (principal) -> (); } diff --git a/src/lib.rs b/src/lib.rs index 15daf15..07a1bb7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,7 @@ mod rc_bytes; use crate::rc_bytes::RcBytes; use ic_cdk::api::{caller, data_certificate, set_certified_data, time, trap}; -use ic_cdk::export::candid::{CandidType, Deserialize, Func, Nat, Principal}; +use ic_cdk::export::candid::{CandidType, Deserialize, Func, Int, Nat, Principal}; use ic_cdk_macros::{init, post_upgrade, pre_upgrade, query, update}; use ic_certified_map::{AsHashTree, Hash, HashTree, RbTree}; use num_traits::ToPrimitive; @@ -81,10 +81,10 @@ struct AssetDetails { #[derive(Clone, Debug, CandidType, Deserialize)] struct AssetEncodingDetails { - modified: Timestamp, content_encoding: String, sha256: Option, length: Nat, + modified: Timestamp, } struct Chunk { @@ -96,7 +96,7 @@ struct Batch { expires_at: Timestamp, } -type Timestamp = u64; +type Timestamp = Int; type BatchId = Nat; type ChunkId = Nat; type Key = String; @@ -210,7 +210,7 @@ struct HttpResponse { } #[derive(Clone, Debug, CandidType, Deserialize)] -struct Token { +struct StreamingCallbackToken { key: String, content_encoding: String, index: Nat, @@ -220,13 +220,16 @@ struct Token { #[derive(Clone, Debug, CandidType, Deserialize)] enum StreamingStrategy { - Callback { callback: Func, token: Token }, + Callback { + callback: Func, + token: StreamingCallbackToken, + }, } #[derive(Clone, Debug, CandidType, Deserialize)] struct StreamingCallbackHttpResponse { body: RcBytes, - token: Option, + token: Option, } #[update] @@ -273,7 +276,7 @@ fn store(arg: StoreArg) { let encoding = asset.encodings.entry(arg.content_encoding).or_default(); encoding.total_length = arg.content.len(); encoding.content_chunks = vec![RcBytes::from(arg.content)]; - encoding.modified = time() as u64; + encoding.modified = Int::from(time() as u64); encoding.sha256 = hash; on_asset_change(&arg.key, asset); @@ -292,7 +295,7 @@ fn create_batch() -> CreateBatchResponse { batches.insert( batch_id.clone(), Batch { - expires_at: now + BATCH_EXPIRY_NANOS, + expires_at: Int::from(now + BATCH_EXPIRY_NANOS), }, ); s.chunks.borrow_mut().retain(|_, c| { @@ -315,7 +318,7 @@ fn create_chunk(arg: CreateChunkArg) -> CreateChunkResponse { let mut batch = batches .get_mut(&arg.batch_id) .unwrap_or_else(|| trap("batch not found")); - batch.expires_at = now + BATCH_EXPIRY_NANOS; + batch.expires_at = Int::from(now + BATCH_EXPIRY_NANOS); let chunk_id = s.next_chunk_id.borrow().clone(); *s.next_chunk_id.borrow_mut() += 1; @@ -437,10 +440,10 @@ fn list() -> Vec { .encodings .iter() .map(|(enc_name, enc)| AssetEncodingDetails { - modified: enc.modified, content_encoding: enc_name.clone(), sha256: Some(ByteBuf::from(enc.sha256)), length: Nat::from(enc.total_length), + modified: enc.modified.clone(), }) .collect(); encodings.sort_by(|l, r| l.content_encoding.cmp(&r.content_encoding)); @@ -461,11 +464,11 @@ fn create_token( enc: &AssetEncoding, key: &str, chunk_index: usize, -) -> Option { +) -> Option { if chunk_index + 1 >= enc.content_chunks.len() { None } else { - Some(Token { + Some(StreamingCallbackToken { key: key.to_string(), content_encoding: enc_name.to_string(), index: Nat::from(chunk_index + 1), @@ -543,16 +546,18 @@ fn build_http_response(path: &str, encodings: Vec, index: usize) -> Http if let Some(certificate_header) = index_redirect_certificate { if let Some(asset) = assets.get(INDEX_FILE) { - for (enc_name, enc) in asset.encodings.iter() { - if enc.certified { - return build_200( - asset, - enc_name, - enc, - INDEX_FILE, - index, - Some(certificate_header), - ); + for enc_name in encodings.iter() { + if let Some(enc) = asset.encodings.get(enc_name) { + if enc.certified { + return build_200( + asset, + enc_name, + enc, + INDEX_FILE, + index, + Some(certificate_header), + ); + } } } } @@ -675,12 +680,12 @@ fn http_request(req: HttpRequest) -> HttpResponse { #[query] fn http_request_streaming_callback( - Token { + StreamingCallbackToken { key, content_encoding, index, - .. - }: Token, + sha256, + }: StreamingCallbackToken, ) -> StreamingCallbackHttpResponse { STATE.with(|s| { let assets = s.assets.borrow(); @@ -691,6 +696,13 @@ fn http_request_streaming_callback( .encodings .get(&content_encoding) .expect("Invalid token on streaming: encoding not found."); + + if let Some(expected_hash) = sha256 { + if expected_hash != enc.sha256 { + trap("sha256 mismatch"); + } + } + // MAX is good enough. This means a chunk would be above 64-bits, which is impossible... let chunk_index = index.0.to_usize().unwrap_or(usize::MAX); @@ -730,7 +742,7 @@ fn do_set_asset_content(arg: SetAssetContentArguments) { let asset = assets .get_mut(&arg.key) .unwrap_or_else(|| trap("asset not found")); - let now = time() as u64; + let now = Int::from(time() as u64); let mut chunks = s.chunks.borrow_mut(); @@ -812,6 +824,8 @@ fn on_asset_change(key: &str, asset: &mut Asset) { if let Some(enc) = asset.encodings.get(*enc_name) { if enc.certified { return; + } else { + break; } } }