From 856f2ba76c64d43c5fd728631bed360996c6bdcc Mon Sep 17 00:00:00 2001 From: Ava Hahn Date: Thu, 6 Apr 2023 15:03:05 -0700 Subject: [PATCH 01/17] open up the subrequest flags so that others may be used Signed-off-by: Ava Hahn --- src/http/request.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/http/request.rs b/src/http/request.rs index 88607fdd..ed7ec743 100644 --- a/src/http/request.rs +++ b/src/http/request.rs @@ -273,6 +273,7 @@ impl Request { pub fn subrequest( &self, uri: &str, + flags: u32, module: &ngx_module_t, post_callback: unsafe extern "C" fn(*mut ngx_http_request_t, *mut c_void, ngx_int_t) -> ngx_int_t, ) -> Status { @@ -297,7 +298,7 @@ impl Request { std::ptr::null_mut(), &mut psr as *mut _, sub_ptr as *mut _, - NGX_HTTP_SUBREQUEST_WAITED as _, + flags as _, ) }; From 3f5ef754b3a543b6769ca53ac12bbb8d9c4c172d Mon Sep 17 00:00:00 2001 From: Ava Hahn Date: Thu, 6 Apr 2023 15:09:46 -0700 Subject: [PATCH 02/17] add a utility to return subrequest depth Signed-off-by: Ava Hahn --- src/http/request.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/http/request.rs b/src/http/request.rs index ed7ec743..20be0e75 100644 --- a/src/http/request.rs +++ b/src/http/request.rs @@ -269,6 +269,12 @@ impl Request { Status::NGX_DONE } + /// how many subrequests deep is this request? + /// will return 0 for a parent request. + pub fn subrequest_depth(&self) -> u32 { + NGX_HTTP_MAX_SUBREQUESTS - self.0.subrequests() + } + /// Send a subrequest pub fn subrequest( &self, From f028d367f73af9715bb0a44876a8c9804adfb98b Mon Sep 17 00:00:00 2001 From: Ava Hahn Date: Thu, 6 Apr 2023 17:17:13 -0700 Subject: [PATCH 03/17] utility for getting response status --- src/http/request.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/http/request.rs b/src/http/request.rs index 20be0e75..f3330945 100644 --- a/src/http/request.rs +++ b/src/http/request.rs @@ -191,6 +191,10 @@ impl Request { self.0.headers_out.status = status.into(); } + pub fn get_status(&self) -> HTTPStatus { + HTTPStatus(self.0.headers_out.status) + } + pub fn add_header_in(&mut self, key: &str, value: &str) -> Option<()> { let table: *mut ngx_table_elt_t = unsafe { ngx_list_push(&mut self.0.headers_in.headers) as _ }; add_to_ngx_table(table, self.0.pool, key, value) From 48920cb2df8a23d2c09b99c740f43f906a74a428 Mon Sep 17 00:00:00 2001 From: Ava Hahn Date: Mon, 10 Apr 2023 11:17:52 -0700 Subject: [PATCH 04/17] subrequest_depth should be subrequests_available to match underlying API --- src/http/request.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/http/request.rs b/src/http/request.rs index f3330945..e1408407 100644 --- a/src/http/request.rs +++ b/src/http/request.rs @@ -274,9 +274,9 @@ impl Request { } /// how many subrequests deep is this request? - /// will return 0 for a parent request. - pub fn subrequest_depth(&self) -> u32 { - NGX_HTTP_MAX_SUBREQUESTS - self.0.subrequests() + /// will return NGX_HTTP_MAX_SUBREQUESTS for a parent request. + pub fn subrequests_available(&self) -> u32 { + self.0.subrequests() } /// Send a subrequest From a3112a17734cfab2b4ae22d71ab38c433d09649a Mon Sep 17 00:00:00 2001 From: Ava Hahn Date: Mon, 10 Apr 2023 12:03:02 -0700 Subject: [PATCH 05/17] fix subrequests_available bug --- src/http/request.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/http/request.rs b/src/http/request.rs index e1408407..c11025fd 100644 --- a/src/http/request.rs +++ b/src/http/request.rs @@ -273,10 +273,15 @@ impl Request { Status::NGX_DONE } - /// how many subrequests deep is this request? + /// how many subrequests are available to make in this request /// will return NGX_HTTP_MAX_SUBREQUESTS for a parent request. pub fn subrequests_available(&self) -> u32 { - self.0.subrequests() + // 1 is subtracted because this function was caught returning 1 extra + // The return value should be (50, 0), with the parent request returning + // NGX_HTTP_MAX_SUBREQUESTS. + // See http://nginx.org/en/docs/dev/development_guide.html#http_subrequests + // for more information + self.0.subrequests() - 1 } /// Send a subrequest From 68393d1bbf25fcda31ed9ba74576c192fabe3e10 Mon Sep 17 00:00:00 2001 From: Ava Hahn Date: Tue, 11 Apr 2023 14:09:32 -0700 Subject: [PATCH 06/17] dont mess up the subrequest Signed-off-by: Ava Hahn --- src/http/request.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/http/request.rs b/src/http/request.rs index c11025fd..7208fd83 100644 --- a/src/http/request.rs +++ b/src/http/request.rs @@ -317,14 +317,8 @@ impl Request { ) }; - // previously call of ngx_http_subrequest() would ensure that the pointer is not null anymore - let mut sr = unsafe { &mut *psr }; - - /* - * 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 _; + // ngx_http_subrequest() ensures that the pointer is no longer null + let sr = unsafe { &mut *psr }; if sr.request_body.is_null() { return Status::NGX_ERROR; From 431fde3d7c20a8cba7a2b0b86245bcebc8203cc0 Mon Sep 17 00:00:00 2001 From: Ava Hahn Date: Tue, 11 Apr 2023 16:54:41 -0700 Subject: [PATCH 07/17] add getters/setters for status line Signed-off-by: Ava Hahn --- src/http/request.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/http/request.rs b/src/http/request.rs index 7208fd83..586f79bd 100644 --- a/src/http/request.rs +++ b/src/http/request.rs @@ -317,13 +317,6 @@ impl Request { ) }; - // ngx_http_subrequest() ensures that the pointer is no longer null - let sr = unsafe { &mut *psr }; - - if sr.request_body.is_null() { - return Status::NGX_ERROR; - } - sr.set_header_only(1 as _); Status(r) } From ee1335cbfbfb3172bccca94c4f91f4efff4b80b4 Mon Sep 17 00:00:00 2001 From: Ava Hahn Date: Tue, 11 Apr 2023 17:12:46 -0700 Subject: [PATCH 08/17] reveal access to output status_line Signed-off-by: Ava Hahn --- src/http/request.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/http/request.rs b/src/http/request.rs index 586f79bd..8de1d797 100644 --- a/src/http/request.rs +++ b/src/http/request.rs @@ -191,6 +191,15 @@ impl Request { self.0.headers_out.status = status.into(); } + /// Set HTTP Status Line of response + pub fn set_status_line(&mut self, status_line: ngx_str_t) { + self.0.headers_out.status_line = status_line; + } + + pub fn get_status_line(&mut self) -> &NgxStr { + unsafe {NgxStr::from_ngx_str(self.0.headers_out.status_line)} + } + pub fn get_status(&self) -> HTTPStatus { HTTPStatus(self.0.headers_out.status) } From da973dc295ff50ca17ce5857e722abc6a1fb11b9 Mon Sep 17 00:00:00 2001 From: Ava Hahn Date: Tue, 11 Apr 2023 17:49:34 -0700 Subject: [PATCH 09/17] need to accept any data Signed-off-by: Ava Hahn --- src/http/request.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/http/request.rs b/src/http/request.rs index 8de1d797..de8a013e 100644 --- a/src/http/request.rs +++ b/src/http/request.rs @@ -196,8 +196,8 @@ impl Request { self.0.headers_out.status_line = status_line; } - pub fn get_status_line(&mut self) -> &NgxStr { - unsafe {NgxStr::from_ngx_str(self.0.headers_out.status_line)} + pub fn get_status_line(&mut self) -> ngx_str_t { + self.0.headers_out.status_line.clone() } pub fn get_status(&self) -> HTTPStatus { @@ -299,6 +299,7 @@ impl Request { uri: &str, flags: u32, module: &ngx_module_t, + data: Option<*mut c_void>, post_callback: unsafe extern "C" fn(*mut ngx_http_request_t, *mut c_void, ngx_int_t) -> ngx_int_t, ) -> Status { let uri_ptr = &mut ngx_str_t::from_str(self.0.pool, uri) as *mut _; @@ -310,7 +311,12 @@ impl Request { let post_subreq = sub_ptr as *const ngx_http_post_subrequest_t as *mut ngx_http_post_subrequest_t; unsafe { (*post_subreq).handler = Some(post_callback); - (*post_subreq).data = self.get_module_ctx_ptr(module); // WARN: safety! ensure that ctx is already set + if let Some(datum) = data { + (*post_subreq).data = datum; + } else { + // WARN: safety! ensure that ctx is already set + (*post_subreq).data = self.get_module_ctx_ptr(module); + } } // ------------- From c3ebad156e7650e5bcfce8135a78fe771417ff76 Mon Sep 17 00:00:00 2001 From: Ava Hahn Date: Tue, 11 Apr 2023 18:15:28 -0700 Subject: [PATCH 10/17] this is way better than individual getters/setters Signed-off-by: Ava Hahn --- src/http/request.rs | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/src/http/request.rs b/src/http/request.rs index de8a013e..05418f71 100644 --- a/src/http/request.rs +++ b/src/http/request.rs @@ -186,24 +186,14 @@ impl Request { unsafe { NgxStr::from_ngx_str((*self.0.headers_in.user_agent).value) } } - /// Set HTTP status of response. - pub fn set_status(&mut self, status: HTTPStatus) { - self.0.headers_out.status = status.into(); - } - - /// Set HTTP Status Line of response - pub fn set_status_line(&mut self, status_line: ngx_str_t) { - self.0.headers_out.status_line = status_line; - } - - pub fn get_status_line(&mut self) -> ngx_str_t { - self.0.headers_out.status_line.clone() - } - pub fn get_status(&self) -> HTTPStatus { HTTPStatus(self.0.headers_out.status) } + pub fn get_headers_out(&self) -> *mut ngx_http_headers_out_t { + &self.0.headers_out as *const _ as *mut _ + } + pub fn add_header_in(&mut self, key: &str, value: &str) -> Option<()> { let table: *mut ngx_table_elt_t = unsafe { ngx_list_push(&mut self.0.headers_in.headers) as _ }; add_to_ngx_table(table, self.0.pool, key, value) From 250d5cedf116c70498319a48d37af4810ed3b51d Mon Sep 17 00:00:00 2001 From: Ava Hahn Date: Thu, 13 Apr 2023 16:41:51 -0700 Subject: [PATCH 11/17] add increment count, remove unsafe access to internals Signed-off-by: Ava Hahn --- src/http/request.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/http/request.rs b/src/http/request.rs index 05418f71..20d73555 100644 --- a/src/http/request.rs +++ b/src/http/request.rs @@ -190,8 +190,10 @@ impl Request { HTTPStatus(self.0.headers_out.status) } - pub fn get_headers_out(&self) -> *mut ngx_http_headers_out_t { - &self.0.headers_out as *const _ as *mut _ + pub fn increment_cycle_count(&mut self) { + self.0.set_count( + self.0.count() + 1 + ); } pub fn add_header_in(&mut self, key: &str, value: &str) -> Option<()> { From ba676db483a6f8c1db64df264c15db71c3794ac8 Mon Sep 17 00:00:00 2001 From: Ava Hahn Date: Fri, 14 Apr 2023 13:26:12 -0700 Subject: [PATCH 12/17] add event timer functions to wrapper Signed-off-by: Ava Hahn --- nginx-sys/wrapper.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/nginx-sys/wrapper.h b/nginx-sys/wrapper.h index c53f3c97..6e160d9f 100644 --- a/nginx-sys/wrapper.h +++ b/nginx-sys/wrapper.h @@ -9,3 +9,12 @@ const size_t NGX_RS_HTTP_SRV_CONF_OFFSET = NGX_HTTP_SRV_CONF_OFFSET; const size_t NGX_RS_HTTP_LOC_CONF_OFFSET = NGX_HTTP_LOC_CONF_OFFSET; const char *NGX_RS_MODULE_SIGNATURE = NGX_MODULE_SIGNATURE; + +// Wrappers for inline functions here +void ngx_add_ev_timer(ngx_event_t *ev, ngx_msec_t time) { + ngx_add_timer(ev, time); +} + +void ngx_del_ev_timer(ngx_event_t *ev) { + ngx_event_del_timer(ev); +} From 06d58c69303cac045e3a2c72401dcbe9e7e78299 Mon Sep 17 00:00:00 2001 From: Ava Hahn Date: Fri, 14 Apr 2023 16:55:41 -0700 Subject: [PATCH 13/17] add event bindings Signed-off-by: Ava Hahn --- nginx-sys/wrapper.h | 9 -------- src/core/event.rs | 52 +++++++++++++++++++++++++++++++++++++++++++++ src/core/mod.rs | 1 + 3 files changed, 53 insertions(+), 9 deletions(-) create mode 100644 src/core/event.rs diff --git a/nginx-sys/wrapper.h b/nginx-sys/wrapper.h index 6e160d9f..c53f3c97 100644 --- a/nginx-sys/wrapper.h +++ b/nginx-sys/wrapper.h @@ -9,12 +9,3 @@ const size_t NGX_RS_HTTP_SRV_CONF_OFFSET = NGX_HTTP_SRV_CONF_OFFSET; const size_t NGX_RS_HTTP_LOC_CONF_OFFSET = NGX_HTTP_LOC_CONF_OFFSET; const char *NGX_RS_MODULE_SIGNATURE = NGX_MODULE_SIGNATURE; - -// Wrappers for inline functions here -void ngx_add_ev_timer(ngx_event_t *ev, ngx_msec_t time) { - ngx_add_timer(ev, time); -} - -void ngx_del_ev_timer(ngx_event_t *ev) { - ngx_event_del_timer(ev); -} diff --git a/src/core/event.rs b/src/core/event.rs new file mode 100644 index 00000000..c3853f6e --- /dev/null +++ b/src/core/event.rs @@ -0,0 +1,52 @@ +use crate::ffi::*; + +#[repr(transparent)] +pub struct Event(ngx_event_t); +impl Event { + pub fn add_timer(&mut self, timer: ngx_msec_t) { + let key: ngx_msec_int_t = unsafe {ngx_current_msec as isize + timer as isize}; + if self.0.timer_set() == 0 { + /* FROM NGX: + * Use a previous timer value if difference between it and a new + * value is less than NGX_TIMER_LAZY_DELAY milliseconds: this allows + * to minimize the rbtree operations for fast connections. + */ + let diff = key - self.0.timer.key as ngx_msec_int_t; + if diff.abs() < NGX_TIMER_LAZY_DELAY as isize { + // TODO add debugging macro + return; + } + + self.del_timer(); + } + + self.0.timer.key = key as ngx_msec_t; + // TODO add debugging macro + unsafe { + ngx_rbtree_insert( + &mut ngx_event_timer_rbtree as *mut _, + &mut self.0.timer as *mut _, + ); + } + + self.0.set_timer_set(1); + } + + pub fn del_timer(&mut self) { + unsafe { + ngx_rbtree_delete( + &mut ngx_event_timer_rbtree as *mut _, + &mut self.0.timer as *mut _, + ); + } + + self.0.set_timer_set(0); + } +} + +impl From<*mut ngx_event_t> for &mut Event { + fn from(evt: *mut ngx_event_t) -> Self { + unsafe {&mut *evt.cast::()} + } +} + diff --git a/src/core/mod.rs b/src/core/mod.rs index a91a4969..43bb210c 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -2,6 +2,7 @@ mod buffer; mod pool; mod status; mod string; +mod event; pub use buffer::*; pub use pool::*; From 1214a900ff8f8b792b2a8b992c85c9538196538d Mon Sep 17 00:00:00 2001 From: Ava Hahn Date: Fri, 14 Apr 2023 17:09:17 -0700 Subject: [PATCH 14/17] extend interface for event binding Signed-off-by: Ava Hahn --- src/core/event.rs | 7 ++++++- src/core/mod.rs | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/core/event.rs b/src/core/event.rs index c3853f6e..aaae2e12 100644 --- a/src/core/event.rs +++ b/src/core/event.rs @@ -1,7 +1,8 @@ use crate::ffi::*; #[repr(transparent)] -pub struct Event(ngx_event_t); +pub struct Event(pub ngx_event_t); + impl Event { pub fn add_timer(&mut self, timer: ngx_msec_t) { let key: ngx_msec_int_t = unsafe {ngx_current_msec as isize + timer as isize}; @@ -42,6 +43,10 @@ impl Event { self.0.set_timer_set(0); } + + pub unsafe fn new_for_request<'a>(req: &'a mut crate::http::Request) -> &'a mut Event { + &mut *(req.pool().alloc(std::mem::size_of::()) as *mut Event) + } } impl From<*mut ngx_event_t> for &mut Event { diff --git a/src/core/mod.rs b/src/core/mod.rs index 43bb210c..0876439e 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -8,6 +8,7 @@ pub use buffer::*; pub use pool::*; pub use status::*; pub use string::*; +pub use event::*; /// Static empty configuration directive initializer for [`ngx_command_t`]. /// From e2c8b5098b7bae1c7d1fdc0900dc8cbb98788b26 Mon Sep 17 00:00:00 2001 From: Ava Hahn Date: Fri, 14 Apr 2023 17:15:13 -0700 Subject: [PATCH 15/17] add Into impelementation for convenience Signed-off-by: Ava Hahn --- src/core/event.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/core/event.rs b/src/core/event.rs index aaae2e12..f26549fe 100644 --- a/src/core/event.rs +++ b/src/core/event.rs @@ -6,7 +6,7 @@ pub struct Event(pub ngx_event_t); impl Event { pub fn add_timer(&mut self, timer: ngx_msec_t) { let key: ngx_msec_int_t = unsafe {ngx_current_msec as isize + timer as isize}; - if self.0.timer_set() == 0 { + if self.0.timer_set() != 0 { /* FROM NGX: * Use a previous timer value if difference between it and a new * value is less than NGX_TIMER_LAZY_DELAY milliseconds: this allows @@ -44,7 +44,7 @@ impl Event { self.0.set_timer_set(0); } - pub unsafe fn new_for_request<'a>(req: &'a mut crate::http::Request) -> &'a mut Event { + pub unsafe fn new_for_request(req: &crate::http::Request) -> &mut Event { &mut *(req.pool().alloc(std::mem::size_of::()) as *mut Event) } } @@ -55,3 +55,8 @@ impl From<*mut ngx_event_t> for &mut Event { } } +impl Into<*mut ngx_event_t> for &mut Event { + fn into(self) -> *mut ngx_event_t { + &mut self.0 as *mut ngx_event_t + } +} From ff337b9287719a6a359b988158c0bba1dc712830 Mon Sep 17 00:00:00 2001 From: Ava Hahn Date: Tue, 18 Apr 2023 15:39:54 -0700 Subject: [PATCH 16/17] implement post_to_queue Signed-off-by: Ava Hahn --- src/core/event.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/core/event.rs b/src/core/event.rs index f26549fe..923c8c8c 100644 --- a/src/core/event.rs +++ b/src/core/event.rs @@ -44,6 +44,20 @@ impl Event { self.0.set_timer_set(0); } + /// translated from ngx_post_event macro + pub fn post_to_queue(&mut self, queue: *mut ngx_queue_t) { + if self.0.posted() == 0{ + self.0.set_posted(1); + unsafe { + // translated from ngx_queue_insert_tail macro + self.0.queue.prev = (*queue).prev; + (*self.0.queue.prev).next = &self.0.queue as *const _ as *mut _; + self.0.queue.next = queue; + (*queue).prev = &self.0.queue as *const _ as *mut _; + } + } + } + pub unsafe fn new_for_request(req: &crate::http::Request) -> &mut Event { &mut *(req.pool().alloc(std::mem::size_of::()) as *mut Event) } From 053d88a1f69ef5b8cff5e27e23507ad11a4855ee Mon Sep 17 00:00:00 2001 From: Matthew Yacobucci Date: Mon, 12 Jun 2023 08:56:37 -0600 Subject: [PATCH 17/17] Code review notes - New log macro that accepts NGX_LOG_DEBUG bitmask filters - Add logging to events - Updated internal_redirect to call request methods: - ngx_named_location, ngx_internal_redirect which accepts args - Subrequest flags for masking and state - Subrequest accepts query parameters as the arg flag - Quelling lint errors and warnings - Quelling fmt workflow errors --- Cargo.lock | 2 +- Cargo.toml | 2 +- nginx-sys/src/lib.rs | 35 ++++---- src/core/event.rs | 96 +++++++++++++++------- src/core/mod.rs | 4 +- src/http/flags.rs | 187 +++++++++++++++++++++++++++++++++++++++++++ src/http/mod.rs | 2 + src/http/request.rs | 100 ++++++++++++++++------- src/log.rs | 15 ++++ 9 files changed, 368 insertions(+), 75 deletions(-) create mode 100644 src/http/flags.rs diff --git a/Cargo.lock b/Cargo.lock index 25ee3863..a69aae9a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -456,7 +456,7 @@ dependencies = [ [[package]] name = "ngx" -version = "0.3.0-beta" +version = "0.4.0-beta" dependencies = [ "nginx-sys", ] diff --git a/Cargo.toml b/Cargo.toml index 6eac1e04..6fed388b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ members = [ [package] name = "ngx" -version = "0.3.0-beta" +version = "0.4.0-beta" edition = "2021" autoexamples = false categories = ["api-bindings", "network-programming"] diff --git a/nginx-sys/src/lib.rs b/nginx-sys/src/lib.rs index ad1c5d4c..cdfd38d5 100644 --- a/nginx-sys/src/lib.rs +++ b/nginx-sys/src/lib.rs @@ -70,11 +70,9 @@ pub use bindings::*; /// let data: &str = "example"; // The string to convert /// let ptr = str_to_uchar(pool, data); /// ``` -pub fn str_to_uchar(pool: *mut ngx_pool_t, data: &str) -> *mut u_char { +pub unsafe fn str_to_uchar(pool: *mut ngx_pool_t, data: &str) -> *mut u_char { let ptr: *mut u_char = unsafe { ngx_palloc(pool, data.len() as _) as _ }; - unsafe { - copy_nonoverlapping(data.as_ptr(), ptr, data.len()); - } + copy_nonoverlapping(data.as_ptr(), ptr, data.len()); ptr } @@ -94,14 +92,6 @@ impl ngx_str_t { } } - /// Convert the nginx string to a `String` by copying its contents. - /// - /// # Returns - /// A new `String` containing the contents of the nginx string. - pub fn to_string(&self) -> String { - return String::from(self.to_str()); - } - /// Create an `ngx_str_t` instance from a `String`. /// /// # Arguments @@ -109,9 +99,13 @@ impl ngx_str_t { /// * `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 passes a raw pointer (pool) to another unsafe + /// function which allocates a buffer from the pool. + /// /// # Returns /// An `ngx_str_t` instance representing the given `String`. - pub fn from_string(pool: *mut ngx_pool_t, data: String) -> Self { + 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() as _, @@ -125,9 +119,13 @@ impl ngx_str_t { /// * `pool` - A pointer to the nginx memory pool (`ngx_pool_t`). /// * `data` - The string slice from which to create the nginx string. /// + /// # Safety + /// This function is marked as unsafe because it passes a raw pointer (pool) to another unsafe + /// function which allocates a buffer from the pool. + /// /// # Returns /// An `ngx_str_t` instance representing the given string slice. - pub fn from_str(pool: *mut ngx_pool_t, data: &str) -> Self { + pub unsafe fn from_str(pool: *mut ngx_pool_t, data: &str) -> Self { ngx_str_t { data: str_to_uchar(pool, data), len: data.len() as _, @@ -190,11 +188,16 @@ impl TryFrom for &str { /// let value: &str = "value"; // The value to add /// let result = add_to_ngx_table(table, pool, key, value); /// ``` -pub fn add_to_ngx_table(table: *mut ngx_table_elt_t, pool: *mut ngx_pool_t, key: &str, value: &str) -> Option<()> { +pub unsafe fn add_to_ngx_table( + table: *mut ngx_table_elt_t, + pool: *mut ngx_pool_t, + key: &str, + value: &str, +) -> Option<()> { if table.is_null() { return None; } - unsafe { table.as_mut() }.map(|table| { + table.as_mut().map(|table| { table.hash = 1; table.key.len = key.len() as _; table.key.data = str_to_uchar(pool, key); diff --git a/src/core/event.rs b/src/core/event.rs index 923c8c8c..fd68e77e 100644 --- a/src/core/event.rs +++ b/src/core/event.rs @@ -1,11 +1,20 @@ use crate::ffi::*; +use crate::ngx_log_debug_mask; +/// Wrapper struct for an `ngx_event_t` pointer, provides an interface into timer methods. #[repr(transparent)] pub struct Event(pub ngx_event_t); impl Event { - pub fn add_timer(&mut self, timer: ngx_msec_t) { - let key: ngx_msec_int_t = unsafe {ngx_current_msec as isize + timer as isize}; + #[inline] + fn ident(&self) -> i32 { + let conn = self.0.data as *const ngx_connection_t; + unsafe { (*conn).fd } + } + + /// Adds a timer to this event. Argument `timer` is in milliseconds. + pub fn add_timer(&mut self, timer_msec: ngx_msec_t) { + let key: ngx_msec_int_t = unsafe { ngx_current_msec as isize + timer_msec as isize }; if self.0.timer_set() != 0 { /* FROM NGX: * Use a previous timer value if difference between it and a new @@ -14,7 +23,14 @@ impl Event { */ let diff = key - self.0.timer.key as ngx_msec_int_t; if diff.abs() < NGX_TIMER_LAZY_DELAY as isize { - // TODO add debugging macro + ngx_log_debug_mask!( + NGX_LOG_DEBUG_EVENT, + self.0.log, + "event time: {}, old: {:?}, new: {:?}", + self.ident(), + self.0.timer.key, + key + ); return; } @@ -22,55 +38,79 @@ impl Event { } self.0.timer.key = key as ngx_msec_t; - // TODO add debugging macro + ngx_log_debug_mask!( + NGX_LOG_DEBUG_EVENT, + self.0.log, + "event time: {}, old: {:?}, new: {:?}", + self.ident(), + self.0.timer.key, + key + ); unsafe { - ngx_rbtree_insert( - &mut ngx_event_timer_rbtree as *mut _, - &mut self.0.timer as *mut _, - ); + ngx_rbtree_insert(&mut ngx_event_timer_rbtree as *mut _, &mut self.0.timer as *mut _); } self.0.set_timer_set(1); } + /// Deletes an associated timer from this event. pub fn del_timer(&mut self) { + ngx_log_debug_mask!( + NGX_LOG_DEBUG_EVENT, + self.0.log, + "event timer del: {}:{:?}", + self.ident(), + self.0.timer.key + ); unsafe { - ngx_rbtree_delete( - &mut ngx_event_timer_rbtree as *mut _, - &mut self.0.timer as *mut _, - ); + ngx_rbtree_delete(&mut ngx_event_timer_rbtree as *mut _, &mut self.0.timer as *mut _); } self.0.set_timer_set(0); } - /// translated from ngx_post_event macro - pub fn post_to_queue(&mut self, queue: *mut ngx_queue_t) { - if self.0.posted() == 0{ + /// Add event to processing queue. Translated from ngx_post_event macro. + /// + /// # Safety + /// This function is marked unsafe because it dereferences a raw pointer. The pointer (queue) + /// MUST NOT be null to satisfy its contract, will panic with null input. + /// + /// # Panics + /// Panics if the given queue is null. + pub unsafe fn post_to_queue(&mut self, queue: *mut ngx_queue_t) { + assert!(!queue.is_null(), "queue is empty"); + if self.0.posted() == 0 { self.0.set_posted(1); - unsafe { - // translated from ngx_queue_insert_tail macro - self.0.queue.prev = (*queue).prev; - (*self.0.queue.prev).next = &self.0.queue as *const _ as *mut _; - self.0.queue.next = queue; - (*queue).prev = &self.0.queue as *const _ as *mut _; - } + // translated from ngx_queue_insert_tail macro + self.0.queue.prev = (*queue).prev; + (*self.0.queue.prev).next = &self.0.queue as *const _ as *mut _; + self.0.queue.next = queue; + (*queue).prev = &self.0.queue as *const _ as *mut _; } } - pub unsafe fn new_for_request(req: &crate::http::Request) -> &mut Event { - &mut *(req.pool().alloc(std::mem::size_of::()) as *mut Event) + /// new_for_request creates an new Event (ngx_event_t) from the Request pool. + /// + /// # Safety + /// This function is marked as unsafe because it involves dereferencing a raw pointer memory + /// allocation from the underlying Nginx pool allocator. + /// + /// # Returns + /// An `Option<&mut Event>` representing the result of the allocation. `Some(&mut Event)` + /// indicates successful allocation, while `None` indicates a null Event. + pub unsafe fn new_for_request(req: &mut crate::http::Request) -> Option<&mut Event> { + Some(&mut *(req.pool().alloc(std::mem::size_of::()) as *mut Event)) } } impl From<*mut ngx_event_t> for &mut Event { fn from(evt: *mut ngx_event_t) -> Self { - unsafe {&mut *evt.cast::()} + unsafe { &mut *evt.cast::() } } } -impl Into<*mut ngx_event_t> for &mut Event { - fn into(self) -> *mut ngx_event_t { - &mut self.0 as *mut ngx_event_t +impl From<&mut Event> for *mut ngx_event_t { + fn from(val: &mut Event) -> Self { + &mut val.0 as *mut ngx_event_t } } diff --git a/src/core/mod.rs b/src/core/mod.rs index 0876439e..b2571ed3 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -1,14 +1,14 @@ mod buffer; +mod event; mod pool; mod status; mod string; -mod event; pub use buffer::*; +pub use event::*; pub use pool::*; pub use status::*; pub use string::*; -pub use event::*; /// Static empty configuration directive initializer for [`ngx_command_t`]. /// diff --git a/src/http/flags.rs b/src/http/flags.rs new file mode 100644 index 00000000..99283533 --- /dev/null +++ b/src/http/flags.rs @@ -0,0 +1,187 @@ +use crate::ffi::ngx_uint_t; +use std::{ + mem, + ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not}, +}; + +/// SubrequestFlags is a bitmask for NGINX subrequest control. +/// Refer to https://nginx.org/en/docs/dev/development_guide.html#http_subrequests for more +/// details. +/// +/// The following flags are available: +/// None: Zero value of the subrequest flag. +/// InMemory - Output is not sent to the client, but rather stored in memory. The flag only affects subrequests which are processed by one of the proxying modules. After a subrequest is finalized its output is available in r->out of type ngx_buf_t. +/// Waited - The subrequest's done flag is set even if the subrequest is not active when it is finalized. This subrequest flag is used by the SSI filter. +/// Clone - The subrequest is created as a clone of its parent. It is started at the same location and proceeds from the same phase as the parent request. +/// Background - The subrequest operates in the background (useful for background cache updates), this type of subrequest does not block any other subrequests or the main request. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(u32)] +pub enum SubrequestFlags { + /// None: Zero value of the subrequest flag. + None = 0, + // unused = 1 ngx_http_request.h:65 + /// InMemory - Output is not sent to the client, but rather stored in memory. The flag only affects subrequests which are processed by one of the proxying modules. After a subrequest is finalized its output is available in r->out of type ngx_buf_t. + InMemory = 2, + /// Waited - The subrequest's done flag is set even if the subrequest is not active when it is finalized. This subrequest flag is used by the SSI filter. + Waited = 4, + /// Clone - The subrequest is created as a clone of its parent. It is started at the same location and proceeds from the same phase as the parent request. + Clone = 8, + /// Background - The subrequest operates in the background (useful for background cache updates), this type of subrequest does not block any other subrequests or the main request. + Background = 16, +} + +impl BitAnd for SubrequestFlags { + type Output = Self; + + #[inline] + fn bitand(self, rhs: Self) -> Self { + unsafe { mem::transmute(self as u32 & rhs as u32) } + } +} + +impl BitAndAssign for SubrequestFlags { + #[inline] + fn bitand_assign(&mut self, rhs: Self) { + *self = *self & rhs; + } +} + +impl BitOr for SubrequestFlags { + type Output = Self; + + #[inline] + fn bitor(self, rhs: Self) -> Self { + unsafe { mem::transmute(self as u32 | rhs as u32) } + } +} + +impl BitOrAssign for SubrequestFlags { + #[inline] + fn bitor_assign(&mut self, rhs: Self) { + *self = *self | rhs; + } +} + +impl BitXor for SubrequestFlags { + type Output = Self; + + #[inline] + fn bitxor(self, rhs: Self) -> Self { + unsafe { std::mem::transmute(self as u32 ^ rhs as u32) } + } +} + +impl BitXorAssign for SubrequestFlags { + #[inline] + fn bitxor_assign(&mut self, rhs: Self) { + *self = *self ^ rhs; + } +} + +impl Not for SubrequestFlags { + type Output = Self; + + #[inline] + fn not(self) -> Self { + unsafe { std::mem::transmute(!(self as u32)) } + } +} + +impl From for u32 { + #[inline] + fn from(flags: SubrequestFlags) -> Self { + flags as u32 + } +} + +impl From for ngx_uint_t { + #[inline] + fn from(flags: SubrequestFlags) -> Self { + flags as ngx_uint_t + } +} + +impl SubrequestFlags { + /// Tests if flag(s) are set on the SubrequestFlags. + #[inline] + pub fn has_flag(&self, flag: SubrequestFlags) -> bool { + (*self as u32 & flag as u32) != 0 + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_and() { + let mut flag: SubrequestFlags = SubrequestFlags::Background; + assert_eq!(flag.has_flag(SubrequestFlags::Background), true); + + let test_flag = flag & SubrequestFlags::InMemory; + assert_eq!(test_flag, SubrequestFlags::None); + assert_eq!(test_flag.has_flag(SubrequestFlags::Background), false); + assert_eq!(test_flag.has_flag(SubrequestFlags::InMemory), false); + + flag &= SubrequestFlags::Clone; + assert_eq!(flag, SubrequestFlags::None); + assert_eq!(flag.has_flag(SubrequestFlags::Clone), false); + assert_eq!(flag.has_flag(SubrequestFlags::Background), false); + } + + #[test] + fn test_or() { + let mut flag: SubrequestFlags = SubrequestFlags::Background; + assert_eq!(flag.has_flag(SubrequestFlags::Background), true); + + let test_flag = flag | SubrequestFlags::InMemory; + assert_eq!(test_flag as u32, 18); + assert_eq!(test_flag.has_flag(SubrequestFlags::Background), true); + assert_eq!(test_flag.has_flag(SubrequestFlags::InMemory), true); + assert_eq!(test_flag.has_flag(SubrequestFlags::Clone), false); + + flag |= SubrequestFlags::Clone; + assert_eq!(flag as u32, 24); + assert_eq!(flag.has_flag(SubrequestFlags::Background), true); + assert_eq!(flag.has_flag(SubrequestFlags::Clone), true); + assert_eq!(flag.has_flag(SubrequestFlags::InMemory), false); + } + + #[test] + fn test_xor() { + let mut flag: SubrequestFlags = SubrequestFlags::Background | SubrequestFlags::InMemory; + assert_eq!(flag as u32, 18); + + let test_flag = flag ^ SubrequestFlags::Background; + assert_eq!(test_flag, SubrequestFlags::InMemory); + assert_eq!(test_flag.has_flag(SubrequestFlags::Background), false); + assert_eq!(test_flag.has_flag(SubrequestFlags::Clone), false); + assert_eq!(test_flag.has_flag(SubrequestFlags::InMemory), true); + + flag ^= SubrequestFlags::Background; + assert_eq!(flag, SubrequestFlags::InMemory); + assert_eq!(flag.has_flag(SubrequestFlags::Background), false); + assert_eq!(flag.has_flag(SubrequestFlags::Clone), false); + assert_eq!(flag.has_flag(SubrequestFlags::InMemory), true); + + flag ^= SubrequestFlags::Clone; + assert_eq!(flag as u32, 10); + assert_eq!(flag.has_flag(SubrequestFlags::Background), false); + assert_eq!(flag.has_flag(SubrequestFlags::Clone), true); + assert_eq!(flag.has_flag(SubrequestFlags::InMemory), true); + } + + #[test] + fn test_not() { + let flag: SubrequestFlags = SubrequestFlags::Background | SubrequestFlags::InMemory; + assert_eq!(flag as u32, 18); + + let test_flag: SubrequestFlags = flag & !SubrequestFlags::Background; + assert_eq!(test_flag, SubrequestFlags::InMemory); + + assert_eq!(!SubrequestFlags::InMemory as i32, -3); + assert_eq!(!SubrequestFlags::Waited as i32, -5); + assert_eq!(!SubrequestFlags::Clone as i32, -9); + assert_eq!(!SubrequestFlags::Background as i32, -17); + } +} diff --git a/src/http/mod.rs b/src/http/mod.rs index 230ce0b5..6e23a0a5 100644 --- a/src/http/mod.rs +++ b/src/http/mod.rs @@ -1,9 +1,11 @@ mod conf; +mod flags; mod module; mod request; mod status; pub use conf::*; +pub use flags::*; pub use module::*; pub use request::*; pub use status::*; diff --git a/src/http/request.rs b/src/http/request.rs index 20d73555..3d5aca27 100644 --- a/src/http/request.rs +++ b/src/http/request.rs @@ -1,5 +1,6 @@ use crate::core::*; use crate::ffi::*; +use crate::http::flags::SubrequestFlags; use crate::http::status::*; use crate::ngx_null_string; use std::fmt; @@ -186,24 +187,35 @@ impl Request { unsafe { NgxStr::from_ngx_str((*self.0.headers_in.user_agent).value) } } + /// Set HTTP status of response. + pub fn set_status(&mut self, status: HTTPStatus) { + self.0.headers_out.status = status.into(); + } + + /// Get HTTP status of response. pub fn get_status(&self) -> HTTPStatus { HTTPStatus(self.0.headers_out.status) } + /// Add one to the request's current cycle count. pub fn increment_cycle_count(&mut self) { - self.0.set_count( - self.0.count() + 1 - ); + self.0.set_count(self.0.count() + 1); } + /// Add header key and value to the input headers object. + /// + /// See https://nginx.org/en/docs/dev/development_guide.html#http_request `headers_in`. pub fn add_header_in(&mut self, key: &str, value: &str) -> Option<()> { let table: *mut ngx_table_elt_t = unsafe { ngx_list_push(&mut self.0.headers_in.headers) as _ }; - add_to_ngx_table(table, self.0.pool, key, value) + unsafe { add_to_ngx_table(table, self.0.pool, key, value) } } + /// Add header key and value to the output headers object. + /// + /// See https://nginx.org/en/docs/dev/development_guide.html#http_request `headers_out`. pub fn add_header_out(&mut self, key: &str, value: &str) -> Option<()> { let table: *mut ngx_table_elt_t = unsafe { ngx_list_push(&mut self.0.headers_out.headers) as _ }; - add_to_ngx_table(table, self.0.pool, key, value) + unsafe { add_to_ngx_table(table, self.0.pool, key, value) } } /// Set response body [Content-Length]. @@ -252,29 +264,61 @@ impl Request { unsafe { Status(ngx_http_output_filter(&mut self.0, body)) } } - /// Perform internal redirect to a location - pub fn internal_redirect(&self, location: &str) -> Status { - assert!(!location.is_empty(), "uri location is empty"); - let uri_ptr = &mut ngx_str_t::from_str(self.0.pool, location) as *mut _; - - // FIXME: check status of ngx_http_named_location or ngx_http_internal_redirect + /// Utility method to perform an internal redirect without args (query parameters) or named + /// location. + /// For full control methods see `ngx_internal_redirect` and `ngx_named_location`. + /// + /// # Safety + /// + /// This method invokes unsafe methods. + pub unsafe fn internal_redirect(&self, location: &str) -> Status { if location.starts_with('@') { - unsafe { - ngx_http_named_location((self as *const Request as *mut Request).cast(), uri_ptr); - } + self.ngx_named_location(location) } else { - unsafe { - ngx_http_internal_redirect( - (self as *const Request as *mut Request).cast(), - uri_ptr, - std::ptr::null_mut(), - ); - } + self.ngx_internal_redirect(location, "") } - Status::NGX_DONE } - /// how many subrequests are available to make in this request + /// Invoke ngx_internal_redirect to perform an internal redirect to a location. + /// + /// # Safety + /// + /// This method calls into unsafe functions on the stack and dereferences raw pointers to + /// interface with NGINX API primitives. + pub unsafe fn ngx_internal_redirect(&self, location: &str, args: &str) -> Status { + assert!(!location.is_empty(), "uri location is empty"); + let uri_ptr = &mut ngx_str_t::from_str(self.0.pool, location) as *mut _; + let args_ptr = if !args.is_empty() { + &mut ngx_str_t::from_str(self.0.pool, location) as *mut _ + } else { + std::ptr::null_mut() + }; + + Status(ngx_http_internal_redirect( + (self as *const Request as *mut Request).cast(), + uri_ptr, + args_ptr, + )) + } + + /// Invoke ngx_named_location to perform an internal redirect to a named location. + /// + /// # Safety + /// + /// This method calls into unsafe functions on the stack and dereferences raw pointers to + /// interface with NGINX API primitives. + pub unsafe fn ngx_named_location(&self, location: &str) -> Status { + assert!(!location.is_empty(), "uri location is empty"); + assert!(location.starts_with('@'), "named location must start with @"); + let uri_ptr = &mut ngx_str_t::from_str(self.0.pool, location) as *mut _; + + Status(ngx_http_named_location( + (self as *const Request as *mut Request).cast(), + uri_ptr, + )) + } + + /// How many subrequests are available to make in this request, /// will return NGX_HTTP_MAX_SUBREQUESTS for a parent request. pub fn subrequests_available(&self) -> u32 { // 1 is subtracted because this function was caught returning 1 extra @@ -289,12 +333,14 @@ impl Request { pub fn subrequest( &self, uri: &str, - flags: u32, + args: &str, + flags: SubrequestFlags, module: &ngx_module_t, data: Option<*mut c_void>, post_callback: unsafe extern "C" fn(*mut ngx_http_request_t, *mut c_void, ngx_int_t) -> ngx_int_t, ) -> Status { - let uri_ptr = &mut ngx_str_t::from_str(self.0.pool, uri) as *mut _; + let uri_ptr = unsafe { &mut ngx_str_t::from_str(self.0.pool, uri) as *mut _ }; + let args_ptr = unsafe { &mut ngx_str_t::from_str(self.0.pool, args) as *mut _ }; // ------------- // allocate memory and set values for ngx_http_post_subrequest_t let sub_ptr = self.pool().alloc(std::mem::size_of::()); @@ -317,10 +363,10 @@ impl Request { ngx_http_subrequest( (self as *const Request as *mut Request).cast(), uri_ptr, - std::ptr::null_mut(), + args_ptr, &mut psr as *mut _, sub_ptr as *mut _, - flags as _, + flags.into(), ) }; diff --git a/src/log.rs b/src/log.rs index 37d4ac4a..7ad76262 100644 --- a/src/log.rs +++ b/src/log.rs @@ -27,3 +27,18 @@ macro_rules! ngx_log_debug_http { $crate::ngx_log_debug!(log, $($arg)*); } } + +#[macro_export] +macro_rules! ngx_log_debug_mask { + ( $mask:expr, $log:expr, $($arg:tt)* ) => { + let log_level = unsafe { (*$log).log_level }; + if log_level & $mask as usize != 0 { + let level = $mask as $crate::ffi::ngx_uint_t; + let fmt = ::std::ffi::CString::new("%s").unwrap(); + let c_message = ::std::ffi::CString::new(format!($($arg)*)).unwrap(); + unsafe { + $crate::ffi::ngx_log_error_core(level, $log, 0, fmt.as_ptr(), c_message.as_ptr()); + } + } + } +}