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