FastCGI implementation in OCaml
1(** Core FastCGI types and constants.
2
3 This module defines the fundamental types used throughout the
4 FastCGI protocol implementation.
5
6 FastCGI is an open extension to CGI that provides high performance for
7 all Internet applications without the penalties of Web server APIs.
8 Unlike traditional CGI programs that are started for each request,
9 FastCGI applications are long-lived processes that can handle multiple
10 requests over persistent connections. *)
11
12(** {1 Core Types} *)
13
14(** FastCGI protocol version.
15
16 The current protocol uses version 1. Future versions of the
17 protocol may increment this value. Applications should check the version
18 field in incoming records and reject unsupported versions.
19
20 Version 1 is represented by the value [1]. *)
21type version = int
22
23(** FastCGI record types.
24
25 All data transmitted over a FastCGI connection is packaged in records.
26 Each record has a type that determines how the record's content should
27 be interpreted. The protocol defines both management records (for
28 protocol-level communication) and application records (for request
29 processing).
30
31 Management records contain information that is not specific to any web
32 server request, such as capability queries and unknown type responses.
33 Application records contain information about a particular request,
34 identified by a non-zero request ID. *)
35type record_type =
36 | Begin_request (** Starts a new request. Sent by the web server to begin
37 processing. Contains the role and connection flags. *)
38 | Abort_request (** Aborts an existing request. Sent by the web server
39 when an HTTP client closes its connection while the
40 request is still being processed. *)
41 | End_request (** Completes a request. Sent by the application to
42 terminate processing, either normally or due to
43 an error condition. *)
44 | Params (** Parameter name-value pairs. Used to transmit CGI-style
45 environment variables from the web server to the
46 application. Sent as a stream. *)
47 | Stdin (** Standard input data. Contains the request body data
48 that would normally be available on stdin in CGI.
49 Sent as a stream. *)
50 | Stdout (** Standard output data. Response data from the application
51 to the web server. This becomes the HTTP response.
52 Sent as a stream. *)
53 | Stderr (** Standard error data. Error messages from the application.
54 Used for logging and debugging. Sent as a stream. *)
55 | Data (** Additional data stream. Used only in the Filter role
56 to transmit file data that needs to be filtered.
57 Sent as a stream. *)
58 | Get_values (** Management record to query application variables.
59 Allows the web server to discover application
60 capabilities like max connections and multiplexing. *)
61 | Get_values_result (** Management record response to Get_values. Contains
62 the requested variable values. *)
63 | Unknown_type (** Management record for unknown type handling. Sent by
64 the application when it receives a record type it
65 doesn't understand. *)
66
67(** FastCGI application roles.
68
69 A FastCGI application can play one of several well-defined roles.
70 The role determines what information the application receives and
71 what it's expected to produce.
72
73 A FastCGI application plays one of several well-defined roles. The most
74 familiar is the Responder role, in which the application receives all
75 the information associated with an HTTP request and generates an HTTP
76 response. *)
77type role =
78 | Responder (** The most common role, equivalent to traditional CGI.
79 The application receives all information associated with
80 an HTTP request and generates an HTTP response. This
81 includes environment variables, request headers, and
82 request body data. *)
83 | Authorizer (** Performs access control decisions. The application
84 receives HTTP request information and generates an
85 authorized/unauthorized decision. In case of authorization,
86 it can associate additional variables with the request.
87 For unauthorized requests, it provides a complete HTTP
88 error response. *)
89 | Filter (** Processes data streams with filtering. The application
90 receives HTTP request information plus an additional
91 data stream from a file stored on the web server, and
92 generates a "filtered" version of the data as an HTTP
93 response. Both the file and the filter can be access
94 controlled. *)
95
96(** Request ID for multiplexing multiple requests over a single connection.
97
98 FastCGI supports multiplexing, allowing multiple concurrent requests
99 to be processed over a single transport connection. Each request is
100 identified by a unique request ID within the scope of that connection.
101
102 The Web server re-uses FastCGI request IDs; the application keeps track
103 of the current state of each request ID on a given transport connection.
104 Request IDs should be small integers to allow efficient tracking using
105 arrays rather than hash tables.
106
107 The value [0] is reserved for management records and is called the
108 "null request ID". *)
109type request_id = int
110
111(** Application-level status code.
112
113 When an application completes a request, it provides an application
114 status code similar to the exit status of a traditional CGI program.
115 A value of [0] indicates success, while non-zero values indicate
116 various error conditions.
117
118 The application sets the appStatus component to the status code that
119 the CGI program would have returned via the exit system call. *)
120type app_status = int
121
122(** Protocol-level status codes.
123
124 In addition to application status, each request completion includes
125 a protocol-level status that indicates whether the request was
126 processed normally or rejected for protocol-related reasons.
127
128 These status codes allow the web server to understand why a request
129 was not processed and take appropriate action. *)
130type protocol_status =
131 | Request_complete (** Normal end of request. The application successfully
132 processed the request and the appStatus field
133 indicates the application-level result. *)
134 | Cant_mpx_conn (** Rejecting a new request because the application
135 cannot multiplex connections. This happens when
136 a web server sends concurrent requests over one
137 connection to an application that processes only
138 one request at a time per connection. *)
139 | Overloaded (** Rejecting a new request because the application
140 is overloaded. This occurs when the application
141 runs out of some resource, such as database
142 connections or memory. *)
143 | Unknown_role (** Rejecting a new request because the requested
144 role is unknown to the application. This happens
145 when the web server specifies a role that the
146 application doesn't implement. *)
147
148(** Connection flags for controlling connection behavior.
149
150 These flags are sent in Begin_request records to control how the
151 connection should be managed after the request completes. *)
152type connection_flags = {
153 keep_conn : bool; (** Keep connection open after request completion.
154
155 If false, the application closes the connection after
156 responding to this request. If true, the application
157 does not close the connection after responding to this
158 request; the Web server retains responsibility for the
159 connection.
160
161 This flag enables connection reuse for better performance,
162 especially important for high-traffic applications. *)
163}
164
165(** {1 Record Types} *)
166
167(** FastCGI record header.
168
169 Every FastCGI record begins with an 8-byte header that identifies
170 the record type, request ID, and content length. This fixed-length
171 prefix allows efficient parsing and proper demultiplexing of records.
172
173 A FastCGI record consists of a fixed-length prefix followed by a
174 variable number of content and padding bytes.
175
176 The header format is platform-independent and uses network byte order
177 for multi-byte integers. *)
178type record_header = {
179 version : int; (** Protocol version. Must be [1] for this specification.
180 Future versions may increment this value. *)
181 record_type : record_type; (** Type of this record, determining how to interpret
182 the content data. *)
183 request_id : request_id; (** Request ID this record belongs to. Value [0] is
184 reserved for management records. *)
185 content_length : int; (** Number of bytes in the content data. Must be
186 between [0] and [65535]. *)
187 padding_length : int; (** Number of padding bytes following content.
188 Must be between [0] and [255]. Used for alignment. *)
189}
190
191(** Begin request record body.
192
193 This record marks the start of a new request and specifies the role
194 the application should play and connection management flags.
195
196 The Web server sends a FCGI_BEGIN_REQUEST record to start a request.
197 The record body contains the role and flags that control request
198 processing. *)
199type begin_request_body = {
200 role : role; (** The role the web server expects the application
201 to play for this request. *)
202 flags : connection_flags; (** Flags controlling connection behavior after
203 request completion. *)
204}
205
206(** End request record body.
207
208 This record marks the completion of a request and provides both
209 application-level and protocol-level status information.
210
211 The application sends a FCGI_END_REQUEST record to terminate a request,
212 either because the application has processed the request or because the
213 application has rejected the request. *)
214type end_request_body = {
215 app_status : app_status; (** Application-level status code, similar to
216 a CGI program's exit status. *)
217 protocol_status : protocol_status; (** Protocol-level status indicating normal
218 completion or rejection reason. *)
219}
220
221(** Complete FastCGI record.
222
223 A complete record consists of the header, content data, and optional
224 padding. The content interpretation depends on the record type.
225
226 Records support padding to allow senders to keep data aligned for more
227 efficient processing. Experience with the X window system protocols shows
228 the performance benefit of such alignment. *)
229type record = {
230 header : record_header; (** Fixed 8-byte header with record metadata. *)
231 content : bytes; (** Variable-length content data. Length must match
232 header.content_length. *)
233 padding : bytes option; (** Optional padding data for alignment. Length must
234 match header.padding_length if present. *)
235}
236
237(** Name-value pair for parameters.
238
239 FastCGI uses a compact binary encoding for transmitting name-value pairs
240 such as CGI environment variables. This encoding supports both short
241 and long names/values efficiently.
242
243 FastCGI transmits a name-value pair as the length of the name, followed
244 by the length of the value, followed by the name, followed by the value.
245 Lengths of 127 bytes and less can be encoded in one byte, while longer
246 lengths are always encoded in four bytes. *)
247type name_value_pair = {
248 name : string; (** Parameter name. For CGI compatibility, these are typically
249 uppercase environment variable names like "REQUEST_METHOD". *)
250 value : string; (** Parameter value. The value does not include a terminating
251 null byte in the FastCGI encoding. *)
252}
253
254(** {1 Request/Response Model} *)
255
256(** Request context containing all information for a FastCGI request.
257
258 This high-level type aggregates all the information needed to process
259 a FastCGI request. It combines the protocol-level details (request ID,
260 role, flags) with the application data (parameters and input streams).
261
262 The request follows a two-level processing model: First, the protocol
263 multiplexes a single transport connection between several independent
264 FastCGI requests. Second, within each request the protocol provides
265 several independent data streams in each direction. *)
266type 'a request = {
267 request_id : request_id; (** Unique identifier for this request within
268 the connection scope. Used for multiplexing. *)
269 role : role; (** The role this application should play for
270 this request (Responder, Authorizer, or Filter). *)
271 flags : connection_flags; (** Connection management flags from the web server. *)
272 params : (string * string) list; (** CGI-style environment variables transmitted
273 via FCGI_PARAMS records. These provide request
274 context like HTTP headers, server info, etc. *)
275 stdin : 'a Eio.Flow.source; (** Standard input stream, containing request body
276 data (equivalent to CGI stdin). For HTTP POST
277 requests, this contains the form data or payload. *)
278 data : 'a Eio.Flow.source option; (** Additional data stream, used only in Filter
279 role. Contains file data that needs to be
280 filtered. [None] for Responder and Authorizer. *)
281}
282
283(** Response builder for constructing FastCGI responses.
284
285 This type provides the output streams for sending response data back
286 to the web server. Following CGI conventions, it separates normal
287 output from error output.
288
289 Both stdout and stderr data pass over a single transport connection from
290 the application to the Web server, rather than requiring separate pipes
291 as with CGI/1.1. *)
292type 'a response = {
293 stdout : 'a Eio.Flow.sink; (** Standard output stream for response data.
294 In Responder role, this contains the HTTP
295 response including headers and body. *)
296 stderr : 'a Eio.Flow.sink; (** Standard error stream for logging and debugging.
297 All role protocols use the FCGI_STDERR stream
298 just the way stderr is used in conventional
299 applications programming: to report application-level
300 errors in an intelligible way. *)
301}
302
303(** Complete response with status.
304
305 When an application finishes processing a request, it must provide
306 both application-level and protocol-level status information. This
307 allows the web server to understand the outcome and take appropriate
308 action.
309
310 This corresponds to the FCGI_END_REQUEST record sent by the application. *)
311type response_result = {
312 app_status : app_status; (** Application exit status, equivalent to what
313 a CGI program would return via exit(). *)
314 protocol_status : protocol_status; (** Protocol-level completion status, indicating
315 normal completion or various rejection reasons. *)
316}
317
318(** {1 Protocol Constants} *)
319
320(** Protocol version constant.
321
322 This constant represents FCGI_VERSION_1. This value should be used in
323 the version field of all record headers. *)
324val version_1 : int
325
326(** Standard file descriptor for FastCGI listening socket.
327
328 The Web server leaves a single file descriptor, FCGI_LISTENSOCK_FILENO,
329 open when the application begins execution. This descriptor refers to a
330 listening socket created by the Web server.
331
332 The value equals STDIN_FILENO (0). Applications can distinguish between
333 CGI and FastCGI invocation by calling getpeername() on this descriptor. *)
334val listensock_fileno : int
335
336(** {2 Record Type Constants}
337
338 These integer constants correspond to the record_type variants and are
339 used in the binary protocol encoding. These are the values transmitted
340 in the type field of record headers. *)
341
342(** Value [1]. Starts a new request. *)
343val fcgi_begin_request : int
344
345(** Value [2]. Aborts an existing request. *)
346val fcgi_abort_request : int
347
348(** Value [3]. Completes a request. *)
349val fcgi_end_request : int
350
351(** Value [4]. Parameter name-value pairs. *)
352val fcgi_params : int
353
354(** Value [5]. Standard input data. *)
355val fcgi_stdin : int
356
357(** Value [6]. Standard output data. *)
358val fcgi_stdout : int
359
360(** Value [7]. Standard error data. *)
361val fcgi_stderr : int
362
363(** Value [8]. Additional data stream (Filter). *)
364val fcgi_data : int
365
366(** Value [9]. Query application variables. *)
367val fcgi_get_values : int
368
369(** Value [10]. Response to Get_values. *)
370val fcgi_get_values_result : int
371
372(** Value [11]. Unknown record type response. *)
373val fcgi_unknown_type : int
374
375(** {2 Role Constants}
376
377 These integer constants correspond to the role variants and are used
378 in Begin_request record bodies. These identify the role the web server
379 expects the application to play. *)
380
381(** Value [1]. Handle HTTP requests and generate responses. *)
382val fcgi_responder : int
383
384(** Value [2]. Perform authorization decisions. *)
385val fcgi_authorizer : int
386
387(** Value [3]. Process data streams with filtering. *)
388val fcgi_filter : int
389
390(** {2 Flag Constants}
391
392 These constants are used in the flags field of Begin_request records
393 to control connection behavior. *)
394
395(** Value [1]. Keep connection open after request.
396 If zero, the application closes the connection after
397 responding to this request. If not zero, the application
398 does not close the connection after responding to this
399 request. *)
400val fcgi_keep_conn : int
401
402(** {2 Protocol Limits}
403
404 These constants define the maximum sizes for various protocol elements,
405 ensuring compatibility and preventing buffer overflows. *)
406
407(** Value [65535]. Maximum bytes in record content.
408 Between 0 and 65535 bytes of data, interpreted
409 according to the record type. *)
410val max_content_length : int
411
412(** Value [255]. Maximum bytes in record padding.
413 Between 0 and 255 bytes of data, which are ignored. *)
414val max_padding_length : int
415
416(** Value [8]. Fixed size of record headers.
417 Number of bytes in a FCGI_Header. Future versions
418 of the protocol will not reduce this number. *)
419val header_length : int
420
421(** {2 Management Record Variables}
422
423 These string constants identify the standard management variables that
424 can be queried using FCGI_GET_VALUES records. They allow the web server
425 to discover application capabilities.
426
427 The initial set provides information to help the server perform application
428 and connection management. *)
429
430(** Variable name "FCGI_MAX_CONNS". The maximum number
431 of concurrent transport connections this application
432 will accept, e.g. "1" or "10". *)
433val fcgi_max_conns : string
434
435(** Variable name "FCGI_MAX_REQS". The maximum number
436 of concurrent requests this application will accept,
437 e.g. "1" or "50". *)
438val fcgi_max_reqs : string
439
440(** Variable name "FCGI_MPXS_CONNS". "0" if this
441 application does not multiplex connections (i.e.
442 handle concurrent requests over each connection),
443 "1" otherwise. *)
444val fcgi_mpxs_conns : string