Skip to content

Commit bcf0ab7

Browse files
committed
Fix access log not working with BBR
1 parent 61dacc0 commit bcf0ab7

File tree

4 files changed

+57
-15
lines changed

4 files changed

+57
-15
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ extproc-mock = []
2626
[dependencies]
2727
ngx = "0.5"
2828
bytes = "1"
29-
tokio = { version = "1.38", features = ["rt-multi-thread", "macros", "time", "net"] }
29+
tokio = { version = "1.38", features = ["rt-multi-thread", "macros", "time", "net", "signal"] }
3030
tokio-stream = "0.1"
3131
tonic = { version = "0.14", features = ["transport", "tls-webpki-roots"] }
3232
tonic-prost = "0.14"

src/lib.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ use std::ffi::{c_char, c_void};
33
use ngx::core;
44
use ngx::ffi::{
55
ngx_array_push, ngx_command_t, ngx_conf_t, ngx_http_add_variable, ngx_http_handler_pt,
6-
ngx_http_module_t, ngx_http_phases_NGX_HTTP_ACCESS_PHASE, ngx_int_t, ngx_module_t, ngx_str_t,
7-
ngx_uint_t, NGX_CONF_TAKE1, NGX_HTTP_LOC_CONF, NGX_HTTP_LOC_CONF_OFFSET, NGX_HTTP_MAIN_CONF,
8-
NGX_HTTP_MODULE, NGX_HTTP_SRV_CONF, NGX_LOG_EMERG,
6+
ngx_http_module_t, ngx_http_phases_NGX_HTTP_PREACCESS_PHASE, ngx_int_t, ngx_module_t,
7+
ngx_str_t, ngx_uint_t, NGX_CONF_TAKE1, NGX_HTTP_LOC_CONF, NGX_HTTP_LOC_CONF_OFFSET,
8+
NGX_HTTP_MAIN_CONF, NGX_HTTP_MODULE, NGX_HTTP_SRV_CONF, NGX_LOG_EMERG,
99
};
1010
use ngx::http::{self, HttpModule};
1111
use ngx::http::{HttpModuleLocationConf, HttpModuleMainConf, NgxHttpCoreModule};

src/modules/bbr.rs

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,6 @@ impl BbrProcessor {
9191
}
9292

