Skip to main content

Overview

The Res module provides functions for writing HTTP responses directly into bigstring buffers. All functions operate on bigstrings for zero-copy I/O and use int16# offsets for efficient stack-allocated position tracking.

Response Buffer Setup

1

Create a response buffer

Create a bigstring buffer for response headers:
let response_buffer_size = 65536  (* 64KB *)
let write_buf = 
  Bigarray.Array1.create Bigarray.char Bigarray.c_layout response_buffer_size
2

Initialize offset

Start writing at offset 0:
let off = Httpz.Buf_write.i16 0
3

Write response components

Each write function returns the new offset:
let off = Res.write_status_line write_buf ~off Res.Success Version.Http_1_1 in
let off = Res.write_header write_buf ~off "Content-Type" "text/html" in
let off = Res.write_crlf write_buf ~off in
(* Send buffer from 0 to off *)

Status Codes

Httpz provides a comprehensive Res.status type with all standard HTTP status codes:

Success Responses (2xx)

Res.Success                 (* 200 OK *)
Res.Created                 (* 201 Created *)
Res.Accepted                (* 202 Accepted *)
Res.No_content              (* 204 No Content *)
Res.Partial_content         (* 206 Partial Content *)

Redirections (3xx)

Res.Moved_permanently       (* 301 Moved Permanently *)
Res.Found                   (* 302 Found *)
Res.See_other               (* 303 See Other *)
Res.Not_modified            (* 304 Not Modified *)
Res.Temporary_redirect      (* 307 Temporary Redirect *)
Res.Permanent_redirect      (* 308 Permanent Redirect *)

Client Errors (4xx)

Res.Bad_request             (* 400 Bad Request *)
Res.Unauthorized            (* 401 Unauthorized *)
Res.Forbidden               (* 403 Forbidden *)
Res.Not_found               (* 404 Not Found *)
Res.Method_not_allowed      (* 405 Method Not Allowed *)
Res.Request_timeout         (* 408 Request Timeout *)
Res.Payload_too_large       (* 413 Payload Too Large *)
Res.Range_not_satisfiable   (* 416 Range Not Satisfiable *)
Res.Too_many_requests       (* 429 Too Many Requests *)

Server Errors (5xx)

Res.Internal_server_error   (* 500 Internal Server Error *)
Res.Not_implemented         (* 501 Not Implemented *)
Res.Service_unavailable     (* 503 Service Unavailable *)
See /home/daytona/workspace/source/lib/res.mli:8 for the complete list of status codes.

Writing Status Line

The status line is written with write_status_line:
val write_status_line : 
  Base_bigstring.t -> off:int16# -> status -> Version.t -> int16#

Example

(* Write "HTTP/1.1 200 OK\r\n" *)
let off = Res.write_status_line buf ~off:(i16 0) Res.Success Version.Http_1_1 in

(* Write "HTTP/1.0 404 Not Found\r\n" *)
let off = Res.write_status_line buf ~off Res.Not_found Version.Http_1_0 in

Writing Headers

String Header Names

For custom headers, use write_header:
(* Write "Content-Type: application/json\r\n" *)
let off = Res.write_header buf ~off "Content-Type" "application/json" in

(* Write "Cache-Control: no-cache\r\n" *)
let off = Res.write_header buf ~off "Cache-Control" "no-cache" in

Typed Header Names

For known headers, use write_header_name for better performance:
(* Write "Content-Type: text/html\r\n" *)
let off = Res.write_header_name buf ~off 
  Header_name.Content_type "text/html" in

(* Write "Server: httpz/1.0\r\n" *)
let off = Res.write_header_name buf ~off 
  Header_name.Server "httpz/1.0" in

Integer Header Values

For integer values, use the _int variants:
(* Write "Max-Age: 3600\r\n" *)
let off = Res.write_header_int buf ~off "Max-Age" 3600 in

(* Write "Content-Length: 1234\r\n" *)
let off = Res.write_header_name_int buf ~off 
  Header_name.Content_length 1234 in

Common Headers

Httpz provides specialized functions for common headers:
(* Write "Content-Length: 4096\r\n" *)
let off = Res.write_content_length buf ~off 4096

Complete Response Example

Simple Success Response

let send_success_response writer content =
  let buf = create_response_buffer () in
  let off = i16 0 in
  
  (* Status line *)
  let off = Res.write_status_line buf ~off Res.Success Version.Http_1_1 in
  
  (* Headers *)
  let off = Res.write_header_name buf ~off 
    Header_name.Content_type "text/plain" in
  let off = Res.write_content_length buf ~off (String.length content) in
  let off = Res.write_connection buf ~off ~keep_alive:true in
  
  (* End of headers *)
  let off = Res.write_crlf buf ~off in
  
  (* Send headers *)
  Writer.write_bigstring writer buf ~pos:0 ~len:(to_int off);
  
  (* Send body *)
  Writer.write writer content;
  Writer.flushed writer

Error Response

