Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ log-fastly = "0.11.9"
thiserror = "2.0.12"
esi = "0.6.1"
quick-xml = "0.38.3"
bytes = "1.11.1"

[build-dependencies]
cxx-build = "1.0"
Expand Down
10 changes: 9 additions & 1 deletion include/fastly/detail/rust_bridge_tags.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ struct ProcessFragmentResponseFnTag {
ProcessFragmentResponseFnTag() = default;
};
} // namespace esi

namespace cache::simple {
struct GetOrSetWithFnTag {
protected:
GetOrSetWithFnTag() = default;
};

} // namespace cache::simple
} // namespace fastly::detail::rust_bridge_tags

#endif
#endif
140 changes: 140 additions & 0 deletions src/cache.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
use std::pin::{Pin, pin};
use std::ptr;
use std::{fmt::Write as _, time::Duration};

use cxx::CxxString;

use crate::ffi::{GetOrSetWithFnResult, GetOrSetWithFnTag};
use crate::{ffi::SimpleCacheErrorCode, http::body::Body};

pub struct SimpleCacheError(fastly::cache::simple::CacheError);

impl SimpleCacheError {
pub fn error_msg(&self, mut out: Pin<&mut CxxString>) {
write!(out, "{}", self.0).expect("This should never fail.");
}
pub fn error_code(&self) -> SimpleCacheErrorCode {
match self.0 {
fastly::cache::simple::CacheError::LimitExceeded => SimpleCacheErrorCode::LimitExceeded,
fastly::cache::simple::CacheError::InvalidOperation => {
SimpleCacheErrorCode::InvalidOperation
}
fastly::cache::simple::CacheError::Unsupported => SimpleCacheErrorCode::Unsupported,
fastly::cache::simple::CacheError::Io(..) => SimpleCacheErrorCode::Io,
fastly::cache::simple::CacheError::Purge(..) => SimpleCacheErrorCode::Purge,
fastly::cache::simple::CacheError::GetOrSet(..) => SimpleCacheErrorCode::GetOrSet,
fastly::cache::simple::CacheError::Other(..) => SimpleCacheErrorCode::Other,
_ => unimplemented!("Unknown cache error"),
}
}
}

#[macro_export]
macro_rules! try_sce {
( $err:ident, $x:expr ) => {
match $x {
std::result::Result::Ok(val) => {
$err.set(std::ptr::null_mut());
val
}
std::result::Result::Err(e) => {
$err.set(Box::into_raw(Box::new(SimpleCacheError(e))));
return Default::default();
}
}
};
}

pub fn f_cache_simple_get(
key: &[u8],
mut out: Pin<&mut *mut Body>,
mut err: Pin<&mut *mut SimpleCacheError>,
) -> bool {
try_sce!(err, fastly::cache::simple::get(key.to_owned()))
.map(|body| out.set(Box::into_raw(Box::new(Body(body)))))
.is_some()
}

pub fn f_cache_simple_get_or_set(
key: &[u8],
value: Box<Body>,
ttl: u32,
mut out: Pin<&mut *mut Body>,
mut err: Pin<&mut *mut SimpleCacheError>,
) -> bool {
out.set(Box::into_raw(Box::new(Body(try_sce!(
err,
fastly::cache::simple::get_or_set(
key.to_owned(),
value.0,
Duration::from_millis(ttl as u64)
)
)))));
true
}

pub struct SimpleCacheEntry(fastly::cache::simple::CacheEntry);

type GetOrSetWithFnType = dyn Fn() -> Result<fastly::cache::simple::CacheEntry, fastly::Error>;
fn shim_get_or_set_with_fn(func: *const GetOrSetWithFnTag) -> Box<GetOrSetWithFnType> {
Box::new(move || {
let mut out = pin!(ptr::null_mut());
let result = unsafe {
crate::manual_ffi::fastly_esi_manualbridge_GetOrSetWithFn_call(func, &mut out)
};
match result {
GetOrSetWithFnResult::Ok => Ok(unsafe { out.read().0 }),
GetOrSetWithFnResult::Err => Err(fastly::Error::msg("error during get_or_set_with")),
_ => unreachable!(),
}
})
}

pub fn f_cache_simple_get_or_set_with(
key: &[u8],
make_entry: *const GetOrSetWithFnTag,
mut out: Pin<&mut *mut Body>,
mut err: Pin<&mut *mut SimpleCacheError>,
) -> bool {
try_sce!(
err,
fastly::cache::simple::get_or_set_with(key.to_owned(), shim_get_or_set_with_fn(make_entry))
)
.map(|bod| out.set(Box::into_raw(Box::new(Body(bod)))))
.is_some()
}

pub fn f_cache_simple_purge(key: &[u8], mut err: Pin<&mut *mut SimpleCacheError>) -> bool {
try_sce!(err, fastly::cache::simple::purge(key.to_owned()));
true
}

pub struct PurgeOptions(fastly::cache::simple::PurgeOptions);

