Fix ngx 0.5.0 API compatibility

- Update HttpModule trait implementation to match ngx 0.5.0 API
- Implement HttpModuleLocationConf as separate unsafe trait
- Fix configuration access using Module::location_conf()
- Replace ngx_null_command macro with explicit null command
- Update imports to use correct constant names
- Suppress C FFI naming convention warnings
This commit is contained in:
Edward Langley
2025-11-15 14:26:39 -08:00
parent b28ff2db17
commit 63fbee6694
8 changed files with 208 additions and 834 deletions

1
.cursorrules Normal file
View File

@ -0,0 +1 @@
- Always use `direnv exec "$PWD"` when invoking cargo and other tools that need access to the nix/direnv environment

4
.envrc
View File

@ -1,5 +1,7 @@
use flake use flake
unset TMPDIR unset TMPDIR
export NGINX_SOURCE_DIR=$PWD/ngx_src/nginx-1.28.0 export NGINX_SOURCE_DIR=$PWD/ngx_src/nginx-1.28.0/
export NGINX_BUILD_DIR=$PWD/ngx_src/nginx-1.28.0/objs/
export ZLIB_VERSION=1.3.1 export ZLIB_VERSION=1.3.1
export NGX_VERSION=1.28.0
export CFLAGS='-Wno-unterminated-string-initialization' export CFLAGS='-Wno-unterminated-string-initialization'

116
.idea/workspace.xml generated Normal file
View File

@ -0,0 +1,116 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AutoImportSettings">
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="CargoProjects">
<cargoProject FILE="$PROJECT_DIR$/Cargo.toml" />
</component>
<component name="ChangeListManager">
<list default="true" id="082e38d9-eb7e-4f8e-b3a3-6d5c499efa60" name="Changes" comment="">
<change beforePath="$PROJECT_DIR$/runit" beforeDir="false" afterPath="$PROJECT_DIR$/runit" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="MacroExpansionManager">
<option name="directoryName" value="3nobPjdc" />
</component>
<component name="ProjectCodeStyleSettingsMigration">
<option name="version" value="2" />
</component>
<component name="ProjectColorInfo">{
&quot;associatedIndex&quot;: 4
}</component>
<component name="ProjectId" id="35X1zv6pKATZ3MYZz7k7SnGMlTz" />
<component name="ProjectViewState">
<option name="autoscrollFromSource" value="true" />
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent"><![CDATA[{
"keyToString": {
"NIXITCH_NIXPKGS_CONFIG": "",
"NIXITCH_NIX_CONF_DIR": "",
"NIXITCH_NIX_OTHER_STORES": "",
"NIXITCH_NIX_PATH": "",
"NIXITCH_NIX_PROFILES": "",
"NIXITCH_NIX_REMOTE": "",
"NIXITCH_NIX_USER_PROFILE_DIR": "",
"RunOnceActivity.ShowReadmeOnStart": "true",
"RunOnceActivity.git.unshallow": "true",
"kotlin-language-version-configured": "true",
"last_opened_file_path": "/Users/edwlan/nginx-test",
"node.js.detected.package.eslint": "true",
"node.js.detected.package.tslint": "true",
"node.js.selected.package.eslint": "(autodetect)",
"node.js.selected.package.tslint": "(autodetect)",
"nodejs_package_manager_path": "npm",
"org.rust.cargo.project.model.PROJECT_DISCOVERY": "true",
"org.rust.cargo.project.model.impl.CargoExternalSystemProjectAware.subscribe.first.balloon": "",
"project.structure.last.edited": "Artifacts",
"project.structure.proportion": "0.15",
"project.structure.side.proportion": "0.2",
"settings.editor.selected.configurable": "preferences.pluginManager",
"vue.rearranger.settings.migration": "true"
}
}]]></component>
<component name="RunManager">
<configuration default="true" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
<option name="command" value="run" />
<envs />
<option name="emulateTerminal" value="true" />
<option name="channel" value="DEFAULT" />
<option name="requiredFeatures" value="true" />
<option name="allFeatures" value="false" />
<option name="withSudo" value="false" />
<option name="buildTarget" value="REMOTE" />
<option name="backtrace" value="SHORT" />
<option name="isRedirectInput" value="false" />
<option name="redirectInputPath" value="" />
<method v="2">
<option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" />
</method>
</configuration>
</component>
<component name="RustProjectSettings">
<option name="toolchainHomeDirectory" value="$PROJECT_DIR$/../.cargo/bin" />
</component>
<component name="SharedIndexes">
<attachedChunks>
<set>
<option value="bundled-jdk-9823dce3aa75-fdfe4dae3a2d-intellij.indexing.shared.core-IU-243.21565.193" />
<option value="bundled-js-predefined-d6986cc7102b-e768b9ed790e-JavaScript-IU-243.21565.193" />
</set>
</attachedChunks>
</component>
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
<component name="SwiftWorkspaceSettings">
<option name="detectedToolchain" value="true" />
<option name="toolchainPathValue" value="/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain" />
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="082e38d9-eb7e-4f8e-b3a3-6d5c499efa60" name="Changes" comment="" />
<created>1763241867264</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1763241867264</updated>
<workItem from="1763241868325" duration="59000" />
<workItem from="1763241931565" duration="328000" />
</task>
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="3" />
</component>
<component name="XSLT-Support.FileAssociations.UIState">
<expand />
<select />
</component>
</project>

