1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
use mlua::{FromLua, Lua, Result, String as LuaString, Table, TableExt, Value};

use crate::{Channel, Headers};

/// This class contains all functions to manipulate an HTTP message.
/// For now, this class is only available from a filter context.
#[derive(Clone)]
pub struct HttpMessage<'lua> {
    lua: &'lua Lua,
    class: Table<'lua>,
}

impl<'lua> HttpMessage<'lua> {
    /// Appends an HTTP header field in the HTTP message whose name is specified in `name` and value is defined in `value`.
    #[inline]
    pub fn add_header(&self, name: &str, value: impl AsRef<[u8]>) -> Result<()> {
        let value = self.lua.create_string(value.as_ref())?;
        self.class.call_method("add_header", (name, value))
    }

    /// Copies the string at the end of incoming data of the HTTP message.
    /// The function returns the copied length on success or -1 if data cannot be copied.
    #[inline]
    pub fn append(&self, data: impl AsRef<[u8]>) -> Result<isize> {
        let data = self.lua.create_string(data.as_ref())?;
        self.class.call_method("append", data)
    }

    /// Returns `length` bytes of incoming data from the HTTP message, starting at the `offset`.
    /// The data are not removed from the buffer.
    #[inline]
    pub fn body(&self, offset: Option<isize>, length: Option<isize>) -> Result<Option<LuaString>> {
        let offset = offset.unwrap_or(0);
        match length {
            Some(length) => self.class.call_method("body", (offset, length)),
            None => self.class.call_method("body", offset),
        }
    }

    /// Returns a corresponding channel attached to the HTTP message.
    #[inline]
    pub fn channel(&self) -> Result<Channel> {
        self.class.raw_get("channel")
    }

    /// Returns true if the end of message is reached.
    #[inline]
    pub fn eom(&self) -> Result<bool> {
        self.class.call_method("eom", ())
    }

    /// Removes all HTTP header fields in the HTTP message whose name is specified in name.
    #[inline]
    pub fn del_header(&self, name: &str) -> Result<()> {
        self.class.call_method("del_header", name)
    }

    /// Returns a table containing all the headers of the HTTP message.
    #[inline]
    pub fn get_headers(&self) -> Result<Headers> {
        self.class.call_method("get_headers", ())
    }

    /// Returns a table containing the start-line of the HTTP message.
    #[inline]
    pub fn get_stline(&self) -> Result<Table> {
        self.class.call_method("get_stline", ())
    }

    /// Forwards `length` bytes of data from the HTTP message.
    /// Returns the amount of data forwarded.
    ///
    /// Because it is called in the filter context, it never yield.
    /// Only available incoming data may be forwarded, event if the requested length exceeds the available amount of incoming data.
    #[inline]
    pub fn forward(&self, length: usize) -> Result<usize> {
        self.class.call_method("forward", length)
    }

    /// Returns the length of incoming data in the HTTP message from the calling filter point of view.
    #[inline]
    pub fn input(&self) -> Result<usize> {
        self.class.call_method("input", ())
    }

    /// Copies the `data` at the `offset` in incoming data of the HTTP message.
    /// Returns the copied length on success or -1 if data cannot be copied.
    ///
    /// By default, if no `offset` is provided, the string is copied in front of incoming data.
    /// A positive `offset` is relative to the beginning of incoming data of the channel buffer while negative offset is relative to their end.
    #[inline]
    pub fn insert(&self, data: impl AsRef<[u8]>, offset: Option<isize>) -> Result<isize> {
        let data = self.lua.create_string(data.as_ref())?;
        let offset = offset.unwrap_or(0);
        self.class
            .call_method::<_, _, isize>("insert", (data, offset))
    }

    /// Returns true if the HTTP message is full.
    #[inline]
    pub fn is_full(&self) -> Result<bool> {
        self.class.call_method("is_full", ())
    }

    /// Returns true if the HTTP message is the response one.
    #[inline]
    pub fn is_resp(&self) -> Result<bool> {
        self.class.call_method("is_resp", ())
    }

    /// Returns true if the HTTP message may still receive data.
    #[inline]
    pub fn may_recv(&self) -> Result<bool> {
        self.class.call_method("may_recv", ())
    }

    /// Returns the length of outgoing data of the HTTP message.
    #[inline]
    pub fn output(&self) -> Result<usize> {
        self.class.call_method("output", ())
    }