9393
fn start_body_reading(request: &mut http::Request, _conf: &ModuleConfig) -> core::Status {
94-
// Start reading the request body without pre-validation
95-
// We'll validate the actual body size during reading
9694
ngx_log_debug_http!(request, "ngx-inference: BBR starting body reading");
9795

9896
let rc = unsafe {
@@ -111,9 +109,36 @@ impl BbrProcessor {
111109
};
112110

113111
match status {
114-
core::Status::NGX_OK => core::Status::NGX_DONE, // Body reading complete, handler called
115-
core::Status::NGX_AGAIN => core::Status::NGX_DONE, // Body reading in progress, handler will be called
116-
_ => core::Status::NGX_ERROR, // Always fail on error
112+
core::Status::NGX_OK => {
113+
// Body was read synchronously, callback already executed
114+
// MUST still call ngx_http_finalize_request for reference counting
115+
// even in sync case, per nginx dev guidance
116+
unsafe {
117+
let r_ptr: *mut ngx::ffi::ngx_http_request_t = request.as_mut();
118+
ngx::ffi::ngx_http_finalize_request(
119+
r_ptr,
120+
ngx::ffi::NGX_DONE as ngx::ffi::ngx_int_t,
121+
);
122+
}
123+
core::Status::NGX_DONE
124+
}
125+
core::Status::NGX_AGAIN => {
126+
// Body reading is async - MUST call ngx_http_finalize_request for reference counting
127+
// Per nginx dev guidance: https://mailman.nginx.org/pipermail/nginx/2023-September/V34S4BNWHPJFUY7RUVBDLHRQ7WGHX3R5.html
128+
unsafe {
129+
let r_ptr: *mut ngx::ffi::ngx_http_request_t = request.as_mut();
130+
(*r_ptr).write_event_handler = Some(ngx::ffi::ngx_http_core_run_phases);
131+
132+
// Call finalize with NGX_DONE to account for reference counting
133+
// The callback will resume phases when body reading completes
134+
ngx::ffi::ngx_http_finalize_request(
135+
r_ptr,
136+
ngx::ffi::NGX_DONE as ngx::ffi::ngx_int_t,
137+
);
138+
}
139+
core::Status::NGX_DONE
140+
}
141+
_ => core::Status::NGX_ERROR,
117142
}
118143
}
119144
}
@@ -126,6 +151,7 @@ impl BbrProcessor {
126151
/// - Modifies nginx internal request structures
127152
/// - Assumes the nginx request pointer is valid and not null
128153
#[allow(clippy::manual_c_str_literals)] // FFI code uses byte strings for cross-platform compatibility
154+
#[allow(unpredictable_function_pointer_comparisons)] // We check write_event_handler for async detection
129155
pub unsafe extern "C" fn bbr_body_read_handler(r: *mut ngx::ffi::ngx_http_request_t) {
130156
// Validate input pointer
131157
if r.is_null() {
@@ -150,7 +176,7 @@ pub unsafe extern "C" fn bbr_body_read_handler(r: *mut ngx::ffi::ngx_http_reques
150176
let conf = match Module::location_conf(request) {
151177
Some(c) => c,
152178
None => {
153-
// No config found, resume processing
179+
// No config found - event loop will resume if needed
154180
return;
155181
}
156182
};
@@ -162,7 +188,7 @@ pub unsafe extern "C" fn bbr_body_read_handler(r: *mut ngx::ffi::ngx_http_reques
162188
conf.bbr_header_name.clone()
163189
};
164190

165-
// If header already present, skip BBR and resume.
191+
// If header already present, skip BBR - event loop will resume if needed
166192
if get_header_in(request, &header_name).is_some() {
167193
return;
168194
}
@@ -208,7 +234,7 @@ pub unsafe extern "C" fn bbr_body_read_handler(r: *mut ngx::ffi::ngx_http_reques
208234

209235
// Extract model directly from JSON body
210236
if body.is_empty() {
211-
// Empty body - skip model extraction and continue processing
237+
// Empty body - skip model extraction, event loop will resume if needed
212238
return;
213239
}
214240

@@ -252,9 +278,24 @@ pub unsafe extern "C" fn bbr_body_read_handler(r: *mut ngx::ffi::ngx_http_reques
252278
);
253279
}
254280

255-
// Body processing complete - resume NGINX phase processing
256-
// We must call ngx_http_core_run_phases(r) to continue after async body reading
257-
unsafe { ngx::ffi::ngx_http_core_run_phases(r) };
281+
// Body processing complete - resume phases from where we left off
282+
// We must call ngx_http_core_run_phases to continue through content/proxy phase
283+
if (*r).write_event_handler == Some(ngx::ffi::ngx_http_core_run_phases) {
284+
let request: &mut http::Request = ngx::http::Request::from_ngx_http_request(r);
285+
ngx_log_debug_http!(
286+
request,
287+
"ngx-inference: BBR callback complete, resuming phases (async mode)"
288+
);
289+
290+
// Resume phases - this will continue through content phase (proxy) and eventually log phase
291+
ngx::ffi::ngx_http_core_run_phases(r);
292+
} else {
293+
let request: &mut http::Request = ngx::http::Request::from_ngx_http_request(r);
294+
ngx_log_info_http!(
295+
request,
296+
"ngx-inference: BBR callback complete (sync mode, no resume needed)"
297+
);
298+
}
258299
}
259300

260301
/// Read the request body from memory and file buffers

0 commit comments

Comments
 (0)