Skip to content

Commit 1e1b437

Browse files
committed
feat: request handler trait
1 parent 807caaa commit 1e1b437

File tree

4 files changed

+123
-35
lines changed

4 files changed

+123
-35
lines changed

examples/curl.rs

Lines changed: 27 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@ use std::ffi::{c_char, c_void};
22

33
use ngx::core;
44
use ngx::ffi::{
5-
ngx_array_push, ngx_command_t, ngx_conf_t, ngx_http_handler_pt, ngx_http_module_t,
6-
ngx_http_phases_NGX_HTTP_ACCESS_PHASE, ngx_int_t, ngx_module_t, ngx_str_t, ngx_uint_t,
5+
ngx_command_t, ngx_conf_t, ngx_http_module_t, ngx_module_t, ngx_str_t, ngx_uint_t,
76
NGX_CONF_TAKE1, NGX_HTTP_LOC_CONF, NGX_HTTP_LOC_CONF_OFFSET, NGX_HTTP_MODULE, NGX_LOG_EMERG,
87
};
9-
use ngx::http::{self, HttpModule, MergeConfigError};
10-
use ngx::http::{HttpModuleLocationConf, HttpModuleMainConf, NgxHttpCoreModule};
11-
use ngx::{http_request_handler, ngx_conf_log_error, ngx_log_debug_http, ngx_string};
8+
use ngx::http::HttpModuleLocationConf;
9+
use ngx::http::{self, HttpModule, MergeConfigError, RequestHandler};
10+
use ngx::{ngx_conf_log_error, ngx_log_debug_http, ngx_string};
1211

1312
struct Module;
1413

@@ -17,20 +16,8 @@ impl http::HttpModule for Module {
1716
unsafe { &*::core::ptr::addr_of!(ngx_http_curl_module) }
1817
}
1918

20-
unsafe extern "C" fn postconfiguration(cf: *mut ngx_conf_t) -> ngx_int_t {
21-
// SAFETY: this function is called with non-NULL cf always
22-
let cf = &mut *cf;
23-
let cmcf = NgxHttpCoreModule::main_conf_mut(cf).expect("http core main conf");
24-
25-
let h = ngx_array_push(
26-
&mut cmcf.phases[ngx_http_phases_NGX_HTTP_ACCESS_PHASE as usize].handlers,
27-
) as *mut ngx_http_handler_pt;
28-
if h.is_null() {
29-
return core::Status::NGX_ERROR.into();
30-
}
31-
// set an Access phase handler
32-
*h = Some(curl_access_handler);
33-
core::Status::NGX_OK.into()
19+
fn request_handlers() -> impl Iterator<Item = impl RequestHandler<Module = Self>> {
20+
::core::iter::once(CurlRequestHandler)
3421
}
3522
}
3623

@@ -90,25 +77,32 @@ impl http::Merge for ModuleConfig {
9077
}
9178
}
9279

