diff --git a/Cargo.lock b/Cargo.lock index 69edeb3..f752c81 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + [[package]] name = "adler" version = "1.0.2" @@ -37,6 +46,21 @@ dependencies = [ "url", ] +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide 0.7.2", + "object", + "rustc-demangle", +] + [[package]] name = "base64" version = "0.13.1" @@ -254,6 +278,7 @@ dependencies = [ "http", "libc", "ngx", + "tokio", ] [[package]] @@ -264,8 +289,8 @@ checksum = "8a3de6e8d11b22ff9edc6d916f890800597d60f8b2da1caf2955c274638d6412" dependencies = [ "cfg-if", "libc", - "redox_syscall", - "windows-sys", + "redox_syscall 0.2.16", + "windows-sys 0.45.0", ] [[package]] @@ -275,7 +300,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" dependencies = [ "crc32fast", - "miniz_oxide", + "miniz_oxide 0.6.2", ] [[package]] @@ -303,12 +328,24 @@ dependencies = [ "version_check", ] +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + [[package]] name = "glob" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + [[package]] name = "hex" version = "0.4.3" @@ -389,9 +426,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.141" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libloading" @@ -412,6 +449,16 @@ dependencies = [ "cc", ] +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "log" version = "0.4.17" @@ -442,6 +489,26 @@ dependencies = [ "adler", ] +[[package]] +name = "miniz_oxide" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.48.0", +] + [[package]] name = "nginx-sys" version = "0.2.1" @@ -490,6 +557,25 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.17.1" @@ -509,7 +595,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a53dbb20faf34b16087a931834cba2d7a73cc74af2b7ef345a4c8324e2409a12" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.45.0", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.4.1", + "smallvec", + "windows-targets 0.48.0", ] [[package]] @@ -524,6 +633,12 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + [[package]] name = "prettyplease" version = "0.2.5" @@ -536,9 +651,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.56" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] @@ -561,6 +676,15 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags", +] + [[package]] name = "regex" version = "1.7.3" @@ -591,6 +715,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + [[package]] name = "rustc-hash" version = "1.1.0" @@ -609,6 +739,12 @@ dependencies = [ "webpki", ] +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "scratch" version = "1.0.5" @@ -664,6 +800,31 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "smallvec" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" + +[[package]] +name = "socket2" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "spin" version = "0.5.2" @@ -719,7 +880,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" dependencies = [ "libc", - "wasi", + "wasi 0.10.0+wasi-snapshot-preview1", "winapi", ] @@ -738,6 +899,36 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +[[package]] +name = "tokio" +version = "1.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.16", +] + [[package]] name = "typenum" version = "1.16.0" @@ -816,6 +1007,12 @@ version = "0.10.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "wasm-bindgen" version = "0.2.84" @@ -959,6 +1156,24 @@ dependencies = [ "windows-targets 0.42.2", ] +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.4", +] + [[package]] name = "windows-targets" version = "0.42.2" @@ -989,6 +1204,21 @@ dependencies = [ "windows_x86_64_msvc 0.48.0", ] +[[package]] +name = "windows-targets" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +dependencies = [ + "windows_aarch64_gnullvm 0.52.4", + "windows_aarch64_msvc 0.52.4", + "windows_i686_gnu 0.52.4", + "windows_i686_msvc 0.52.4", + "windows_x86_64_gnu 0.52.4", + "windows_x86_64_gnullvm 0.52.4", + "windows_x86_64_msvc 0.52.4", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -1001,6 +1231,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -1013,6 +1249,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" + [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -1025,6 +1267,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +[[package]] +name = "windows_i686_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" + [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -1037,6 +1285,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +[[package]] +name = "windows_i686_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -1049,6 +1303,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -1061,6 +1321,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -1073,6 +1339,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" + [[package]] name = "xattr" version = "0.2.3" diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 713f991..dcb870e 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -33,5 +33,13 @@ name = "upstream" path = "upstream.rs" crate-type = ["cdylib"] +[[example]] +name = "async" +path = "async.rs" +crate-type = ["cdylib"] + +[dependencies] +tokio = { version = "1.33.0", features = ["full"] } + [features] linux = [] diff --git a/examples/async.conf b/examples/async.conf new file mode 100644 index 0000000..d96876e --- /dev/null +++ b/examples/async.conf @@ -0,0 +1,24 @@ +daemon off; +master_process off; +# worker_processes 1; + +load_module modules/libasync.so; +error_log error.log debug; + +events { } + +http { + server { + listen *:8000; + server_name localhost; + location / { + root html; + index index.html index.htm; + async on; + } + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root html; + } + } +} diff --git a/examples/async.rs b/examples/async.rs new file mode 100644 index 0000000..6b0c1e1 --- /dev/null +++ b/examples/async.rs @@ -0,0 +1,255 @@ +use ngx::ffi::{ + nginx_version, ngx_array_push, ngx_command_t, ngx_conf_t, ngx_cycle, ngx_event_t, ngx_http_core_module, + ngx_http_core_run_phases, ngx_http_handler_pt, ngx_http_module_t, ngx_http_phases_NGX_HTTP_ACCESS_PHASE, + ngx_http_request_t, ngx_int_t, ngx_module_t, ngx_posted_events, ngx_queue_s, ngx_str_t, ngx_uint_t, NGX_CONF_TAKE1, + NGX_HTTP_LOC_CONF, NGX_HTTP_MODULE, NGX_RS_HTTP_LOC_CONF_OFFSET, NGX_RS_MODULE_SIGNATURE, +}; +use ngx::http::MergeConfigError; +use ngx::{core, core::Status, http, http::HTTPModule}; +use ngx::{http_request_handler, ngx_log_debug_http, ngx_modules, ngx_null_command, ngx_string}; +use std::os::raw::{c_char, c_void}; +use std::sync::atomic::AtomicBool; +use std::sync::Arc; +use std::time::Instant; +use tokio::runtime::Runtime; + +struct Module; + +impl http::HTTPModule for Module { + type MainConf = (); + type SrvConf = (); + type LocConf = ModuleConfig; + + unsafe extern "C" fn postconfiguration(cf: *mut ngx_conf_t) -> ngx_int_t { + let cmcf = http::ngx_http_conf_get_module_main_conf(cf, &ngx_http_core_module); + + let h = ngx_array_push(&mut (*cmcf).phases[ngx_http_phases_NGX_HTTP_ACCESS_PHASE as usize].handlers) + as *mut ngx_http_handler_pt; + if h.is_null() { + return core::Status::NGX_ERROR.into(); + } + // set an Access phase handler + *h = Some(async_access_handler); + core::Status::NGX_OK.into() + } +} + +#[derive(Debug)] +struct ModuleConfig { + enable: bool, + rt: Runtime, +} + +impl Default for ModuleConfig { + fn default() -> Self { + Self { + enable: Default::default(), + rt: tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .unwrap(), + } + } +} + +#[no_mangle] +static mut ngx_http_async_commands: [ngx_command_t; 2] = [ + ngx_command_t { + name: ngx_string!("async"), + type_: (NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1) as ngx_uint_t, + set: Some(ngx_http_async_commands_set_enable), + conf: NGX_RS_HTTP_LOC_CONF_OFFSET, + offset: 0, + post: std::ptr::null_mut(), + }, + ngx_null_command!(), +]; + +#[no_mangle] +static ngx_http_async_module_ctx: ngx_http_module_t = ngx_http_module_t { + preconfiguration: Some(Module::preconfiguration), + postconfiguration: Some(Module::postconfiguration), + create_main_conf: Some(Module::create_main_conf), + init_main_conf: Some(Module::init_main_conf), + create_srv_conf: Some(Module::create_srv_conf), + merge_srv_conf: Some(Module::merge_srv_conf), + create_loc_conf: Some(Module::create_loc_conf), + merge_loc_conf: Some(Module::merge_loc_conf), +}; + +ngx_modules!(ngx_http_async_module); + +#[no_mangle] +pub static mut ngx_http_async_module: ngx_module_t = ngx_module_t { + ctx_index: ngx_uint_t::max_value(), + index: ngx_uint_t::max_value(), + name: std::ptr::null_mut(), + spare0: 0, + spare1: 0, + version: nginx_version as ngx_uint_t, + signature: NGX_RS_MODULE_SIGNATURE.as_ptr() as *const c_char, + + ctx: &ngx_http_async_module_ctx as *const _ as *mut _, + commands: unsafe { &ngx_http_async_commands[0] as *const _ as *mut _ }, + type_: NGX_HTTP_MODULE as ngx_uint_t, + + init_master: None, + init_module: None, + init_process: None, + init_thread: None, + exit_thread: None, + exit_process: None, + exit_master: None, + + spare_hook0: 0, + spare_hook1: 0, + spare_hook2: 0, + spare_hook3: 0, + spare_hook4: 0, + spare_hook5: 0, + spare_hook6: 0, + spare_hook7: 0, +}; + +impl http::Merge for ModuleConfig { + fn merge(&mut self, prev: &ModuleConfig) -> Result<(), MergeConfigError> { + if prev.enable { + self.enable = true; + }; + Ok(()) + } +} + +unsafe extern "C" fn check_async_work_done(event: *mut ngx_event_t) { + let event = &mut (*event); + let data = Arc::from_raw(event.data as *const EventData); + let req = &mut (*(data.request as *const _ as *mut ngx_http_request_t)); + if data.done_flag.load(std::sync::atomic::Ordering::Relaxed) { + (*req.main).set_count((*req.main).count() - 1); + ngx_http_core_run_phases(req); + } else { + // this doesn't have have good performance but works as a simple thread-safe example and doesn't causes + // segfault. The best method that provides both thread-safety and performance requires + // an nginx patch. + post_event(event, &ngx_posted_events as *const _ as _); + } +} + +struct RequestCTX { + event_data: Option>, +} + +struct EventData { + done_flag: AtomicBool, + request: *mut ngx_http_request_t, +} + +unsafe impl Send for EventData {} +unsafe impl Sync for EventData {} + +// same as ngx_post_event +// source: https://github.com/nginxinc/ngx-rust/pull/31/files#diff-132330bb775bed17fb9990ec2b56e6c52e6a9e56d62f2114fade95e4decdba08R80-R90 +unsafe fn post_event(event: *mut ngx_event_t, queue: *mut ngx_queue_s) { + let event = &mut (*event); + if event.posted() == 0 { + event.set_posted(1); + // translated from ngx_queue_insert_tail macro + event.queue.prev = (*queue).prev; + (*event.queue.prev).next = &event.queue as *const _ as *mut _; + event.queue.next = queue; + (*queue).prev = &event.queue as *const _ as *mut _; + } +} + +http_request_handler!(async_access_handler, |request: &mut http::Request| { + let co = unsafe { request.get_module_loc_conf::(&ngx_http_async_module) }; + let co = co.expect("module config is none"); + if !co.enable { + return core::Status::NGX_DECLINED; + } + + let event_data = unsafe { + let ctx = request.get_inner().ctx.add(ngx_http_async_module.ctx_index); + if (*ctx).is_null() { + let ctx_data = &mut *(request.pool().alloc(std::mem::size_of::()) as *mut RequestCTX); + ctx_data.event_data = Some(Arc::new(EventData { + done_flag: AtomicBool::new(false), + request: &request.get_inner() as *const _ as *mut _, + })); + *ctx = ctx_data as *const _ as _; + ctx_data.event_data.as_ref().unwrap().clone() + } else { + let ctx = &*(ctx as *const RequestCTX); + if ctx + .event_data + .as_ref() + .unwrap() + .done_flag + .load(std::sync::atomic::Ordering::Relaxed) + { + return core::Status::NGX_OK; + } else { + return core::Status::NGX_DONE; + } + } + }; + + event_data.done_flag.load(std::sync::atomic::Ordering::Relaxed); + + // create a posted event + unsafe { + let event = &mut *(request.pool().calloc(std::mem::size_of::()) as *mut ngx_event_t); + event.handler = Some(check_async_work_done); + event.data = Arc::into_raw(event_data.clone()) as _; + event.log = (*ngx_cycle).log; + + post_event(event, &ngx_posted_events as *const _ as _); + } + + ngx_log_debug_http!(request, "async module enabled: {}", co.enable); + + co.rt.spawn(async move { + let start = Instant::now(); + tokio::time::sleep(std::time::Duration::from_secs(2)).await; + let req = unsafe { http::Request::from_ngx_http_request(event_data.request) }; + // not really thread safe, we should apply all these operation in nginx thread + // but this is just an example. proper way would be storing these headers in the request ctx + // and apply them when we get back to the nginx thread. + req.add_header_out("X-Async-Time", start.elapsed().as_millis().to_string().as_str()); + + event_data.done_flag.store(true, std::sync::atomic::Ordering::Release); + // there is a small issue here. If traffic is low we may get stuck behind a 300ms timer + // in the nginx event loop. To workaround it we can notify the event loop using pthread_kill( nginx_thread, SIGIO ) + // to wake up the event loop. (or patch nginx and use the same trick as the thread pool) + }); + + unsafe { + (*request.get_inner().main).set_count((*request.get_inner().main).count() + 1); + } + core::Status::NGX_DONE +}); + +#[no_mangle] +extern "C" fn ngx_http_async_commands_set_enable( + cf: *mut ngx_conf_t, + _cmd: *mut ngx_command_t, + conf: *mut c_void, +) -> *mut c_char { + unsafe { + let conf = &mut *(conf as *mut ModuleConfig); + let args = (*(*cf).args).elts as *mut ngx_str_t; + + let val = (*args.add(1)).to_str(); + + // set default value optionally + conf.enable = false; + + if val.len() == 2 && val.eq_ignore_ascii_case("on") { + conf.enable = true; + } else if val.len() == 3 && val.eq_ignore_ascii_case("off") { + conf.enable = false; + } + }; + + std::ptr::null_mut() +} diff --git a/src/http/request.rs b/src/http/request.rs index 552b70d..2dd8b54 100644 --- a/src/http/request.rs +++ b/src/http/request.rs @@ -368,6 +368,11 @@ impl Request { pub fn headers_out_iterator(&self) -> NgxListIterator { unsafe { list_iterator(&self.0.headers_out.headers) } } + + /// Returns the inner data structure that the Request object is wrapping. + pub fn get_inner(&self) -> &ngx_http_request_t { + &self.0 + } } // trait OnSubRequestDone { @@ -692,4 +697,3 @@ enum MethodInner { Trace, Connect, } -