Skip to main content

varnish_sys/vcl/
probe.rs

1//! Health check probe definitions for Varnish backends
2//!
3//! A [`Probe`] describes how Varnish should actively check whether a backend is healthy.
4//! It is passed to Varnish when constructing a backend, and Varnish will periodically send
5//! the probe request and evaluate the response against the configured thresholds.
6//!
7//! Use [`Request::Url`] for a simple HTTP GET probe, or [`Request::Text`] to send a raw
8//! request string (useful for non-HTTP protocols or custom verbs).
9//!
10//! [`CowProbe`] is a borrowed variant that avoids allocation when converting from VCL types.
11
12use std::borrow::Cow;
13use std::ffi::{c_char, c_uint, CStr};
14use std::time::Duration;
15
16use serde::{Deserialize, Serialize};
17
18use crate::ffi::{vrt_backend_probe, VCL_DURATION, VCL_PROBE, VRT_BACKEND_PROBE_MAGIC};
19use crate::vcl::{IntoVCL, VclError, Workspace};
20
21/// The request sent by Varnish to check backend health.
22#[derive(Debug, Clone, Deserialize, Serialize)]
23pub enum Request<T> {
24    /// Send an HTTP `GET` request to this URL path (e.g. `"/health"`).
25    Url(T),
26    /// Send a raw request string verbatim (e.g. a full HTTP request or a custom protocol message).
27    Text(T),
28}
29
30/// A backend health check probe definition.
31///
32/// Describes how frequently Varnish polls a backend and what constitutes a healthy response.
33/// Pass this to the backend constructor; Varnish owns the actual probing loop.
34#[derive(Debug, Clone, Deserialize, Serialize)]
35pub struct Probe<T = String> {
36    /// The request to send on each probe attempt.
37    pub request: Request<T>,
38    /// How long to wait for a response before considering the probe failed.
39    pub timeout: Duration,
40    /// How often to send probe requests.
41    pub interval: Duration,
42    /// The HTTP status code considered healthy (e.g. `200`). `0` means any 2xx status.
43    pub exp_status: c_uint,
44    /// Number of most recent probes to consider when evaluating health (sliding window size).
45    pub window: c_uint,
46    /// Minimum number of successful probes within [`window`](Self::window) to consider the backend healthy.
47    pub threshold: c_uint,
48    /// Number of probes that are assumed successful when the backend starts up.
49    pub initial: c_uint,
50}
51
52/// A [`Probe`] that borrows its request string to avoid allocation when converting from VCL.
53pub type CowProbe<'a> = Probe<Cow<'a, str>>;
54
55impl CowProbe<'_> {
56    /// Convert this borrowed probe into an owned [`Probe<String>`].
57    pub fn to_owned(&self) -> Probe {
58        Probe {
59            request: match &self.request {
60                Request::Url(cow) => Request::Url(cow.to_string()),
61                Request::Text(cow) => Request::Text(cow.to_string()),
62            },
63            timeout: self.timeout,
64            interval: self.interval,
65            exp_status: self.exp_status,
66            window: self.window,
67            threshold: self.threshold,
68            initial: self.initial,
69        }
70    }
71}
72
73/// Helper to convert a probe into a VCL object
74pub(crate) fn into_vcl_probe<T: AsRef<str>>(
75    src: Probe<T>,
76    ws: &mut Workspace,
77) -> Result<VCL_PROBE, VclError> {
78    let probe = ws.copy_value(vrt_backend_probe {
79        magic: VRT_BACKEND_PROBE_MAGIC,
80        timeout: src.timeout.into(),
81        interval: src.interval.into(),
82        exp_status: src.exp_status,
83        window: src.window,
84        initial: src.initial,
85        ..Default::default()
86    })?;
87
88    match src.request {
89        Request::Url(s) => {
90            probe.url = s.as_ref().into_vcl(ws)?.0;
91        }
92        Request::Text(s) => {
93            probe.request = s.as_ref().into_vcl(ws)?.0;
94        }
95    }
96
97    Ok(VCL_PROBE(probe))
98}
99
100/// Helper to convert a VCL probe into a Rust probe wrapper
101pub(crate) fn from_vcl_probe<'a, T: From<Cow<'a, str>>>(value: VCL_PROBE) -> Option<Probe<T>> {
102    let pr = unsafe { value.0.as_ref()? };
103    assert!(
104        (pr.url.is_null() && !pr.request.is_null()) || pr.request.is_null() && !pr.url.is_null()
105    );
106    Some(Probe {
107        request: if pr.url.is_null() {
108            Request::Text(from_str(pr.request).into())
109        } else {
110            Request::Url(from_str(pr.url).into())
111        },
112        timeout: VCL_DURATION(pr.timeout).into(),
113        interval: VCL_DURATION(pr.interval).into(),
114        exp_status: pr.exp_status,
115        window: pr.window,
116        threshold: pr.threshold,
117        initial: pr.initial,
118    })
119}
120
121/// Helper function to convert a C string into a Rust string
122fn from_str<'a>(value: *const c_char) -> Cow<'a, str> {
123    if value.is_null() {
124        Cow::Borrowed("")
125    } else {
126        // FIXME: this should NOT be lossy IMO
127        unsafe { CStr::from_ptr(value).to_string_lossy() }
128    }
129}