varnish_sys/vcl/backend/
director.rs1use std::ffi::{c_char, c_void, CString};
2use std::ptr;
3use std::ptr::null;
4
5use crate::ffi::{VclEvent, VCL_BACKEND, VCL_BOOL, VCL_TIME};
6use crate::vcl::{Buffer, Ctx, VclResult};
7use crate::{ffi, validate_director};
8
9use super::{BackendRef, ProbeResult};
10
11pub trait VclDirector {
20 fn resolve(&self, ctx: &mut Ctx) -> Option<BackendRef>;
28
29 fn probe(&self, ctx: &mut Ctx) -> ProbeResult;
35
36 fn report(&self, _ctx: &mut Ctx, _vsb: &mut Buffer) {}
40
41 fn report_details(&self, _ctx: &mut Ctx, _vsb: &mut Buffer) {}
45
46 fn report_json(&self, _ctx: &mut Ctx, vsb: &mut Buffer) {
50 let _ = vsb.write(&"{}");
51 }
52
53 fn report_details_json(&self, _ctx: &mut Ctx, vsb: &mut Buffer) {
57 let _ = vsb.write(&"{}");
58 }
59
60 fn event(&self, event: VclEvent) {
64 let _ = event;
65 }
66}
67
68#[derive(Debug)]
77pub struct Director<D: VclDirector> {
78 #[expect(dead_code)]
79 methods: Box<ffi::vdi_methods>,
80 inner: Box<D>,
81 #[expect(dead_code)]
82 ctype: CString,
83 backend_ref: BackendRef,
84}
85
86impl<D: VclDirector> Director<D> {
87 pub fn new(ctx: &mut Ctx, director_type: &str, vcl_name: &str, inner: D) -> VclResult<Self> {
92 let mut inner = Box::new(inner);
93 let ctype = CString::new(director_type).map_err(|e| e.to_string())?;
94 let cname = CString::new(vcl_name).map_err(|e| e.to_string())?;
95 let methods = Box::new(ffi::vdi_methods {
96 type_: ctype.as_ptr(),
97 magic: ffi::VDI_METHODS_MAGIC,
98 http1pipe: None,
99 healthy: Some(wrap_director_healthy::<D>),
100 resolve: Some(wrap_director_resolve::<D>),
101 gethdrs: None,
102 getip: None,
103 finish: None,
104 event: Some(wrap_director_event::<D>),
105 release: None,
106 destroy: None,
107 panic: None,
108 list: Some(wrap_director_list::<D>),
109 });
110
111 let bep = unsafe {
112 ffi::VRT_AddDirector(
113 ctx.raw,
114 &raw const *methods,
115 ptr::from_mut::<D>(&mut *inner).cast::<c_void>(),
116 c"%.*s".as_ptr(),
117 cname.as_bytes().len(),
118 cname.as_ptr().cast::<c_char>(),
119 )
120 };
121 if bep.0.is_null() {
122 return Err(format!("VRT_AddDirector returned null while creating {vcl_name}").into());
123 }
124
125 unsafe {
126 assert_eq!((*bep.0).magic, ffi::DIRECTOR_MAGIC);
127 }
128
129 let backend_ref = unsafe {
130 BackendRef::new_without_refcount(bep).expect("Backend pointer should never be null")
131 };
132
133 Ok(Director {
134 methods,
135 inner,
136 ctype,
137 backend_ref,
138 })
139 }
140
141 pub fn get_inner(&self) -> &D {
143 &self.inner
144 }
145
146 pub fn get_inner_mut(&mut self) -> &mut D {
148 &mut self.inner
149 }
150
151 pub fn resolve(&self, ctx: &Ctx) -> VCL_BACKEND {
156 unsafe { ffi::VRT_DirectorResolve(ctx.raw, self.backend_ref.vcl_ptr()) }
157 }
158
159 pub fn probe(&self, ctx: &Ctx) -> ProbeResult {
161 self.backend_ref.probe(ctx)
162 }
163}
164
165impl<D: VclDirector> Drop for Director<D> {
166 fn drop(&mut self) {
167 unsafe {
168 let mut bep = self.backend_ref.vcl_ptr();
169 ffi::VRT_DelDirector(&raw mut bep);
170 }
171 }
172}
173
174impl<D: VclDirector> AsRef<BackendRef> for Director<D> {
175 fn as_ref(&self) -> &BackendRef {
176 &self.backend_ref
177 }
178}
179
180unsafe extern "C" fn wrap_director_resolve<D: VclDirector>(
183 ctxp: *const ffi::vrt_ctx,
184 director: VCL_BACKEND,
185) -> VCL_BACKEND {
186 let mut ctx = Ctx::from_ptr(ctxp);
187 let dir = validate_director(director);
188 let dir_impl: &D = &*dir.priv_.cast::<D>();
189 dir_impl
190 .resolve(&mut ctx)
191 .map_or(VCL_BACKEND(null()), |backend_ref| backend_ref.vcl_ptr())
192}
193
194unsafe extern "C" fn wrap_director_healthy<D: VclDirector>(
195 ctxp: *const ffi::vrt_ctx,
196 director: VCL_BACKEND,
197 changed: *mut VCL_TIME,
198) -> VCL_BOOL {
199 let mut ctx = Ctx::from_ptr(ctxp);
200 let dir = validate_director(director);
201 let dir_impl: &D = &*dir.priv_.cast::<D>();
202 let result = dir_impl.probe(&mut ctx);
203 if !changed.is_null() {
204 if let Ok(t) = result.last_changed.try_into() {
207 *changed = t;
208 }
209 }
210 result.healthy.into()
211}
212
213unsafe extern "C" fn wrap_director_list<D: VclDirector>(
214 ctxp: *const ffi::vrt_ctx,
215 director: VCL_BACKEND,
216 vsbp: *mut ffi::vsb,
217 detailed: i32,
218 json: i32,
219) {
220 let mut ctx = Ctx::from_ptr(ctxp);
221 let mut vsb = Buffer::from_ptr(vsbp);
222 let dir = validate_director(director);
223 let dir_impl: &D = &*dir.priv_.cast::<D>();
224 match (json != 0, detailed != 0) {
225 (true, true) => dir_impl.report_details_json(&mut ctx, &mut vsb),
226 (true, false) => dir_impl.report_json(&mut ctx, &mut vsb),
227 (false, true) => dir_impl.report_details(&mut ctx, &mut vsb),
228 (false, false) => dir_impl.report(&mut ctx, &mut vsb),
229 }
230}
231
232unsafe extern "C" fn wrap_director_event<D: VclDirector>(director: VCL_BACKEND, ev: VclEvent) {
233 let dir = validate_director(director);
234 let dir_impl: &D = &*dir.priv_.cast::<D>();
235 dir_impl.event(ev);
236}