haproxy_api/
http_message.rs

1use std::ops::Deref;
2
3use mlua::{FromLua, Lua, Result, String as LuaString, Table, TableExt, Value};
4
5use crate::{Channel, Headers};
6
7/// This class contains all functions to manipulate an HTTP message.
8/// For now, this class is only available from a filter context.
9#[derive(Clone)]
10pub struct HttpMessage<'lua> {
11    lua: &'lua Lua,
12    class: Table<'lua>,
13}
14
15impl<'lua> HttpMessage<'lua> {
16    /// Appends an HTTP header field in the HTTP message whose name is specified in `name` and value is defined in `value`.
17    #[inline]
18    pub fn add_header(&self, name: &str, value: impl AsRef<[u8]>) -> Result<()> {
19        let value = self.lua.create_string(value.as_ref())?;
20        self.class.call_method("add_header", (name, value))
21    }
22
23    /// Copies the string at the end of incoming data of the HTTP message.
24    /// The function returns the copied length on success or -1 if data cannot be copied.
25    #[inline]
26    pub fn append(&self, data: impl AsRef<[u8]>) -> Result<isize> {
27        let data = self.lua.create_string(data.as_ref())?;
28        self.class.call_method("append", data)
29    }
30
31    /// Returns `length` bytes of incoming data from the HTTP message, starting at the `offset`.
32    /// The data are not removed from the buffer.
33    #[inline]
34    pub fn body(
35        &self,
36        offset: Option<isize>,
37        length: Option<isize>,
38    ) -> Result<Option<LuaString<'lua>>> {
39        let offset = offset.unwrap_or(0);
40        match length {
41            Some(length) => self.class.call_method("body", (offset, length)),
42            None => self.class.call_method("body", offset),
43        }
44    }
45
46    /// Returns a corresponding channel attached to the HTTP message.
47    #[inline]
48    pub fn channel(&self) -> Result<Channel<'lua>> {
49        self.class.raw_get("channel")
50    }
51
52    /// Returns true if the end of message is reached.
53    #[inline]
54    pub fn eom(&self) -> Result<bool> {
55        self.class.call_method("eom", ())
56    }
57
58    /// Removes all HTTP header fields in the HTTP message whose name is specified in name.
59    #[inline]
60    pub fn del_header(&self, name: &str) -> Result<()> {
61        self.class.call_method("del_header", name)
62    }
63
64    /// Returns a table containing all the headers of the HTTP message.
65    #[inline]
66    pub fn get_headers(&self) -> Result<Headers<'lua>> {
67        self.class.call_method("get_headers", ())
68    }
69
70    /// Returns a table containing the start-line of the HTTP message.
71    #[inline]
72    pub fn get_stline(&self) -> Result<Table<'lua>> {
73        self.class.call_method("get_stline", ())
74    }
75
76    /// Forwards `length` bytes of data from the HTTP message.
77    /// Returns the amount of data forwarded.
78    ///
79    /// Because it is called in the filter context, it never yield.
80    /// Only available incoming data may be forwarded, event if the requested length exceeds the available amount of incoming data.
81    #[inline]
82    pub fn forward(&self, length: usize) -> Result<usize> {
83        self.class.call_method("forward", length)
84    }
85
86    /// Returns the length of incoming data in the HTTP message from the calling filter point of view.
87    #[inline]
88    pub fn input(&self) -> Result<usize> {
89        self.class.call_method("input", ())
90    }
91
92    /// Copies the `data` at the `offset` in incoming data of the HTTP message.
93    /// Returns the copied length on success or -1 if data cannot be copied.
94    ///
95    /// By default, if no `offset` is provided, the string is copied in front of incoming data.
96    /// A positive `offset` is relative to the beginning of incoming data of the channel buffer while negative offset is relative to their end.
97    #[inline]
98    pub fn insert(&self, data: impl AsRef<[u8]>, offset: Option<isize>) -> Result<isize> {
99        let data = self.lua.create_string(data.as_ref())?;
100        let offset = offset.unwrap_or(0);
101        self.class.call_method::<_, isize>("insert", (data, offset))
102    }
103
104    /// Returns true if the HTTP message is full.
105    #[inline]
106    pub fn is_full(&self) -> Result<bool> {
107        self.class.call_method("is_full", ())
108    }
109
110    /// Returns true if the HTTP message is the response one.
111    #[inline]
112    pub fn is_resp(&self) -> Result<bool> {
113        self.class.call_method("is_resp", ())
114    }
115
116    /// Returns true if the HTTP message may still receive data.
117    #[inline]
118    pub fn may_recv(&self) -> Result<bool> {
119        self.class.call_method("may_recv", ())
120    }
121
122    /// Returns the length of outgoing data of the HTTP message.
123    #[inline]
124    pub fn output(&self) -> Result<usize> {
125        self.class.call_method("output", ())
126    }
127
128    /// Copies the `data` in front of incoming data of the HTTP message.
129    /// Returns the copied length on success or -1 if data cannot be copied.
130    #[inline]
131    pub fn prepend(&self, data: impl AsRef<[u8]>) -> Result<isize> {
132        let data = self.lua.create_string(data.as_ref())?;
133        self.class.call_method::<_, isize>("prepend", data)
134    }
135
136    /// Removes `length` bytes of incoming data of the HTTP message, starting at `offset`.
137    /// Returns number of bytes removed on success.
138    #[inline]
139    pub fn remove(&self, offset: Option<isize>, length: Option<usize>) -> Result<isize> {
140        let offset = offset.unwrap_or(0);
141        match length {
142            Some(length) => self.class.call_method("remove", (offset, length)),
143            None => self.class.call_method("remove", offset),
144        }
145    }
146
147    /// Matches the regular expression in all occurrences of header field `name` according to `regex`,
148    /// and replaces them with the `replace`.
149    ///
150    /// The replacement value can contain back references like 1, 2, ...
151    /// This function acts on whole header lines, regardless of the number of values they may contain.
152    #[inline]
153    pub fn rep_header(&self, name: &str, regex: &str, replace: &str) -> Result<()> {
154        self.class.call_method("rep_header", (name, regex, replace))
155    }
156
157    /// Matches the regular expression on every comma-delimited value of header field `name` according to `regex`,
158    /// and replaces them with the `replace`.
159    ///
160    /// The replacement value can contain back references like 1, 2, ...
161    #[inline]
162    pub fn rep_value(&self, name: &str, regex: &str, replace: &str) -> Result<()> {
163        self.class.call_method("rep_value", (name, regex, replace))
164    }
165
166    /// Requires immediate send of the `data`.
167    /// It means the `data` is copied at the beginning of incoming data of the HTTP message and immediately forwarded.
168    ///
169    /// Because it is called in the filter context, it never yield.
170    #[inline]
171    pub fn send(&self, data: impl AsRef<[u8]>) -> Result<isize> {
172        let data = self.lua.create_string(data.as_ref())?;
173        self.class.call_method("send", data)
174    }
175
176    /// Replaces `length` bytes of incoming data of the HTTP message, starting at `offset`, by the string `data`.
177    /// Returns the copied length on success or -1 if data cannot be copied.
178    #[inline]
179    pub fn set(
180        &self,
181        data: impl AsRef<[u8]>,
182        offset: Option<isize>,
183        length: Option<usize>,
184    ) -> Result<isize> {
185        let data = self.lua.create_string(data.as_ref())?;
186        let offset = offset.unwrap_or(0);
187        match length {
188            Some(length) => self.class.call_method("set", (data, offset, length)),
189            None => self.class.call_method("set", (data, offset)),
190        }
191    }
192
193    /// Sets or removes the flag that indicates end of message.
194    #[inline]
195    pub fn set_eom(&self, eom: bool) -> Result<()> {
196        match eom {
197            true => self.class.call_method("set_eom", ()),
198            false => self.class.call_method("unset_eom", ()),
199        }
200    }
201
202    /// Replaces all occurrence of all header matching the `name`, by only one containing the `value`.
203    #[inline]
204    pub fn set_header(&self, name: &str, value: impl AsRef<[u8]>) -> Result<()> {
205        let value = self.lua.create_string(value.as_ref())?;
206        self.class.call_method("set_header", (name, value))
207    }
208
209    /// Rewrites the request method.
210    #[inline]
211    pub fn set_method(&self, method: &str) -> Result<()> {
212        self.class.call_method("set_method", method)
213    }
214
215    /// Rewrites the request path.
216    #[inline]
217    pub fn set_path(&self, path: &str) -> Result<()> {
218        self.class.call_method("set_path", path)
219    }
220
221    /// Rewrites the request’s query string which appears after the first question mark "?".
222    #[inline]
223    pub fn set_query(&self, query: &str) -> Result<()> {
224        self.class.call_method("set_query", query)
225    }
226
227    /// Rewrites the response status code with the new `status` and optional `reason`.
228    /// If no custom reason is provided, it will be generated from the status.
229    #[inline]
230    pub fn set_status(&self, status: u16, reason: Option<&str>) -> Result<()> {
231        self.class.call_method("set_status", (status, reason))
232    }
233
234    /// Rewrites the request URI.
235    #[inline]
236    pub fn set_uri(&self, uri: &str) -> Result<()> {
237        self.class.call_method("set_uri", uri)
238    }
239}
240
241impl<'lua> FromLua<'lua> for HttpMessage<'lua> {
242    #[inline]
243    fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
244        let class = Table::from_lua(value, lua)?;
245        Ok(HttpMessage { lua, class })
246    }
247}
248
249impl<'lua> Deref for HttpMessage<'lua> {
250    type Target = Table<'lua>;
251
252    #[inline]
253    fn deref(&self) -> &Self::Target {
254        &self.class
255    }
256}