nginx_sys/lib.rs
1#![doc = include_str!("../README.md")]
2#![warn(missing_docs)]
3#![no_std]
4
5mod event;
6mod queue;
7
8use core::fmt;
9use core::mem::offset_of;
10use core::ptr::{self, copy_nonoverlapping};
11use core::slice;
12
13#[doc(hidden)]
14mod bindings {
15 #![allow(missing_docs)]
16 #![allow(non_upper_case_globals)]
17 #![allow(non_camel_case_types)]
18 #![allow(non_snake_case)]
19 #![allow(dead_code)]
20 #![allow(clippy::all)]
21 #![allow(improper_ctypes)]
22 #![allow(rustdoc::broken_intra_doc_links)]
23 include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
24}
25#[doc(no_inline)]
26pub use bindings::*;
27
28pub use event::*;
29pub use queue::*;
30
31/// The offset of the `main_conf` field in the `ngx_http_conf_ctx_t` struct.
32///
33/// This is used to access the main configuration context for an HTTP module.
34pub const NGX_HTTP_MAIN_CONF_OFFSET: usize = offset_of!(ngx_http_conf_ctx_t, main_conf);
35
36/// The offset of the `srv_conf` field in the `ngx_http_conf_ctx_t` struct.
37///
38/// This is used to access the server configuration context for an HTTP module.
39pub const NGX_HTTP_SRV_CONF_OFFSET: usize = offset_of!(ngx_http_conf_ctx_t, srv_conf);
40
41/// The offset of the `loc_conf` field in the `ngx_http_conf_ctx_t` struct.
42///
43/// This is used to access the location configuration context for an HTTP module.
44pub const NGX_HTTP_LOC_CONF_OFFSET: usize = offset_of!(ngx_http_conf_ctx_t, loc_conf);
45
46/// Convert a byte slice to a raw pointer (`*mut u_char`) allocated in the given nginx memory pool.
47///
48/// # Safety
49///
50/// The caller must provide a valid pointer to the memory pool.
51pub unsafe fn bytes_to_uchar(pool: *mut ngx_pool_t, data: &[u8]) -> Option<*mut u_char> {
52 let ptr: *mut u_char = ngx_pnalloc(pool, data.len()) as _;
53 if ptr.is_null() {
54 return None;
55 }
56 copy_nonoverlapping(data.as_ptr(), ptr, data.len());
57 Some(ptr)
58}
59
60/// Convert a string slice (`&str`) to a raw pointer (`*mut u_char`) allocated in the given nginx memory pool.
61///
62/// # Arguments
63///
64/// * `pool` - A pointer to the nginx memory pool (`ngx_pool_t`).
65/// * `data` - The string slice to convert to a raw pointer.
66///
67/// # Safety
68/// This function is marked as unsafe because it involves raw pointer manipulation and direct memory allocation using `ngx_pnalloc`.
69///
70/// # Returns
71/// A raw pointer (`*mut u_char`) to the allocated memory containing the converted string data.
72///
73/// # Example
74/// ```rust,ignore
75/// let pool: *mut ngx_pool_t = ...; // Obtain a pointer to the nginx memory pool
76/// let data: &str = "example"; // The string to convert
77/// let ptr = str_to_uchar(pool, data);
78/// ```
79pub unsafe fn str_to_uchar(pool: *mut ngx_pool_t, data: &str) -> *mut u_char {
80 let ptr: *mut u_char = ngx_pnalloc(pool, data.len()) as _;
81 debug_assert!(!ptr.is_null());
82 copy_nonoverlapping(data.as_ptr(), ptr, data.len());
83 ptr
84}
85
86impl ngx_str_t {
87 /// Returns the contents of this `ngx_str_t` as a byte slice.
88 ///
89 /// The returned slice will **not** contain the optional nul terminator that `ngx_str_t.data`
90 /// may have.
91 #[inline]
92 pub fn as_bytes(&self) -> &[u8] {
93 if self.is_empty() {
94 &[]
95 } else {
96 // SAFETY: `ngx_str_t` with non-zero len must contain a valid correctly aligned pointer
97 unsafe { slice::from_raw_parts(self.data, self.len) }
98 }
99 }
100
101 /// Returns the contents of this `ngx_str_t` as a mutable byte slice.
102 #[inline]
103 pub fn as_bytes_mut(&mut self) -> &mut [u8] {
104 if self.is_empty() {
105 &mut []
106 } else {
107 // SAFETY: `ngx_str_t` with non-zero len must contain a valid correctly aligned pointer
108 unsafe { slice::from_raw_parts_mut(self.data, self.len) }
109 }
110 }
111
112 /// Returns `true` if the string has a length of 0.
113 #[inline]
114 pub fn is_empty(&self) -> bool {
115 self.len == 0
116 }
117
118 /// Convert the nginx string to a string slice (`&str`).
119 ///
120 /// # Panics
121 /// This function panics if the `ngx_str_t` is not valid UTF-8.
122 ///
123 /// # Returns
124 /// A string slice (`&str`) representing the nginx string.
125 pub fn to_str(&self) -> &str {
126 core::str::from_utf8(self.as_bytes()).unwrap()
127 }
128
129 /// Creates an empty `ngx_str_t` instance.
130 ///
131 /// This method replaces the `ngx_null_string` C macro.
132 pub const fn empty() -> Self {
133 ngx_str_t {
134 len: 0,
135 data: ptr::null_mut(),
136 }
137 }
138
139 /// Create an `ngx_str_t` instance from a byte slice.
140 ///
141 /// # Safety
142 ///
143 /// The caller must provide a valid pointer to a memory pool.
144 pub unsafe fn from_bytes(pool: *mut ngx_pool_t, src: &[u8]) -> Option<Self> {
145 bytes_to_uchar(pool, src).map(|data| Self { data, len: src.len() })
146 }
147
148 /// Create an `ngx_str_t` instance from a string slice (`&str`).
149 ///
150 /// # Arguments
151 ///
152 /// * `pool` - A pointer to the nginx memory pool (`ngx_pool_t`).
153 /// * `data` - The string slice from which to create the nginx string.
154 ///
155 /// # Safety
156 /// This function is marked as unsafe because it accepts a raw pointer argument. There is no
157 /// way to know if `pool` is pointing to valid memory. The caller must provide a valid pool to
158 /// avoid indeterminate behavior.
159 ///
160 /// # Returns
161 /// An `ngx_str_t` instance representing the given string slice.
162 pub unsafe fn from_str(pool: *mut ngx_pool_t, data: &str) -> Self {
163 ngx_str_t {
164 data: str_to_uchar(pool, data),
165 len: data.len(),
166 }
167 }
168}
169
170impl Default for ngx_str_t {
171 fn default() -> Self {
172 Self::empty()
173 }
174}
175
176impl From<ngx_str_t> for &[u8] {
177 fn from(s: ngx_str_t) -> Self {
178 if s.len == 0 || s.data.is_null() {
179 return Default::default();
180 }
181 unsafe { slice::from_raw_parts(s.data, s.len) }
182 }
183}
184
185impl fmt::Display for ngx_str_t {
186 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
187 // The implementation is similar to an inlined `String::from_utf8_lossy`, with two
188 // important differences:
189 //
190 // - it writes directly to the Formatter instead of allocating a temporary String
191 // - invalid sequences are represented as escaped individual bytes
192 for chunk in self.as_bytes().utf8_chunks() {
193 f.write_str(chunk.valid())?;
194 for byte in chunk.invalid() {
195 f.write_str("\\x")?;
196 fmt::LowerHex::fmt(byte, f)?;
197 }
198 }
199 Ok(())
200 }
201}
202
203impl TryFrom<ngx_str_t> for &str {
204 type Error = core::str::Utf8Error;
205
206 fn try_from(s: ngx_str_t) -> Result<Self, Self::Error> {
207 core::str::from_utf8(s.into())
208 }
209}
210
211impl ngx_command_t {
212 /// Creates a new empty [`ngx_command_t`] instance.
213 ///
214 /// This method replaces the `ngx_null_command` C macro. This is typically used to terminate an
215 /// array of configuration directives.
216 ///
217 /// [`ngx_command_t`]: https://nginx.org/en/docs/dev/development_guide.html#config_directives
218 pub const fn empty() -> Self {
219 Self {
220 name: ngx_str_t::empty(),
221 type_: 0,
222 set: None,
223 conf: 0,
224 offset: 0,
225 post: ptr::null_mut(),
226 }
227 }
228}
229
230impl ngx_module_t {
231 /// Create a new `ngx_module_t` instance with default values.
232 pub const fn default() -> Self {
233 Self {
234 ctx_index: ngx_uint_t::MAX,
235 index: ngx_uint_t::MAX,
236 name: ptr::null_mut(),
237 spare0: 0,
238 spare1: 0,
239 version: nginx_version as ngx_uint_t,
240 signature: NGX_RS_MODULE_SIGNATURE.as_ptr(),
241 ctx: ptr::null_mut(),
242 commands: ptr::null_mut(),
243 type_: 0,
244 init_master: None,
245 init_module: None,
246 init_process: None,
247 init_thread: None,
248 exit_thread: None,
249 exit_process: None,
250 exit_master: None,
251 spare_hook0: 0,
252 spare_hook1: 0,
253 spare_hook2: 0,
254 spare_hook3: 0,
255 spare_hook4: 0,
256 spare_hook5: 0,
257 spare_hook6: 0,
258 spare_hook7: 0,
259 }
260 }
261}
262
263/// Add a key-value pair to an nginx table entry (`ngx_table_elt_t`) in the given nginx memory pool.
264///
265/// # Arguments
266///
267/// * `table` - A pointer to the nginx table entry (`ngx_table_elt_t`) to modify.
268/// * `pool` - A pointer to the nginx memory pool (`ngx_pool_t`) for memory allocation.
269/// * `key` - The key string to add to the table entry.
270/// * `value` - The value string to add to the table entry.
271///
272/// # Safety
273/// This function is marked as unsafe because it involves raw pointer manipulation and direct memory allocation using `str_to_uchar`.
274///
275/// # Returns
276/// An `Option<()>` representing the result of the operation. `Some(())` indicates success, while `None` indicates a null table pointer.
277///
278/// # Example
279/// ```rust
280/// # use nginx_sys::*;
281/// # unsafe fn example(pool: *mut ngx_pool_t, headers: *mut ngx_list_t) {
282/// // Obtain a pointer to the nginx table entry
283/// let table: *mut ngx_table_elt_t = ngx_list_push(headers).cast();
284/// assert!(!table.is_null());
285/// let key: &str = "key"; // The key to add
286/// let value: &str = "value"; // The value to add
287/// let result = add_to_ngx_table(table, pool, key, value);
288/// # }
289/// ```
290pub unsafe fn add_to_ngx_table(
291 table: *mut ngx_table_elt_t,
292 pool: *mut ngx_pool_t,
293 key: impl AsRef<[u8]>,
294 value: impl AsRef<[u8]>,
295) -> Option<()> {
296 if let Some(table) = table.as_mut() {
297 let key = key.as_ref();
298 table.key = ngx_str_t::from_bytes(pool, key)?;
299 table.value = ngx_str_t::from_bytes(pool, value.as_ref())?;
300 table.lowcase_key = ngx_pnalloc(pool, table.key.len).cast();
301 if table.lowcase_key.is_null() {
302 return None;
303 }
304 table.hash = ngx_hash_strlow(table.lowcase_key, table.key.data, table.key.len);
305 return Some(());
306 }
307 None
308}
309
310#[cfg(test)]
311mod tests {
312 extern crate alloc;
313 use alloc::string::ToString;
314
315 use super::*;
316
317 #[test]
318 fn ngx_str_display() {
319 let pairs: &[(&[u8], &str)] = &[
320 (b"", ""),
321 (b"Ferris the \xf0\x9f\xa6\x80", "Ferris the 🦀"),
322 (b"\xF0\x90\x80", "\\xf0\\x90\\x80"),
323 (b"\xF0\x90\x80Hello World", "\\xf0\\x90\\x80Hello World"),
324 (b"Hello \xF0\x90\x80World", "Hello \\xf0\\x90\\x80World"),
325 (b"Hello World\xF0\x90\x80", "Hello World\\xf0\\x90\\x80"),
326 ];
327
328 for (bytes, expected) in pairs {
329 let str = ngx_str_t {
330 data: bytes.as_ptr().cast_mut(),
331 len: bytes.len(),
332 };
333 assert_eq!(str.to_string(), *expected);
334 }
335 }
336}