Skip to content

Commit a9a8ae6

Browse files
committed
feat: request handler trait
1 parent 9bfaba3 commit a9a8ae6

File tree

4 files changed

+151
-35
lines changed

4 files changed

+151
-35
lines changed

examples/curl.rs

Lines changed: 29 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ 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_int_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::{
9+
self, HttpHandlerRegistrar, HttpModule, HttpModuleLocationConf, HttpRequestHandler,
10+
MergeConfigError,
11+
};
12+
use ngx::{ngx_conf_log_error, ngx_log_debug_http, ngx_string};
1213

1314
struct Module;
1415

@@ -17,20 +18,8 @@ impl http::HttpModule for Module {
1718
unsafe { &*::core::ptr::addr_of!(ngx_http_curl_module) }
1819
}
1920

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()
21+
fn request_handler_registrars<'cf>() -> impl Iterator<Item = fn(&'cf mut ngx_conf_t) -> bool> {
22+
::core::iter::once(CurlRequestHandler::register as _)
3423
}
3524
}
3625

@@ -90,25 +79,32 @@ impl http::Merge for ModuleConfig {
9079
}
9180
}
9281

93-
http_request_handler!(curl_access_handler, |request: &mut http::Request| {
94-
let co = Module::location_conf(request).expect("module config is none");
82+
struct CurlRequestHandler;
9583

96-
ngx_log_debug_http!(request, "curl module enabled: {}", co.enable);
84+
impl HttpRequestHandler<Option<ngx_int_t>> for CurlRequestHandler {
85+
const PHASE: nginx_sys::NgxHttpPhases = nginx_sys::NgxHttpPhases::Access;
86+
type Module = Module;
9787

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::NGX_O_DECLINED
88+
fn handler(request: &mut http::Request) -> Option<ngx_int_t> {
89+
let co = Module::location_conf(request).expect("module config is none");
90+
91+
ngx_log_debug_http!(request, "curl module enabled: {}", co.enable);
92+
93+
match co.enable {
94+
true => {
95+
if request
96+
.user_agent()
97+
.is_some_and(|ua| ua.as_bytes().starts_with(b"curl"))
98+
{
99+
http::HTTPStatus::FORBIDDEN.into()
100+
} else {
101+
core::NGX_O_DECLINED
102+
}
107103
}
104+
false => core::NGX_O_DECLINED,
108105
}
109-
false => core::NGX_O_DECLINED,
110106
}
111-
});
107+
}
112108

