varnish_sys/extensions.rs
1use std::ffi::c_void;
2use std::ptr;
3
4use crate::ffi::{vmod_data, vmod_priv, vmod_priv_methods, vrt_ctx};
5use crate::validate_vrt_ctx;
6use crate::vcl::PerVclState;
7
8/// SAFETY: ensured by Varnish itself
9unsafe impl Sync for vmod_data {}
10
11/// Take ownership of the object of type `T` and return it as a `Box<T>`.
12/// The original pointer is set to null.
13///
14/// SAFETY: `priv_` must reference a valid `T` object pointer or `NULL`
15unsafe fn get_owned_bbox<T>(priv_: &mut *mut c_void) -> Option<Box<T>> {
16 let obj = ptr::replace(priv_, ptr::null_mut());
17 if obj.is_null() {
18 None
19 } else {
20 Some(Box::from_raw(obj.cast::<T>()))
21 }
22}
23
24impl vmod_priv {
25 /// Transfer ownership of the object to the caller, cleaning up the internal state.
26 ///
27 /// SAFETY: `priv_` must reference a valid `T` object pointer or `NULL`
28 pub unsafe fn take<T>(&mut self) -> Option<Box<T>> {
29 // methods does not need to be dropped because `put` always sets it to a static reference
30 self.methods = ptr::null();
31 get_owned_bbox(&mut self.priv_)
32 }
33
34 #[expect(clippy::unnecessary_box_returns)]
35 pub unsafe fn take_per_vcl<T>(&mut self) -> Box<PerVclState<T>> {
36 if let Some(v) = self.take::<PerVclState<T>>() {
37 v
38 } else {
39 let o = PerVclState::<T>::default();
40 Box::new(o)
41 }
42 }
43
44 /// Use the object as a reference, without taking ownership.
45 ///
46 /// SAFETY:
47 /// * `priv_` must reference a valid `T` object pointer or `NULL`
48 /// * `take()` must not be called on the same `vmod_priv` object until the returned reference is dropped
49 /// * cleanup must not be done on the object until the returned reference is dropped
50 /// * assumes `Box<T>` is equivalent to `&T` when used as a readonly reference, i.e. a box is just a pointer
51 pub unsafe fn get_ref<T>(&self) -> Option<&T> {
52 self.priv_.cast::<T>().as_ref()
53 }
54}
55
56/// SAFETY: ensured by Varnish itself
57unsafe impl Sync for vmod_priv_methods {}
58
59impl vmod_priv {
60 /// Set the object and methods for the `vmod_priv`, and the corresponding static methods.
61 ///
62 /// SAFETY: The type of `obj` must match the type of the function pointers in `methods`.
63 pub unsafe fn put<T>(&mut self, obj: Box<T>, methods: &'static vmod_priv_methods) {
64 self.priv_ = Box::into_raw(obj).cast();
65 self.methods = methods;
66 }
67
68 /// A Varnish callback function to free a `vmod_priv` object.
69 /// Here we take the ownership and immediately drop the object of type `T`.
70 /// Note that here we get `*priv_` directly, not the `*vmod_priv`
71 ///
72 /// SAFETY: `priv_` must be a valid pointer to a `T` object or `NULL`.
73 pub unsafe extern "C" fn on_fini<T>(_ctx: *const vrt_ctx, mut priv_: *mut c_void) {
74 drop(get_owned_bbox::<T>(&mut priv_));
75 }
76
77 /// A Varnish callback function to clean up the `PerVclState` object.
78 /// Similar to `on_fini`, but also unregisters filters.
79 ///
80 /// SAFETY: `priv_` must be a valid pointer to a `T` object or `NULL`.
81 pub unsafe extern "C" fn on_fini_per_vcl<T>(ctx: *const vrt_ctx, mut priv_: *mut c_void) {
82 if let Some(obj) = get_owned_bbox::<PerVclState<T>>(&mut priv_) {
83 let PerVclState {
84 mut fetch_filters,
85 mut delivery_filters,
86 user_data,
87 } = *obj;
88 let ctx = validate_vrt_ctx(ctx);
89 ctx.fetch_filters(&mut fetch_filters).unregister_all();
90 ctx.delivery_filters(&mut delivery_filters).unregister_all();
91 drop(user_data);
92 }
93 }
94}