    /// Copies the `data` in front of incoming data of the HTTP message.
    /// Returns the copied length on success or -1 if data cannot be copied.
    #[inline]
    pub fn prepend(&self, data: impl AsRef<[u8]>) -> Result<isize> {
        let data = self.lua.create_string(data.as_ref())?;
        self.class.call_method::<_, _, isize>("prepend", data)
    }

    /// Removes `length` bytes of incoming data of the HTTP message, starting at `offset`.
    /// Returns number of bytes removed on success.
    #[inline]
    pub fn remove(&self, offset: Option<isize>, length: Option<usize>) -> Result<isize> {
        let offset = offset.unwrap_or(0);
        match length {
            Some(length) => self.class.call_method("remove", (offset, length)),
            None => self.class.call_method("remove", offset),
        }
    }

    /// Matches the regular expression in all occurrences of header field `name` according to `regex`,
    /// and replaces them with the `replace`.
    ///
    /// The replacement value can contain back references like 1, 2, ...
    /// This function acts on whole header lines, regardless of the number of values they may contain.
    #[inline]
    pub fn rep_header(&self, name: &str, regex: &str, replace: &str) -> Result<()> {
        self.class.call_method("rep_header", (name, regex, replace))
    }

    /// Matches the regular expression on every comma-delimited value of header field `name` according to `regex`,
    /// and replaces them with the `replace`.
    ///
    /// The replacement value can contain back references like 1, 2, ...
    #[inline]
    pub fn rep_value(&self, name: &str, regex: &str, replace: &str) -> Result<()> {
        self.class.call_method("rep_value", (name, regex, replace))
    }

    /// Requires immediate send of the `data`.
    /// It means the `data` is copied at the beginning of incoming data of the HTTP message and immediately forwarded.
    ///
    /// Because it is called in the filter context, it never yield.
    #[inline]
    pub fn send(&self, data: impl AsRef<[u8]>) -> Result<isize> {
        let data = self.lua.create_string(data.as_ref())?;
        self.class.call_method("send", data)
    }

    /// Replaces `length` bytes of incoming data of the HTTP message, starting at `offset`, by the string `data`.
    /// Returns the copied length on success or -1 if data cannot be copied.
    #[inline]
    pub fn set(
        &self,
        data: impl AsRef<[u8]>,
        offset: Option<isize>,
        length: Option<usize>,
    ) -> Result<isize> {
        let data = self.lua.create_string(data.as_ref())?;
        let offset = offset.unwrap_or(0);
        match length {
            Some(length) => self.class.call_method("set", (data, offset, length)),
            None => self.class.call_method("set", (data, offset)),
        }
    }

    /// Sets or removes the flag that indicates end of message.
    #[inline]
    pub fn set_eom(&self, eom: bool) -> Result<()> {
        match eom {
            true => self.class.call_method("set_eom", ()),
            false => self.class.call_method("unset_eom", ()),
        }
    }

    /// Replaces all occurrence of all header matching the `name`, by only one containing the `value`.
    #[inline]
    pub fn set_header(&self, name: &str, value: impl AsRef<[u8]>) -> Result<()> {
        let value = self.lua.create_string(value.as_ref())?;
        self.class.call_method("set_header", (name, value))
    }

    /// Rewrites the request method.
    #[inline]
    pub fn set_method(&self, method: &str) -> Result<()> {
        self.class.call_method("set_method", method)
    }

    /// Rewrites the request path.
    #[inline]
    pub fn set_path(&self, path: &str) -> Result<()> {
        self.class.call_method("set_path", path)
    }

    /// Rewrites the request’s query string which appears after the first question mark "?".
    #[inline]
    pub fn set_query(&self, query: &str) -> Result<()> {
        self.class.call_method("set_query", query)
    }

    /// Rewrites the response status code with the new `status` and optional `reason`.
    /// If no custom reason is provided, it will be generated from the status.
    #[inline]
    pub fn set_status(&self, status: u16, reason: Option<&str>) -> Result<()> {
        self.class.call_method("set_status", (status, reason))
    }

    /// Rewrites the request URI.
    #[inline]
    pub fn set_uri(&self, uri: &str) -> Result<()> {
        self.class.call_method("set_uri", uri)
    }
}

impl<'lua> FromLua<'lua> for HttpMessage<'lua> {
    #[inline]
    fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
        let class = Table::from_lua(value, lua)?;
        Ok(HttpMessage { lua, class })
    }
}