diff --git a/Cargo.lock b/Cargo.lock index a6b7f398..903bdf47 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -330,6 +330,33 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + [[package]] name = "form_urlencoded" version = "1.2.2" @@ -596,6 +623,7 @@ version = "0.5.0" dependencies = [ "allocator-api2", "async-task", + "foreign-types", "lock_api", "nginx-sys", "pin-project-lite", diff --git a/Cargo.toml b/Cargo.toml index c8ab9ee2..a30b9090 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,6 +39,7 @@ targets = [] [dependencies] allocator-api2 = { version = "0.3.1", default-features = false } async-task = { version = "4.7.1", optional = true } +foreign-types = "0.5.0" lock_api = "0.4.13" nginx-sys = { path = "nginx-sys", version = "0.5.0"} pin-project-lite = { version = "0.2.16", optional = true } diff --git a/examples/async.rs b/examples/async.rs index 47f5de8e..ee2d52fe 100644 --- a/examples/async.rs +++ b/examples/async.rs @@ -4,7 +4,7 @@ use std::sync::atomic::{AtomicBool, AtomicPtr, Ordering}; use std::sync::{Arc, OnceLock}; use std::time::Instant; -use ngx::core; +use ngx::core::{self, ForeignTypeRef}; use ngx::ffi::{ ngx_array_push, ngx_command_t, ngx_conf_t, ngx_connection_t, ngx_event_t, ngx_http_handler_pt, ngx_http_module_t, ngx_http_phases_NGX_HTTP_ACCESS_PHASE, ngx_int_t, ngx_module_t, @@ -171,7 +171,7 @@ http_request_handler!(async_access_handler, |request: &mut http::Request| { unsafe { ngx_post_event(&mut ctx.event, addr_of_mut!(ngx_posted_next_events)) }; // Request is no longer needed and can be converted to something movable to the async block - let req = AtomicPtr::new(request.into()); + let req = AtomicPtr::new(request.as_ptr()); let done_flag = ctx.done.clone(); let rt = ngx_http_async_runtime(); diff --git a/examples/upstream.rs b/examples/upstream.rs index 5ea2c81b..e5b61715 100644 --- a/examples/upstream.rs +++ b/examples/upstream.rs @@ -9,7 +9,7 @@ use std::ffi::{c_char, c_void}; use std::mem; -use ngx::core::{Pool, Status}; +use ngx::core::{ForeignTypeRef, Pool, Status}; use ngx::ffi::{ ngx_atoi, ngx_command_t, ngx_conf_t, ngx_connection_t, ngx_event_free_peer_pt, ngx_event_get_peer_pt, ngx_http_module_t, ngx_http_upstream_init_peer_pt, @@ -133,7 +133,7 @@ http_upstream_init_peer_pt!( }; let original_init_peer = hccf.original_init_peer.unwrap(); - if unsafe { original_init_peer(request.into(), us) != Status::NGX_OK.into() } { + if unsafe { original_init_peer(request.as_ptr(), us) != Status::NGX_OK.into() } { return Status::NGX_ERROR; } diff --git a/src/core/mod.rs b/src/core/mod.rs index ad8264c4..0ecc6a36 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -10,6 +10,8 @@ pub use slab::SlabPool; pub use status::*; pub use string::*; +pub use foreign_types::ForeignTypeRef; + /// Gets an outer object pointer from a pointer to one of its fields. /// While there is no corresponding C macro, the pattern is common in the NGINX source. /// diff --git a/src/http/request.rs b/src/http/request.rs index 3671717c..0af31a3d 100644 --- a/src/http/request.rs +++ b/src/http/request.rs @@ -89,30 +89,21 @@ macro_rules! http_variable_get { /// requests. /// /// See -#[repr(transparent)] -pub struct Request(ngx_http_request_t); +pub struct Request(foreign_types::Opaque); -impl<'a> From<&'a Request> for *const ngx_http_request_t { - fn from(request: &'a Request) -> Self { - &request.0 as *const _ - } -} - -impl<'a> From<&'a mut Request> for *mut ngx_http_request_t { - fn from(request: &'a mut Request) -> Self { - &request.0 as *const _ as *mut _ - } +unsafe impl foreign_types::ForeignTypeRef for Request { + type CType = ngx_http_request_t; } impl AsRef for Request { fn as_ref(&self) -> &ngx_http_request_t { - &self.0 + unsafe { &*self.as_ptr() } } } impl AsMut for Request { fn as_mut(&mut self) -> &mut ngx_http_request_t { - &mut self.0 + unsafe { &mut *self.as_ptr() } } } @@ -123,20 +114,21 @@ impl Request { /// /// The caller has provided a valid non-null pointer to a valid `ngx_http_request_t` /// which shares the same representation as `Request`. + #[inline] pub unsafe fn from_ngx_http_request<'a>(r: *mut ngx_http_request_t) -> &'a mut Request { - &mut *r.cast::() + ForeignTypeRef::from_ptr_mut(r) } /// Is this the main request (as opposed to a subrequest)? pub fn is_main(&self) -> bool { - let main = self.0.main.cast(); + let main = self.as_ref().main.cast(); core::ptr::eq(self, main) } /// Request pool. pub fn pool(&self) -> Pool { // SAFETY: This request is allocated from `pool`, thus must be a valid pool. - unsafe { Pool::from_ngx_pool(self.0.pool) } + unsafe { Pool::from_ngx_pool(self.as_ref().pool) } } /// Returns the result as an `Option` if it exists, otherwise `None`. @@ -147,17 +139,17 @@ impl Request { /// [`ngx_http_upstream_t`] is best described in /// pub fn upstream(&self) -> Option<*mut ngx_http_upstream_t> { - if self.0.upstream.is_null() { + if self.as_ref().upstream.is_null() { return None; } - Some(self.0.upstream) + Some(self.as_ref().upstream) } /// Pointer to a [`ngx_connection_t`] client connection object. /// /// [`ngx_connection_t`]: https://nginx.org/en/docs/dev/development_guide.html#connection pub fn connection(&self) -> *mut ngx_connection_t { - self.0.connection + self.as_ref().connection } /// Pointer to a [`ngx_log_t`]. @@ -169,7 +161,7 @@ impl Request { /// Get Module context pointer fn get_module_ctx_ptr(&self, module: &ngx_module_t) -> *mut c_void { - unsafe { *self.0.ctx.add(module.ctx_index) } + unsafe { *self.as_ref().ctx.add(module.ctx_index) } } /// Get Module context @@ -183,9 +175,9 @@ impl Request { /// Sets the value as the module's context. /// /// See - pub fn set_module_ctx(&self, value: *mut c_void, module: &ngx_module_t) { + pub fn set_module_ctx(&mut self, value: *mut c_void, module: &ngx_module_t) { unsafe { - *self.0.ctx.add(module.ctx_index) = value; + *self.as_mut().ctx.add(module.ctx_index) = value; }; } @@ -210,15 +202,19 @@ impl Request { /// /// [request body]: https://nginx.org/en/docs/dev/development_guide.html#http_request_body pub fn discard_request_body(&mut self) -> Status { - unsafe { Status(ngx_http_discard_request_body(&mut self.0)) } + unsafe { Status(ngx_http_discard_request_body(self.as_ptr())) } } /// Client HTTP [User-Agent]. /// /// [User-Agent]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent pub fn user_agent(&self) -> Option<&NgxStr> { - if !self.0.headers_in.user_agent.is_null() { - unsafe { Some(NgxStr::from_ngx_str((*self.0.headers_in.user_agent).value)) } + if !self.as_ref().headers_in.user_agent.is_null() { + unsafe { + Some(NgxStr::from_ngx_str( + (*self.as_ref().headers_in.user_agent).value, + )) + } } else { None } @@ -226,7 +222,7 @@ impl Request { /// Set HTTP status of response. pub fn set_status(&mut self, status: HTTPStatus) { - self.0.headers_out.status = status.into(); + self.as_mut().headers_out.status = status.into(); } /// Add header to the `headers_in` object. @@ -234,8 +230,8 @@ impl Request { /// See 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 _ }; - unsafe { add_to_ngx_table(table, self.0.pool, key, value) } + unsafe { ngx_list_push(&mut self.as_mut().headers_in.headers) as _ }; + unsafe { add_to_ngx_table(table, self.as_ref().pool, key, value) } } /// Add header to the `headers_out` object. @@ -243,44 +239,44 @@ impl Request { /// See 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 _ }; - unsafe { add_to_ngx_table(table, self.0.pool, key, value) } + unsafe { ngx_list_push(&mut self.as_mut().headers_out.headers) as _ }; + unsafe { add_to_ngx_table(table, self.as_ref().pool, key, value) } } /// Set response body [Content-Length]. /// /// [Content-Length]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Length pub fn set_content_length_n(&mut self, n: usize) { - self.0.headers_out.content_length_n = n as off_t; + self.as_mut().headers_out.content_length_n = n as off_t; } /// Send the output header. /// /// Do not call this function until all output headers are set. pub fn send_header(&mut self) -> Status { - unsafe { Status(ngx_http_send_header(&mut self.0)) } + unsafe { Status(ngx_http_send_header(self.as_ptr())) } } /// Flag indicating that the output does not require a body. /// /// For example, this flag is used by `HTTP HEAD` requests. pub fn header_only(&self) -> bool { - self.0.header_only() != 0 + self.as_ref().header_only() != 0 } /// request method pub fn method(&self) -> Method { - Method::from_ngx(self.0.method) + Method::from_ngx(self.as_ref().method) } /// path part of request only pub fn path(&self) -> &NgxStr { - unsafe { NgxStr::from_ngx_str(self.0.uri) } + unsafe { NgxStr::from_ngx_str(self.as_ref().uri) } } /// full uri - containing path and args pub fn unparsed_uri(&self) -> &NgxStr { - unsafe { NgxStr::from_ngx_str(self.0.unparsed_uri) } + unsafe { NgxStr::from_ngx_str(self.as_ref().unparsed_uri) } } /// Send the [response body]. @@ -290,13 +286,13 @@ impl Request { /// /// [response body]: https://nginx.org/en/docs/dev/development_guide.html#http_request_body pub fn output_filter(&mut self, body: &mut ngx_chain_t) -> Status { - unsafe { Status(ngx_http_output_filter(&mut self.0, body)) } + unsafe { Status(ngx_http_output_filter(self.as_ptr(), 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 = unsafe { &mut ngx_str_t::from_str(self.0.pool, location) as *mut _ }; + let uri_ptr = unsafe { &mut ngx_str_t::from_str(self.as_ref().pool, location) as *mut _ }; // FIXME: check status of ngx_http_named_location or ngx_http_internal_redirect if location.starts_with('@') { @@ -326,7 +322,7 @@ impl Request { ngx_int_t, ) -> ngx_int_t, ) -> Status { - let uri_ptr = unsafe { &mut ngx_str_t::from_str(self.0.pool, uri) as *mut _ }; + let uri_ptr = unsafe { &mut ngx_str_t::from_str(self.as_ref().pool, uri) as *mut _ }; // ------------- // allocate memory and set values for ngx_http_post_subrequest_t let sub_ptr = self @@ -377,13 +373,13 @@ impl Request { /// Iterate over headers_in /// each header item is (&str, &str) (borrowed) pub fn headers_in_iterator(&self) -> NgxListIterator<'_> { - unsafe { list_iterator(&self.0.headers_in.headers) } + unsafe { list_iterator(&self.as_ref().headers_in.headers) } } /// Iterate over headers_out /// each header item is (&str, &str) (borrowed) pub fn headers_out_iterator(&self) -> NgxListIterator<'_> { - unsafe { list_iterator(&self.0.headers_out.headers) } + unsafe { list_iterator(&self.as_ref().headers_out.headers) } } } @@ -392,21 +388,21 @@ impl crate::http::HttpModuleConfExt for Request { unsafe fn http_main_conf_unchecked(&self, module: &ngx_module_t) -> Option> { // SAFETY: main_conf[module.ctx_index] is either NULL or allocated with ngx_p(c)alloc and // explicitly initialized by the module - NonNull::new((*self.0.main_conf.add(module.ctx_index)).cast()) + NonNull::new((*self.as_ref().main_conf.add(module.ctx_index)).cast()) } #[inline] unsafe fn http_server_conf_unchecked(&self, module: &ngx_module_t) -> Option> { // SAFETY: srv_conf[module.ctx_index] is either NULL or allocated with ngx_p(c)alloc and // explicitly initialized by the module - NonNull::new((*self.0.srv_conf.add(module.ctx_index)).cast()) + NonNull::new((*self.as_ref().srv_conf.add(module.ctx_index)).cast()) } #[inline] unsafe fn http_location_conf_unchecked(&self, module: &ngx_module_t) -> Option> { // SAFETY: loc_conf[module.ctx_index] is either NULL or allocated with ngx_p(c)alloc and // explicitly initialized by the module - NonNull::new((*self.0.loc_conf.add(module.ctx_index)).cast()) + NonNull::new((*self.as_ref().loc_conf.add(module.ctx_index)).cast()) } } @@ -417,7 +413,7 @@ impl crate::http::HttpModuleConfExt for Request { impl fmt::Debug for Request { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Request") - .field("request_", &self.0) + .field("request_", &self.as_ref()) .finish() } }