use core::error;
use core::ffi::c_void;
use core::fmt;
use core::ptr::NonNull;
use core::slice;
use core::str::FromStr;
use crate::core::*;
use crate::ffi::*;
use crate::http::status::*;
#[macro_export]
macro_rules! http_request_handler {
( $name: ident, $handler: expr ) => {
extern "C" fn $name(r: *mut $crate::ffi::ngx_http_request_t) -> $crate::ffi::ngx_int_t {
let status: $crate::core::Status =
$handler(unsafe { &mut $crate::http::Request::from_ngx_http_request(r) });
status.0
}
};
}
#[macro_export]
macro_rules! http_subrequest_handler {
( $name: ident, $handler: expr ) => {
unsafe extern "C" fn $name(
r: *mut $crate::ffi::ngx_http_request_t,
data: *mut ::core::ffi::c_void,
rc: $crate::ffi::ngx_int_t,
) -> $crate::ffi::ngx_int_t {
$handler(r, data, rc)
}
};
}
#[macro_export]
macro_rules! http_variable_set {
( $name: ident, $handler: expr ) => {
unsafe extern "C" fn $name(
r: *mut $crate::ffi::ngx_http_request_t,
v: *mut $crate::ffi::ngx_variable_value_t,
data: usize,
) {
$handler(
unsafe { &mut $crate::http::Request::from_ngx_http_request(r) },
v,
data,
);
}
};
}
#[macro_export]
macro_rules! http_variable_get {
( $name: ident, $handler: expr ) => {
unsafe extern "C" fn $name(
r: *mut $crate::ffi::ngx_http_request_t,
v: *mut $crate::ffi::ngx_variable_value_t,
data: usize,
) -> $crate::ffi::ngx_int_t {
let status: $crate::core::Status = $handler(
unsafe { &mut $crate::http::Request::from_ngx_http_request(r) },
v,
data,
);
status.0
}
};
}
#[repr(transparent)]
pub struct Request(ngx_http_request_t);
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 _
}
}
impl AsRef<ngx_http_request_t> for Request {
fn as_ref(&self) -> &ngx_http_request_t {
&self.0
}
}
impl AsMut<ngx_http_request_t> for Request {
fn as_mut(&mut self) -> &mut ngx_http_request_t {
&mut self.0
}
}
impl Request {
pub unsafe fn from_ngx_http_request<'a>(r: *mut ngx_http_request_t) -> &'a mut Request {
&mut *r.cast::<Request>()
}
pub fn is_main(&self) -> bool {
let main = self.0.main.cast();
core::ptr::eq(self, main)
}
pub fn pool(&self) -> Pool {
unsafe { Pool::from_ngx_pool(self.0.pool) }
}
pub fn upstream(&self) -> Option<*mut ngx_http_upstream_t> {
if self.0.upstream.is_null() {
return None;
}
Some(self.0.upstream)
}
pub fn connection(&self) -> *mut ngx_connection_t {
self.0.connection
}
pub fn log(&self) -> *mut ngx_log_t {
unsafe { (*self.connection()).log }
}
fn get_module_ctx_ptr(&self, module: &ngx_module_t) -> *mut c_void {
unsafe { *self.0.ctx.add(module.ctx_index) }
}
pub fn get_module_ctx<T>(&self, module: &ngx_module_t) -> Option<&T> {
let ctx = self.get_module_ctx_ptr(module).cast::<T>();
unsafe { ctx.as_ref() }
}
pub fn set_module_ctx(&self, value: *mut c_void, module: &ngx_module_t) {
unsafe {
*self.0.ctx.add(module.ctx_index) = value;
};
}
pub fn get_complex_value(&self, cv: &ngx_http_complex_value_t) -> Option<&NgxStr> {
let r = (self as *const Request as *mut Request).cast();
let val = cv as *const ngx_http_complex_value_t as *mut ngx_http_complex_value_t;
unsafe {
let mut value = ngx_str_t::default();
if ngx_http_complex_value(r, val, &mut value) != NGX_OK as ngx_int_t {
return None;
}
Some(NgxStr::from_ngx_str(value))
}
}
pub fn discard_request_body(&mut self) -> Status {
unsafe { Status(ngx_http_discard_request_body(&mut self.0)) }
}
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)) }
} else {
None
}
}
pub fn set_status(&mut self, status: HTTPStatus) {
self.0.headers_out.status = status.into();
}
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) }
}
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) }
}
pub fn set_content_length_n(&mut self, n: usize) {
self.0.headers_out.content_length_n = n as off_t;
}
pub fn send_header(&mut self) -> Status {
unsafe { Status(ngx_http_send_header(&mut self.0)) }
}
pub fn header_only(&self) -> bool {
self.0.header_only() != 0
}
pub fn method(&self) -> Method {
Method::from_ngx(self.0.method)
}
pub fn path(&self) -> &NgxStr {
unsafe { NgxStr::from_ngx_str(self.0.uri) }
}
pub fn unparsed_uri(&self) -> &NgxStr {
unsafe { NgxStr::from_ngx_str(self.0.unparsed_uri) }
}
pub fn output_filter(&mut self, body: &mut ngx_chain_t) -> Status {
unsafe { Status(ngx_http_output_filter(&mut self.0, body)) }
}
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 _ };
if location.starts_with('@') {
unsafe {
ngx_http_named_location((self as *const Request as *mut Request).cast(), uri_ptr);
}
} else {
unsafe {
ngx_http_internal_redirect(
(self as *const Request as *mut Request).cast(),
uri_ptr,
core::ptr::null_mut(),
);
}
}
Status::NGX_DONE
}
pub fn subrequest(
&self,
uri: &str,
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 {
let uri_ptr = unsafe { &mut ngx_str_t::from_str(self.0.pool, uri) as *mut _ };
let sub_ptr = self
.pool()
.alloc(core::mem::size_of::<ngx_http_post_subrequest_t>());
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); }
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,
core::ptr::null_mut(),
&mut psr as *mut _,
sub_ptr as *mut _,
NGX_HTTP_SUBREQUEST_WAITED as _,
)
};
let sr = unsafe { &mut *psr };
sr.request_body =
self.pool()
.alloc(core::mem::size_of::<ngx_http_request_body_t>()) as *mut _;
if sr.request_body.is_null() {
return Status::NGX_ERROR;
}
sr.set_header_only(1 as _);
Status(r)
}
pub fn headers_in_iterator(&self) -> NgxListIterator<'_> {
unsafe { list_iterator(&self.0.headers_in.headers) }
}
pub fn headers_out_iterator(&self) -> NgxListIterator<'_> {
unsafe { list_iterator(&self.0.headers_out.headers) }
}
}
impl crate::http::HttpModuleConfExt for Request {
#[inline]
unsafe fn http_main_conf_unchecked<T>(&self, module: &ngx_module_t) -> Option<NonNull<T>> {
NonNull::new((*self.0.main_conf.add(module.ctx_index)).cast())
}
#[inline]
unsafe fn http_server_conf_unchecked<T>(&self, module: &ngx_module_t) -> Option<NonNull<T>> {
NonNull::new((*self.0.srv_conf.add(module.ctx_index)).cast())
}
#[inline]
unsafe fn http_location_conf_unchecked<T>(&self, module: &ngx_module_t) -> Option<NonNull<T>> {
NonNull::new((*self.0.loc_conf.add(module.ctx_index)).cast())
}
}
impl fmt::Debug for Request {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Request")
.field("request_", &self.0)
.finish()
}
}
pub struct NgxListIterator<'a> {
part: Option<ListPart<'a>>,
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 }
}
}
pub unsafe fn list_iterator(list: &ngx_list_t) -> NgxListIterator<'_> {
NgxListIterator {
part: Some((&list.part).into()),
i: 0,
}
}
impl<'a> Iterator for NgxListIterator<'a> {
type Item = (&'a NgxStr, &'a NgxStr);
fn next(&mut self) -> Option<Self::Item> {
let part = self.part.as_mut()?;
if self.i >= part.arr.len() {
if let Some(next_part_raw) = unsafe { part.raw.next.as_ref() } {
*part = next_part_raw.into();
self.i = 0;
} else {
self.part = None;
return None;
}
}
let header = &part.arr[self.i];
self.i += 1;
unsafe {
Some((
NgxStr::from_ngx_str(header.key),
NgxStr::from_ngx_str(header.value),
))
}
}
}
pub struct InvalidMethod {
_priv: (),
}
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct Method(MethodInner);
impl Method {
pub const UNKNOWN: Method = Method(MethodInner::Unknown);
pub const GET: Method = Method(MethodInner::Get);
pub const HEAD: Method = Method(MethodInner::Head);
pub const POST: Method = Method(MethodInner::Post);
pub const PUT: Method = Method(MethodInner::Put);
pub const DELETE: Method = Method(MethodInner::Delete);
pub const MKCOL: Method = Method(MethodInner::Mkcol);
pub const COPY: Method = Method(MethodInner::Copy);
pub const MOVE: Method = Method(MethodInner::Move);
pub const OPTIONS: Method = Method(MethodInner::Options);
pub const PROPFIND: Method = Method(MethodInner::Propfind);
pub const PROPPATCH: Method = Method(MethodInner::Proppatch);
pub const LOCK: Method = Method(MethodInner::Lock);
pub const UNLOCK: Method = Method(MethodInner::Unlock);
pub const PATCH: Method = Method(MethodInner::Patch);
pub const TRACE: Method = Method(MethodInner::Trace);
pub const CONNECT: Method = Method(MethodInner::Connect);
#[inline]
pub fn as_str(&self) -> &str {
match self.0 {
MethodInner::Unknown => "UNKNOWN",
MethodInner::Get => "GET",
MethodInner::Head => "HEAD",
MethodInner::Post => "POST",
MethodInner::Put => "PUT",
MethodInner::Delete => "DELETE",
MethodInner::Mkcol => "MKCOL",
MethodInner::Copy => "COPY",
MethodInner::Move => "MOVE",
MethodInner::Options => "OPTIONS",
MethodInner::Propfind => "PROPFIND",
MethodInner::Proppatch => "PROPPATCH",
MethodInner::Lock => "LOCK",
MethodInner::Unlock => "UNLOCK",
MethodInner::Patch => "PATCH",
MethodInner::Trace => "TRACE",
MethodInner::Connect => "CONNECT",
}
}
fn from_bytes(_t: &[u8]) -> Result<Method, InvalidMethod> {
todo!()
}
fn from_ngx(t: ngx_uint_t) -> Method {
let t = t as _;
match t {
crate::ffi::NGX_HTTP_GET => Method(MethodInner::Get),
crate::ffi::NGX_HTTP_HEAD => Method(MethodInner::Head),
crate::ffi::NGX_HTTP_POST => Method(MethodInner::Post),
crate::ffi::NGX_HTTP_PUT => Method(MethodInner::Put),
crate::ffi::NGX_HTTP_DELETE => Method(MethodInner::Delete),
crate::ffi::NGX_HTTP_MKCOL => Method(MethodInner::Mkcol),
crate::ffi::NGX_HTTP_COPY => Method(MethodInner::Copy),
crate::ffi::NGX_HTTP_MOVE => Method(MethodInner::Move),
crate::ffi::NGX_HTTP_OPTIONS => Method(MethodInner::Options),
crate::ffi::NGX_HTTP_PROPFIND => Method(MethodInner::Propfind),
crate::ffi::NGX_HTTP_PROPPATCH => Method(MethodInner::Proppatch),
crate::ffi::NGX_HTTP_LOCK => Method(MethodInner::Lock),
crate::ffi::NGX_HTTP_UNLOCK => Method(MethodInner::Unlock),
crate::ffi::NGX_HTTP_PATCH => Method(MethodInner::Patch),
crate::ffi::NGX_HTTP_TRACE => Method(MethodInner::Trace),
#[cfg(nginx1_21_1)]
crate::ffi::NGX_HTTP_CONNECT => Method(MethodInner::Connect),
_ => Method(MethodInner::Unknown),
}
}
}
impl AsRef<str> for Method {
#[inline]
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl<'a> PartialEq<&'a Method> for Method {
#[inline]
fn eq(&self, other: &&'a Method) -> bool {
self == *other
}
}
impl PartialEq<Method> for &Method {
#[inline]
fn eq(&self, other: &Method) -> bool {
*self == other
}
}
impl PartialEq<str> for Method {
#[inline]
fn eq(&self, other: &str) -> bool {
self.as_ref() == other
}
}
impl PartialEq<Method> for str {
#[inline]
fn eq(&self, other: &Method) -> bool {
self == other.as_ref()
}
}
impl<'a> PartialEq<&'a str> for Method {
#[inline]
fn eq(&self, other: &&'a str) -> bool {
self.as_ref() == *other
}
}
impl PartialEq<Method> for &str {
#[inline]
fn eq(&self, other: &Method) -> bool {
*self == other.as_ref()
}
}
impl fmt::Debug for Method {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_ref())
}
}
impl fmt::Display for Method {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.write_str(self.as_ref())
}
}
impl<'a> From<&'a Method> for Method {
#[inline]
fn from(t: &'a Method) -> Self {
t.clone()
}
}
impl<'a> TryFrom<&'a [u8]> for Method {
type Error = InvalidMethod;
#[inline]
fn try_from(t: &'a [u8]) -> Result<Self, Self::Error> {
Method::from_bytes(t)
}
}
impl<'a> TryFrom<&'a str> for Method {
type Error = InvalidMethod;
#[inline]
fn try_from(t: &'a str) -> Result<Self, Self::Error> {
TryFrom::try_from(t.as_bytes())
}
}
impl FromStr for Method {
type Err = InvalidMethod;
#[inline]
fn from_str(t: &str) -> Result<Self, Self::Err> {
TryFrom::try_from(t)
}
}
impl InvalidMethod {
#[allow(dead_code)]
fn new() -> InvalidMethod {
InvalidMethod { _priv: () }
}
}
impl fmt::Debug for InvalidMethod {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("InvalidMethod")
.finish()
}
}
impl fmt::Display for InvalidMethod {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("invalid HTTP method")
}
}
impl error::Error for InvalidMethod {}
#[derive(Clone, PartialEq, Eq, Hash)]
enum MethodInner {
Unknown,
Get,
Head,
Post,
Put,
Delete,
Mkcol,
Copy,
Move,
Options,
Propfind,
Proppatch,
Lock,
Unlock,
Patch,
Trace,
Connect,
}