Skip to main content

varnish_sys/vcl/backend/
backend_ref.rs

1use std::ffi::CStr;
2use std::ptr::null;
3use std::time::SystemTime;
4
5use crate::ffi;
6use crate::ffi::{VCL_BACKEND, VCL_TIME};
7
8/// Result from a probe health check
9///
10/// Contains both the current health status and when it last changed.
11#[derive(Debug, Clone, Copy)]
12pub struct ProbeResult {
13    pub healthy: bool,
14    pub last_changed: SystemTime,
15}
16
17/// `BackendRef` can be created from a [`VCL_BACKEND`] and will handle proper
18/// refcounting. When dropped, the refcount will be decreased. This is the
19/// type that director vmods should use when storing backend references,
20/// instead of dealing with [`VCL_BACKEND`] directly.
21#[derive(Debug)]
22pub struct BackendRef {
23    refcounted: bool,
24    bep: VCL_BACKEND,
25}
26
27impl BackendRef {
28    /// Create a `BackendRef` from a `VCL_BACKEND`.
29    pub unsafe fn new(bep: VCL_BACKEND) -> Option<Self> {
30        if bep.0.is_null() {
31            return None;
32        }
33        unsafe {
34            let dir = bep.0.as_ref()?;
35            assert_eq!(dir.magic, ffi::DIRECTOR_MAGIC);
36            let vdir = dir.vdir.as_mut().expect("vdir can't be null");
37            assert_eq!(vdir.magic, ffi::VCLDIR_MAGIC);
38            if vdir.flags & ffi::VDIR_FLG_NOREFCNT == 0 {
39                ffi::Lck__Lock(
40                    &raw mut vdir.dlck,
41                    c"BackendRef::new".as_ptr(),
42                    line!() as i32,
43                );
44                assert!(vdir.refcnt > 0);
45                vdir.refcnt += 1;
46                ffi::Lck__Unlock(
47                    &raw mut vdir.dlck,
48                    c"BackendRef::new".as_ptr(),
49                    line!() as i32,
50                );
51            }
52        }
53        Some(BackendRef {
54            bep,
55            refcounted: true,
56        })
57    }
58
59    // this doesn't grab a reference and is only used to
60    // create the backend_ref fields of Backend, NativeBackend and Director
61    pub(super) unsafe fn new_without_refcount(bep: VCL_BACKEND) -> Option<Self> {
62        if bep.0.is_null() {
63            return None;
64        }
65        Some(BackendRef {
66            bep,
67            refcounted: false,
68        })
69    }
70
71    /// Test the underlying backend for its health.
72    pub fn probe(&self, ctx: &crate::vcl::Ctx) -> ProbeResult {
73        let mut changed = VCL_TIME::default();
74        let healthy = unsafe { ffi::VRT_Healthy(ctx.raw, self.bep, &raw mut changed).into() };
75        let last_changed = <VCL_TIME as Into<SystemTime>>::into(changed);
76        ProbeResult {
77            healthy,
78            last_changed,
79        }
80    }
81
82    /// Return the VCL name of the underlying backend.
83    pub fn name(&self) -> &CStr {
84        assert!(!self.bep.0.is_null());
85        unsafe {
86            let dir = *self.bep.0;
87            assert_eq!(dir.magic, ffi::DIRECTOR_MAGIC);
88            CStr::from_ptr(dir.vcl_name)
89        }
90    }
91
92    /// Return the `C` pointer to the underlying backend.
93    pub unsafe fn vcl_ptr(&self) -> VCL_BACKEND {
94        self.bep
95    }
96}
97
98impl Clone for BackendRef {
99    fn clone(&self) -> BackendRef {
100        // self.vcl_ptr() will never be null
101        unsafe { BackendRef::new(self.vcl_ptr()).expect("BackendRef vcl_ptr must not be null") }
102    }
103}
104
105impl Drop for BackendRef {
106    fn drop(&mut self) {
107        assert!(!self.bep.0.is_null());
108        if self.refcounted {
109            unsafe {
110                ffi::VRT_Assign_Backend(&raw mut self.bep, VCL_BACKEND(null()));
111            }
112        }
113    }
114}