844
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,7 @@ edition = "2024"
crate-type = ["cdylib"] crate-type = ["cdylib"]
[dependencies] [dependencies]
ngx = "0.4.1" ngx = "0.5.0"
rusqlite = "0.37.0" rusqlite = "0.37.0"
handlebars = "6.3.2" handlebars = "6.3.2"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }

View File

@ -1,5 +1,5 @@
# Add the path to your library here. # Add the path to your library here.
load_module target/debug/libnginx_test.so; load_module target/debug/libnginx_test.dylib;
worker_processes 1; worker_processes 1;

2
runit
View File

@ -1,4 +1,4 @@
/home/edwlan/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/.cache/nginx/1.23.3/linux-x86_64/sbin/nginx \ ./ngx_src/nginx-1.28.0/objs/nginx \
-e /dev/stdout \ -e /dev/stdout \
-p "$PWD" \ -p "$PWD" \
-c conf/howto.conf \ -c conf/howto.conf \

View File

@ -1,15 +1,16 @@
use handlebars::Handlebars; use handlebars::Handlebars;
use ngx::core::Buffer; use ngx::core::Buffer;
use ngx::ffi::{ use ngx::ffi::{
NGX_CONF_TAKE1, NGX_HTTP_LOC_CONF, NGX_HTTP_MODULE, NGX_RS_HTTP_LOC_CONF_OFFSET, NGX_CONF_TAKE1, NGX_HTTP_LOC_CONF, NGX_HTTP_MODULE, NGX_HTTP_LOC_CONF_OFFSET,
NGX_RS_MODULE_SIGNATURE, nginx_version, ngx_array_push, ngx_chain_t, ngx_command_t, NGX_RS_MODULE_SIGNATURE, nginx_version, ngx_array_push, ngx_chain_t, ngx_command_t,
ngx_conf_t, ngx_http_core_module, ngx_http_handler_pt, ngx_conf_t, ngx_http_handler_pt, ngx_http_module_t,
ngx_http_module_t, ngx_http_phases_NGX_HTTP_ACCESS_PHASE, ngx_http_request_t, ngx_int_t, ngx_http_phases_NGX_HTTP_ACCESS_PHASE, ngx_int_t, ngx_module_t, ngx_str_t, ngx_uint_t,
ngx_module_t, ngx_str_t, ngx_uint_t, };
use ngx::http::{
HttpModule, HttpModuleLocationConf, HttpModuleMainConf, MergeConfigError, NgxHttpCoreModule,
}; };
use ngx::http::{HTTPModule, MergeConfigError};
use ngx::{core, core::Status, http}; use ngx::{core, core::Status, http};
use ngx::{http_request_handler, ngx_log_debug_http, ngx_modules, ngx_null_command, ngx_string}; use ngx::{http_request_handler, ngx_log_debug_http, ngx_modules, ngx_string};
use rusqlite::{Connection, Result}; use rusqlite::{Connection, Result};
use serde_json::json; use serde_json::json;
use std::os::raw::{c_char, c_void}; use std::os::raw::{c_char, c_void};
@ -17,19 +18,20 @@ use std::ptr::addr_of;
struct Module; struct Module;
// Implement our HTTPModule trait, we're creating a postconfiguration method to install our // Implement our HttpModule trait, we're creating a postconfiguration method to install our
// handler's Access phase function. // handler's Access phase function.
impl http::HTTPModule for Module { impl http::HttpModule for Module {
type MainConf = (); fn module() -> &'static ngx_module_t {
type SrvConf = (); unsafe { &*addr_of!(ngx_http_howto_module) }
type LocConf = ModuleConfig; }
unsafe extern "C" fn postconfiguration(cf: *mut ngx_conf_t) -> ngx_int_t { unsafe extern "C" fn postconfiguration(cf: *mut ngx_conf_t) -> ngx_int_t {
unsafe { unsafe {
let htcf = http::ngx_http_conf_get_module_main_conf(cf, &*addr_of!(ngx_http_core_module)); let htcf =
NgxHttpCoreModule::main_conf_mut(&*cf).expect("failed to get core main conf");
let h = ngx_array_push( let h = ngx_array_push(
&mut (*htcf).phases[ngx_http_phases_NGX_HTTP_ACCESS_PHASE as usize].handlers, &mut htcf.phases[ngx_http_phases_NGX_HTTP_ACCESS_PHASE as usize].handlers,
) as *mut ngx_http_handler_pt; ) as *mut ngx_http_handler_pt;
if h.is_null() { if h.is_null() {
return core::Status::NGX_ERROR.into(); return core::Status::NGX_ERROR.into();
@ -42,6 +44,11 @@ impl http::HTTPModule for Module {
} }
} }
// Implement HttpModuleLocationConf to define our location-specific configuration
unsafe impl HttpModuleLocationConf for Module {
type LocationConf = ModuleConfig;
}
// Create a ModuleConfig to save our configuration state. // Create a ModuleConfig to save our configuration state.
#[derive(Debug, Default)] #[derive(Debug, Default)]
struct ModuleConfig { struct ModuleConfig {
@ -70,15 +77,16 @@ impl http::Merge for ModuleConfig {
} }
// Create our "C" module context with function entrypoints for NGINX event loop. This "binds" our // Create our "C" module context with function entrypoints for NGINX event loop. This "binds" our
// HTTPModule implementation to functions callable from C. // HttpModule implementation to functions callable from C.
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
#[allow(non_upper_case_globals)]
static ngx_http_howto_module_ctx: ngx_http_module_t = ngx_http_module_t { static ngx_http_howto_module_ctx: ngx_http_module_t = ngx_http_module_t {
preconfiguration: Some(Module::preconfiguration), preconfiguration: Some(Module::preconfiguration),
postconfiguration: Some(Module::postconfiguration), postconfiguration: Some(Module::postconfiguration),
create_main_conf: Some(Module::create_main_conf), create_main_conf: None,
init_main_conf: Some(Module::init_main_conf), init_main_conf: None,
create_srv_conf: Some(Module::create_srv_conf), create_srv_conf: None,
merge_srv_conf: Some(Module::merge_srv_conf), merge_srv_conf: None,
create_loc_conf: Some(Module::create_loc_conf), create_loc_conf: Some(Module::create_loc_conf),
merge_loc_conf: Some(Module::merge_loc_conf), merge_loc_conf: Some(Module::merge_loc_conf),
}; };
@ -89,6 +97,7 @@ static ngx_http_howto_module_ctx: ngx_http_module_t = ngx_http_module_t {
ngx_modules!(ngx_http_howto_module); ngx_modules!(ngx_http_howto_module);
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
#[allow(non_upper_case_globals)]
pub static mut ngx_http_howto_module: ngx_module_t = ngx_module_t { pub static mut ngx_http_howto_module: ngx_module_t = ngx_module_t {
ctx_index: ngx_uint_t::max_value(), ctx_index: ngx_uint_t::max_value(),
index: ngx_uint_t::max_value(), index: ngx_uint_t::max_value(),
@ -121,14 +130,15 @@ pub static mut ngx_http_howto_module: ngx_module_t = ngx_module_t {
}; };
// Register and allocate our command structures for directive generation and eventual storage. Be // Register and allocate our command structures for directive generation and eventual storage. Be
// sure to terminate the array with the ngx_null_command! macro. // sure to terminate the array with an empty command.
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
#[allow(non_upper_case_globals)]
static mut ngx_http_howto_commands: [ngx_command_t; 4] = [ static mut ngx_http_howto_commands: [ngx_command_t; 4] = [
ngx_command_t { ngx_command_t {
name: ngx_string!("sqlite_db"), name: ngx_string!("sqlite_db"),
type_: (NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1) as ngx_uint_t, type_: (NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1) as ngx_uint_t,
set: Some(ngx_http_howto_commands_set_db_path), set: Some(ngx_http_howto_commands_set_db_path),
conf: NGX_RS_HTTP_LOC_CONF_OFFSET, conf: NGX_HTTP_LOC_CONF_OFFSET,
offset: 0, offset: 0,
post: std::ptr::null_mut(), post: std::ptr::null_mut(),
}, },
@ -136,7 +146,7 @@ static mut ngx_http_howto_commands: [ngx_command_t; 4] = [
name: ngx_string!("sqlite_query"), name: ngx_string!("sqlite_query"),
type_: (NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1) as ngx_uint_t, type_: (NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1) as ngx_uint_t,
set: Some(ngx_http_howto_commands_set_query), set: Some(ngx_http_howto_commands_set_query),
conf: NGX_RS_HTTP_LOC_CONF_OFFSET, conf: NGX_HTTP_LOC_CONF_OFFSET,
offset: 0, offset: 0,
post: std::ptr::null_mut(), post: std::ptr::null_mut(),
}, },
@ -144,11 +154,21 @@ static mut ngx_http_howto_commands: [ngx_command_t; 4] = [
name: ngx_string!("sqlite_template"), name: ngx_string!("sqlite_template"),
type_: (NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1) as ngx_uint_t, type_: (NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1) as ngx_uint_t,
set: Some(ngx_http_howto_commands_set_template_path), set: Some(ngx_http_howto_commands_set_template_path),
conf: NGX_RS_HTTP_LOC_CONF_OFFSET, conf: NGX_HTTP_LOC_CONF_OFFSET,
offset: 0,
post: std::ptr::null_mut(),
},
ngx_command_t {
name: ngx_str_t {
len: 0,
data: std::ptr::null_mut(),
},
type_: 0,
set: None,
conf: 0,
offset: 0, offset: 0,
post: std::ptr::null_mut(), post: std::ptr::null_mut(),
}, },
ngx_null_command!(),
]; ];
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
@ -242,10 +262,7 @@ fn execute_query(db_path: &str, query: &str) -> Result<Vec<std::collections::Has
// //
// The function body is implemented as a Rust closure. // The function body is implemented as a Rust closure.
http_request_handler!(howto_access_handler, |request: &mut http::Request| { http_request_handler!(howto_access_handler, |request: &mut http::Request| {
let co = unsafe { let co = Module::location_conf(request).expect("module config is none");
request.get_module_loc_conf::<ModuleConfig>(&*addr_of!(ngx_http_howto_module))
};
let co = co.expect("module config is none");
// Check if all required config values are set // Check if all required config values are set
if co.db_path.is_empty() || co.query.is_empty() || co.template_path.is_empty() { if co.db_path.is_empty() || co.query.is_empty() || co.template_path.is_empty() {