diff options
author | Satya Rohith <me@satyarohith.com> | 2022-10-03 10:52:54 +0530 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-10-03 10:52:54 +0530 |
commit | eacd6a7f295a9a8ce4f4ca38cbf3e9905c4a5d02 (patch) | |
tree | c42049ce8a6360c5d72cd1f13795d9f41f8b4dd5 /ext/cache/lib.rs | |
parent | e2990be264776d4d17e0fa982f74e1ad54624d0d (diff) |
chore(ext/cache): make helper functions public (#16117)
Diffstat (limited to 'ext/cache/lib.rs')
-rw-r--r-- | ext/cache/lib.rs | 199 |
1 files changed, 174 insertions, 25 deletions
diff --git a/ext/cache/lib.rs b/ext/cache/lib.rs index 350efbc38..e6fdaa764 100644 --- a/ext/cache/lib.rs +++ b/ext/cache/lib.rs @@ -20,6 +20,38 @@ use std::path::PathBuf; use std::rc::Rc; use std::sync::Arc; +#[derive(Clone)] +pub struct CreateCache<C: Cache + 'static>(pub Arc<dyn Fn() -> C>); + +pub fn init<CA: Cache + 'static>( + maybe_create_cache: Option<CreateCache<CA>>, +) -> Extension { + Extension::builder() + .js(include_js_files!( + prefix "deno:ext/cache", + "01_cache.js", + )) + .ops(vec![ + op_cache_storage_open::decl::<CA>(), + op_cache_storage_has::decl::<CA>(), + op_cache_storage_delete::decl::<CA>(), + op_cache_put::decl::<CA>(), + op_cache_match::decl::<CA>(), + op_cache_delete::decl::<CA>(), + ]) + .state(move |state| { + if let Some(create_cache) = maybe_create_cache.clone() { + state.put(create_cache); + } + Ok(()) + }) + .build() +} + +pub fn get_declaration() -> PathBuf { + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("lib.deno_cache.d.ts") +} + #[derive(Deserialize, Serialize, Debug, Clone)] #[serde(rename_all = "camelCase")] pub struct CachePutRequest { @@ -181,34 +213,151 @@ where } } -#[derive(Clone)] -pub struct CreateCache<C: Cache + 'static>(pub Arc<dyn Fn() -> C>); +/// Check if headers, mentioned in the vary header, of query request +/// and cached request are equal. +pub fn vary_header_matches( + vary_header: &ByteString, + query_request_headers: &[(ByteString, ByteString)], + cached_request_headers: &[(ByteString, ByteString)], +) -> bool { + let vary_header = match std::str::from_utf8(vary_header) { + Ok(vary_header) => vary_header, + Err(_) => return false, + }; + let headers = get_headers_from_vary_header(vary_header); + for header in headers { + let query_header = get_header(&header, query_request_headers); + let cached_header = get_header(&header, cached_request_headers); + if query_header != cached_header { + return false; + } + } + true +} -pub fn init<CA: Cache + 'static>( - maybe_create_cache: Option<CreateCache<CA>>, -) -> Extension { - Extension::builder() - .js(include_js_files!( - prefix "deno:ext/cache", - "01_cache.js", - )) - .ops(vec![ - op_cache_storage_open::decl::<CA>(), - op_cache_storage_has::decl::<CA>(), - op_cache_storage_delete::decl::<CA>(), - op_cache_put::decl::<CA>(), - op_cache_match::decl::<CA>(), - op_cache_delete::decl::<CA>(), - ]) - .state(move |state| { - if let Some(create_cache) = maybe_create_cache.clone() { - state.put(create_cache); +#[test] +fn test_vary_header_matches() { + let vary_header = ByteString::from("accept-encoding"); + let query_request_headers = vec![( + ByteString::from("accept-encoding"), + ByteString::from("gzip"), + )]; + let cached_request_headers = vec![( + ByteString::from("accept-encoding"), + ByteString::from("gzip"), + )]; + assert!(vary_header_matches( + &vary_header, + &query_request_headers, + &cached_request_headers + )); + let vary_header = ByteString::from("accept-encoding"); + let query_request_headers = vec![( + ByteString::from("accept-encoding"), + ByteString::from("gzip"), + )]; + let cached_request_headers = + vec![(ByteString::from("accept-encoding"), ByteString::from("br"))]; + assert!(!vary_header_matches( + &vary_header, + &query_request_headers, + &cached_request_headers + )); +} + +/// Get headers from the vary header. +pub fn get_headers_from_vary_header(vary_header: &str) -> Vec<String> { + vary_header + .split(',') + .map(|s| s.trim().to_lowercase()) + .collect() +} + +#[test] +fn test_get_headers_from_vary_header() { + let headers = get_headers_from_vary_header("accept-encoding"); + assert_eq!(headers, vec!["accept-encoding"]); + let headers = get_headers_from_vary_header("accept-encoding, user-agent"); + assert_eq!(headers, vec!["accept-encoding", "user-agent"]); +} + +/// Get value for the header with the given name. +pub fn get_header( + name: &str, + headers: &[(ByteString, ByteString)], +) -> Option<ByteString> { + headers + .iter() + .find(|(k, _)| { + if let Ok(k) = std::str::from_utf8(k) { + k.eq_ignore_ascii_case(name) + } else { + false } - Ok(()) }) - .build() + .map(|(_, v)| v.to_owned()) } -pub fn get_declaration() -> PathBuf { - PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("lib.deno_cache.d.ts") +#[test] +fn test_get_header() { + let headers = vec![ + ( + ByteString::from("accept-encoding"), + ByteString::from("gzip"), + ), + ( + ByteString::from("content-type"), + ByteString::from("application/json"), + ), + ( + ByteString::from("vary"), + ByteString::from("accept-encoding"), + ), + ]; + let value = get_header("accept-encoding", &headers); + assert_eq!(value, Some(ByteString::from("gzip"))); + let value = get_header("content-type", &headers); + assert_eq!(value, Some(ByteString::from("application/json"))); + let value = get_header("vary", &headers); + assert_eq!(value, Some(ByteString::from("accept-encoding"))); +} + +/// Serialize headers into bytes. +pub fn serialize_headers(headers: &[(ByteString, ByteString)]) -> Vec<u8> { + let mut serialized_headers = Vec::new(); + for (name, value) in headers { + serialized_headers.extend_from_slice(name); + serialized_headers.extend_from_slice(b"\r\n"); + serialized_headers.extend_from_slice(value); + serialized_headers.extend_from_slice(b"\r\n"); + } + serialized_headers +} + +/// Deserialize bytes into headers. +pub fn deserialize_headers( + serialized_headers: &[u8], +) -> Vec<(ByteString, ByteString)> { + let mut headers = Vec::new(); + let mut piece = None; + let mut start = 0; + for (i, byte) in serialized_headers.iter().enumerate() { + if byte == &b'\r' && serialized_headers.get(i + 1) == Some(&b'\n') { + if piece.is_none() { + piece = Some(start..i); + } else { + let name = piece.unwrap(); + let value = start..i; + headers.push(( + ByteString::from(&serialized_headers[name]), + ByteString::from(&serialized_headers[value]), + )); + piece = None; + } + start = i + 2; + } + } + assert!(piece.is_none()); + assert_eq!(start, serialized_headers.len()); + headers } |