93-
http_request_handler!(curl_access_handler, |request: &mut http::Request| {
94-
let co = Module::location_conf(request).expect("module config is none");
80+
struct CurlRequestHandler;
81+
82+
impl RequestHandler for CurlRequestHandler {
83+
const PHASE: nginx_sys::NgxHttpPhases = nginx_sys::NgxHttpPhases::Access;
84+
type Module = Module;
9585

96-
ngx_log_debug_http!(request, "curl module enabled: {}", co.enable);
86+
fn handler(request: &mut http::Request) -> ngx::core::NgxResult {
87+
let co = Module::location_conf(request).expect("module config is none");
9788

98-
match co.enable {
99-
true => {
100-
if request
101-
.user_agent()
102-
.is_some_and(|ua| ua.as_bytes().starts_with(b"curl"))
103-
{
104-
http::HTTPStatus::FORBIDDEN.into()
105-
} else {
106-
core::Status::NGX_DECLINED.into()
89+
ngx_log_debug_http!(request, "curl module enabled: {}", co.enable);
90+
91+
match co.enable {
92+
true => {
93+
if request
94+
.user_agent()
95+
.is_some_and(|ua| ua.as_bytes().starts_with(b"curl"))
96+
{
97+
http::HTTPStatus::FORBIDDEN.into()
98+
} else {
99+
core::Status::NGX_DECLINED.into()
100+
}
107101
}
102+
false => core::Status::NGX_DECLINED.into(),
108103
}
109-
false => core::Status::NGX_DECLINED.into(),
110104
}
111-
});
105+
}
112106

113107
extern "C" fn ngx_http_curl_commands_set_enable(
114108
cf: *mut ngx_conf_t,

nginx-sys/src/http.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,30 @@ pub const NGX_HTTP_SRV_CONF_OFFSET: usize = offset_of!(ngx_http_conf_ctx_t, srv_
1616
///
1717
/// This is used to access the location configuration context for an HTTP module.
1818
pub const NGX_HTTP_LOC_CONF_OFFSET: usize = offset_of!(ngx_http_conf_ctx_t, loc_conf);
19+
20+
/// HTTP phases in which a module can register handlers.
21+
#[repr(u32)]
22+
pub enum NgxHttpPhases {
23+
/// Post-read phase
24+
PostRead = crate::ngx_http_phases_NGX_HTTP_POST_READ_PHASE,
25+
/// Server rewrite phase
26+
ServerRewrite = crate::ngx_http_phases_NGX_HTTP_SERVER_REWRITE_PHASE,
27+
/// Find configuration phase
28+
FindConfig = crate::ngx_http_phases_NGX_HTTP_FIND_CONFIG_PHASE,
29+
/// Rewrite phase
30+
Rewrite = crate::ngx_http_phases_NGX_HTTP_REWRITE_PHASE,
31+
/// Post-rewrite phase
32+
PostRewrite = crate::ngx_http_phases_NGX_HTTP_POST_REWRITE_PHASE,
33+
/// Pre-access phase
34+
Preaccess = crate::ngx_http_phases_NGX_HTTP_PREACCESS_PHASE,
35+
/// Access phase
36+
Access = crate::ngx_http_phases_NGX_HTTP_ACCESS_PHASE,
37+
/// Post-access phase
38+
PostAccess = crate::ngx_http_phases_NGX_HTTP_POST_ACCESS_PHASE,
39+
/// Pre-content phase
40+
PreContent = crate::ngx_http_phases_NGX_HTTP_PRECONTENT_PHASE,
41+
/// Content phase
42+
Content = crate::ngx_http_phases_NGX_HTTP_CONTENT_PHASE,
43+
/// Log phase
44+
Log = crate::ngx_http_phases_NGX_HTTP_LOG_PHASE,
45+
}

src/http/module.rs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use core::ptr;
66
use crate::core::NGX_CONF_ERROR;
77
use crate::core::*;
88
use crate::ffi::*;
9+
use crate::http::RequestHandler;
910

1011
/// MergeConfigError - configuration cannot be merged with levels above.
1112
#[derive(Debug)]
@@ -52,22 +53,43 @@ pub trait HttpModule {
5253
/// Returns reference to a global variable of type [ngx_module_t] created for this module.
5354
fn module() -> &'static ngx_module_t;
5455

56+
/// Returns an iterator over request handlers provided by this module.
57+
fn request_handlers() -> impl Iterator<Item = impl RequestHandler<Module = Self>> {
58+
// returns empty iterator by default
59+
core::iter::empty::<super::EmptyHandler<Self>>()
60+
}
61+
/// Register all request handlers provided by this module.
62+
///
5563
/// # Safety
5664
///
5765
/// Callers should provide valid non-null `ngx_conf_t` arguments. Implementers must
5866
/// guard against null inputs or risk runtime errors.
59-
unsafe extern "C" fn preconfiguration(_cf: *mut ngx_conf_t) -> ngx_int_t {
67+
unsafe fn register_request_handlers(cf: *mut ngx_conf_t) -> ngx_int_t {
68+
let cf = unsafe { &mut *cf };
69+
for rh in Self::request_handlers() {
70+
if !rh.register(cf) {
71+
return Status::NGX_ERROR.into();
72+
}
73+
}
6074
Status::NGX_OK.into()
6175
}
6276

6377
/// # Safety
6478
///
6579
/// Callers should provide valid non-null `ngx_conf_t` arguments. Implementers must
6680
/// guard against null inputs or risk runtime errors.
67-
unsafe extern "C" fn postconfiguration(_cf: *mut ngx_conf_t) -> ngx_int_t {
81+
unsafe extern "C" fn preconfiguration(_cf: *mut ngx_conf_t) -> ngx_int_t {
6882
Status::NGX_OK.into()
6983
}
7084

85+
/// # Safety
86+
///
87+
/// Callers should provide valid non-null `ngx_conf_t` arguments. Implementers must
88+
/// guard against null inputs or risk runtime errors.
89+
unsafe extern "C" fn postconfiguration(cf: *mut ngx_conf_t) -> ngx_int_t {
90+
Self::register_request_handlers(cf)
91+
}
92+
7193
/// # Safety
7294
///
7395
/// Callers should provide valid non-null `ngx_conf_t` arguments. Implementers must

src/http/request.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use core::error;
22
use core::ffi::c_void;
33
use core::fmt;
4+
use core::marker::PhantomData;
45
use core::ptr::NonNull;
56
use core::slice;
67
use core::str::FromStr;
@@ -10,6 +11,7 @@ use allocator_api2::alloc::Allocator;
1011
use crate::core::*;
1112
use crate::ffi::*;
1213
use crate::http::status::*;
14+
use crate::http::{HttpModule, HttpModuleMainConf, NgxHttpCoreModule};
1315

1416
/// Define a static request handler.
1517
///
@@ -89,6 +91,49 @@ macro_rules! http_variable_get {
8991
};
9092
}
9193

94+
/// Trait for static request handler.
95+
pub trait RequestHandler {
96+
/// The phase in which the handler is invoked
97+
const PHASE: NgxHttpPhases;
98+
/// The module to which the handler belongs
99+
type Module: HttpModule + ?Sized;
100+
/// The handler function
101+
fn handler(request: &mut Request) -> NgxResult;
102+
/// Wrapper for the handler function to match the C function signature
103+
///
104+
/// # Safety
105+
/// The caller must provide a valid non-null pointer to an `ngx_http_request_t`
106+
unsafe extern "C" fn handler_wrapper(r: *mut ngx_http_request_t) -> ngx_int_t {
107+
let res: NgxResult = Self::handler(unsafe { Request::from_ngx_http_request(r) });
108+
res.unwrap_or_else(|_| Status::NGX_ERROR.into())
109+
}
110+
/// Register a request handler for a specified phase.
111+
fn register(&self, cf: &mut ngx_conf_t) -> bool {
112+
let cmcf = NgxHttpCoreModule::main_conf_mut(cf).expect("http core main conf");
113+
let h: *mut ngx_http_handler_pt =
114+
unsafe { ngx_array_push(&mut cmcf.phases[Self::PHASE as usize].handlers) as _ };
115+
if h.is_null() {
116+
return false;
117+
}
118+
// set an H::PHASE phase handler
119+
unsafe {
120+
*h = Some(Self::handler_wrapper);
121+
}
122+
true
123+
}
124+
}
125+
126+
pub(crate) struct EmptyHandler<M: HttpModule + ?Sized>(PhantomData<M>);
127+
128+
impl<M: HttpModule + ?Sized> RequestHandler for EmptyHandler<M> {
129+
const PHASE: NgxHttpPhases = NgxHttpPhases::Access;
130+
type Module = M;
131+
132+
fn handler(_request: &mut Request) -> NgxResult {
133+
Status::NGX_OK.into()
134+
}
135+
}
136+
92137
/// Wrapper struct for an [`ngx_http_request_t`] pointer, providing methods for working with HTTP
93138
/// requests.
94139
///

0 commit comments

Comments
 (0)