haproxy_api/
core.rs

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