pub fn m_static_cache_simple_purge_options_pop_scope() -> Box<PurgeOptions> {
Box::new(PurgeOptions(
fastly::cache::simple::PurgeOptions::pop_scope(),
))
}

pub fn m_static_cache_simple_purge_options_global_scope() -> Box<PurgeOptions> {
Box::new(PurgeOptions(
fastly::cache::simple::PurgeOptions::global_scope(),
))
}

pub fn f_cache_simple_purge_with_opts(
key: &[u8],
opts: Box<PurgeOptions>,
mut err: Pin<&mut *mut SimpleCacheError>,
) -> bool {
try_sce!(
err,
fastly::cache::simple::purge_with_opts(key.to_owned(), opts.0)
);
true
}

pub fn f_cache_simple_error_force_symbols(x: Box<SimpleCacheError>) -> Box<SimpleCacheError> {
x
}
77 changes: 76 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#![allow(clippy::boxed_local, clippy::needless_lifetimes)]

use backend::*;
use cache::*;
use config_store::*;
use device_detection::*;
use error::*;
Expand All @@ -17,6 +18,7 @@ use secret_store::*;
use security::*;

mod backend;
mod cache;
mod config_store;
mod device_detection;
mod error;
Expand Down Expand Up @@ -1115,6 +1117,12 @@ mod ffi {
type ProcessFragmentResponseFnTag;
}

#[namespace = "fastly::detail::rust_bridge_tags::cache::simple"]
unsafe extern "C++" {
include!("fastly/detail/rust_bridge_tags.h");
type GetOrSetWithFnTag;
}

#[namespace = "fastly::sys::esi"]
extern "Rust" {
type Processor;
Expand Down Expand Up @@ -1146,13 +1154,74 @@ mod ffi {
is_escaped_content: bool,
) -> Box<Processor>;
}

#[namespace = "fastly::sys::cache::simple"]
#[repr(usize)]
pub enum GetOrSetWithFnResult {
Ok = 0,
Err = 1,
}

#[namespace = "fastly::sys::cache::simple"]
#[derive(Debug, Clone, Copy)]
#[repr(usize)]
pub enum SimpleCacheErrorCode {
LimitExceeded,
InvalidOperation,
Unsupported,
Io,
Purge,
GetOrSet,
Other,
}

#[namespace = "fastly::sys::cache::simple"]
extern "Rust" {
type SimpleCacheError;
fn error_msg(&self, mut out: Pin<&mut CxxString>);
fn error_code(&self) -> SimpleCacheErrorCode;
fn f_cache_simple_error_force_symbols(x: Box<SimpleCacheError>) -> Box<SimpleCacheError>;
}

#[namespace = "fastly::sys::cache::simple"]
extern "Rust" {
type SimpleCacheEntry;
type PurgeOptions;
fn f_cache_simple_get(
key: &[u8],
mut out: Pin<&mut *mut Body>,
mut err: Pin<&mut *mut SimpleCacheError>,
) -> bool;
fn f_cache_simple_get_or_set(
key: &[u8],
value: Box<Body>,
ttl: u32,
mut out: Pin<&mut *mut Body>,
mut err: Pin<&mut *mut SimpleCacheError>,
) -> bool;
unsafe fn f_cache_simple_get_or_set_with(
key: &[u8],
make_entry: *const GetOrSetWithFnTag,
mut out: Pin<&mut *mut Body>,
mut err: Pin<&mut *mut SimpleCacheError>,
) -> bool;
fn f_cache_simple_purge(key: &[u8], mut err: Pin<&mut *mut SimpleCacheError>) -> bool;
fn f_cache_simple_purge_with_opts(
key: &[u8],
opts: Box<PurgeOptions>,
mut err: Pin<&mut *mut SimpleCacheError>,
) -> bool;
fn m_static_cache_simple_purge_options_pop_scope() -> Box<PurgeOptions>;
fn m_static_cache_simple_purge_options_global_scope() -> Box<PurgeOptions>;
}
}

// Some types (notably callback functions) are not supported by CXX at all, so we
// define manual FFI bindings for them here.
mod manual_ffi {
use crate::ffi::{
DispatchFragmentRequestFnResult, DispatchFragmentRequestFnTag, ProcessFragmentResponseFnTag,
DispatchFragmentRequestFnResult, DispatchFragmentRequestFnTag, GetOrSetWithFnResult,
GetOrSetWithFnTag, ProcessFragmentResponseFnTag,
};

// We never rely on the layout of Rust types passed to these functions,
Expand All @@ -1175,5 +1244,11 @@ mod manual_ffi {
response: *mut crate::Response,
out_response: &mut *mut crate::Response,
) -> bool;

#[link_name = "fastly$cache$simple$manualbridge$GetOrSetWithFn$call"]
pub(crate) fn fastly_esi_manualbridge_GetOrSetWithFn_call(
func: *const GetOrSetWithFnTag,
out: &mut *mut crate::SimpleCacheEntry,
) -> GetOrSetWithFnResult;
}
}
Loading