113109
extern "C" fn ngx_http_curl_commands_set_enable(
114110
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: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,22 +52,43 @@ pub trait HttpModule {
5252
/// Returns reference to a global variable of type [ngx_module_t] created for this module.
5353
fn module() -> &'static ngx_module_t;
5454

55+
/// Returns an iterator over request handler registrar functions provided by this module.
56+
fn request_handler_registrars<'cf>() -> impl Iterator<Item = fn(&'cf mut ngx_conf_t) -> bool> {
57+
core::iter::empty()
58+
}
59+
60+
/// Register all request handlers provided by this module.
61+
///
5562
/// # Safety
5663
///
5764
/// Callers should provide valid non-null `ngx_conf_t` arguments. Implementers must
5865
/// guard against null inputs or risk runtime errors.
59-
unsafe extern "C" fn preconfiguration(_cf: *mut ngx_conf_t) -> ngx_int_t {
66+
unsafe fn register_request_handlers(cf: *mut ngx_conf_t) -> ngx_int_t {
67+
for registrar in Self::request_handler_registrars() {
68+
let rcf = unsafe { &mut *cf };
69+
if !registrar(rcf) {
70+
return Status::NGX_ERROR.into();
71+
}
72+
}
6073
Status::NGX_OK.into()
6174
}
6275

6376
/// # Safety
6477
///
6578
/// Callers should provide valid non-null `ngx_conf_t` arguments. Implementers must
6679
/// guard against null inputs or risk runtime errors.
67-
unsafe extern "C" fn postconfiguration(_cf: *mut ngx_conf_t) -> ngx_int_t {
80+
unsafe extern "C" fn preconfiguration(_cf: *mut ngx_conf_t) -> ngx_int_t {
6881
Status::NGX_OK.into()
6982
}
7083

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

src/http/request.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use allocator_api2::alloc::Allocator;
1010
use crate::core::*;
1111
use crate::ffi::*;
1212
use crate::http::status::*;
13+
use crate::http::{HttpModule, HttpModuleMainConf, NgxHttpCoreModule};
1314

1415
/// Define a static request handler.
1516
///
@@ -89,6 +90,77 @@ macro_rules! http_variable_get {
8990
};
9091
}
9192

93+
/// Trait for static request handler.
94+
/// Predefined return types are `Option<ngx_int_t>`
95+
/// and `Result<ngx_int_t, E>` where `E: ::core::error::Error`.
96+
/// Handler with `Result<ngx_int_t, E>` return type logs the error message automatically.
97+
pub trait HttpRequestHandler<ReturnType> {
98+
/// The phase in which the handler is invoked
99+
const PHASE: NgxHttpPhases;
100+
/// The module to which the handler belongs
101+
type Module: HttpModule;
102+
103+
/// The handler function
104+
fn handler(request: &mut Request) -> ReturnType;
105+
}
106+
107+
trait HttpHandlerInternal<ReturnType>: HttpRequestHandler<ReturnType> {
108+
fn convert(r: &Request, res: ReturnType) -> ngx_int_t;
109+
110+
unsafe extern "C" fn handler_wrapper(r: *mut ngx_http_request_t) -> ngx_int_t {
111+
let r = unsafe { Request::from_ngx_http_request(r) };
112+
let res = Self::handler(r);
113+
Self::convert(r, res)
114+
}
115+
}
116+
117+
impl<T> HttpHandlerInternal<Option<ngx_int_t>> for T
118+
where
119+
T: HttpRequestHandler<Option<ngx_int_t>>,
120+
{
121+
fn convert(_r: &Request, res: Option<ngx_int_t>) -> ngx_int_t {
122+
res.unwrap_or(NGX_ERROR as _)
123+
}
124+
}
125+
126+
impl<T, E> HttpHandlerInternal<Result<ngx_int_t, E>> for T
127+
where
128+
T: HttpRequestHandler<Result<ngx_int_t, E>>,
129+
E: ::core::error::Error,
130+
{
131+
fn convert(r: &Request, res: Result<ngx_int_t, E>) -> ngx_int_t {
132+
res.unwrap_or_else(|err| {
133+
crate::ngx_log_error!(NGX_LOG_ERR, r.log(), "{err}");
134+
NGX_ERROR as _
135+
})
136+
}
137+
}
138+
139+
/// Trait for registering a request handler.
140+
pub trait HttpHandlerRegistrar<ReturnType> {
141+
/// Register a request handler for a specified phase.
142+
fn register(cf: &mut ngx_conf_t) -> bool;
143+
}
144+
145+
impl<T, ReturnType> HttpHandlerRegistrar<ReturnType> for T
146+
where
147+
T: HttpHandlerInternal<ReturnType>,
148+
{
149+
fn register(cf: &mut ngx_conf_t) -> bool {
150+
let cmcf = NgxHttpCoreModule::main_conf_mut(cf).expect("http core main conf");
151+
let h: *mut ngx_http_handler_pt =
152+
unsafe { ngx_array_push(&mut cmcf.phases[Self::PHASE as usize].handlers) as _ };
153+
if h.is_null() {
154+
return false;
155+
}
156+
// set an H::PHASE phase handler
157+
unsafe {
158+
*h = Some(Self::handler_wrapper);
159+
}
160+
true
161+
}
162+
}
163+
92164
/// Wrapper struct for an [`ngx_http_request_t`] pointer, providing methods for working with HTTP
93165
/// requests.
94166
///

0 commit comments

Comments
 (0)