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