diff --git a/src/handlers.rs b/src/handlers.rs index 3b1c3f4..70279e7 100644 --- a/src/handlers.rs +++ b/src/handlers.rs @@ -16,7 +16,11 @@ use std::ptr; pub struct VtsHandler; impl VtsHandler { - pub extern "C" fn vts_status_handler(r: *mut ngx_http_request_t) -> ngx_int_t { + /// # Safety + /// + /// The `r` pointer must be a valid nginx request pointer. + /// The caller must ensure the pointer remains valid for the duration of this call. + pub unsafe extern "C" fn vts_status_handler(r: *mut ngx_http_request_t) -> ngx_int_t { unsafe { // TODO: Fix nginx module integration // let loc_conf = ngx_http_get_module_loc_conf(r, &crate::ngx_http_vts_module as *const _ as *mut _) as *mut VtsConfig; diff --git a/src/lib.rs b/src/lib.rs index 6e58c64..6dfa0e3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -302,6 +302,33 @@ pub extern "C" fn vts_collect_nginx_connections() { } } +/// Update server zone statistics from nginx request processing +/// This should be called from nginx log phase for each request +/// +/// # Safety +/// +/// The `server_name` pointer must be a valid null-terminated C string. +/// The caller must ensure the pointer remains valid for the duration of this call. +#[no_mangle] +pub unsafe extern "C" fn vts_update_server_stats_ffi( + server_name: *const c_char, + status: u16, + bytes_in: u64, + bytes_out: u64, + request_time: u64, +) { + if server_name.is_null() { + return; + } + + let server_name_str = match std::ffi::CStr::from_ptr(server_name).to_str() { + Ok(s) => s, + Err(_) => return, + }; + + update_server_zone_stats(server_name_str, status, bytes_in, bytes_out, request_time); +} + /// Update VTS statistics from nginx (to be called periodically) /// This should be called from nginx worker process periodically to collect /// all types of statistics including connections, server zones, and upstream data @@ -311,7 +338,7 @@ pub extern "C" fn vts_update_statistics() { vts_collect_nginx_connections(); // Note: Server zone statistics are updated automatically when requests are processed - // via update_server_zone_stats() calls from nginx request processing + // via vts_update_server_stats_ffi() calls from nginx request processing // Note: Upstream statistics are updated automatically when upstream requests complete // via vts_update_upstream_stats_ffi() calls from nginx upstream processing @@ -327,7 +354,7 @@ pub extern "C" fn vts_update_statistics() { /// The returned pointer is valid until the next call to this function. /// The caller must not free the returned pointer. #[no_mangle] -pub extern "C" fn ngx_http_vts_get_status() -> *const c_char { +pub unsafe extern "C" fn ngx_http_vts_get_status() -> *const c_char { use std::sync::Mutex; static STATUS_CACHE: Mutex> = Mutex::new(None); @@ -400,7 +427,8 @@ http_request_handler!(vts_status_handler, |request: &mut http::Request| { /// /// A formatted string containing VTS status information fn generate_vts_status_content() -> String { - // First, collect current nginx connection statistics + // Collect current nginx connection statistics only in production + #[cfg(not(test))] vts_collect_nginx_connections(); let manager = VTS_MANAGER @@ -483,6 +511,9 @@ mod integration_tests { manager.connections = Default::default(); } + // Set up connection statistics for the test + update_connection_stats(1, 0, 1, 0, 16, 16); + // Add some sample server zone data update_server_zone_stats("example.com", 200, 1024, 2048, 150); update_server_zone_stats("example.com", 404, 512, 256, 80); diff --git a/src/ngx_vts_wrapper.c b/src/ngx_vts_wrapper.c index 8e47e1b..28e05d6 100644 --- a/src/ngx_vts_wrapper.c +++ b/src/ngx_vts_wrapper.c @@ -21,6 +21,15 @@ extern void vts_track_upstream_request( uint16_t status_code ); +// External Rust functions +extern void vts_update_server_stats_ffi( + const char* server_name, + uint16_t status, + uint64_t bytes_in, + uint64_t bytes_out, + uint64_t request_time +); + // External Rust initialization function extern ngx_int_t ngx_http_vts_init_rust_module(ngx_conf_t *cf); @@ -104,21 +113,62 @@ ngx_http_vts_log_handler(ngx_http_request_t *r) ngx_cpystrn(server_addr_buf, (u_char*)"unknown", sizeof(server_addr_buf)); } - // Call Rust function to update statistics + // Update server zone statistics for all requests + u_char server_name_buf[256]; + ngx_str_t *server_name = &r->headers_in.server; + if (server_name->len > 0 && server_name->len < sizeof(server_name_buf) - 1) { + ngx_memcpy(server_name_buf, server_name->data, server_name->len); + server_name_buf[server_name->len] = '\0'; + } else { + ngx_cpystrn(server_name_buf, (u_char*)"_", sizeof(server_name_buf)); + } + + // Calculate total request time in milliseconds using nginx's builtin calculation + ngx_msec_t request_time = 0; + // Always calculate request time using request start time + ngx_time_t *tp = ngx_timeofday(); + request_time = (ngx_msec_t) ((tp->sec - r->start_sec) * 1000 + (tp->msec - r->start_msec)); + + // Get response status (use r->headers_out.status if available, otherwise default) + ngx_uint_t response_status = r->headers_out.status ? r->headers_out.status : status_code; + if (response_status == 0) { + response_status = 200; // Default to 200 if no status available + } + + // Calculate bytes sent and received for this request + off_t bytes_in = r->request_length; + off_t bytes_out = r->connection->sent; + + // Call Rust function to update server zone statistics ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0, - "VTS LOG_PHASE: Calling vts_track_upstream_request - upstream: %s, server: %s, status: %d", - upstream_name_buf, server_addr_buf, status_code); - - vts_track_upstream_request( - (const char*)upstream_name_buf, - (const char*)server_addr_buf, - (uint64_t)r->start_sec, - (uint64_t)r->start_msec, - (uint64_t)upstream_response_time, - (uint64_t)bytes_sent, - (uint64_t)bytes_received, - (uint16_t)status_code + "VTS LOG_PHASE: Updating server stats - server: %s, status: %d, bytes_in: %O, bytes_out: %O", + server_name_buf, response_status, bytes_in, bytes_out); + + vts_update_server_stats_ffi( + (const char*)server_name_buf, + (uint16_t)response_status, + (uint64_t)bytes_in, + (uint64_t)bytes_out, + (uint64_t)request_time ); + + // Call Rust function to update upstream statistics (if upstream exists) + if (upstream_name.len > 0) { + ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0, + "VTS LOG_PHASE: Calling vts_track_upstream_request - upstream: %s, server: %s, status: %d", + upstream_name_buf, server_addr_buf, status_code); + + vts_track_upstream_request( + (const char*)upstream_name_buf, + (const char*)server_addr_buf, + (uint64_t)r->start_sec, + (uint64_t)r->start_msec, + (uint64_t)upstream_response_time, + (uint64_t)bytes_sent, + (uint64_t)bytes_received, + (uint16_t)status_code + ); + } ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0, "VTS LOG_PHASE: vts_track_upstream_request completed");