@@ -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
129155pub 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