diff --git a/Cargo.toml b/Cargo.toml index c5826018..d7383ddb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,10 +25,16 @@ repository.workspace = true rust-version.workspace = true [dependencies] -nginx-sys = { path = "nginx-sys", version = "0.5.0"} +nginx-sys = { path = "nginx-sys", default-features=false, version = "0.5.0"} [features] -default = ["vendored"] +default = ["vendored","std"] +# Enables the components using memory allocation. +# If no `std` flag, `alloc` crate is internally used instead. This flag is mainly for `no_std` build. +alloc = [] +# Enables the components using `std` crate. +# Currently the only difference to `alloc` flag is `std::error::Error` implementation. +std = ["alloc"] # Build our own copy of the NGINX by default. # This could be disabled with `--no-default-features` to minimize the dependency tree # when building against an existing copy of the NGINX with the NGX_OBJS variable. diff --git a/examples/Cargo.toml b/examples/Cargo.toml index a75830bc..8f6f2c38 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -12,7 +12,7 @@ build = "../build.rs" [dependencies] nginx-sys = { path = "../nginx-sys/", default-features = false } -ngx = { path = "../", default-features = false } +ngx = { path = "../", default-features = false, features = ["std"] } [dev-dependencies] aws-sign-v4 = "0.3.0" diff --git a/nginx-sys/build/main.rs b/nginx-sys/build/main.rs index fadf50b2..087410b7 100644 --- a/nginx-sys/build/main.rs +++ b/nginx-sys/build/main.rs @@ -97,6 +97,7 @@ fn generate_binding(nginx_build_dir: PathBuf) { .header("build/wrapper.h") .clang_args(clang_args) .layout_tests(false) + .use_core() .generate() .expect("Unable to generate bindings"); diff --git a/nginx-sys/src/lib.rs b/nginx-sys/src/lib.rs index 8264428f..28d4cd62 100644 --- a/nginx-sys/src/lib.rs +++ b/nginx-sys/src/lib.rs @@ -1,9 +1,10 @@ #![doc = include_str!("../README.md")] #![warn(missing_docs)] +#![no_std] -use std::fmt; -use std::ptr::copy_nonoverlapping; -use std::slice; +use core::fmt; +use core::ptr::copy_nonoverlapping; +use core::slice; #[doc(hidden)] mod bindings { @@ -104,7 +105,7 @@ impl ngx_str_t { /// # Returns /// A string slice (`&str`) representing the nginx string. pub fn to_str(&self) -> &str { - std::str::from_utf8(self.as_bytes()).unwrap() + core::str::from_utf8(self.as_bytes()).unwrap() } /// Create an `ngx_str_t` instance from a byte slice. @@ -116,27 +117,6 @@ impl ngx_str_t { bytes_to_uchar(pool, src).map(|data| Self { data, len: src.len() }) } - /// Create an `ngx_str_t` instance from a `String`. - /// - /// # Arguments - /// - /// * `pool` - A pointer to the nginx memory pool (`ngx_pool_t`). - /// * `data` - The `String` from which to create the nginx string. - /// - /// # Safety - /// This function is marked as unsafe because it accepts a raw pointer argument. There is no - /// way to know if `pool` is pointing to valid memory. The caller must provide a valid pool to - /// avoid indeterminate behavior. - /// - /// # Returns - /// An `ngx_str_t` instance representing the given `String`. - pub unsafe fn from_string(pool: *mut ngx_pool_t, data: String) -> Self { - ngx_str_t { - data: str_to_uchar(pool, data.as_str()), - len: data.len(), - } - } - /// Create an `ngx_str_t` instance from a string slice (`&str`). /// /// # Arguments @@ -168,26 +148,29 @@ impl From for &[u8] { } } -impl TryFrom for String { - type Error = std::string::FromUtf8Error; - - fn try_from(s: ngx_str_t) -> Result { - let bytes: &[u8] = s.into(); - String::from_utf8(bytes.into()) - } -} - impl fmt::Display for ngx_str_t { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", String::from_utf8_lossy((*self).into())) + // The implementation is similar to an inlined `String::from_utf8_lossy`, with two + // important differences: + // + // - it writes directly to the Formatter instead of allocating a temporary String + // - invalid sequences are represented as escaped individual bytes + for chunk in self.as_bytes().utf8_chunks() { + f.write_str(chunk.valid())?; + for byte in chunk.invalid() { + f.write_str("\\x")?; + fmt::LowerHex::fmt(byte, f)?; + } + } + Ok(()) } } impl TryFrom for &str { - type Error = std::str::Utf8Error; + type Error = core::str::Utf8Error; fn try_from(s: ngx_str_t) -> Result { - std::str::from_utf8(s.into()) + core::str::from_utf8(s.into()) } } @@ -221,18 +204,47 @@ impl TryFrom for &str { pub unsafe fn add_to_ngx_table( table: *mut ngx_table_elt_t, pool: *mut ngx_pool_t, - key: &str, - value: &str, + key: impl AsRef<[u8]>, + value: impl AsRef<[u8]>, ) -> Option<()> { - if table.is_null() { - return None; + if let Some(table) = table.as_mut() { + let key = key.as_ref(); + table.key = ngx_str_t::from_bytes(pool, key)?; + table.value = ngx_str_t::from_bytes(pool, value.as_ref())?; + table.lowcase_key = ngx_pnalloc(pool, table.key.len).cast(); + if table.lowcase_key.is_null() { + return None; + } + table.hash = ngx_hash_strlow(table.lowcase_key, table.key.data, table.key.len); + return Some(()); + } + None +} + +#[cfg(test)] +mod tests { + extern crate alloc; + use alloc::string::ToString; + + use super::*; + + #[test] + fn ngx_str_display() { + let pairs: &[(&[u8], &str)] = &[ + (b"", ""), + (b"Ferris the \xf0\x9f\xa6\x80", "Ferris the 🦀"), + (b"\xF0\x90\x80", "\\xf0\\x90\\x80"), + (b"\xF0\x90\x80Hello World", "\\xf0\\x90\\x80Hello World"), + (b"Hello \xF0\x90\x80World", "Hello \\xf0\\x90\\x80World"), + (b"Hello World\xF0\x90\x80", "Hello World\\xf0\\x90\\x80"), + ]; + + for (bytes, expected) in pairs { + let str = ngx_str_t { + data: bytes.as_ptr().cast_mut(), + len: bytes.len(), + }; + assert_eq!(str.to_string(), *expected); + } } - table.as_mut().map(|table| { - table.hash = 1; - table.key.len = key.len(); - table.key.data = str_to_uchar(pool, key); - table.value.len = value.len(); - table.value.data = str_to_uchar(pool, value); - table.lowcase_key = str_to_uchar(pool, String::from(key).to_ascii_lowercase().as_str()); - }) } diff --git a/src/core/buffer.rs b/src/core/buffer.rs index 90862384..f3e2100c 100644 --- a/src/core/buffer.rs +++ b/src/core/buffer.rs @@ -1,4 +1,4 @@ -use std::slice; +use core::slice; use crate::ffi::*; diff --git a/src/core/mod.rs b/src/core/mod.rs index d38462be..07ef4341 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -22,7 +22,7 @@ macro_rules! ngx_null_command { set: None, conf: 0, offset: 0, - post: ::std::ptr::null_mut(), + post: ::core::ptr::null_mut(), } }; } diff --git a/src/core/pool.rs b/src/core/pool.rs index 4099065f..bc65ad57 100644 --- a/src/core/pool.rs +++ b/src/core/pool.rs @@ -1,5 +1,5 @@ -use std::ffi::c_void; -use std::{mem, ptr}; +use core::ffi::c_void; +use core::{mem, ptr}; use crate::core::buffer::{Buffer, MemoryBuffer, TemporaryBuffer}; use crate::ffi::*; diff --git a/src/core/status.rs b/src/core/status.rs index 67208495..46bb79f7 100644 --- a/src/core/status.rs +++ b/src/core/status.rs @@ -1,4 +1,4 @@ -use std::fmt; +use core::fmt; use crate::ffi::*; diff --git a/src/core/string.rs b/src/core/string.rs index 47d6d6ef..15e9a71d 100644 --- a/src/core/string.rs +++ b/src/core/string.rs @@ -1,6 +1,10 @@ -use std::borrow::Cow; -use std::slice; -use std::str::{self, Utf8Error}; +use core::slice; +use core::str::{self, Utf8Error}; + +#[cfg(all(not(feature = "std"), feature = "alloc"))] +use alloc::{borrow::Cow, string::String}; +#[cfg(feature = "std")] +use std::{borrow::Cow, string::String}; use crate::ffi::*; @@ -27,7 +31,7 @@ macro_rules! ngx_null_string { () => { $crate::ffi::ngx_str_t { len: 0, - data: ::std::ptr::null_mut(), + data: ::core::ptr::null_mut(), } }; } @@ -64,6 +68,7 @@ impl NgxStr { /// Converts an [`NgxStr`] into a [`Cow`], replacing invalid UTF-8 sequences. /// /// See [`String::from_utf8_lossy`]. + #[cfg(feature = "alloc")] pub fn to_string_lossy(&self) -> Cow { String::from_utf8_lossy(self.as_bytes()) } diff --git a/src/http/conf.rs b/src/http/conf.rs index f5de90d4..c9f14eaa 100644 --- a/src/http/conf.rs +++ b/src/http/conf.rs @@ -1,4 +1,4 @@ -use std::ffi::c_void; +use core::ffi::c_void; use crate::ffi::*; diff --git a/src/http/module.rs b/src/http/module.rs index 6a0f587f..6f25c97b 100644 --- a/src/http/module.rs +++ b/src/http/module.rs @@ -1,5 +1,6 @@ -use std::ffi::{c_char, c_void}; -use std::ptr; +use core::ffi::{c_char, c_void}; +use core::fmt; +use core::ptr; use crate::core::NGX_CONF_ERROR; use crate::core::*; @@ -12,10 +13,11 @@ pub enum MergeConfigError { NoValue, } +#[cfg(feature = "std")] impl std::error::Error for MergeConfigError {} -impl std::fmt::Display for MergeConfigError { - fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { +impl fmt::Display for MergeConfigError { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { match self { MergeConfigError::NoValue => "no value".fmt(fmt), } diff --git a/src/http/request.rs b/src/http/request.rs index 4679ecd5..c9af30cc 100644 --- a/src/http/request.rs +++ b/src/http/request.rs @@ -1,7 +1,7 @@ -use std::error::Error; -use std::ffi::c_void; -use std::fmt; -use std::str::FromStr; +use core::ffi::c_void; +use core::fmt; +use core::slice; +use core::str::FromStr; use crate::core::*; use crate::ffi::*; @@ -30,7 +30,7 @@ macro_rules! http_subrequest_handler { ( $name: ident, $handler: expr ) => { unsafe extern "C" fn $name( r: *mut $crate::ffi::ngx_http_request_t, - data: *mut ::std::ffi::c_void, + data: *mut ::core::ffi::c_void, rc: $crate::ffi::ngx_int_t, ) -> $crate::ffi::ngx_int_t { $handler(r, data, rc) @@ -116,7 +116,7 @@ impl Request { /// Is this the main request (as opposed to a subrequest)? pub fn is_main(&self) -> bool { let main = self.0.main.cast(); - std::ptr::eq(self, main) + core::ptr::eq(self, main) } /// Request pool. @@ -337,7 +337,7 @@ impl Request { ngx_http_internal_redirect( (self as *const Request as *mut Request).cast(), uri_ptr, - std::ptr::null_mut(), + core::ptr::null_mut(), ); } } @@ -354,7 +354,7 @@ impl Request { let uri_ptr = unsafe { &mut ngx_str_t::from_str(self.0.pool, uri) as *mut _ }; // ------------- // allocate memory and set values for ngx_http_post_subrequest_t - let sub_ptr = self.pool().alloc(std::mem::size_of::()); + let sub_ptr = self.pool().alloc(core::mem::size_of::()); // assert!(sub_ptr.is_null()); let post_subreq = sub_ptr as *const ngx_http_post_subrequest_t as *mut ngx_http_post_subrequest_t; @@ -364,12 +364,12 @@ impl Request { } // ------------- - let mut psr: *mut ngx_http_request_t = std::ptr::null_mut(); + let mut psr: *mut ngx_http_request_t = core::ptr::null_mut(); let r = unsafe { ngx_http_subrequest( (self as *const Request as *mut Request).cast(), uri_ptr, - std::ptr::null_mut(), + core::ptr::null_mut(), &mut psr as *mut _, sub_ptr as *mut _, NGX_HTTP_SUBREQUEST_WAITED as _, @@ -383,7 +383,7 @@ impl Request { * allocate fake request body to avoid attempts to read it and to make * sure real body file (if already read) won't be closed by upstream */ - sr.request_body = self.pool().alloc(std::mem::size_of::()) as *mut _; + sr.request_body = self.pool().alloc(core::mem::size_of::()) as *mut _; if sr.request_body.is_null() { return Status::NGX_ERROR; @@ -393,13 +393,13 @@ impl Request { } /// Iterate over headers_in - /// each header item is (String, String) (copied) + /// each header item is (&str, &str) (borrowed) pub fn headers_in_iterator(&self) -> NgxListIterator { unsafe { list_iterator(&self.0.headers_in.headers) } } /// Iterate over headers_out - /// each header item is (String, String) (copied) + /// each header item is (&str, &str) (borrowed) pub fn headers_out_iterator(&self) -> NgxListIterator { unsafe { list_iterator(&self.0.headers_out.headers) } } @@ -422,63 +422,61 @@ impl fmt::Debug for Request { /// Iterator for [`ngx_list_t`] types. /// -/// Implementes the std::iter::Iterator trait. -pub struct NgxListIterator { - done: bool, - part: *const ngx_list_part_t, - h: *const ngx_table_elt_t, +/// Implementes the core::iter::Iterator trait. +pub struct NgxListIterator<'a> { + part: Option>, i: ngx_uint_t, } +struct ListPart<'a> { + raw: &'a ngx_list_part_t, + arr: &'a [ngx_table_elt_t], +} +impl<'a> From<&'a ngx_list_part_t> for ListPart<'a> { + fn from(raw: &'a ngx_list_part_t) -> Self { + let arr = if raw.nelts != 0 { + unsafe { slice::from_raw_parts(raw.elts.cast(), raw.nelts) } + } else { + &[] + }; + Self { raw, arr } + } +} /// Creates new HTTP header iterator /// /// # Safety /// /// The caller has provided a valid [`ngx_str_t`] which can be dereferenced validly. -pub unsafe fn list_iterator(list: *const ngx_list_t) -> NgxListIterator { - let part: *const ngx_list_part_t = &(*list).part; - +pub unsafe fn list_iterator(list: &ngx_list_t) -> NgxListIterator { NgxListIterator { - done: false, - part, - h: (*part).elts as *const ngx_table_elt_t, + part: Some((&list.part).into()), i: 0, } } // iterator for ngx_list_t -impl Iterator for NgxListIterator { - // type Item = (&str,&str); - // TODO: try to use str instead of string +impl<'a> Iterator for NgxListIterator<'a> { + // TODO: try to use struct instead of &str pair // something like pub struct Header(ngx_table_elt_t); // then header would have key and value - type Item = (String, String); + type Item = (&'a str, &'a str); fn next(&mut self) -> Option { - unsafe { - if self.done { - None + let part = self.part.as_mut()?; + if self.i >= part.arr.len() { + if let Some(next_part_raw) = unsafe { part.raw.next.as_ref() } { + // loop back + *part = next_part_raw.into(); + self.i = 0; } else { - if self.i >= (*self.part).nelts { - if (*self.part).next.is_null() { - self.done = true; - return None; - } - - // loop back - self.part = (*self.part).next; - self.h = (*self.part).elts as *mut ngx_table_elt_t; - self.i = 0; - } - - let header: *const ngx_table_elt_t = self.h.add(self.i); - let header_name: ngx_str_t = (*header).key; - let header_value: ngx_str_t = (*header).value; - self.i += 1; - Some((header_name.to_string(), header_value.to_string())) + self.part = None; + return None; } } + let header = &part.arr[self.i]; + self.i += 1; + Some((header.key.to_str(), header.value.to_str())) } } @@ -711,7 +709,8 @@ impl fmt::Display for InvalidMethod { } } -impl Error for InvalidMethod {} +#[cfg(feature = "std")] +impl std::error::Error for InvalidMethod {} #[derive(Clone, PartialEq, Eq, Hash)] enum MethodInner { diff --git a/src/http/status.rs b/src/http/status.rs index e5622125..ec77a924 100644 --- a/src/http/status.rs +++ b/src/http/status.rs @@ -1,5 +1,4 @@ -use std::error::Error; -use std::fmt; +use core::fmt; use crate::core::Status; use crate::ffi::*; @@ -25,11 +24,12 @@ impl InvalidHTTPStatusCode { impl fmt::Display for InvalidHTTPStatusCode { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str("invalid status code".to_string().as_str()) + f.write_str("invalid status code") } } -impl Error for InvalidHTTPStatusCode {} +#[cfg(feature = "std")] +impl std::error::Error for InvalidHTTPStatusCode {} impl From for Status { fn from(val: HTTPStatus) -> Self { diff --git a/src/lib.rs b/src/lib.rs index 641fec86..52f8e882 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,7 +32,12 @@ //! # now you can use dynamic modules with the NGINX //! ``` +// support both std and no_std +#![cfg_attr(not(feature = "std"), no_std)] #![warn(missing_docs)] +#[cfg(all(not(feature = "std"), feature = "alloc"))] +extern crate alloc; + /// The core module. /// /// This module provides fundamental utilities needed to interface with many NGINX primitives. @@ -54,6 +59,9 @@ pub mod http; /// The log module. /// /// This module provides an interface into the NGINX logger framework. +/// +/// This module is temporally available only with `std` feature. +#[cfg(feature = "std")] pub mod log; /// Define modules exported by this library. @@ -67,20 +75,20 @@ macro_rules! ngx_modules { #[allow(non_upper_case_globals)] pub static mut ngx_modules: [*const $crate::ffi::ngx_module_t; $crate::count!($( $mod, )+) + 1] = [ $( unsafe { &$mod } as *const $crate::ffi::ngx_module_t, )+ - ::std::ptr::null() + ::core::ptr::null() ]; #[no_mangle] #[allow(non_upper_case_globals)] - pub static mut ngx_module_names: [*const ::std::ffi::c_char; $crate::count!($( $mod, )+) + 1] = [ - $( concat!(stringify!($mod), "\0").as_ptr() as *const ::std::ffi::c_char, )+ - ::std::ptr::null() + pub static mut ngx_module_names: [*const ::core::ffi::c_char; $crate::count!($( $mod, )+) + 1] = [ + $( concat!(stringify!($mod), "\0").as_ptr() as *const ::core::ffi::c_char, )+ + ::core::ptr::null() ]; #[no_mangle] #[allow(non_upper_case_globals)] - pub static mut ngx_module_order: [*const ::std::ffi::c_char; 1] = [ - ::std::ptr::null() + pub static mut ngx_module_order: [*const ::core::ffi::c_char; 1] = [ + ::core::ptr::null() ]; }; }