A memory pool is an allocator that obtains memory from a managed region and releases all allocations together when the pool is destroyed. NGINX keeps a pool on each request, so temporary response buffers can share the request lifetime without individual cleanup code. section 4.6
Allocating an HTTP response buffer
A response writer uses ngx_pcalloc(request.pool, ...) to allocate an ngx_buf_t and its response bytes. It checks for null pointers, copies the response into the allocated memory, and lets NGINX reclaim both buffers when request handling ends. section 4.6
unsafe fn write_response(
request: &mut ngx_http_request_t,
response_body: &str,
status_code: ngx_uint_t,
) -> Result<(), &'static str> {
let headers = &mut request.headers_out;
headers.status = status_code;
let response_bytes = response_body.as_bytes();
headers.content_length_n = response_bytes.len() as off_t;
let rc = ngx_http_send_header(request); #1
if rc != 0 {
return Err("failed to send headers");
}
let buf_p =
ngx_pcalloc(request.pool, std::mem::size_of::<
ngx_buf_t>() as size_t) #2
as *mut ngx_buf_t;
if buf_p.is_null() {
return Err("Failed to allocate buffer");
}
let buf = &mut (*buf_p);
buf.set_last_buf(1); #3
buf.set_last_in_chain(1);
buf.set_memory(1);
let response_buffer =
ngx_pcalloc(request.pool, response_bytes.len() as size_t); #4
if response_buffer.is_null() {
return Err("Failed to allocate response buffer");
}
std::ptr::copy_nonoverlapping( #5
response_bytes.as_ptr(),
response_buffer as *mut u8,
response_bytes.len(),
);
buf.pos = response_buffer as *mut u8;
buf.last = response_buffer.offset(
response_bytes.len() as isize) as *mut u8;
let mut out_chain = ngx_chain_t {
buf,
next: std::ptr::null_mut(),
};
if ngx_http_output_filter(request, &mut out_chain) != 0 { #6
return Err("Failed to perform http output filter chain");
}
Ok(())
}
Inspect the request-pool allocations alongside the size_of calculation, null check, buffer initialization, and response-buffer allocation. section 4.6
