//! Experimental Compute@Edge features.
use crate::{
abi,
http::{
header::{HeaderName, HeaderValue},
request::{handle::RequestHandle, CacheKeyGen, Request},
},
Error,
};
use sha2::{Digest, Sha256};
use std::sync::Arc;
/// Parse a user agent string.
///
/// # Stability
///
/// This is part of an experimental API that is subject to change or removal even in minor versions
/// of this crate.
pub fn uap_parse(
user_agent: &str,
) -> Result<(String, Option<String>, Option<String>, Option<String>), Error> {
let user_agent: &[u8] = user_agent.as_ref();
let max_length = 255;
let mut family = Vec::with_capacity(max_length);
let mut major = Vec::with_capacity(max_length);
let mut minor = Vec::with_capacity(max_length);
let mut patch = Vec::with_capacity(max_length);
let mut family_nwritten = 0;
let mut major_nwritten = 0;
let mut minor_nwritten = 0;
let mut patch_nwritten = 0;
let status = unsafe {
abi::fastly_uap::parse(
user_agent.as_ptr(),
user_agent.len(),
family.as_mut_ptr(),
family.capacity(),
&mut family_nwritten,
major.as_mut_ptr(),
major.capacity(),
&mut major_nwritten,
minor.as_mut_ptr(),
minor.capacity(),
&mut minor_nwritten,
patch.as_mut_ptr(),
patch.capacity(),
&mut patch_nwritten,
)
};
if status.is_err() {
return Err(Error::msg("fastly_uap::parse failed"));
}
assert!(
family_nwritten <= family.capacity(),
"fastly_uap::parse wrote too many bytes for family"
);
unsafe {
family.set_len(family_nwritten);
}
assert!(
major_nwritten <= major.capacity(),
"fastly_uap::parse wrote too many bytes for major"
);
unsafe {
major.set_len(major_nwritten);
}
assert!(
minor_nwritten <= minor.capacity(),
"fastly_uap::parse wrote too many bytes for minor"
);
unsafe {
minor.set_len(minor_nwritten);
}
assert!(
patch_nwritten <= patch.capacity(),
"fastly_uap::parse wrote too many bytes for patch"
);
unsafe {
patch.set_len(patch_nwritten);
}
Ok((
String::from_utf8_lossy(&family).to_string(),
Some(String::from_utf8_lossy(&major).to_string()),
Some(String::from_utf8_lossy(&minor).to_string()),
Some(String::from_utf8_lossy(&patch).to_string()),
))
}
/// An extension trait for [`Request`]s that adds methods for controlling cache keys.
///
/// # Stability
///
/// This is part of an experimental API that is subject to change or removal even in minor versions
/// of this crate.
pub trait RequestCacheKey {
/// See [`Request::set_cache_key()`].
///
/// # Stability
///
/// This is part of an experimental API that is subject to change or removal even in minor
/// versions of this crate.
fn set_cache_key(&mut self, key: [u8; 32]);
/// See [`Request::with_cache_key()`].
///
/// # Stability
///
/// This is part of an experimental API that is subject to change or removal even in minor
/// versions of this crate.
fn with_cache_key(self, key: [u8; 32]) -> Self;
/// See [`Request::set_cache_key_fn()`].
///
/// # Stability
///
/// This is part of an experimental API that is subject to change or removal even in minor
/// versions of this crate.
fn set_cache_key_fn(&mut self, f: impl Fn(&Request) -> [u8; 32] + Send + Sync + 'static);
/// See [`Request::with_cache_key_fn()`].
///
/// # Stability
///
/// This is part of an experimental API that is subject to change or removal even in minor
/// versions of this crate.
fn with_cache_key_fn(self, f: impl Fn(&Request) -> [u8; 32] + Send + Sync + 'static) -> Self;
/// See [`Request::set_cache_key_str()`].
///
/// # Stability
///
/// This is part of an experimental API that is subject to change or removal even in minor
/// versions of this crate.
fn set_cache_key_str(&mut self, key_str: impl AsRef<[u8]>);
/// See [`Request::with_cache_key_str()`].
///
/// # Stability
///
/// This is part of an experimental API that is subject to change or removal even in minor
/// versions of this crate.
fn with_cache_key_str(self, key_str: impl AsRef<[u8]>) -> Self;
}
impl RequestCacheKey for Request {
/// Set the cache key to be used when attempting to satisfy this request from a cached response.
///
/// # Stability
///
/// This is part of an experimental API that is subject to change or removal even in minor
/// versions of this crate.
fn set_cache_key(&mut self, key: [u8; 32]) {
self.cache_key = Some(CacheKeyGen::Set(key));
}
/// Builder-style equivalent of [`set_cache_key()`](Self::set_cache_key()).
///
/// # Stability
///
/// This is part of an experimental API that is subject to change or removal even in minor
/// versions of this crate.
fn with_cache_key(mut self, key: [u8; 32]) -> Self {
self.set_cache_key(key);
self
}
/// Set the function that will be used to compute the cache key for this request.
///
/// # Stability
///
/// This is part of an experimental API that is subject to change or removal even in minor
/// versions of this crate.
fn set_cache_key_fn(&mut self, f: impl Fn(&Request) -> [u8; 32] + Send + Sync + 'static) {
self.cache_key = Some(CacheKeyGen::Lazy(Arc::new(f)));
}
/// Builder-style equivalent of [`set_cache_key_fn()`](Self::set_cache_key_fn()).
///
/// # Stability
///
/// This is part of an experimental API that is subject to change or removal even in minor
/// versions of this crate.
fn with_cache_key_fn(
mut self,
f: impl Fn(&Request) -> [u8; 32] + Send + Sync + 'static,
) -> Self {
self.set_cache_key_fn(f);
self
}
/// Set a string as the cache key to be used when attempting to satisfy this request from a
/// cached response.
///
/// The string representation of the key is hashed to the same `[u8; 32]` representation used by
/// [`set_cache_key()`][`Self::set_cache_key()`].
///
/// # Stability
///
/// This is part of an experimental API that is subject to change or removal even in minor
/// versions of this crate.
fn set_cache_key_str(&mut self, key_str: impl AsRef<[u8]>) {
let mut sha = Sha256::new();
sha.update(key_str);
sha.update(b"\x00\xf0\x9f\xa7\x82\x00"); // extra salt
self.set_cache_key(*sha.finalize().as_ref())
}
/// Builder-style equivalent of [`set_cache_key_str()`](Self::set_cache_key_str()).
///
/// # Stability
///
/// This is part of an experimental API that is subject to change or removal even in minor
/// versions of this crate.
fn with_cache_key_str(mut self, key_str: impl AsRef<[u8]>) -> Self {
self.set_cache_key_str(key_str);
self
}
}
/// An extension trait for [`RequestHandle`](RequestHandle)s that adds methods for controlling cache
/// keys.
///
/// # Stability
///
/// This is part of an experimental API that is subject to change or removal even in minor versions
/// of this crate.
pub trait RequestHandleCacheKey {
/// See [`RequestHandle::set_cache_key()`](RequestHandle::set_cache_key).
///
/// # Stability
///
/// This is part of an experimental API that is subject to change or removal even in minor
/// versions of this crate.
fn set_cache_key(&mut self, key: &[u8; 32]);
}
impl RequestHandleCacheKey for RequestHandle {
/// Set the cache key to be used when attempting to satisfy this request from a cached response.
///
/// # Stability
///
/// This is part of an experimental API that is subject to change or removal even in minor
/// versions of this crate.
fn set_cache_key(&mut self, key: &[u8; 32]) {
const DIGITS: &[u8; 16] = b"0123456789ABCDEF";
let mut hex = [0; 64];
for (i, b) in key.iter().enumerate() {
hex[i * 2] = DIGITS[(b >> 4) as usize];
hex[i * 2 + 1] = DIGITS[(b & 0xf) as usize];
}
self.insert_header(
&HeaderName::from_static("fastly-xqd-cache-key"),
&HeaderValue::from_bytes(&hex).unwrap(),
)
}
}