|
| 1 | +use ngx::http::{ |
| 2 | + AsyncHandler, AsyncSubRequestBuilder, AsyncSubRequestError, HttpModule, HttpModuleLocationConf, |
| 3 | + HttpPhase, Merge, MergeConfigError, Request, add_phase_handler, |
| 4 | +}; |
| 5 | +use ngx::{async_ as ngx_async, ngx_log_debug_http, ngx_log_error}; |
| 6 | + |
| 7 | +use nginx_sys::{ |
| 8 | + NGX_CONF_TAKE1, NGX_HTTP_LOC_CONF, NGX_HTTP_LOC_CONF_OFFSET, ngx_command_t, ngx_conf_t, |
| 9 | + ngx_flag_t, ngx_http_complex_value_t, ngx_http_module_t, ngx_http_request_t, |
| 10 | + ngx_http_send_response, ngx_int_t, ngx_module_t, ngx_str_t, ngx_uint_t, |
| 11 | +}; |
| 12 | + |
| 13 | +const NGX_CONF_UNSET_FLAG: ngx_flag_t = nginx_sys::NGX_CONF_UNSET as _; |
| 14 | + |
| 15 | +struct SampleAsyncHandler; |
| 16 | + |
| 17 | +enum SampleAsyncHandlerError { |
| 18 | + SubrequestCreationFailed(AsyncSubRequestError), |
| 19 | + SubrequestFailed(ngx_int_t), |
| 20 | +} |
| 21 | + |
| 22 | +impl core::fmt::Display for SampleAsyncHandlerError { |
| 23 | + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
| 24 | + match self { |
| 25 | + SampleAsyncHandlerError::SubrequestCreationFailed(e) => { |
| 26 | + write!(f, "Subrequest creation failed: {}", e) |
| 27 | + } |
| 28 | + SampleAsyncHandlerError::SubrequestFailed(rc) => { |
| 29 | + write!(f, "Subrequest failed with return code: {}", rc) |
| 30 | + } |
| 31 | + } |
| 32 | + } |
| 33 | +} |
| 34 | + |
| 35 | +impl From<AsyncSubRequestError> for SampleAsyncHandlerError { |
| 36 | + fn from(err: AsyncSubRequestError) -> Self { |
| 37 | + SampleAsyncHandlerError::SubrequestCreationFailed(err) |
| 38 | + } |
| 39 | +} |
| 40 | + |
| 41 | +impl From<ngx_int_t> for SampleAsyncHandlerError { |
| 42 | + fn from(rc: ngx_int_t) -> Self { |
| 43 | + SampleAsyncHandlerError::SubrequestFailed(rc) |
| 44 | + } |
| 45 | +} |
| 46 | + |
| 47 | +impl AsyncHandler for SampleAsyncHandler { |
| 48 | + const PHASE: HttpPhase = HttpPhase::Access; |
| 49 | + type Module = Module; |
| 50 | + type Output = Result<ngx_int_t, SampleAsyncHandlerError>; |
| 51 | + |
| 52 | + async fn worker(request: &mut Request) -> Self::Output { |
| 53 | + ngx_log_debug_http!(request, "worker started"); |
| 54 | + |
| 55 | + let co = Module::location_conf(request).expect("module config is none"); |
| 56 | + ngx_log_debug_http!(request, "async_request module enabled: {}", co.enable); |
| 57 | + |
| 58 | + if co.enable != 1 { |
| 59 | + return Ok(nginx_sys::NGX_DECLINED as _); |
| 60 | + } |
| 61 | + |
| 62 | + let log = request.log(); |
| 63 | + let request_ptr: *mut ngx_http_request_t = request.as_mut(); |
| 64 | + let uri: &str = if co.uri.is_empty() { |
| 65 | + "/proxy" |
| 66 | + } else { |
| 67 | + co.uri.to_str().unwrap_or("/proxy") |
| 68 | + }; |
| 69 | + |
| 70 | + let fut = AsyncSubRequestBuilder::new(uri) |
| 71 | + .args("arg1=val1&arg2=val2") |
| 72 | + .in_memory() |
| 73 | + .waited() |
| 74 | + .build(request)?; |
| 75 | + |
| 76 | + let (subrc, sr) = fut.await.map_err(SampleAsyncHandlerError::from)?; |
| 77 | + |
| 78 | + ngx_log_error!(nginx_sys::NGX_LOG_INFO, log, "Subrequest rc {}", subrc); |
| 79 | + |
| 80 | + if subrc != nginx_sys::NGX_OK as _ { |
| 81 | + return Err(subrc.into()); |
| 82 | + } |
| 83 | + |
| 84 | + ngx_log_error!( |
| 85 | + nginx_sys::NGX_LOG_INFO, |
| 86 | + log, |
| 87 | + "Subrequest status: {:?}", |
| 88 | + sr.get_status() |
| 89 | + ); |
| 90 | + |
| 91 | + ngx_async::sleep(core::time::Duration::from_secs(2)).await; |
| 92 | + |
| 93 | + let mut resp_len: usize = 0; |
| 94 | + |
| 95 | + let mut rc = nginx_sys::NGX_OK as ngx_int_t; |
| 96 | + |
| 97 | + if let Some(out) = sr.get_out() { |
| 98 | + if !out.buf.is_null() { |
| 99 | + let b = unsafe { &*out.buf }; |
| 100 | + resp_len = unsafe { b.last.offset_from(b.pos) } as usize; |
| 101 | + |
| 102 | + let sr_ptr: *const ngx_http_request_t = sr.as_ref(); |
| 103 | + |
| 104 | + let mut ct: ngx_str_t = (unsafe { *sr_ptr }).headers_out.content_type; |
| 105 | + |
| 106 | + let mut cv: ngx_http_complex_value_t = unsafe { core::mem::zeroed() }; |
| 107 | + cv.value = ngx_str_t { |
| 108 | + len: resp_len as _, |
| 109 | + data: b.pos as _, |
| 110 | + }; |
| 111 | + |
| 112 | + rc = unsafe { |
| 113 | + ngx_http_send_response(request_ptr, sr.get_status().0, &raw mut ct, &raw mut cv) |
| 114 | + }; |
| 115 | + |
| 116 | + if rc == nginx_sys::NGX_OK as _ { |
| 117 | + rc = nginx_sys::NGX_HTTP_OK as _; |
| 118 | + } |
| 119 | + } |
| 120 | + } |
| 121 | + |
| 122 | + ngx_log_error!( |
| 123 | + nginx_sys::NGX_LOG_INFO, |
| 124 | + log, |
| 125 | + "Async handler after timeout; subrequest response length: {}", |
| 126 | + resp_len |
| 127 | + ); |
| 128 | + |
| 129 | + Ok(rc) |
| 130 | + } |
| 131 | +} |
| 132 | + |
| 133 | +static NGX_HTTP_ASYNC_REQUEST_MODULE_CTX: ngx_http_module_t = ngx_http_module_t { |
| 134 | + preconfiguration: None, |
| 135 | + postconfiguration: Some(Module::postconfiguration), |
| 136 | + create_main_conf: None, |
| 137 | + init_main_conf: None, |
| 138 | + create_srv_conf: None, |
| 139 | + merge_srv_conf: None, |
| 140 | + create_loc_conf: Some(Module::create_loc_conf), |
| 141 | + merge_loc_conf: Some(Module::merge_loc_conf), |
| 142 | +}; |
| 143 | + |
| 144 | +#[cfg(feature = "export-modules")] |
| 145 | +ngx::ngx_modules!(ngx_http_async_request_module); |
| 146 | + |
| 147 | +#[used] |
| 148 | +#[allow(non_upper_case_globals)] |
| 149 | +#[cfg_attr(not(feature = "export-modules"), unsafe(no_mangle))] |
| 150 | +pub static mut ngx_http_async_request_module: ngx_module_t = ngx_module_t { |
| 151 | + ctx: &raw const NGX_HTTP_ASYNC_REQUEST_MODULE_CTX as _, |
| 152 | + commands: unsafe { &raw mut NGX_HTTP_ASYNC_REQUEST_COMMANDS[0] }, |
| 153 | + type_: nginx_sys::NGX_HTTP_MODULE as _, |
| 154 | + ..ngx_module_t::default() |
| 155 | +}; |
| 156 | + |
| 157 | +struct Module; |
| 158 | + |
| 159 | +impl HttpModule for Module { |
| 160 | + fn module() -> &'static ngx_module_t { |
| 161 | + unsafe { &*::core::ptr::addr_of!(ngx_http_async_request_module) } |
| 162 | + } |
| 163 | + |
| 164 | + unsafe extern "C" fn postconfiguration(cf: *mut ngx_conf_t) -> ngx_int_t { |
| 165 | + // SAFETY: this function is called with non-NULL cf always |
| 166 | + let cf = unsafe { &mut *cf }; |
| 167 | + add_phase_handler::<SampleAsyncHandler>(cf) |
| 168 | + .map_or(nginx_sys::NGX_ERROR as _, |_| nginx_sys::NGX_OK as _) |
| 169 | + } |
| 170 | +} |
| 171 | + |
| 172 | +#[derive(Debug)] |
| 173 | +struct ModuleConfig { |
| 174 | + enable: ngx_flag_t, |
| 175 | + uri: ngx_str_t, |
| 176 | +} |
| 177 | + |
| 178 | +impl Default for ModuleConfig { |
| 179 | + fn default() -> Self { |
| 180 | + Self { |
| 181 | + enable: NGX_CONF_UNSET_FLAG, |
| 182 | + uri: ngx_str_t::empty(), |
| 183 | + } |
| 184 | + } |
| 185 | +} |
| 186 | + |
| 187 | +impl Merge for ModuleConfig { |
| 188 | + fn merge(&mut self, prev: &ModuleConfig) -> Result<(), MergeConfigError> { |
| 189 | + if self.enable == NGX_CONF_UNSET_FLAG { |
| 190 | + if prev.enable != NGX_CONF_UNSET_FLAG { |
| 191 | + self.enable = prev.enable; |
| 192 | + } else { |
| 193 | + self.enable = 0; |
| 194 | + } |
| 195 | + } |
| 196 | + if self.uri.len == 0 { |
| 197 | + self.uri = prev.uri; |
| 198 | + } |
| 199 | + Ok(()) |
| 200 | + } |
| 201 | +} |
| 202 | + |
| 203 | +unsafe impl HttpModuleLocationConf for Module { |
| 204 | + type LocationConf = ModuleConfig; |
| 205 | +} |
| 206 | + |
| 207 | +static mut NGX_HTTP_ASYNC_REQUEST_COMMANDS: [ngx_command_t; 3] = [ |
| 208 | + ngx_command_t { |
| 209 | + name: ngx::ngx_string!("async_request"), |
| 210 | + type_: (NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1) as ngx_uint_t, |
| 211 | + set: Some(nginx_sys::ngx_conf_set_flag_slot), |
| 212 | + conf: NGX_HTTP_LOC_CONF_OFFSET, |
| 213 | + offset: core::mem::offset_of!(ModuleConfig, enable), |
| 214 | + post: core::ptr::null_mut(), |
| 215 | + }, |
| 216 | + ngx_command_t { |
| 217 | + name: ngx::ngx_string!("async_uri"), |
| 218 | + type_: (NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1) as ngx_uint_t, |
| 219 | + set: Some(nginx_sys::ngx_conf_set_str_slot), |
| 220 | + conf: NGX_HTTP_LOC_CONF_OFFSET, |
| 221 | + offset: core::mem::offset_of!(ModuleConfig, uri), |
| 222 | + post: core::ptr::null_mut(), |
| 223 | + }, |
| 224 | + ngx_command_t::empty(), |
| 225 | +]; |
0 commit comments