FastCGI implementation in OCaml
1(** FastCGI protocol implementation for OCaml using Eio.
2
3 This library provides a type-safe, high-performance implementation of the
4 FastCGI protocol using OCaml's Eio effects-based IO library. It supports
5 all three FastCGI roles: Responder, Authorizer, and Filter.
6
7 FastCGI is an open extension to CGI that provides high performance for all
8 Internet applications without the penalties of Web server APIs. Unlike
9 traditional CGI programs that are started for each request, FastCGI
10 applications are long-lived processes that can handle multiple requests
11 over persistent connections.
12
13 The library follows Eio conventions for structured concurrency,
14 capability-based security, and resource management. All network I/O is
15 performed using Eio's effects system, enabling high-performance concurrent
16 request processing.
17
18 {2 Key Features}
19
20 - {b Type Safety}: Leverages OCaml's type system to prevent protocol errors
21 - {b High Performance}: Connection multiplexing and keep-alive for efficiency
22 - {b Structured Concurrency}: Automatic resource cleanup using Eio switches
23 - {b All FastCGI Roles}: Complete support for Responder, Authorizer, and Filter
24 - {b Standards Compliant}: Full implementation of FastCGI Protocol 1.0
25
26 {2 Usage Example}
27
28 {[
29 let hello_handler request response =
30 let http_req = Responder.request_of_fastcgi request in
31 let http_resp = Responder.response_of_fastcgi response in
32
33 http_resp.write_status 200;
34 http_resp.write_header "Content-Type" "text/html";
35 http_resp.write_body "<h1>Hello, FastCGI!</h1>";
36 http_resp.finish ();
37
38 { app_status = 0; protocol_status = Request_complete }
39
40 let () = Eio_main.run @@ fun env ->
41 let net = Eio.Stdenv.net env in
42 Switch.run @@ fun sw ->
43 Server.run_default ~sw ~net
44 ~handler:(Handler.Responder hello_handler)
45 ~listen_address:(`Tcp (Eio.Net.Sockaddr.stream, 9000))
46 ]}
47
48 {2 References}
49
50 - {{:https://github.com/ocaml-multicore/eio} Eio Documentation} *)
51
52(** {1 Core Types} *)
53
54(** Re-export core types from Fastcgi_types.
55
56 This includes all the fundamental FastCGI protocol types such as
57 [record_type], [role], [request], [response], and protocol constants.
58 This includes all the fundamental FastCGI protocol types. *)
59include module type of Fastcgi_types
60
61(** {1 Protocol Implementation} *)
62
63(** Wire protocol parsing and serialization. *)
64module Protocol = Fastcgi_protocol
65
66(** {1 Role-Specific Implementations} *)
67
68(** Responder role implementation. *)
69module Responder = Fastcgi_responder
70
71(** Authorizer role implementation. *)
72module Authorizer = Fastcgi_authorizer
73
74(** Filter role implementation. *)
75module Filter = Fastcgi_filter
76
77(** {1 Application Interface} *)
78
79(** Application handler signatures.
80
81 This module defines the core handler types that applications implement
82 to process FastCGI requests. Each handler type corresponds to one of
83 the three FastCGI roles: Responder, Authorizer, and Filter. *)
84module Handler : sig
85 (** Responder handler: process HTTP request and generate response.
86
87 This is the most common handler type, equivalent to traditional CGI.
88 A Responder FastCGI application has the same purpose as a CGI/1.1 program:
89 it receives all the information associated with an HTTP request and generates
90 an HTTP response.
91
92 @param request Complete FastCGI request with parameters and input streams
93 @param response Output streams for writing response data
94 @return Response result with application and protocol status *)
95 type 'a responder = 'a request -> 'a response -> response_result
96
97 (** Authorizer handler: make authorization decision.
98
99 An Authorizer FastCGI application receives all the information associated
100 with an HTTP request and generates an authorized/unauthorized decision.
101 In case of an authorized decision, the Authorizer can also associate
102 name-value pairs with the HTTP request.
103
104 @param request FastCGI request with authentication context
105 @param response Output streams for authorization response
106 @return Response result indicating authorization decision *)
107 type 'a authorizer = 'a request -> 'a response -> response_result
108
109 (** Filter handler: process data stream with filtering.
110
111 A Filter FastCGI application receives all the information associated
112 with an HTTP request, plus an extra stream of data from a file stored
113 on the Web server, and generates a 'filtered' version of the data stream
114 as an HTTP response.
115
116 @param request FastCGI request including both HTTP context and file data
117 @param response Output streams for filtered response
118 @return Response result with filtered content status *)
119 type 'a filter = 'a request -> 'a response -> response_result
120
121 (** Generic handler that can handle any role.
122
123 This variant type allows applications to support multiple roles or
124 to be configured at runtime for different roles. The web server
125 specifies the desired role in each Begin_request record. *)
126 type 'a handler =
127 | Responder of 'a responder (** Handle HTTP requests and responses *)
128 | Authorizer of 'a authorizer (** Handle authorization decisions *)
129 | Filter of 'a filter (** Handle data filtering *)
130end
131
132(** Application configuration.
133
134 This type encapsulates all the settings needed to configure a FastCGI
135 application's behavior. It includes both resource limits and the
136 application logic (handler function).
137
138 The configuration values affect how the application responds to
139 FCGI_GET_VALUES management queries from the web server. *)
140type 'a app_config = {
141 max_connections : int; (** Maximum concurrent transport connections
142 this application will accept. Corresponds to
143 FCGI_MAX_CONNS management variable. *)
144 max_requests : int; (** Maximum concurrent requests this application
145 will accept across all connections. Corresponds
146 to FCGI_MAX_REQS management variable. *)
147 multiplex_connections : bool; (** Whether this application can multiplex
148 connections (handle concurrent requests over
149 each connection). Corresponds to FCGI_MPXS_CONNS
150 management variable. *)
151 handler : 'a Handler.handler; (** Application request handler that implements
152 the core application logic. *)
153}
154
155(** {1 Connection Management} *)
156
157(** Connection manager for handling FastCGI protocol.
158
159 This module provides low-level connection management for FastCGI applications.
160 It handles the binary protocol details, record parsing/serialization, and
161 request multiplexing over individual transport connections.
162
163 After a FastCGI process accepts a connection on its listening socket, the
164 process executes a simple protocol to receive and send data. The protocol
165 serves two purposes: First, it multiplexes a single transport connection
166 between several independent FastCGI requests. Second, within each request
167 the protocol provides several independent data streams in each direction. *)
168module Connection : sig
169 (** Opaque connection type.
170
171 Represents a single transport connection from a web server. The connection
172 handles protocol parsing, request demultiplexing, and stream management.
173 Each connection can handle multiple concurrent requests if the application
174 supports multiplexing. *)
175 type 'a t
176
177 (** Connection statistics.
178
179 Provides runtime metrics about connection usage for monitoring and
180 debugging purposes. These statistics can help tune application
181 performance and detect potential issues. *)
182 type stats = {
183 active_requests : int; (** Number of requests currently being processed
184 on this connection. *)
185 total_requests : int; (** Total number of requests processed since
186 connection establishment. *)
187 bytes_sent : int; (** Total bytes sent to the web server (responses). *)
188 bytes_received : int; (** Total bytes received from the web server (requests). *)
189 }
190
191 (** Create a connection from a network flow.
192
193 Initializes a new FastCGI connection over an established transport.
194 The connection will parse incoming records and manage request state
195 according to the FastCGI protocol.
196
197 The connection handles a simple protocol to receive and send data,
198 including record headers, content data, and proper multiplexing.
199
200 @param sw Switch for managing connection lifetime and automatic cleanup
201 @param flow Two-way network flow for bidirectional communication
202 @return New connection ready to process FastCGI requests
203 @raise Invalid_argument if the flow is already closed *)
204 val create : sw:Eio.Switch.t -> 'a Eio.Flow.two_way -> 'a t
205
206 (** Accept and process a single request on the connection.
207
208 Waits for the next FastCGI request to arrive on this connection,
209 then processes it using the provided handler. This function handles
210 all protocol details including record parsing, stream management,
211 and response generation.
212
213 The function is concurrent-safe and can be called multiple times
214 if the application supports request multiplexing.
215
216 @param conn Connection to process request on
217 @param handler Application handler for the request
218 @return Promise that resolves to the response result when processing completes
219 @raise Fastcgi.Error.Fastcgi_error on protocol violations *)
220 val process_request : 'a t -> 'a Handler.handler -> response_result Eio.Promise.t
221
222 (** Get connection statistics.
223
224 Returns current runtime statistics for this connection. This information
225 can be used for monitoring, load balancing decisions, and performance
226 tuning.
227
228 @param conn Connection to query
229 @return Current statistics snapshot *)
230 val stats : 'a t -> stats
231
232 (** Close the connection gracefully.
233
234 Closes the transport connection after completing any active requests.
235 The Web server controls the lifetime of transport connections and can
236 close a connection when no requests are active.
237
238 @param conn Connection to close *)
239 val close : 'a t -> unit
240end
241
242(** FastCGI server for accepting and managing connections.
243
244 This module provides a high-level server that listens for incoming
245 connections and manages them according to FastCGI protocol requirements.
246 It handles connection acceptance, lifecycle management, and graceful
247 shutdown.
248
249 The server supports both Unix domain sockets and TCP sockets, as
250 commonly used by web servers like nginx, Apache, and lighttpd. *)
251module Server : sig
252 (** Server configuration.
253
254 Complete configuration for a FastCGI server, including application
255 settings, network binding, and resource limits. *)
256 type 'a config = {
257 app : 'a app_config; (** Application configuration including handlers
258 and capability limits. *)
259 listen_address : [
260 | `Unix of string (** Unix domain socket path. Common for local
261 communication with web servers. *)
262 | `Tcp of string * int (** TCP socket address (host) and port.
263 Used for remote FastCGI servers. *)
264 ];
265 backlog : int; (** Listen socket backlog size. Controls how
266 many pending connections can be queued. *)
267 max_connections : int; (** Maximum concurrent connections to accept.
268 Should align with app.max_connections. *)
269 }
270
271 (** Run a FastCGI server.
272
273 Starts a FastCGI server with the specified configuration. The server
274 will listen for connections, accept them within the configured limits,
275 and process requests using the application handler.
276
277 The server implements the standard FastCGI application lifecycle:
278 a FastCGI application calls accept() on the socket referred to by file
279 descriptor FCGI_LISTENSOCK_FILENO to accept a new transport connection.
280
281 This function blocks until the switch is cancelled or an error occurs.
282
283 @param sw Switch for managing server lifetime and automatic cleanup
284 @param net Network capability for creating listening sockets
285 @param config Complete server configuration
286 @raise Sys_error if unable to bind to the specified address *)
287 val run :
288 sw:Eio.Switch.t ->
289 net:[< `Generic | `Unix ] Eio.Net.ty Eio.Resource.t ->
290 _ config ->
291 unit
292
293 (** Run server with default configuration.
294
295 Convenience function to start a FastCGI server with sensible defaults.
296 Useful for simple applications that don't need custom configuration.
297
298 Default settings:
299 - max_connections: 10
300 - max_requests: 50
301 - multiplex_connections: true
302 - backlog: 5
303
304 @param sw Switch for managing server lifetime
305 @param net Network capability for creating sockets
306 @param handler Application handler function
307 @param listen_address Address to listen on (Unix socket or TCP)
308 @raise Sys_error if unable to bind to the specified address *)
309 val run_default :
310 sw:Eio.Switch.t ->
311 net:[< `Generic | `Unix ] Eio.Net.ty Eio.Resource.t ->
312 handler:_ Handler.handler ->
313 listen_address:[`Unix of string | `Tcp of string * int] ->
314 unit
315end
316
317(** {1 Error Handling} *)
318
319(** FastCGI specific errors. *)
320module Error : sig
321 (** Error types. *)
322 type t =
323 | Protocol_error of string (** Protocol violation *)
324 | Invalid_record of string (** Malformed record *)
325 | Unsupported_version of int (** Unsupported protocol version *)
326 | Unknown_record_type of int (** Unknown record type *)
327 | Request_id_conflict of request_id (** Duplicate request ID *)
328 | Connection_closed (** Connection unexpectedly closed *)
329 | Application_error of string (** Application-specific error *)
330
331 (** FastCGI exception. *)
332 exception Fastcgi_error of t
333
334 (** Convert error to string description. *)
335 val to_string : t -> string
336
337 (** Raise a FastCGI error. *)
338 val raise : t -> 'a
339end
340
341(** {1 Resource Management} *)
342
343(** Resource management utilities. *)
344module Resource : sig
345 (** Request context with automatic cleanup. *)
346 type 'a request_context = {
347 request : 'a request;
348 response : 'a response;
349 switch : Eio.Switch.t; (** Switch for request-scoped resources *)
350 }
351
352 (** Create a request context with automatic resource management.
353
354 @param sw Parent switch
355 @param request FastCGI request
356 @param f Function to execute with request context
357 @return Result of function execution *)
358 val with_request_context :
359 sw:Eio.Switch.t ->
360 'a request ->
361 ('a request_context -> 'b) ->
362 'b
363
364 (** Buffer management for efficient I/O. *)
365 module Buffer : sig
366 (** Buffer type. *)
367 type t
368
369 (** Create a buffer with specified size. *)
370 val create : size:int -> t
371
372 (** Read data into buffer from source.
373
374 @param buffer Buffer to read into
375 @param source Source to read from
376 @return Number of bytes read *)
377 val read_into : t -> 'a Eio.Flow.source -> int
378
379 (** Write data from buffer to sink.
380
381 @param buffer Buffer to write from
382 @param sink Sink to write to
383 @param length Number of bytes to write *)
384 val write_from : t -> 'a Eio.Flow.sink -> int -> unit
385
386 (** Clear buffer contents. *)
387 val clear : t -> unit
388 end
389end