haproxy_api/
http.rs

1use std::marker::PhantomData;
2use std::ops::Deref;
3
4use mlua::{
5    FromLua, IntoLua, Lua, ObjectLike, Result, String as LuaString, Table, TablePairs, Value,
6};
7
8/// The "Http" class contain all the HTTP manipulation functions.
9#[derive(Clone)]
10pub struct Http(Table);
11
12#[derive(Clone)]
13pub struct Headers(Table);
14
15impl Http {
16    /// Returns a `Headers` table containing all the request headers.
17    #[inline]
18    pub fn req_get_headers(&self) -> Result<Headers> {
19        self.0.call_method("req_get_headers", ())
20    }
21
22    /// Returns a `Headers` table containing all the response headers.
23    #[inline]
24    pub fn res_get_headers(&self) -> Result<Headers> {
25        self.0.call_method("res_get_headers", ())
26    }
27
28    /// Appends an HTTP header field `name` with `value` in the request.
29    #[inline]
30    pub fn req_add_header(&self, name: &str, value: impl IntoLua) -> Result<()> {
31        self.0.call_method("req_add_header", (name, value))
32    }
33
34    /// Appends an HTTP header field `name` with `value` in the response.
35    #[inline]
36    pub fn res_add_header(&self, name: &str, value: impl IntoLua) -> Result<()> {
37        self.0.call_method("res_add_header", (name, value))
38    }
39
40    /// Removes all HTTP header fields in the request by `name`.
41    #[inline]
42    pub fn req_del_header(&self, name: &str) -> Result<()> {
43        self.0.call_method("req_del_header", name)
44    }
45
46    /// Removes all HTTP header fields in the response by `name`.
47    #[inline]
48    pub fn res_del_header(&self, name: &str) -> Result<()> {
49        self.0.call_method("res_del_header", name)
50    }
51
52    /// Replaces all occurrence of HTTP request header `name`, by only one containing the `value`.
53    #[inline]
54    pub fn req_set_header(&self, name: &str, value: impl IntoLua) -> Result<()> {
55        self.0.call_method("req_set_header", (name, value))
56    }
57
58    /// Replaces all occurrence of HTTP response header `name`, by only one containing the `value`.
59    #[inline]
60    pub fn res_set_header(&self, name: &str, value: impl IntoLua) -> Result<()> {
61        self.0.call_method("res_set_header", (name, value))
62    }
63
64    /// Matches the regular expression in all occurrences of HTTP request header `name` according to `regex`,
65    /// and replaces them with the `replace` argument.
66    ///
67    /// The replacement value can contain back references like 1, 2, ...
68    #[inline]
69    pub fn req_rep_header(&self, name: &str, regex: &str, replace: &str) -> Result<()> {
70        self.0.call_method("req_rep_header", (name, regex, replace))
71    }
72
73    /// Matches the regular expression in all occurrences of HTTP response header `name` according to `regex`,
74    /// and replaces them with the `replace` argument.
75    ///
76    /// The replacement value can contain back references like 1, 2, ...
77    #[inline]
78    pub fn res_rep_header(&self, name: &str, regex: &str, replace: &str) -> Result<()> {
79        self.0.call_method("res_rep_header", (name, regex, replace))
80    }
81
82    /// Rewrites the request method with the `method`.
83    #[inline]
84    pub fn req_set_method(&self, method: &str) -> Result<()> {
85        self.0.call_method("req_set_method", method)
86    }
87
88    /// Rewrites the request path with the `path`.
89    #[inline]
90    pub fn req_set_path(&self, path: &str) -> Result<()> {
91        self.0.call_method("req_set_path", path)
92    }
93
94    /// Rewrites the request’s query string which appears after the first question mark (`?`)
95    /// with the `query`.
96    #[inline]
97    pub fn req_set_query(&self, query: &str) -> Result<()> {
98        self.0.call_method("req_set_query", query)
99    }
100
101    /// Rewrites the request URI with the `uri`.
102    #[inline]
103    pub fn req_set_uri(&self, uri: &str) -> Result<()> {
104        self.0.call_method("req_set_uri", uri)
105    }
106
107    /// Rewrites the response status code.
108    /// If no custom reason is provided, it will be generated from the status.
109    #[inline]
110    pub fn res_set_status(&self, status: u16, reason: Option<&str>) -> Result<()> {
111        self.0.call_method("res_set_status", (status, reason))
112    }
113}
114
115impl Deref for Http {
116    type Target = Table;
117
118    #[inline]
119    fn deref(&self) -> &Self::Target {
120        &self.0
121    }
122}
123
124impl FromLua for Http {
125    #[inline]
126    fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
127        Ok(Http(Table::from_lua(value, lua)?))
128    }
129}
130
131impl Headers {
132    #[inline]
133    pub fn pairs<V: FromLua>(&self) -> HeaderPairs<'_, V> {
134        HeaderPairs {
135            pairs: self.0.pairs(),
136            phantom: PhantomData,
137        }
138    }
139
140    /// Returns all header fields by `name`.
141    #[inline]
142    pub fn get<V: FromLua>(&self, name: &str) -> Result<Vec<V>> {
143        let name = name.to_ascii_lowercase();
144        let mut result = Vec::new();
145        if let Some(values) = self.0.get::<Option<Table>>(name)? {
146            let mut pairs = values.pairs::<i32, V>().collect::<Result<Vec<_>>>()?;
147            pairs.sort_by_key(|x| x.0);
148            result = pairs.into_iter().map(|(_, v)| v).collect();
149        }
150        Ok(result)
151    }
152
153    /// Returns first header field by `name`.
154    #[inline]
155    pub fn get_first<V: FromLua>(&self, name: &str) -> Result<Option<V>> {
156        let name = name.to_ascii_lowercase();
157        if let Some(values) = self.0.get::<Option<Table>>(name)? {
158            return values.get(0); // Indexes starts from "0"
159        }
160        Ok(None)
161    }
162}
163
164impl Deref for Headers {
165    type Target = Table;
166
167    #[inline]
168    fn deref(&self) -> &Self::Target {
169        &self.0
170    }
171}
172
173impl FromLua for Headers {
174    #[inline]
175    fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
176        Ok(Headers(Table::from_lua(value, lua)?))
177    }
178}
179
180pub struct HeaderPairs<'a, V: FromLua> {
181    pairs: TablePairs<'a, LuaString, Table>,
182    phantom: PhantomData<V>,
183}
184
185impl<V: FromLua> Iterator for HeaderPairs<'_, V> {
186    type Item = Result<(String, Vec<V>)>;
187
188    fn next(&mut self) -> Option<Self::Item> {
189        match self.pairs.next() {
190            Some(Ok(item)) => {
191                let name = item.0.to_string_lossy();
192                let pairs = item.1.pairs::<i32, V>().collect::<Result<Vec<_>>>();
193                match pairs {
194                    Ok(mut pairs) => {
195                        pairs.sort_by_key(|x| x.0);
196                        Some(Ok((name, pairs.into_iter().map(|(_, v)| v).collect())))
197                    }
198                    Err(e) => Some(Err(e)),
199                }
200            }
201            Some(Err(e)) => Some(Err(e)),
202            None => None,
203        }
204    }
205}