haproxy_api/
core.rs

1use std::collections::HashMap;
2#[cfg(feature = "async")]
3use std::future::Future;
4use std::ops::Deref;
5
6use mlua::{AnyUserData, AsChunk, FromLuaMulti, IntoLua, Lua, Result, Table, TableExt, Value};
7
8use crate::filter::UserFilterWrapper;
9use crate::{Proxy, UserFilter};
10
11/// The "Core" class contains all the HAProxy core functions.
12#[derive(Clone)]
13pub struct Core<'lua> {
14    lua: &'lua Lua,
15    class: Table<'lua>,
16}
17
18#[derive(Debug, Copy, Clone)]
19pub struct Time {
20    pub sec: u64,
21    pub usec: u64,
22}
23
24#[derive(Debug, Copy, Clone)]
25pub enum Action {
26    TcpReq,
27    TcpRes,
28    HttpReq,
29    HttpRes,
30    HttpAfterRes,
31}
32
33impl Action {
34    fn as_str(&self) -> &'static str {
35        match self {
36            Action::TcpReq => "tcp-req",
37            Action::TcpRes => "tcp-res",
38            Action::HttpReq => "http-req",
39            Action::HttpRes => "http-res",
40            Action::HttpAfterRes => "http-after-res",
41        }
42    }
43}
44
45#[derive(Debug, Copy, Clone)]
46pub enum ServiceMode {
47    Tcp,
48    Http,
49}
50
51#[derive(Debug, Copy, Clone)]
52pub enum LogLevel {
53    Emerg,
54    Alert,
55    Crit,
56    Err,
57    Warning,
58    Notice,
59    Info,
60    Debug,
61}
62
63impl<'lua> Core<'lua> {
64    /// Creates new Core object using Lua global `core`
65    #[inline]
66    pub fn new(lua: &'lua Lua) -> Result<Self> {
67        let class: Table = lua.globals().get("core")?;
68        Ok(Core { lua, class })
69    }
70
71    /// Returns a map of declared proxies (frontends and backends), indexed by proxy name.
72    #[inline]
73    pub fn proxies(&self) -> Result<HashMap<String, Proxy<'lua>>> {
74        self.class.get("proxies")
75    }
76
77    /// Returns a map of declared proxies with backend capability, indexed by the backend name.
78    #[inline]
79    pub fn backends(&self) -> Result<HashMap<String, Proxy<'lua>>> {
80        self.class.get("backends")
81    }
82
83    /// Returns a map of declared proxies with frontend capability, indexed by the frontend name.
84    #[inline]
85    pub fn frontends(&self) -> Result<HashMap<String, Proxy<'lua>>> {
86        self.class.get("frontends")
87    }
88
89    /// Returns the executing thread number starting at 0.
90    /// If thread is 0, Lua scope is shared by all threads, otherwise the scope is dedicated to a single thread.
91    /// This is HAProxy >=2.4 feature.
92    #[inline]
93    pub fn thread(&self) -> Result<u16> {
94        self.class.get("thread")
95    }
96
97    /// Sends a log on the default syslog server if it is configured and on the stderr if it is allowed.
98    #[inline]
99    pub fn log(&self, level: LogLevel, msg: impl AsRef<str>) -> Result<()> {
100        let msg = msg.as_ref();
101        self.class.call_function("log", (level, msg))
102    }
103
104    /// Adds the ACL `key` in the ACLs list referenced by `filename`.
105    #[inline]
106    pub fn add_acl(&self, filename: &str, key: &str) -> Result<()> {
107        self.class.call_function("add_acl", (filename, key))
108    }
109
110    /// Deletes the ACL entry by `key` in the ACLs list referenced by `filename`.
111    #[inline]
112    pub fn del_acl(&self, filename: &str, key: &str) -> Result<()> {
113        self.class.call_function("del_acl", (filename, key))
114    }
115
116    /// Deletes the map entry indexed with the specified `key` in the list of maps
117    /// referenced by his `filename`.
118    #[inline]
119    pub fn del_map(&self, filename: &str, key: &str) -> Result<()> {
120        self.class.call_function("del_map", (filename, key))
121    }
122
123    /// Sets the `value` associated to the `key` in the map referenced by `filename`.
124    #[inline]
125    pub fn set_map(&self, filename: &str, key: &str, value: &str) -> Result<()> {
126        self.class.call_function("set_map", (filename, key, value))
127    }
128
129    /// Returns HAProxy core information (uptime, pid, memory pool usage, tasks number, ...).
130    #[inline]
131    pub fn get_info(&self) -> Result<Vec<String>> {
132        self.class.call_function("get_info", ())
133    }
134
135    /// Returns the current time.
136    /// The time returned is fixed by the HAProxy core and assures than the hour will be monotonic
137    /// and that the system call `gettimeofday` will not be called too.
138    #[inline]
139    pub fn now(&self) -> Result<Time> {
140        let time: Table = self.class.call_function("now", ())?;
141        Ok(Time {
142            sec: time.get("sec")?,
143            usec: time.get("usec")?,
144        })
145    }
146
147    /// Registers a function executed as an action.
148    /// The expected actions are `tcp-req`, `tcp-res`, `http-req`, `http-res` or `http-after-res`.
149    /// All the registered actions can be used in HAProxy with the prefix `lua.`.
150    pub fn register_action<A, F>(
151        &self,
152        name: &str,
153        actions: &[Action],
154        nb_args: usize,
155        func: F,
156    ) -> Result<()>
157    where
158        A: FromLuaMulti<'lua>,
159        F: Fn(&'lua Lua, A) -> Result<()> + Send + 'static,
160    {
161        let func = self.lua.create_function(func)?;
162        let actions = actions.iter().map(|act| act.as_str()).collect::<Vec<_>>();
163        self.class
164            .call_function("register_action", (name, actions, func, nb_args))
165    }
166
167    /// Registers an asynchronous function executed as an action.
168    ///
169    /// See [`Core::register_action`] for more details.
170    pub fn register_async_action<F, A, FR>(
171        &self,
172        name: &str,
173        actions: &[Action],
174        nb_args: usize,
175        func: F,
176    ) -> Result<()>
177    where
178        F: Fn(A) -> FR + 'static,
179        A: FromLuaMulti<'lua> + 'static,
180        FR: Future<Output = Result<()>> + Send + 'static,
181    {
182        let func = crate::r#async::create_async_function(self.lua, func)?;
183        let actions = actions.iter().map(|act| act.as_str()).collect::<Vec<_>>();
184        self.class
185            .call_function("register_action", (name, actions, func, nb_args))
186    }
187
188    /// Same as [`register_action`] but using Lua function.
189    ///
190    /// [`register_action`]: #method.register_action
191    pub fn register_lua_action<'a, S>(
192        &self,
193        name: &str,
194        actions: &[&str],
195        nb_args: usize,
196        code: S,
197    ) -> Result<()>
198    where
199        S: AsChunk<'lua, 'a>,
200    {
201        let func = self.lua.load(code).into_function()?;
202        self.class
203            .call_function("register_action", (name, actions.to_vec(), func, nb_args))
204    }
205
206    /// Registers a function executed as a converter.
207    /// All the registered converters can be used in HAProxy with the prefix `lua.`.
208    pub fn register_converters<A, R, F>(&self, name: &str, func: F) -> Result<()>
209    where
210        A: FromLuaMulti<'lua>,
211        R: IntoLua<'lua>,
212        F: Fn(&'lua Lua, A) -> Result<R> + Send + 'static,
213    {
214        let func = self.lua.create_function(func)?;
215        self.class
216            .call_function("register_converters", (name, func))
217    }
218
219    /// Same as [`register_converters`] but using Lua function.
220    ///
221    /// [`register_converters`]: #method.register_converters
222    pub fn register_lua_converters<'a, S>(&self, name: &str, code: S) -> Result<()>
223    where
224        S: AsChunk<'lua, 'a>,
225    {
226        let func = self.lua.load(code).into_function()?;
227        self.class
228            .call_function("register_converters", (name, func))
229    }
230
231    /// Registers a function executed as sample fetch.
232    /// All the registered sample fetch can be used in HAProxy with the prefix `lua.`.
233    pub fn register_fetches<A, R, F>(&self, name: &str, func: F) -> Result<()>
234    where
235        A: FromLuaMulti<'lua>,
236        R: IntoLua<'lua>,
237        F: Fn(&'lua Lua, A) -> Result<R> + Send + 'static,
238    {
239        let func = self.lua.create_function(func)?;
240        self.class.call_function("register_fetches", (name, func))
241    }
242
243    /// Same as [`register_fetches`] but using Lua function.
244    ///
245    /// [`register_fetches`]: #method.register_fetches
246    pub fn register_lua_fetches<'a, S>(&self, name: &str, code: S) -> Result<()>
247    where
248        S: AsChunk<'lua, 'a>,
249    {
250        let func = self.lua.load(code).into_function()?;
251        self.class.call_function("register_fetches", (name, func))
252    }
253
254    /// Registers a custom filter that implements [`UserFilter`] trait.
255    pub fn register_filter<T: UserFilter + 'static>(&self, name: &str) -> Result<()> {
256        let lua = self.lua;
257        let func = lua.create_function(|_, (class, args): (Table, Table)| {
258            class.raw_set("args", args)?;
259            Ok(class)
260        });
261        let filter_class = UserFilterWrapper::<T>::make_class(lua)?;
262        self.class
263            .call_function("register_filter", (name, filter_class, func))
264    }
265
266    /// Registers a Lua function executed as a service.
267    /// All the registered service can be used in HAProxy with the prefix `lua.`.
268    pub fn register_lua_service<'a, S>(&self, name: &str, mode: ServiceMode, code: S) -> Result<()>
269    where
270        S: AsChunk<'lua, 'a>,
271    {
272        let func = self.lua.load(code).into_function()?;
273        let mode = match mode {
274            ServiceMode::Tcp => "tcp",
275            ServiceMode::Http => "http",
276        };
277        self.class
278            .call_function("register_service", (name, mode, func))
279    }
280
281    /// Registers a function executed after the configuration parsing.
282    /// This is useful to check any parameters.
283    pub fn register_init<F>(&self, func: F) -> Result<()>
284    where
285        F: Fn(&'lua Lua) -> Result<()> + Send + 'static,
286    {
287        let func = self.lua.create_function(move |lua, ()| func(lua))?;
288        self.class.call_function("register_init", func)
289    }
290
291    /// Registers and start an independent task.
292    /// The task is started when the HAProxy main scheduler starts.
293    pub fn register_task<F>(&self, func: F) -> Result<()>
294    where
295        F: Fn(&'lua Lua) -> Result<()> + Send + 'static,
296    {
297        let func = self.lua.create_function(move |lua, ()| func(lua))?;
298        self.class.call_function("register_task", func)
299    }
300
301    /// Registers and start an independent asynchronous task.
302    #[cfg(feature = "async")]
303    pub fn register_async_task<F, FR>(&self, func: F) -> Result<()>
304    where
305        F: Fn() -> FR + 'static,
306        FR: Future<Output = Result<()>> + Send + 'static,
307    {
308        let func = crate::r#async::create_async_function(self.lua, move |()| func())?;
309        self.class.call_function("register_task", func)
310    }
311
312    /// Same as [`register_task`] but using Lua function.
313    ///
314    /// [`register_task`]: #method.register_task
315    pub fn register_lua_task<'a, S>(&self, code: S) -> Result<()>
316    where
317        S: AsChunk<'lua, 'a>,
318    {
319        let func = self.lua.load(code).into_function()?;
320        self.class.call_function("register_task", func)
321    }
322
323    /// Registers a Lua function executed as a cli command.
324    pub fn register_lua_cli<'a, S>(&self, path: &[&str], usage: &str, code: S) -> Result<()>
325    where
326        S: AsChunk<'lua, 'a>,
327    {
328        let func = self.lua.load(code).into_function()?;
329        self.class
330            .call_function("register_cli", (path, usage, func))
331    }
332
333    /// Changes the nice of the current task or current session.
334    #[inline]
335    pub fn set_nice(&self, nice: i32) -> Result<()> {
336        self.class.call_function("set_nice", nice)
337    }
338
339    /// Parses ipv4 or ipv6 addresses and its facultative associated network.
340    #[inline]
341    pub fn parse_addr(&self, addr: &str) -> Result<AnyUserData<'lua>> {
342        self.class.call_function("parse_addr", addr)
343    }
344
345    /// Matches two networks.
346    /// For example "127.0.0.1/32" matches "127.0.0.0/8". The order of network is not important.
347    #[inline]
348    pub fn match_addr(&self, addr1: AnyUserData, addr2: AnyUserData) -> Result<bool> {
349        self.class.call_function("match_addr", (addr1, addr2))
350    }
351
352    pub fn event_sub<'a, S>(&self, event_types: &[&str], code: S) -> Result<()>
353    where
354        S: AsChunk<'lua, 'a>,
355    {
356        let func = self.lua.load(code).into_function()?;
357        self.class.call_function("event_sub", (event_types, func))
358    }
359}
360
361impl<'lua> Deref for Core<'lua> {
362    type Target = Table<'lua>;
363
364    #[inline]
365    fn deref(&self) -> &Self::Target {
366        &self.class
367    }
368}
369
370impl<'lua> IntoLua<'lua> for LogLevel {
371    #[inline]
372    fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
373        (match self {
374            LogLevel::Emerg => 0,
375            LogLevel::Alert => 1,
376            LogLevel::Crit => 2,
377            LogLevel::Err => 3,
378            LogLevel::Warning => 4,
379            LogLevel::Notice => 5,
380            LogLevel::Info => 6,
381            LogLevel::Debug => 7,
382        })
383        .into_lua(lua)
384    }
385}