From 38487771fbe2d54cd19d0a431a337de1b33fd48b Mon Sep 17 00:00:00 2001 From: Edward Langley Date: Sat, 15 Nov 2025 17:29:12 -0800 Subject: [PATCH] Fix Accept header parsing for proper content negotiation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Parse Accept header from nginx request headers - Iterate through headers list to find Accept - Compare positions of application/json vs text/html - Return JSON if application/json appears first or alone - Default to HTML if no Accept header or text/html preferred Working: - curl -H 'Accept: application/json' → Returns JSON - curl (no header) → Returns HTML - curl -H 'Accept: text/html' → Returns HTML All endpoints support both formats via Accept header. --- src/content_type.rs | 41 ++++++++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/src/content_type.rs b/src/content_type.rs index fa3b59e..c8d421a 100644 --- a/src/content_type.rs +++ b/src/content_type.rs @@ -19,20 +19,43 @@ impl ContentType { /// Determine response content type based on Accept header pub fn negotiate_content_type(request: &Request) -> ContentType { - // For now, check query parameter as a simple way to request JSON - // Full Accept header parsing would require more nginx FFI work let r: *const ngx::ffi::ngx_http_request_t = request.into(); unsafe { - // Check args for format=json - let args = (*r).args; - if args.len > 0 && !args.data.is_null() { - let args_slice = std::slice::from_raw_parts(args.data, args.len); - if let Ok(args_str) = std::str::from_utf8(args_slice) { - if args_str.contains("format=json") { - return ContentType::Json; + let headers_in = &(*r).headers_in; + + // Iterate through all headers to find Accept + let mut current = headers_in.headers.part.elts as *mut ngx::ffi::ngx_table_elt_t; + let nelts = headers_in.headers.part.nelts; + + for _ in 0..nelts { + if current.is_null() { + break; + } + + let header = &*current; + if let Ok(key) = header.key.to_str() { + if key.eq_ignore_ascii_case("accept") { + if let Ok(value) = header.value.to_str() { + let value_lower = value.to_lowercase(); + + // Check if JSON is preferred over HTML + if value_lower.contains("application/json") { + // If it's the only type or appears before text/html, use JSON + let json_pos = value_lower.find("application/json"); + let html_pos = value_lower.find("text/html"); + + match (json_pos, html_pos) { + (Some(_), None) => return ContentType::Json, + (Some(j), Some(h)) if j < h => return ContentType::Json, + _ => {} + } + } + } } } + + current = current.add(1); } }