FastCGI implementation in OCaml
1(** Responder role implementation for FastCGI.
2
3 The Responder role is the most common FastCGI role, equivalent to
4 traditional CGI applications. It handles HTTP requests and generates
5 HTTP responses.
6
7 A Responder FastCGI application has the same purpose as a CGI/1.1 program:
8 it receives all the information associated with an HTTP request and generates
9 an HTTP response. This includes CGI environment variables, request headers,
10 request body data, and produces HTTP response headers and body content.
11
12 The key difference from traditional CGI is that Responder applications are
13 long-lived processes that can handle multiple requests efficiently, avoiding
14 the overhead of process creation for each request.
15
16 This module provides high-level abstractions for working with HTTP requests
17 and responses, automatically handling the conversion between FastCGI protocol
18 elements and familiar HTTP concepts. *)
19
20(** {1 HTTP Request/Response Types} *)
21
22(** CGI-style environment variables.
23
24 A list of name-value pairs containing the CGI environment variables
25 passed from the web server. These typically include variables like
26 REQUEST_METHOD, REQUEST_URI, HTTP_HOST, CONTENT_TYPE, etc. *)
27type cgi_env = (string * string) list
28
29(** HTTP request information extracted from FastCGI parameters.
30
31 This type represents an HTTP request in a convenient form for application
32 processing. It extracts and parses the most commonly needed information
33 from the CGI environment variables provided by the web server. *)
34type 'a http_request = {
35 method_ : string; (** HTTP method (GET, POST, PUT, DELETE, etc.).
36 Extracted from REQUEST_METHOD CGI variable. *)
37 uri : string; (** Request URI path component, without query string.
38 Extracted from REQUEST_URI or SCRIPT_NAME + PATH_INFO. *)
39 query_string : string; (** URL query string parameters (after the '?' in the URL).
40 Extracted from QUERY_STRING CGI variable. *)
41 content_type : string option; (** MIME type of the request body, if present.
42 Extracted from CONTENT_TYPE CGI variable. *)
43 content_length : int option; (** Length of the request body in bytes, if known.
44 Extracted from CONTENT_LENGTH CGI variable. *)
45 headers : (string * string) list; (** HTTP headers sent by the client. Extracted from
46 CGI variables with HTTP_ prefix. *)
47 body : 'a Eio.Flow.source; (** Request body data stream. For POST requests,
48 this contains form data or payload content. *)
49}
50
51(** HTTP response builder for constructing responses.
52
53 This type provides a streaming interface for generating HTTP responses.
54 It handles proper HTTP formatting including status line, headers, and
55 body content. The response is written incrementally to avoid buffering
56 large responses in memory. *)
57type 'a http_response = {
58 write_status : int -> unit; (** Set the HTTP status code (200, 404, 500, etc.).
59 Must be called before writing headers or body. *)
60 write_header : string -> string -> unit; (** Add an HTTP response header.
61 Common headers include Content-Type, Set-Cookie, etc. *)
62 write_body : string -> unit; (** Write string content to the response body.
63 Handles encoding and streaming automatically. *)
64 write_body_chunk : bytes -> unit; (** Write binary data to the response body.
65 Useful for file downloads or binary content. *)
66 finish : unit -> unit; (** Complete the response and close the connection.
67 Must be called to ensure proper termination. *)
68}
69
70(** {1 Conversion Functions} *)
71
72(** Convert FastCGI request to HTTP request.
73
74 Extracts HTTP-specific information from FastCGI parameters
75 and creates an HTTP request object.
76
77 @param request FastCGI request
78 @return HTTP request with extracted information *)
79val request_of_fastcgi : 'a Fastcgi_types.request -> 'a http_request
80
81(** Create HTTP response writer from FastCGI response.
82
83 Wraps FastCGI response streams to provide HTTP-style
84 response writing interface.
85
86 @param response FastCGI response
87 @return HTTP response writer *)
88val response_of_fastcgi : 'a Fastcgi_types.response -> 'a http_response
89
90(** {1 Handler Utilities} *)
91
92(** Responder handler type. *)
93type 'a responder_handler = 'a Fastcgi_types.request -> 'a Fastcgi_types.response -> Fastcgi_types.response_result
94
95(** Convenience handler wrapper for HTTP-style handlers.
96
97 Converts an HTTP-style handler function into a FastCGI handler.
98 This allows writing handlers that work with HTTP request/response
99 objects instead of raw FastCGI types.
100
101 @param handler HTTP handler function
102 @return FastCGI responder handler *)
103val http_handler :
104 ('a http_request -> 'a http_response -> unit) ->
105 'a responder_handler
106
107(** {1 Common HTTP Operations} *)
108
109(** Send a simple text response.
110
111 @param response HTTP response writer
112 @param status HTTP status code
113 @param content_type MIME type
114 @param body Response body text *)
115val send_text_response :
116 'a http_response ->
117 status:int ->
118 content_type:string ->
119 body:string ->
120 unit
121
122(** Send a JSON response.
123
124 @param response HTTP response writer
125 @param status HTTP status code
126 @param json JSON string *)
127val send_json_response :
128 'a http_response ->
129 status:int ->
130 json:string ->
131 unit
132
133(** Send an HTML response.
134
135 @param response HTTP response writer
136 @param status HTTP status code
137 @param html HTML content *)
138val send_html_response :
139 'a http_response ->
140 status:int ->
141 html:string ->
142 unit
143
144(** Send an error response.
145
146 @param response HTTP response writer
147 @param status HTTP error status code
148 @param message Error message *)
149val send_error_response :
150 'a http_response ->
151 status:int ->
152 message:string ->
153 unit
154
155(** Send a redirect response.
156
157 @param response HTTP response writer
158 @param status Redirect status code (301, 302, etc.)
159 @param location Target URL *)
160val send_redirect_response :
161 'a http_response ->
162 status:int ->
163 location:string ->
164 unit
165
166(** {1 Request Parsing} *)
167
168(** Parse query string into parameter map.
169
170 @param query_string URL-encoded query string
171 @return Association list of parameter name-value pairs *)
172val parse_query_string : string -> (string * string) list
173
174(** Parse form data from request body.
175
176 Supports both application/x-www-form-urlencoded and
177 multipart/form-data content types.
178
179 @param request HTTP request
180 @return Association list of form field name-value pairs *)
181val parse_form_data : 'a http_request -> (string * string) list
182
183(** Get request header value.
184
185 @param request HTTP request
186 @param name Header name (case-insensitive)
187 @return Header value if present *)
188val get_header : 'a http_request -> string -> string option
189
190(** Get request parameter from query string or form data.
191
192 @param request HTTP request
193 @param name Parameter name
194 @return Parameter value if present *)
195val get_param : 'a http_request -> string -> string option
196
197(** {1 File Operations} *)
198
199(** File upload information. *)
200type 'a file_upload = {
201 filename : string option; (** Original filename *)
202 content_type : string option; (** MIME type *)
203 size : int; (** File size in bytes *)
204 data : 'a Eio.Flow.source; (** File content stream *)
205}
206
207(** Parse file uploads from multipart form data.
208
209 @param request HTTP request with multipart content
210 @return Association list of field name to file upload *)
211val parse_file_uploads : 'a http_request -> (string * 'a file_upload) list
212
213(** Save uploaded file to filesystem.
214
215 @param fs Filesystem capability
216 @param path Destination path
217 @param upload File upload to save *)
218val save_upload :
219 fs:Eio.Fs.dir_ty Eio.Path.t ->
220 path:string ->
221 upload:'a file_upload ->
222 unit