let send_404_response writer =
  let buf = create_response_buffer () in
  let message = "Not Found" in
  let off = i16 0 in
  
  let off = Res.write_status_line buf ~off Res.Not_found Version.Http_1_1 in
  let off = Res.write_header_name buf ~off 
    Header_name.Content_type "text/plain" in
  let off = Res.write_content_length buf ~off (String.length message) in
  let off = Res.write_connection buf ~off ~keep_alive:false in
  let off = Res.write_crlf buf ~off in
  
  Writer.write_bigstring writer buf ~pos:0 ~len:(to_int off);
  Writer.write writer message;
  Writer.flushed writer

JSON Response

let send_json_response writer json_string =
  let buf = create_response_buffer () in
  let off = i16 0 in
  
  let off = Res.write_status_line buf ~off Res.Success Version.Http_1_1 in
  let off = Res.write_header_name buf ~off 
    Header_name.Content_type "application/json" in
  let off = Res.write_content_length buf ~off (String.length json_string) in
  let off = Res.write_header buf ~off "Cache-Control" "no-cache" in
  let off = Res.write_connection buf ~off ~keep_alive:true in
  let off = Res.write_crlf buf ~off in
  
  Writer.write_bigstring writer buf ~pos:0 ~len:(to_int off);
  Writer.write writer json_string;
  Writer.flushed writer

Redirects

let send_redirect writer location ~permanent =
  let buf = create_response_buffer () in
  let off = i16 0 in
  
  let status = if permanent then Res.Moved_permanently else Res.Found in
  let off = Res.write_status_line buf ~off status Version.Http_1_1 in
  let off = Res.write_header_name buf ~off Header_name.Location location in
  let off = Res.write_content_length buf ~off 0 in
  let off = Res.write_connection buf ~off ~keep_alive:true in
  let off = Res.write_crlf buf ~off in
  
  Writer.write_bigstring writer buf ~pos:0 ~len:(to_int off);
  Writer.flushed writer

304 Not Modified Response

For conditional requests with ETags:
let send_not_modified writer ~etag ~last_modified =
  let buf = create_response_buffer () in
  let off = i16 0 in
  
  let off = Res.write_status_line buf ~off Res.Not_modified Version.Http_1_1 in
  let off = Res.write_header buf ~off "ETag" etag in
  let off = Date.write_last_modified buf ~off 
    (Float_u.of_float last_modified) in
  let off = Res.write_connection buf ~off ~keep_alive:true in
  let off = Res.write_crlf buf ~off in
  
  Writer.write_bigstring writer buf ~pos:0 ~len:(to_int off);
  Writer.flushed writer
See the ETags guide for more information on conditional requests.

Writing Chunked Responses

For responses with unknown content length:
let send_chunked_response writer =
  let buf = create_response_buffer () in
  let off = i16 0 in
  
  (* Write headers with Transfer-Encoding: chunked *)
  let off = Res.write_status_line buf ~off Res.Success Version.Http_1_1 in
  let off = Res.write_header_name buf ~off 
    Header_name.Content_type "text/plain" in
  let off = Res.write_transfer_encoding_chunked buf ~off in
  let off = Res.write_connection buf ~off ~keep_alive:true in
  let off = Res.write_crlf buf ~off in
  
  Writer.write_bigstring writer buf ~pos:0 ~len:(to_int off);
  
  (* Send chunks *)
  let chunk_data = "Hello, World!" in
  let chunk_buf = create_response_buffer () in
  let off = i16 0 in
  let off = Res.write_chunk_header chunk_buf ~off ~size:(String.length chunk_data) in
  Writer.write_bigstring writer chunk_buf ~pos:0 ~len:(to_int off);
  Writer.write writer chunk_data;
  
  let off = i16 0 in
  let off = Res.write_chunk_footer chunk_buf ~off in
  Writer.write_bigstring writer chunk_buf ~pos:0 ~len:(to_int off);
  
  (* Final chunk *)
  let off = i16 0 in
  let off = Res.write_final_chunk chunk_buf ~off in
  Writer.write_bigstring writer chunk_buf ~pos:0 ~len:(to_int off);
  Writer.flushed writer
See the Chunked Encoding guide for detailed information on chunked transfer encoding.

Status Code Utilities

The Res module provides utility functions for working with status codes:
(* Get numeric code *)
let code = Res.status_code Res.Not_found in  (* 404 *)

(* Get reason phrase *)
let reason = Res.status_reason Res.Not_found in  (* "Not Found" *)

(* Get "CODE Reason" string *)
let full = Res.status_to_string Res.Not_found in  (* "404 Not Found" *)

(* Pretty-print *)
Format.printf "%a@." Res.pp_status Res.Success

Best Practices

  1. Reuse buffers: Create response buffers once and reuse them across requests
  2. Thread offsets: Always use the returned offset for the next write operation
  3. Measure buffer usage: Ensure your buffer is large enough for all headers
  4. Use typed headers: Prefer write_header_name over write_header for known headers
  5. Zero-copy I/O: Use Writer.write_bigstring to send headers without copying

See Also

Build docs developers (and LLMs) love