this repo has no description
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

reset

+3 -2787
-43
examples/basic_filter.ml
··· 1 - (* Basic filter that uppercases text content *) 2 - 3 - open Fastcgi 4 - 5 - let uppercase_filter_logic filter_req response = 6 - let input = filter_req.Filter.data_stream in 7 - let output = response.stdout in 8 - 9 - (* Simple text transformation: read input and write uppercase output *) 10 - let buffer = Cstruct.create 4096 in 11 - let rec process () = 12 - try 13 - let n = Eio.Flow.single_read input buffer in 14 - if n > 0 then begin 15 - let text = Cstruct.to_string ~len:n buffer in 16 - let uppercase_text = String.uppercase_ascii text in 17 - Eio.Flow.copy_string uppercase_text output; 18 - process () 19 - end 20 - with 21 - | End_of_file -> () 22 - in 23 - process () 24 - 25 - let filter_handler request response = 26 - let filter_req = Filter.request_of_fastcgi request in 27 - 28 - (* Write HTTP headers *) 29 - Eio.Flow.copy_string "Status: 200 OK\r\n" response.stdout; 30 - Eio.Flow.copy_string "Content-Type: text/plain\r\n" response.stdout; 31 - Eio.Flow.copy_string "\r\n" response.stdout; 32 - 33 - (* Process the data stream *) 34 - uppercase_filter_logic filter_req response; 35 - 36 - { app_status = 0; protocol_status = Request_complete } 37 - 38 - let () = Eio_main.run @@ fun env -> 39 - let net = Eio.Stdenv.net env in 40 - Eio.Switch.run @@ fun sw -> 41 - Server.run_default ~sw ~net 42 - ~handler:(Handler.Filter filter_handler) 43 - ~listen_address:(`Tcp ("127.0.0.1", 9003))
-4
examples/dune
··· 1 - (executables 2 - (public_names hello_responder echo_responder simple_authorizer basic_filter) 3 - (names hello_responder echo_responder simple_authorizer basic_filter) 4 - (libraries fastcgi eio_main cstruct))
-40
examples/echo_responder.ml
··· 1 - (* Echo server that shows request information *) 2 - 3 - open Fastcgi 4 - 5 - let echo_handler request response = 6 - let http_req = Responder.request_of_fastcgi request in 7 - let http_resp = Responder.response_of_fastcgi response in 8 - 9 - http_resp.write_status 200; 10 - http_resp.write_header "Content-Type" "text/html"; 11 - 12 - http_resp.write_body "<h1>FastCGI Echo Server</h1>"; 13 - http_resp.write_body ("<p><strong>Method:</strong> " ^ http_req.method_ ^ "</p>"); 14 - http_resp.write_body ("<p><strong>URI:</strong> " ^ http_req.uri ^ "</p>"); 15 - http_resp.write_body ("<p><strong>Query String:</strong> " ^ http_req.query_string ^ "</p>"); 16 - 17 - (match http_req.content_type with 18 - | Some ct -> http_resp.write_body ("<p><strong>Content-Type:</strong> " ^ ct ^ "</p>") 19 - | None -> ()); 20 - 21 - (match http_req.content_length with 22 - | Some cl -> http_resp.write_body ("<p><strong>Content-Length:</strong> " ^ string_of_int cl ^ "</p>") 23 - | None -> ()); 24 - 25 - http_resp.write_body "<h2>Headers:</h2><ul>"; 26 - List.iter (fun (name, value) -> 27 - http_resp.write_body ("<li><strong>" ^ name ^ ":</strong> " ^ value ^ "</li>") 28 - ) http_req.headers; 29 - http_resp.write_body "</ul>"; 30 - 31 - http_resp.finish (); 32 - 33 - { app_status = 0; protocol_status = Request_complete } 34 - 35 - let () = Eio_main.run @@ fun env -> 36 - let net = Eio.Stdenv.net env in 37 - Eio.Switch.run @@ fun sw -> 38 - Server.run_default ~sw ~net 39 - ~handler:(Handler.Responder echo_handler) 40 - ~listen_address:(`Tcp ("127.0.0.1", 9001))
-22
examples/hello_responder.ml
··· 1 - (* Simple Hello World FastCGI Responder application *) 2 - 3 - open Fastcgi 4 - 5 - let hello_handler request response = 6 - let _http_req = Responder.request_of_fastcgi request in 7 - let http_resp = Responder.response_of_fastcgi response in 8 - 9 - http_resp.write_status 200; 10 - http_resp.write_header "Content-Type" "text/html"; 11 - http_resp.write_body "<h1>Hello, FastCGI!</h1>"; 12 - http_resp.write_body "<p>This is a simple FastCGI responder application.</p>"; 13 - http_resp.finish (); 14 - 15 - { app_status = 0; protocol_status = Request_complete } 16 - 17 - let () = Eio_main.run @@ fun env -> 18 - let net = Eio.Stdenv.net env in 19 - Eio.Switch.run @@ fun sw -> 20 - Server.run_default ~sw ~net 21 - ~handler:(Handler.Responder hello_handler) 22 - ~listen_address:(`Tcp ("127.0.0.1", 9000))
-29
examples/simple_authorizer.ml
··· 1 - (* Simple authorization server *) 2 - 3 - open Fastcgi 4 - 5 - let auth_logic auth_req = 6 - (* Simple authorization: allow only GET requests to /public/* paths *) 7 - match auth_req.Authorizer.method_, auth_req.uri with 8 - | "GET", uri when String.starts_with ~prefix:"/public/" uri -> 9 - Authorizer.Authorized [("USER_AUTHORIZED", "true"); ("ACCESS_LEVEL", "public")] 10 - | "GET", "/login" -> 11 - Authorizer.Authorized [("LOGIN_PAGE", "true")] 12 - | _ -> 13 - Authorizer.Unauthorized { 14 - status = 403; 15 - headers = [("Content-Type", "text/html")]; 16 - body = "<h1>403 Forbidden</h1><p>Access denied. Only GET requests to /public/* are allowed.</p>"; 17 - } 18 - 19 - let auth_handler request response = 20 - let auth_req = Authorizer.request_of_fastcgi request in 21 - let result = auth_logic auth_req in 22 - Authorizer.response_of_result result response 23 - 24 - let () = Eio_main.run @@ fun env -> 25 - let net = Eio.Stdenv.net env in 26 - Eio.Switch.run @@ fun sw -> 27 - Server.run_default ~sw ~net 28 - ~handler:(Handler.Authorizer auth_handler) 29 - ~listen_address:(`Tcp ("127.0.0.1", 9002))
+3 -10
lib/dune
··· 3 3 (name fastcgi) 4 4 (libraries eio) 5 5 (modules 6 - fastcgi_types 7 6 fastcgi 8 - fastcgi_protocol 9 - fastcgi_responder 10 - fastcgi_authorizer 11 - fastcgi_filter) 7 + ) 12 8 (modules_without_implementation 13 - fastcgi_types 14 9 fastcgi 15 - fastcgi_protocol 16 - fastcgi_responder 17 - fastcgi_authorizer 18 - fastcgi_filter)) 10 + ) 11 + )
-389
lib/fastcgi.mli
··· 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. *) 59 - include module type of Fastcgi_types 60 - 61 - (** {1 Protocol Implementation} *) 62 - 63 - (** Wire protocol parsing and serialization. *) 64 - module Protocol = Fastcgi_protocol 65 - 66 - (** {1 Role-Specific Implementations} *) 67 - 68 - (** Responder role implementation. *) 69 - module Responder = Fastcgi_responder 70 - 71 - (** Authorizer role implementation. *) 72 - module Authorizer = Fastcgi_authorizer 73 - 74 - (** Filter role implementation. *) 75 - module 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. *) 84 - module 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 *) 130 - end 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. *) 140 - type '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. *) 168 - module 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 240 - end 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. *) 251 - module 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 315 - end 316 - 317 - (** {1 Error Handling} *) 318 - 319 - (** FastCGI specific errors. *) 320 - module 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 339 - end 340 - 341 - (** {1 Resource Management} *) 342 - 343 - (** Resource management utilities. *) 344 - module 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 389 - end
-229
lib/fastcgi_authorizer.mli
··· 1 - (** Authorizer role implementation for FastCGI. 2 - 3 - The Authorizer role performs access control decisions for web requests. 4 - An Authorizer FastCGI application receives all the information associated 5 - with an HTTP request and generates an authorized/unauthorized decision. 6 - 7 - In case of an authorized decision, the Authorizer can associate name-value 8 - pairs with the HTTP request for use by subsequent processing stages. When 9 - giving an unauthorized decision, the Authorizer sends a complete response 10 - to the HTTP client with appropriate error information. 11 - 12 - This role is commonly used for: 13 - - Authentication and authorization checks 14 - - Access control based on IP addresses, user roles, or request attributes 15 - - Rate limiting and quota enforcement 16 - - Custom security policies 17 - 18 - The web server may proceed with additional access checks after a successful 19 - authorization, including requests to other Authorizers, depending on its 20 - configuration. *) 21 - 22 - (** {1 Authorization Types} *) 23 - 24 - (** Authorization result. 25 - 26 - Represents the outcome of an authorization decision. The Authorizer can 27 - either grant access (possibly with additional variables) or deny access 28 - with a complete error response. *) 29 - type auth_result = 30 - | Authorized of (string * string) list (** Request is authorized. The list contains 31 - variable bindings that will be added to 32 - the request environment for subsequent 33 - processing stages. *) 34 - | Unauthorized of { 35 - status : int; (** HTTP status code for the error response 36 - (typically 401, 403, or 429). *) 37 - headers : (string * string) list; (** HTTP headers for the error response. 38 - May include WWW-Authenticate, etc. *) 39 - body : string; (** Error response body content sent to the client. *) 40 - } (** Request is denied. The web server will send this error 41 - response directly to the client without further processing. *) 42 - 43 - (** Authorization request information. 44 - 45 - Contains the relevant information from an HTTP request needed to make 46 - authorization decisions. This is extracted from the CGI environment 47 - variables provided by the web server. *) 48 - type auth_request = { 49 - method_ : string; (** HTTP method (GET, POST, etc.). *) 50 - uri : string; (** Request URI being accessed. *) 51 - remote_addr : string option; (** Client IP address, if available. 52 - Useful for IP-based access control. *) 53 - remote_user : string option; (** Authenticated username, if any. 54 - Set by prior authentication stages. *) 55 - auth_type : string option; (** Authentication method used (Basic, Bearer, etc.). 56 - Indicates how the user was authenticated. *) 57 - headers : (string * string) list; (** HTTP headers from the request. 58 - May contain authorization headers, user agents, etc. *) 59 - } 60 - 61 - (** {1 Conversion Functions} *) 62 - 63 - (** Convert FastCGI request to authorization request. 64 - 65 - Extracts authorization-relevant information from FastCGI 66 - parameters and creates an auth request object. 67 - 68 - @param request FastCGI request 69 - @return Authorization request *) 70 - val request_of_fastcgi : 'a Fastcgi_types.request -> auth_request 71 - 72 - (** Convert authorization result to FastCGI response. 73 - 74 - Writes the authorization decision to the FastCGI response 75 - streams in the correct format. 76 - 77 - @param result Authorization decision 78 - @param response FastCGI response 79 - @return Response result *) 80 - val response_of_result : auth_result -> 'a Fastcgi_types.response -> Fastcgi_types.response_result 81 - 82 - (** {1 Handler Utilities} *) 83 - 84 - (** Authorizer handler type. *) 85 - type 'a authorizer_handler = 'a Fastcgi_types.request -> 'a Fastcgi_types.response -> Fastcgi_types.response_result 86 - 87 - (** Convenience handler wrapper for authorization handlers. 88 - 89 - Converts an authorization handler function into a FastCGI handler. 90 - This allows writing handlers that work with auth request/result 91 - types instead of raw FastCGI types. 92 - 93 - @param handler Authorization handler function 94 - @return FastCGI authorizer handler *) 95 - val auth_handler : 96 - (auth_request -> auth_result) -> 97 - 'a authorizer_handler 98 - 99 - (** {1 Common Authorization Patterns} *) 100 - 101 - (** Create a simple allow/deny authorizer. 102 - 103 - @param predicate Function that returns true for authorized requests 104 - @return Authorization result *) 105 - val simple_authorizer : (auth_request -> bool) -> (auth_request -> auth_result) 106 - 107 - (** IP-based authorization. 108 - 109 - @param allowed_ips List of allowed IP addresses/ranges 110 - @param request Authorization request 111 - @return Authorization result *) 112 - val ip_authorizer : string list -> auth_request -> auth_result 113 - 114 - (** User-based authorization. 115 - 116 - @param allowed_users List of allowed usernames 117 - @param request Authorization request 118 - @return Authorization result *) 119 - val user_authorizer : string list -> auth_request -> auth_result 120 - 121 - (** Role-based authorization. 122 - 123 - @param get_user_roles Function to get roles for a user 124 - @param required_roles Roles required for access 125 - @param request Authorization request 126 - @return Authorization result *) 127 - val role_authorizer : 128 - get_user_roles:(string -> string list) -> 129 - required_roles:string list -> 130 - auth_request -> 131 - auth_result 132 - 133 - (** Time-based authorization. 134 - 135 - @param allowed_hours List of allowed hours (0-23) 136 - @param request Authorization request 137 - @return Authorization result *) 138 - val time_authorizer : int list -> auth_request -> auth_result 139 - 140 - (** {1 Authentication Integration} *) 141 - 142 - (** Basic authentication handler. 143 - 144 - @param realm Authentication realm 145 - @param verify_credentials Function to verify username/password 146 - @param request Authorization request 147 - @return Authorization result *) 148 - val basic_auth : 149 - realm:string -> 150 - verify_credentials:(string -> string -> bool) -> 151 - auth_request -> 152 - auth_result 153 - 154 - (** Bearer token authentication. 155 - 156 - @param verify_token Function to verify and decode token 157 - @param request Authorization request 158 - @return Authorization result *) 159 - val bearer_auth : 160 - verify_token:(string -> (string * (string * string) list) option) -> 161 - auth_request -> 162 - auth_result 163 - 164 - (** API key authentication. 165 - 166 - @param header_name Header containing API key 167 - @param verify_key Function to verify API key 168 - @param request Authorization request 169 - @return Authorization result *) 170 - val api_key_auth : 171 - header_name:string -> 172 - verify_key:(string -> (string * (string * string) list) option) -> 173 - auth_request -> 174 - auth_result 175 - 176 - (** {1 Response Helpers} *) 177 - 178 - (** Create a standard 401 Unauthorized response. 179 - 180 - @param realm Authentication realm 181 - @param message Error message 182 - @return Unauthorized result *) 183 - val unauthorized_response : 184 - realm:string -> 185 - message:string -> 186 - auth_result 187 - 188 - (** Create a standard 403 Forbidden response. 189 - 190 - @param message Error message 191 - @return Unauthorized result *) 192 - val forbidden_response : 193 - message:string -> 194 - auth_result 195 - 196 - (** Create a custom error response. 197 - 198 - @param status HTTP status code 199 - @param headers Additional headers 200 - @param body Response body 201 - @return Unauthorized result *) 202 - val error_response : 203 - status:int -> 204 - headers:(string * string) list -> 205 - body:string -> 206 - auth_result 207 - 208 - (** {1 Variable Binding} *) 209 - 210 - (** Create authorized response with variables. 211 - 212 - Variables are passed to subsequent FastCGI applications 213 - as environment variables with "Variable-" prefix. 214 - 215 - @param variables Name-value pairs to bind 216 - @return Authorized result *) 217 - val authorized_with_variables : (string * string) list -> auth_result 218 - 219 - (** Add authentication information as variables. 220 - 221 - @param user Authenticated username 222 - @param auth_type Authentication method 223 - @param additional Additional variables 224 - @return Variable bindings *) 225 - val auth_variables : 226 - user:string -> 227 - auth_type:string -> 228 - additional:(string * string) list -> 229 - (string * string) list
-329
lib/fastcgi_filter.mli
··· 1 - (** Filter role implementation for FastCGI. 2 - 3 - The Filter role processes data streams with filtering capabilities. 4 - A Filter FastCGI application receives all the information associated with 5 - an HTTP request, plus an extra stream of data from a file stored on the 6 - web server, and generates a "filtered" version of the data stream as an 7 - HTTP response. 8 - 9 - This role is useful for: 10 - - Dynamic content transformation (e.g., server-side includes, templating) 11 - - Format conversion (e.g., Markdown to HTML, image resizing) 12 - - Content compression and optimization 13 - - Real-time content modification based on request context 14 - 15 - A Filter is similar in functionality to a Responder that takes a data file 16 - as a parameter. The key difference is that with a Filter, both the data file 17 - and the Filter itself can be access controlled using the web server's access 18 - control mechanisms, while a Responder that takes the name of a data file as 19 - a parameter must perform its own access control checks on the data file. 20 - 21 - The web server presents the Filter with environment variables first, then 22 - standard input (normally form POST data), and finally the data file input 23 - that needs to be processed. *) 24 - 25 - (** {1 Filter Types} *) 26 - 27 - (** Filter request with data stream. 28 - 29 - Represents a complete Filter request including both the HTTP request 30 - context and the data stream that needs to be processed. *) 31 - type 'a filter_request = { 32 - request : 'a Fastcgi_types.request; (** Base FastCGI request containing HTTP context, 33 - parameters, and form data. *) 34 - data_stream : 'a Eio.Flow.source; (** File data stream to be filtered. 35 - This is the content that needs processing. *) 36 - data_last_modified : float option; (** File modification time as seconds since epoch. 37 - Useful for caching and conditional requests. *) 38 - data_length : int option; (** Expected length of the data stream in bytes. 39 - May be None if length is unknown. *) 40 - } 41 - 42 - (** Filter metadata about the data being processed. *) 43 - type filter_metadata = { 44 - filename : string option; (** Original filename *) 45 - mime_type : string option; (** MIME type of data *) 46 - last_modified : float option; (** Last modification time *) 47 - size : int option; (** Data size in bytes *) 48 - etag : string option; (** Entity tag for caching *) 49 - } 50 - 51 - (** Filter context with additional processing information. *) 52 - type 'a filter_context = { 53 - filter_request : 'a filter_request; (** Request and data *) 54 - metadata : filter_metadata; (** File metadata *) 55 - cache_control : cache_policy; (** Caching policy *) 56 - } 57 - 58 - (** Cache policy for filtered content. *) 59 - and cache_policy = 60 - | No_cache (** No caching *) 61 - | Cache_for of int (** Cache for N seconds *) 62 - | Cache_until of float (** Cache until timestamp *) 63 - | Conditional_cache of (filter_metadata -> bool) (** Conditional caching *) 64 - 65 - (** {1 Conversion Functions} *) 66 - 67 - (** Convert FastCGI request to filter request. 68 - 69 - Extracts filter-specific information from FastCGI streams 70 - and creates a filter request object. 71 - 72 - @param request FastCGI request 73 - @return Filter request with data stream *) 74 - val request_of_fastcgi : 'a Fastcgi_types.request -> 'a filter_request 75 - 76 - (** Create filter context with metadata. 77 - 78 - @param filter_request Filter request 79 - @param metadata File metadata 80 - @param cache_control Caching policy 81 - @return Filter context *) 82 - val context_of_request : 83 - 'a filter_request -> 84 - metadata:filter_metadata -> 85 - cache_control:cache_policy -> 86 - 'a filter_context 87 - 88 - (** {1 Handler Utilities} *) 89 - 90 - (** Filter handler type. *) 91 - type 'a filter_handler = 'a Fastcgi_types.request -> 'a Fastcgi_types.response -> Fastcgi_types.response_result 92 - 93 - (** Convenience handler wrapper for filter handlers. 94 - 95 - Converts a filter handler function into a FastCGI handler. 96 - This allows writing handlers that work with filter request/response 97 - types instead of raw FastCGI types. 98 - 99 - @param handler Filter handler function 100 - @return FastCGI filter handler *) 101 - 102 - val filter_handler : 103 - ('a filter_request -> 'a Fastcgi_types.response -> unit) -> 104 - 'a filter_handler 105 - 106 - (** Context-aware filter handler. 107 - 108 - Similar to filter_handler but provides additional context 109 - including metadata and caching information. 110 - 111 - @param handler Context-aware filter handler 112 - @return FastCGI filter handler *) 113 - val context_filter_handler : 114 - ('a filter_context -> 'a Fastcgi_types.response -> unit) -> 115 - 'a filter_handler 116 - 117 - (** {1 Common Filter Operations} *) 118 - 119 - (** Text processing filters. *) 120 - module Text : sig 121 - (** Convert text encoding. 122 - 123 - @param from_encoding Source encoding 124 - @param to_encoding Target encoding 125 - @param input Text stream 126 - @param output Filtered stream *) 127 - val convert_encoding : 128 - from_encoding:string -> 129 - to_encoding:string -> 130 - input:'a Eio.Flow.source -> 131 - output:'a Eio.Flow.sink -> 132 - unit 133 - 134 - (** Find and replace text patterns. 135 - 136 - @param pattern Regular expression pattern 137 - @param replacement Replacement text 138 - @param input Text stream 139 - @param output Filtered stream *) 140 - val find_replace : 141 - pattern:string -> 142 - replacement:string -> 143 - input:'a Eio.Flow.source -> 144 - output:'a Eio.Flow.sink -> 145 - unit 146 - 147 - (** Template substitution. 148 - 149 - @param variables Variable substitutions 150 - @param input Template stream 151 - @param output Processed stream *) 152 - val template_substitute : 153 - variables:(string * string) list -> 154 - input:'a Eio.Flow.source -> 155 - output:'a Eio.Flow.sink -> 156 - unit 157 - 158 - (** Markdown to HTML conversion. 159 - 160 - @param options Markdown processing options 161 - @param input Markdown stream 162 - @param output HTML stream *) 163 - val markdown_to_html : 164 - options:string list -> 165 - input:'a Eio.Flow.source -> 166 - output:'a Eio.Flow.sink -> 167 - unit 168 - end 169 - 170 - (** Image processing filters. *) 171 - module Image : sig 172 - (** Image resize operation. 173 - 174 - @param width Target width 175 - @param height Target height 176 - @param input Image stream 177 - @param output Resized stream *) 178 - val resize : 179 - width:int -> 180 - height:int -> 181 - input:'a Eio.Flow.source -> 182 - output:'a Eio.Flow.sink -> 183 - unit 184 - 185 - (** Image format conversion. 186 - 187 - @param format Target format (jpeg, png, etc.) 188 - @param quality Quality setting (for lossy formats) 189 - @param input Image stream 190 - @param output Converted stream *) 191 - val convert_format : 192 - format:string -> 193 - quality:int option -> 194 - input:'a Eio.Flow.source -> 195 - output:'a Eio.Flow.sink -> 196 - unit 197 - 198 - (** Image compression. 199 - 200 - @param level Compression level 201 - @param input Image stream 202 - @param output Compressed stream *) 203 - val compress : 204 - level:int -> 205 - input:'a Eio.Flow.source -> 206 - output:'a Eio.Flow.sink -> 207 - unit 208 - end 209 - 210 - (** Data compression filters. *) 211 - module Compression : sig 212 - (** Gzip compression. 213 - 214 - @param level Compression level (1-9) 215 - @param input Data stream 216 - @param output Compressed stream *) 217 - val gzip : 218 - level:int -> 219 - input:'a Eio.Flow.source -> 220 - output:'a Eio.Flow.sink -> 221 - unit 222 - 223 - (** Brotli compression. 224 - 225 - @param level Compression level 226 - @param input Data stream 227 - @param output Compressed stream *) 228 - val brotli : 229 - level:int -> 230 - input:'a Eio.Flow.source -> 231 - output:'a Eio.Flow.sink -> 232 - unit 233 - 234 - (** Auto-detect and compress. 235 - 236 - @param preferred_formats Preferred compression formats 237 - @param accept_encoding Client Accept-Encoding header 238 - @param input Data stream 239 - @param output Compressed stream *) 240 - val auto_compress : 241 - preferred_formats:string list -> 242 - accept_encoding:string option -> 243 - input:'a Eio.Flow.source -> 244 - output:'a Eio.Flow.sink -> 245 - string option (** Returns content-encoding used *) 246 - end 247 - 248 - (** {1 Caching and Optimization} *) 249 - 250 - (** Check if content should be served from cache. 251 - 252 - @param metadata File metadata 253 - @param if_modified_since Client If-Modified-Since header 254 - @param if_none_match Client If-None-Match header 255 - @return True if content is not modified *) 256 - val check_not_modified : 257 - metadata:filter_metadata -> 258 - if_modified_since:string option -> 259 - if_none_match:string option -> 260 - bool 261 - 262 - (** Generate appropriate cache headers. 263 - 264 - @param metadata File metadata 265 - @param cache_policy Caching policy 266 - @return List of cache-related headers *) 267 - val generate_cache_headers : 268 - metadata:filter_metadata -> 269 - cache_policy:cache_policy -> 270 - (string * string) list 271 - 272 - (** {1 Streaming Operations} *) 273 - 274 - (** Stream processor for chunk-by-chunk filtering. *) 275 - type 'a stream_processor = { 276 - process_chunk : bytes -> bytes option; (** Process a data chunk *) 277 - finalize : unit -> bytes option; (** Finalize and get remaining data *) 278 - reset : unit -> unit; (** Reset processor state *) 279 - } 280 - 281 - (** Create a streaming filter. 282 - 283 - @param processor Stream processor 284 - @param input Source stream 285 - @param output Target stream *) 286 - val streaming_filter : 287 - processor:'a stream_processor -> 288 - input:'a Eio.Flow.source -> 289 - output:'a Eio.Flow.sink -> 290 - unit 291 - 292 - (** Chain multiple filters together. 293 - 294 - @param filters List of filter functions 295 - @param input Source stream 296 - @param output Target stream *) 297 - val chain_filters : 298 - filters:(('a Eio.Flow.source -> 'a Eio.Flow.sink -> unit) list) -> 299 - input:'a Eio.Flow.source -> 300 - output:'a Eio.Flow.sink -> 301 - unit 302 - 303 - (** {1 Error Handling} *) 304 - 305 - (** Filter-specific errors. *) 306 - type filter_error = 307 - | Data_corruption of string (** Data corruption detected *) 308 - | Unsupported_format of string (** Unsupported file format *) 309 - | Processing_failed of string (** Filter processing failed *) 310 - | Resource_exhausted of string (** Out of memory/disk space *) 311 - 312 - (** Filter exception. *) 313 - exception Filter_error of filter_error 314 - 315 - (** Convert filter error to string. *) 316 - val filter_error_to_string : filter_error -> string 317 - 318 - (** Safe filter wrapper that handles errors gracefully. 319 - 320 - @param fallback Fallback function if filter fails 321 - @param filter Primary filter function 322 - @param input Source stream 323 - @param output Target stream *) 324 - val safe_filter : 325 - fallback:('a Eio.Flow.source -> 'a Eio.Flow.sink -> unit) -> 326 - filter:('a Eio.Flow.source -> 'a Eio.Flow.sink -> unit) -> 327 - input:'a Eio.Flow.source -> 328 - output:'a Eio.Flow.sink -> 329 - unit
-414
lib/fastcgi_protocol.mli
··· 1 - (** FastCGI wire protocol parsing and serialization. 2 - 3 - This module handles the low-level FastCGI protocol details including 4 - record parsing, name-value pair encoding, and binary data serialization 5 - using Eio's Buf_read and Buf_write modules. 6 - 7 - The FastCGI protocol uses a binary record format where all data is 8 - transmitted as 8-byte fixed headers followed by variable-length content 9 - and optional padding. Multi-byte integers use network byte order for 10 - platform independence. 11 - 12 - The protocol supports several key features: 13 - - Request multiplexing over single connections 14 - - Variable-length encoding for efficient name-value pairs 15 - - Stream-based data transmission with proper termination 16 - - Alignment padding for optimal performance 17 - 18 - This module provides the low-level primitives for encoding and decoding 19 - these protocol elements, allowing higher-level modules to focus on 20 - application logic rather than binary protocol details. *) 21 - 22 - (** Protocol parsing error. 23 - 24 - Raised when invalid or malformed protocol data is encountered during 25 - parsing operations. This includes version mismatches, invalid record 26 - structures, malformed length encodings, and other protocol violations. *) 27 - exception Protocol_error of string 28 - 29 - (** {1 Protocol Parsing} 30 - 31 - These functions parse incoming FastCGI records and protocol elements 32 - from binary data streams. They validate protocol compliance and convert 33 - binary data into OCaml types for processing. *) 34 - 35 - (** Parse a FastCGI record header from a buffer. 36 - 37 - Reads exactly 8 bytes from the buffer to construct a record header. 38 - The header contains version, record type, request ID, content length, 39 - and padding length fields using network byte order. 40 - 41 - The function validates that the version is supported (currently only 42 - version 1) and that the record type is recognized. 43 - 44 - @param buf Input buffer positioned at the start of a record header 45 - @return Parsed record header with validated fields 46 - @raise Protocol_error if version is unsupported or data is malformed *) 47 - val parse_record_header : Eio.Buf_read.t -> Fastcgi_types.record_header 48 - 49 - (** Parse a complete FastCGI record from a buffer. 50 - 51 - Reads a record header followed by the specified amount of content data 52 - and padding. This is the primary function for reading FastCGI records 53 - from network streams. 54 - 55 - The function ensures that exactly the number of content and padding 56 - bytes specified in the header are read, maintaining proper stream 57 - alignment for subsequent records. 58 - 59 - @param buf Input buffer positioned at the start of a complete record 60 - @return Complete parsed record with header, content, and optional padding 61 - @raise Protocol_error if the record structure is invalid *) 62 - val parse_record : Eio.Buf_read.t -> Fastcgi_types.record 63 - 64 - (** Parse begin request body from record content. 65 - 66 - Extracts role and connection flags from the 8-byte Begin_request record 67 - body. The role indicates whether the application should act as a 68 - Responder, Authorizer, or Filter. The flags control connection lifetime 69 - management. 70 - 71 - @param content Record content bytes (must be exactly 8 bytes) 72 - @return Begin request body with role and flags 73 - @raise Protocol_error if content length is wrong or role is unknown *) 74 - val parse_begin_request : bytes -> Fastcgi_types.begin_request_body 75 - 76 - (** Parse end request body from record content. 77 - 78 - Extracts application status and protocol status from the 8-byte 79 - End_request record body. The application status is similar to a 80 - program exit code, while protocol status indicates completion 81 - or rejection reasons. 82 - 83 - @param content Record content bytes (must be exactly 8 bytes) 84 - @return End request body with status codes 85 - @raise Protocol_error if content length is wrong or status is invalid *) 86 - val parse_end_request : bytes -> Fastcgi_types.end_request_body 87 - 88 - (** Parse name-value pairs from record content. 89 - 90 - Decodes the compact binary encoding used for parameter transmission. 91 - Names and values can be up to 127 bytes (1-byte length) or longer 92 - (4-byte length with high bit set). This encoding allows efficient 93 - transmission of CGI environment variables and other parameters. 94 - 95 - @param content Record content bytes containing encoded name-value pairs 96 - @return List of decoded name-value pairs 97 - @raise Protocol_error if the encoding is malformed *) 98 - val parse_name_value_pairs : bytes -> Fastcgi_types.name_value_pair list 99 - 100 - (** {1 Protocol Serialization} 101 - 102 - These functions serialize OCaml types into the binary FastCGI protocol 103 - format for transmission to web servers. All multi-byte integers are 104 - written in network byte order for platform independence. *) 105 - 106 - (** Write a FastCGI record header to a buffer. 107 - 108 - Serializes a record header into exactly 8 bytes using network byte order. 109 - The header format is: version (1 byte), type (1 byte), request ID (2 bytes), 110 - content length (2 bytes), padding length (1 byte), reserved (1 byte). 111 - 112 - @param buf Output buffer to write the header to 113 - @param header Record header to serialize *) 114 - val write_record_header : Eio.Buf_write.t -> Fastcgi_types.record_header -> unit 115 - 116 - (** Write a complete FastCGI record to a buffer. 117 - 118 - Serializes a complete record including header, content data, and padding. 119 - This is the primary function for sending FastCGI records over network 120 - connections. The function ensures proper alignment by writing any 121 - specified padding bytes. 122 - 123 - @param buf Output buffer to write the complete record to 124 - @param record Complete record with header, content, and optional padding *) 125 - val write_record : Eio.Buf_write.t -> Fastcgi_types.record -> unit 126 - 127 - (** Write begin request body to bytes. 128 - 129 - Serializes role and connection flags into the 8-byte Begin_request record 130 - body format. The first 2 bytes contain the role, the next byte contains 131 - flags, and the remaining 5 bytes are reserved (set to zero). 132 - 133 - @param body Begin request body with role and flags 134 - @return 8-byte serialized record body *) 135 - val write_begin_request : Fastcgi_types.begin_request_body -> bytes 136 - 137 - (** Write end request body to bytes. 138 - 139 - Serializes application and protocol status into the 8-byte End_request 140 - record body format. The first 4 bytes contain the application status, 141 - the next byte contains the protocol status, and the remaining 3 bytes 142 - are reserved. 143 - 144 - @param body End request body with status codes 145 - @return 8-byte serialized record body *) 146 - val write_end_request : Fastcgi_types.end_request_body -> bytes 147 - 148 - (** Write name-value pairs to bytes. 149 - 150 - Serializes name-value pairs using the compact binary encoding where 151 - each pair is encoded as: name length, value length, name data, value data. 152 - Lengths up to 127 bytes use 1-byte encoding; longer lengths use 4-byte 153 - encoding with the high bit set. 154 - 155 - @param pairs List of name-value pairs to encode 156 - @return Serialized bytes containing all encoded pairs *) 157 - val write_name_value_pairs : Fastcgi_types.name_value_pair list -> bytes 158 - 159 - (** {1 Type Conversions} *) 160 - 161 - (** Convert record type to integer. *) 162 - val record_type_to_int : Fastcgi_types.record_type -> int 163 - 164 - (** Convert integer to record type. 165 - 166 - @param i Integer value 167 - @return Record type 168 - @raise Protocol_error if unknown *) 169 - val record_type_of_int : int -> Fastcgi_types.record_type 170 - 171 - (** Convert role to integer. *) 172 - val role_to_int : Fastcgi_types.role -> int 173 - 174 - (** Convert integer to role. 175 - 176 - @param i Integer value 177 - @return Role 178 - @raise Protocol_error if unknown *) 179 - val role_of_int : int -> Fastcgi_types.role 180 - 181 - (** Convert protocol status to integer. *) 182 - val protocol_status_to_int : Fastcgi_types.protocol_status -> int 183 - 184 - (** Convert integer to protocol status. 185 - 186 - @param i Integer value 187 - @return Protocol status 188 - @raise Protocol_error if unknown *) 189 - val protocol_status_of_int : int -> Fastcgi_types.protocol_status 190 - 191 - (** {1 Length Encoding} 192 - 193 - FastCGI uses a variable-length encoding for efficient transmission of 194 - length values in name-value pairs. This encoding minimizes overhead 195 - for short strings while supporting arbitrarily long values. *) 196 - 197 - (** Encode a length value using FastCGI variable-length encoding. 198 - 199 - Short lengths (0-127) are encoded in a single byte with the high bit 200 - clear. Longer lengths (128 and above) are encoded in 4 bytes with the 201 - high bit of the first byte set to 1, and the remaining 31 bits containing 202 - the actual length value. 203 - 204 - This encoding provides optimal space efficiency for typical use cases 205 - where most parameter names and values are relatively short. 206 - 207 - @param length Length value to encode (must be non-negative) 208 - @return 1-byte or 4-byte encoded length 209 - @raise Invalid_argument if length is negative *) 210 - val encode_length : int -> bytes 211 - 212 - (** Decode a length value from FastCGI variable-length encoding. 213 - 214 - Reads either 1 or 4 bytes from the buffer depending on the high bit 215 - of the first byte. Returns both the decoded length and the number of 216 - bytes consumed to allow proper buffer advancement. 217 - 218 - @param buf Input buffer positioned at the start of an encoded length 219 - @return Tuple of (decoded length, bytes consumed) 220 - @raise Protocol_error if the encoding is invalid or buffer is too short *) 221 - val decode_length : Eio.Buf_read.t -> int * int 222 - 223 - (** {1 Record Construction} 224 - 225 - These convenience functions create properly formatted FastCGI records 226 - with correct headers and content. They handle the binary encoding details 227 - and ensure protocol compliance. *) 228 - 229 - (** Create a begin request record. 230 - 231 - Constructs a Begin_request record to initiate a new FastCGI request. 232 - This record type is sent by web servers to start request processing 233 - and specifies the role the application should play. 234 - 235 - The record contains the application role (Responder, Authorizer, or Filter) 236 - and connection flags that control whether the connection should be kept 237 - alive after the request completes. 238 - 239 - @param request_id Unique request identifier for multiplexing 240 - @param role Role the application should play for this request 241 - @param flags Connection management flags 242 - @return Complete Begin_request record ready for transmission *) 243 - val make_begin_request : 244 - request_id:Fastcgi_types.request_id -> 245 - role:Fastcgi_types.role -> 246 - flags:Fastcgi_types.connection_flags -> 247 - Fastcgi_types.record 248 - 249 - (** Create an end request record. 250 - 251 - Constructs an End_request record to complete a FastCGI request. 252 - This record is sent by applications to indicate request completion 253 - and provide both application-level and protocol-level status information. 254 - 255 - The application status is similar to a program exit code, where 0 256 - indicates success and non-zero values indicate errors. The protocol 257 - status indicates whether the request was completed normally or rejected 258 - for protocol-related reasons. 259 - 260 - @param request_id Request ID that is being completed 261 - @param app_status Application exit status (0 for success) 262 - @param protocol_status Protocol completion status 263 - @return Complete End_request record ready for transmission *) 264 - val make_end_request : 265 - request_id:Fastcgi_types.request_id -> 266 - app_status:Fastcgi_types.app_status -> 267 - protocol_status:Fastcgi_types.protocol_status -> 268 - Fastcgi_types.record 269 - 270 - (** Create a params record. 271 - 272 - @param request_id Request ID 273 - @param params Parameter name-value pairs 274 - @return Params record *) 275 - val make_params_record : 276 - request_id:Fastcgi_types.request_id -> 277 - params:(string * string) list -> 278 - Fastcgi_types.record 279 - 280 - (** Create a stream record (stdin, stdout, stderr, data). 281 - 282 - @param record_type Stream record type 283 - @param request_id Request ID 284 - @param data Stream data 285 - @return Stream record *) 286 - val make_stream_record : 287 - record_type:Fastcgi_types.record_type -> 288 - request_id:Fastcgi_types.request_id -> 289 - data:bytes -> 290 - Fastcgi_types.record 291 - 292 - (** Create an empty stream record (marks end of stream). 293 - 294 - @param record_type Stream record type 295 - @param request_id Request ID 296 - @return Empty stream record *) 297 - val make_empty_stream_record : 298 - record_type:Fastcgi_types.record_type -> 299 - request_id:Fastcgi_types.request_id -> 300 - Fastcgi_types.record 301 - 302 - (** Create a get values record. 303 - 304 - @param variables Variable names to query 305 - @return Get values record *) 306 - val make_get_values_record : 307 - variables:string list -> 308 - Fastcgi_types.record 309 - 310 - (** Create a get values result record. 311 - 312 - @param values Variable name-value pairs 313 - @return Get values result record *) 314 - val make_get_values_result_record : 315 - values:(string * string) list -> 316 - Fastcgi_types.record 317 - 318 - (** Create an unknown type record. 319 - 320 - @param unknown_type Unknown record type value 321 - @return Unknown type record *) 322 - val make_unknown_type_record : 323 - unknown_type:int -> 324 - Fastcgi_types.record 325 - 326 - (** Create an abort request record. 327 - 328 - @param request_id Request ID to abort 329 - @return Abort request record *) 330 - val make_abort_request_record : 331 - request_id:Fastcgi_types.request_id -> 332 - Fastcgi_types.record 333 - 334 - (** {1 Validation} 335 - 336 - These functions validate protocol elements for compliance with FastCGI 337 - requirements. They help detect malformed data and ensure protocol 338 - correctness during parsing and construction. *) 339 - 340 - (** Validate a record header. 341 - 342 - Checks that a record header contains valid values for all fields. 343 - This includes verifying the protocol version, record type bounds, 344 - content length limits, and padding length constraints. 345 - 346 - @param header Record header to validate 347 - @return True if the header is valid and protocol-compliant 348 - @raise Protocol_error if any field contains invalid values *) 349 - val validate_record_header : Fastcgi_types.record_header -> bool 350 - 351 - (** Validate a complete record. 352 - 353 - Performs comprehensive validation of a complete record including 354 - header validation and content/padding length consistency checks. 355 - Ensures the record can be safely transmitted or processed. 356 - 357 - @param record Complete record to validate 358 - @return True if the entire record is valid and well-formed 359 - @raise Protocol_error if any part of the record is invalid *) 360 - val validate_record : Fastcgi_types.record -> bool 361 - 362 - (** Check if a record type is a management record. 363 - 364 - Management records use request ID 0 and contain protocol-level information 365 - such as capability queries (Get_values) and unknown type responses. 366 - They are not associated with any specific request. 367 - 368 - @param record_type Record type to classify 369 - @return True if this is a management record type *) 370 - val is_management_record : Fastcgi_types.record_type -> bool 371 - 372 - (** Check if a record type is a stream record. 373 - 374 - Stream records (Params, Stdin, Stdout, Stderr, Data) can be sent in 375 - multiple parts and are terminated with an empty record of the same type. 376 - This allows streaming of arbitrarily large data without buffering. 377 - 378 - @param record_type Record type to classify 379 - @return True if this is a stream record type *) 380 - val is_stream_record : Fastcgi_types.record_type -> bool 381 - 382 - (** {1 Protocol Constants} 383 - 384 - Essential constants and utility functions for FastCGI protocol 385 - implementation. These values are defined by the protocol specification. *) 386 - 387 - (** Null request ID for management records. 388 - 389 - Management records always use request ID 0 to indicate they are not 390 - associated with any specific request. Applications must use non-zero 391 - request IDs for all application records. *) 392 - val null_request_id : Fastcgi_types.request_id 393 - 394 - (** Maximum content length for a single record. 395 - 396 - FastCGI records can contain at most 65535 bytes of content data. 397 - Larger data must be split across multiple records of the same type. *) 398 - val max_content_length : int 399 - 400 - (** Maximum padding length for a record. 401 - 402 - Records can have at most 255 bytes of padding for alignment purposes. 403 - Padding bytes are ignored by the receiver. *) 404 - val max_padding_length : int 405 - 406 - (** Calculate optimal padding for 8-byte alignment. 407 - 408 - Computes the number of padding bytes needed to align the total record 409 - size to an 8-byte boundary. This provides optimal performance on most 410 - architectures by ensuring proper memory alignment. 411 - 412 - @param content_length Length of the record content data 413 - @return Number of padding bytes needed (0-7) *) 414 - val calculate_padding : int -> int
-222
lib/fastcgi_responder.mli
··· 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. *) 27 - type 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. *) 34 - type '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. *) 57 - type '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 *) 79 - val 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 *) 88 - val response_of_fastcgi : 'a Fastcgi_types.response -> 'a http_response 89 - 90 - (** {1 Handler Utilities} *) 91 - 92 - (** Responder handler type. *) 93 - type '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 *) 103 - val 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 *) 115 - val 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 *) 127 - val 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 *) 138 - val 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 *) 149 - val 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 *) 160 - val 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 *) 172 - val 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 *) 181 - val 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 *) 188 - val 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 *) 195 - val get_param : 'a http_request -> string -> string option 196 - 197 - (** {1 File Operations} *) 198 - 199 - (** File upload information. *) 200 - type '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 *) 211 - val 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 *) 218 - val save_upload : 219 - fs:Eio.Fs.dir_ty Eio.Path.t -> 220 - path:string -> 221 - upload:'a file_upload -> 222 - unit
-444
lib/fastcgi_types.mli
··· 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]. *) 21 - type 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. *) 35 - type 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. *) 77 - type 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". *) 109 - type 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. *) 120 - type 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. *) 130 - type 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. *) 152 - type 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. *) 178 - type 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. *) 199 - type 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. *) 214 - type 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. *) 229 - type 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. *) 247 - type 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. *) 266 - type '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. *) 292 - type '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. *) 311 - type 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. *) 324 - val 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. *) 334 - val 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. *) 343 - val fcgi_begin_request : int 344 - 345 - (** Value [2]. Aborts an existing request. *) 346 - val fcgi_abort_request : int 347 - 348 - (** Value [3]. Completes a request. *) 349 - val fcgi_end_request : int 350 - 351 - (** Value [4]. Parameter name-value pairs. *) 352 - val fcgi_params : int 353 - 354 - (** Value [5]. Standard input data. *) 355 - val fcgi_stdin : int 356 - 357 - (** Value [6]. Standard output data. *) 358 - val fcgi_stdout : int 359 - 360 - (** Value [7]. Standard error data. *) 361 - val fcgi_stderr : int 362 - 363 - (** Value [8]. Additional data stream (Filter). *) 364 - val fcgi_data : int 365 - 366 - (** Value [9]. Query application variables. *) 367 - val fcgi_get_values : int 368 - 369 - (** Value [10]. Response to Get_values. *) 370 - val fcgi_get_values_result : int 371 - 372 - (** Value [11]. Unknown record type response. *) 373 - val 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. *) 382 - val fcgi_responder : int 383 - 384 - (** Value [2]. Perform authorization decisions. *) 385 - val fcgi_authorizer : int 386 - 387 - (** Value [3]. Process data streams with filtering. *) 388 - val 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. *) 400 - val 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. *) 410 - val max_content_length : int 411 - 412 - (** Value [255]. Maximum bytes in record padding. 413 - Between 0 and 255 bytes of data, which are ignored. *) 414 - val 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. *) 419 - val 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". *) 433 - val 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". *) 438 - val 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. *) 444 - val fcgi_mpxs_conns : string
-612
spec/OCAML.md
··· 1 - # OCaml FastCGI Specification 2 - 3 - This document specifies an OCaml interface for the FastCGI protocol using the Eio effects-based IO library. The design follows Eio conventions for structured concurrency, capability-based security, and resource management. 4 - 5 - ## Table of Contents 6 - 7 - 1. [Overview](#overview) 8 - 2. [Core Types](#core-types) 9 - 3. [Protocol Constants](#protocol-constants) 10 - 4. [Record Types](#record-types) 11 - 5. [Request/Response Model](#requestresponse-model) 12 - 6. [Application Interface](#application-interface) 13 - 7. [Connection Management](#connection-management) 14 - 8. [Role Implementations](#role-implementations) 15 - 9. [Error Handling](#error-handling) 16 - 10. [Resource Management](#resource-management) 17 - 11. [Examples](#examples) 18 - 19 - ## Overview 20 - 21 - The OCaml FastCGI implementation provides a high-level, type-safe interface for building FastCGI applications that can handle multiple concurrent requests efficiently. It leverages OCaml 5's effects system through Eio for structured concurrency and resource safety. 22 - 23 - ### Key Design Principles 24 - 25 - - **Capability-based**: All external resources (network, filesystem) are explicitly passed as capabilities 26 - - **Structured concurrency**: Use Eio switches for automatic resource cleanup 27 - - **Type safety**: Leverage OCaml's type system to prevent protocol errors 28 - - **Performance**: Support connection multiplexing and keep-alive for efficiency 29 - - **Extensibility**: Support all three FastCGI roles with room for future extensions 30 - 31 - ## Core Types 32 - 33 - ```ocaml 34 - (** FastCGI protocol version *) 35 - type version = int 36 - 37 - (** FastCGI record types *) 38 - type record_type = 39 - | Begin_request 40 - | Abort_request 41 - | End_request 42 - | Params 43 - | Stdin 44 - | Stdout 45 - | Stderr 46 - | Data 47 - | Get_values 48 - | Get_values_result 49 - | Unknown_type 50 - 51 - (** FastCGI roles *) 52 - type role = 53 - | Responder (** Handle HTTP requests and generate responses *) 54 - | Authorizer (** Perform authorization decisions *) 55 - | Filter (** Process data streams with filtering *) 56 - 57 - (** Request ID for multiplexing *) 58 - type request_id = int 59 - 60 - (** Application status code *) 61 - type app_status = int 62 - 63 - (** Protocol status codes *) 64 - type protocol_status = 65 - | Request_complete (** Normal completion *) 66 - | Cant_mpx_conn (** Cannot multiplex connection *) 67 - | Overloaded (** Application overloaded *) 68 - | Unknown_role (** Unknown role requested *) 69 - 70 - (** Connection flags *) 71 - type connection_flags = { 72 - keep_conn : bool; (** Keep connection open after request *) 73 - } 74 - ``` 75 - 76 - ## Protocol Constants 77 - 78 - ```ocaml 79 - module Constants = struct 80 - (** Protocol version *) 81 - let version_1 = 1 82 - 83 - (** Standard file descriptor for FastCGI listening socket *) 84 - let listensock_fileno = 0 85 - 86 - (** Record type constants *) 87 - let fcgi_begin_request = 1 88 - let fcgi_abort_request = 2 89 - let fcgi_end_request = 3 90 - let fcgi_params = 4 91 - let fcgi_stdin = 5 92 - let fcgi_stdout = 6 93 - let fcgi_stderr = 7 94 - let fcgi_data = 8 95 - let fcgi_get_values = 9 96 - let fcgi_get_values_result = 10 97 - let fcgi_unknown_type = 11 98 - 99 - (** Role constants *) 100 - let fcgi_responder = 1 101 - let fcgi_authorizer = 2 102 - let fcgi_filter = 3 103 - 104 - (** Flag constants *) 105 - let fcgi_keep_conn = 1 106 - 107 - (** Maximum sizes *) 108 - let max_content_length = 65535 109 - let max_padding_length = 255 110 - let header_length = 8 111 - 112 - (** Management record variables *) 113 - let fcgi_max_conns = "FCGI_MAX_CONNS" 114 - let fcgi_max_reqs = "FCGI_MAX_REQS" 115 - let fcgi_mpxs_conns = "FCGI_MPXS_CONNS" 116 - end 117 - ``` 118 - 119 - ## Record Types 120 - 121 - ```ocaml 122 - (** FastCGI record header *) 123 - type record_header = { 124 - version : int; 125 - record_type : record_type; 126 - request_id : request_id; 127 - content_length : int; 128 - padding_length : int; 129 - } 130 - 131 - (** Begin request record body *) 132 - type begin_request_body = { 133 - role : role; 134 - flags : connection_flags; 135 - } 136 - 137 - (** End request record body *) 138 - type end_request_body = { 139 - app_status : app_status; 140 - protocol_status : protocol_status; 141 - } 142 - 143 - (** Complete FastCGI record *) 144 - type record = { 145 - header : record_header; 146 - content : bytes; 147 - padding : bytes option; 148 - } 149 - 150 - (** Name-value pair for parameters *) 151 - type name_value_pair = { 152 - name : string; 153 - value : string; 154 - } 155 - ``` 156 - 157 - ## Request/Response Model 158 - 159 - ```ocaml 160 - (** Request context containing all information for a FastCGI request *) 161 - type 'a request = { 162 - request_id : request_id; 163 - role : role; 164 - flags : connection_flags; 165 - params : (string * string) list; 166 - stdin : 'a Eio.Flow.source; 167 - data : 'a Eio.Flow.source option; (** Only for Filter role *) 168 - } 169 - 170 - (** Response builder for constructing FastCGI responses *) 171 - type 'a response = { 172 - stdout : 'a Eio.Flow.sink; 173 - stderr : 'a Eio.Flow.sink; 174 - } 175 - 176 - (** Complete response with status *) 177 - type response_result = { 178 - app_status : app_status; 179 - protocol_status : protocol_status; 180 - } 181 - ``` 182 - 183 - ## Application Interface 184 - 185 - ```ocaml 186 - (** Application handler signature *) 187 - module Handler = struct 188 - (** Responder handler: process HTTP request and generate response *) 189 - type 'a responder = 'a request -> 'a response -> response_result 190 - 191 - (** Authorizer handler: make authorization decision *) 192 - type 'a authorizer = 'a request -> 'a response -> response_result 193 - 194 - (** Filter handler: process data stream with filtering *) 195 - type 'a filter = 'a request -> 'a response -> response_result 196 - 197 - (** Generic handler that can handle any role *) 198 - type 'a handler = 199 - | Responder of 'a responder 200 - | Authorizer of 'a authorizer 201 - | Filter of 'a filter 202 - end 203 - 204 - (** Application configuration *) 205 - type 'a app_config = { 206 - max_connections : int; (** Maximum concurrent connections *) 207 - max_requests : int; (** Maximum concurrent requests *) 208 - multiplex_connections : bool; (** Support connection multiplexing *) 209 - handler : 'a Handler.handler; (** Application request handler *) 210 - } 211 - ``` 212 - 213 - ## Connection Management 214 - 215 - ```ocaml 216 - (** Connection manager for handling FastCGI protocol *) 217 - module Connection = struct 218 - (** Opaque connection type *) 219 - type 'a t 220 - 221 - (** Create a connection from a network flow *) 222 - val create : sw:Eio.Switch.t -> 'a Eio.Flow.two_way -> 'a t 223 - 224 - (** Accept and process a single request on the connection *) 225 - val process_request : 'a t -> 'a Handler.handler -> response_result Eio.Promise.t 226 - 227 - (** Get connection statistics *) 228 - val stats : 'a t -> { 229 - active_requests : int; 230 - total_requests : int; 231 - bytes_sent : int; 232 - bytes_received : int; 233 - } 234 - 235 - (** Close the connection gracefully *) 236 - val close : 'a t -> unit 237 - end 238 - 239 - (** FastCGI server for accepting and managing connections *) 240 - module Server = struct 241 - (** Server configuration *) 242 - type 'a config = { 243 - app : 'a app_config; 244 - listen_address : [ 245 - | `Unix of string (** Unix domain socket path *) 246 - | `Tcp of Eio.Net.Ipaddr.t * int (** TCP address and port *) 247 - ]; 248 - backlog : int; (** Listen backlog size *) 249 - max_connections : int; (** Maximum concurrent connections *) 250 - } 251 - 252 - (** Run a FastCGI server *) 253 - val run : 254 - sw:Eio.Switch.t -> 255 - net:'a Eio.Net.t -> 256 - 'a config -> 257 - unit 258 - 259 - (** Run server with default configuration *) 260 - val run_default : 261 - sw:Eio.Switch.t -> 262 - net:'a Eio.Net.t -> 263 - handler:'a Handler.handler -> 264 - listen_address:[`Unix of string | `Tcp of Eio.Net.Ipaddr.t * int] -> 265 - unit 266 - end 267 - ``` 268 - 269 - ## Role Implementations 270 - 271 - ### Responder 272 - 273 - ```ocaml 274 - (** Responder role implementation *) 275 - module Responder = struct 276 - (** CGI-style environment variables *) 277 - type cgi_env = (string * string) list 278 - 279 - (** HTTP request information *) 280 - type http_request = { 281 - method_ : string; (** HTTP method (GET, POST, etc.) *) 282 - uri : string; (** Request URI *) 283 - query_string : string; (** Query string parameters *) 284 - content_type : string option; (** Content-Type header *) 285 - content_length : int option; (** Content-Length header *) 286 - headers : (string * string) list; (** Additional HTTP headers *) 287 - body : 'a Eio.Flow.source; (** Request body stream *) 288 - } 289 - 290 - (** HTTP response builder *) 291 - type 'a http_response = { 292 - write_status : int -> unit; (** Set HTTP status code *) 293 - write_header : string -> string -> unit; (** Add response header *) 294 - write_body : string -> unit; (** Write response body *) 295 - write_body_chunk : bytes -> unit; (** Write body chunk *) 296 - finish : unit -> unit; (** Complete response *) 297 - } 298 - 299 - (** Convert FastCGI request to HTTP request *) 300 - val request_of_fastcgi : 'a request -> http_request 301 - 302 - (** Create HTTP response writer from FastCGI response *) 303 - val response_of_fastcgi : 'a response -> 'a http_response 304 - 305 - (** Convenience handler wrapper for HTTP-style handlers *) 306 - val http_handler : 307 - (http_request -> 'a http_response -> unit) -> 308 - 'a Handler.responder 309 - end 310 - ``` 311 - 312 - ### Authorizer 313 - 314 - ```ocaml 315 - (** Authorizer role implementation *) 316 - module Authorizer = struct 317 - (** Authorization result *) 318 - type auth_result = 319 - | Authorized of (string * string) list (** Authorized with variable bindings *) 320 - | Unauthorized of { (** Unauthorized with error response *) 321 - status : int; 322 - headers : (string * string) list; 323 - body : string; 324 - } 325 - 326 - (** Authorization request information *) 327 - type auth_request = { 328 - method_ : string; 329 - uri : string; 330 - remote_addr : string option; 331 - remote_user : string option; 332 - auth_type : string option; 333 - headers : (string * string) list; 334 - } 335 - 336 - (** Convert FastCGI request to authorization request *) 337 - val request_of_fastcgi : 'a request -> auth_request 338 - 339 - (** Convert authorization result to FastCGI response *) 340 - val response_of_result : auth_result -> 'a response -> response_result 341 - 342 - (** Convenience handler wrapper for authorization handlers *) 343 - val auth_handler : 344 - (auth_request -> auth_result) -> 345 - 'a Handler.authorizer 346 - end 347 - ``` 348 - 349 - ### Filter 350 - 351 - ```ocaml 352 - (** Filter role implementation *) 353 - module Filter = struct 354 - (** Filter request with data stream *) 355 - type 'a filter_request = { 356 - request : 'a request; (** Base FastCGI request *) 357 - data_stream : 'a Eio.Flow.source; (** File data to filter *) 358 - data_last_modified : float option; (** File modification time *) 359 - data_length : int option; (** Expected data length *) 360 - } 361 - 362 - (** Convert FastCGI request to filter request *) 363 - val request_of_fastcgi : 'a request -> 'a filter_request 364 - 365 - (** Convenience handler wrapper for filter handlers *) 366 - val filter_handler : 367 - ('a filter_request -> 'a response -> unit) -> 368 - 'a Handler.filter 369 - end 370 - ``` 371 - 372 - ## Error Handling 373 - 374 - ```ocaml 375 - (** FastCGI specific errors *) 376 - module Error = struct 377 - type t = 378 - | Protocol_error of string (** Protocol violation *) 379 - | Invalid_record of string (** Malformed record *) 380 - | Unsupported_version of int (** Unsupported protocol version *) 381 - | Unknown_record_type of int (** Unknown record type *) 382 - | Request_id_conflict of request_id (** Duplicate request ID *) 383 - | Connection_closed (** Connection unexpectedly closed *) 384 - | Application_error of string (** Application-specific error *) 385 - 386 - exception Fastcgi_error of t 387 - 388 - (** Convert error to string description *) 389 - val to_string : t -> string 390 - 391 - (** Raise a FastCGI error *) 392 - val raise : t -> 'a 393 - end 394 - ``` 395 - 396 - ## Resource Management 397 - 398 - ```ocaml 399 - (** Resource management utilities *) 400 - module Resource = struct 401 - (** Request context with automatic cleanup *) 402 - type 'a request_context = { 403 - request : 'a request; 404 - response : 'a response; 405 - switch : Eio.Switch.t; (** Switch for request-scoped resources *) 406 - } 407 - 408 - (** Create a request context with automatic resource management *) 409 - val with_request_context : 410 - sw:Eio.Switch.t -> 411 - 'a request -> 412 - ('a request_context -> 'b) -> 413 - 'b 414 - 415 - (** Buffer management for efficient I/O *) 416 - module Buffer = struct 417 - type t 418 - 419 - val create : size:int -> t 420 - val read_into : t -> 'a Eio.Flow.source -> int 421 - val write_from : t -> 'a Eio.Flow.sink -> int -> unit 422 - val clear : t -> unit 423 - end 424 - end 425 - ``` 426 - 427 - ## Examples 428 - 429 - ### Basic Responder Application 430 - 431 - ```ocaml 432 - open Eio.Std 433 - 434 - let hello_handler request response = 435 - let http_req = Responder.request_of_fastcgi request in 436 - let http_resp = Responder.response_of_fastcgi response in 437 - 438 - http_resp.write_status 200; 439 - http_resp.write_header "Content-Type" "text/html"; 440 - http_resp.write_body "<h1>Hello, FastCGI!</h1>"; 441 - http_resp.finish (); 442 - 443 - { app_status = 0; protocol_status = Request_complete } 444 - 445 - let run_server env = 446 - let net = Eio.Stdenv.net env in 447 - let config = { 448 - app = { 449 - max_connections = 10; 450 - max_requests = 50; 451 - multiplex_connections = true; 452 - handler = Handler.Responder hello_handler; 453 - }; 454 - listen_address = `Tcp (Eio.Net.Ipaddr.V4.loopback, 9000); 455 - backlog = 5; 456 - max_connections = 10; 457 - } in 458 - 459 - Switch.run @@ fun sw -> 460 - Server.run ~sw ~net config 461 - 462 - let () = Eio_main.run run_server 463 - ``` 464 - 465 - ### File-serving Responder 466 - 467 - ```ocaml 468 - let file_server_handler ~cwd request response = 469 - let http_req = Responder.request_of_fastcgi request in 470 - let http_resp = Responder.response_of_fastcgi response in 471 - 472 - let path = Eio.Path.(cwd / String.drop_prefix http_req.uri 1) in 473 - 474 - match Eio.Path.load path with 475 - | content -> 476 - http_resp.write_status 200; 477 - http_resp.write_header "Content-Type" "text/html"; 478 - http_resp.write_body content; 479 - http_resp.finish () 480 - | exception (Eio.Io (Eio.Fs.E (Not_found _), _)) -> 481 - http_resp.write_status 404; 482 - http_resp.write_header "Content-Type" "text/plain"; 483 - http_resp.write_body "File not found"; 484 - http_resp.finish () 485 - | exception ex -> 486 - http_resp.write_status 500; 487 - http_resp.write_header "Content-Type" "text/plain"; 488 - http_resp.write_body ("Server error: " ^ Printexc.to_string ex); 489 - http_resp.finish (); 490 - 491 - { app_status = 0; protocol_status = Request_complete } 492 - 493 - let run_file_server env = 494 - let net = Eio.Stdenv.net env in 495 - let cwd = Eio.Stdenv.cwd env in 496 - let handler = file_server_handler ~cwd in 497 - 498 - Switch.run @@ fun sw -> 499 - Server.run_default ~sw ~net ~handler:(Handler.Responder handler) 500 - ~listen_address:(`Tcp (Eio.Net.Ipaddr.V4.loopback, 9000)) 501 - ``` 502 - 503 - ### Authorization Application 504 - 505 - ```ocaml 506 - let auth_handler auth_req = 507 - match auth_req.remote_user with 508 - | Some user when String.starts_with user "admin_" -> 509 - Authorizer.Authorized [("AUTH_USER", user); ("AUTH_LEVEL", "admin")] 510 - | Some user -> 511 - Authorizer.Authorized [("AUTH_USER", user); ("AUTH_LEVEL", "user")] 512 - | None -> 513 - Authorizer.Unauthorized { 514 - status = 401; 515 - headers = [("WWW-Authenticate", "Basic realm=\"FastCGI\"")]; 516 - body = "Authentication required"; 517 - } 518 - 519 - let run_auth_server env = 520 - let net = Eio.Stdenv.net env in 521 - let handler = Authorizer.auth_handler auth_handler in 522 - 523 - Switch.run @@ fun sw -> 524 - Server.run_default ~sw ~net ~handler:(Handler.Authorizer handler) 525 - ~listen_address:(`Unix "/tmp/auth.sock") 526 - ``` 527 - 528 - ### Filter Application 529 - 530 - ```ocaml 531 - let markdown_filter filter_req response = 532 - let data = Eio.Flow.read_all filter_req.data_stream in 533 - let html = Markdown.to_html data in (* Assuming markdown library *) 534 - 535 - Eio.Flow.copy_string "Content-Type: text/html\r\n\r\n" response.stdout; 536 - Eio.Flow.copy_string html response.stdout; 537 - 538 - { app_status = 0; protocol_status = Request_complete } 539 - 540 - let run_filter_server env = 541 - let net = Eio.Stdenv.net env in 542 - let handler = Filter.filter_handler markdown_filter in 543 - 544 - Switch.run @@ fun sw -> 545 - Server.run_default ~sw ~net ~handler:(Handler.Filter handler) 546 - ~listen_address:(`Tcp (Eio.Net.Ipaddr.V4.loopback, 9001)) 547 - ``` 548 - 549 - ### Connection Management Example 550 - 551 - ```ocaml 552 - let custom_connection_handler env = 553 - let net = Eio.Stdenv.net env in 554 - 555 - Switch.run @@ fun sw -> 556 - let server_socket = Eio.Net.listen net ~sw ~reuse_addr:true ~backlog:5 557 - (`Tcp (Eio.Net.Ipaddr.V4.loopback, 9000)) in 558 - 559 - let handler = Handler.Responder hello_handler in 560 - 561 - Eio.Net.run_server server_socket (fun flow _addr -> 562 - Switch.run @@ fun conn_sw -> 563 - let conn = Connection.create ~sw:conn_sw flow in 564 - 565 - let rec process_loop () = 566 - match Connection.process_request conn handler with 567 - | response -> process_loop () 568 - | exception End_of_file -> () 569 - | exception ex -> 570 - Eio.traceln "Connection error: %s" (Printexc.to_string ex) 571 - in 572 - process_loop () 573 - ) 574 - ``` 575 - 576 - ## Implementation Notes 577 - 578 - ### Wire Protocol Details 579 - 580 - The implementation must handle: 581 - 582 - 1. **Binary record format**: 8-byte headers with version, type, request ID, content length, and padding 583 - 2. **Name-value pair encoding**: Variable-length encoding for parameter names and values 584 - 3. **Stream management**: Proper handling of stdin, stdout, stderr, and data streams 585 - 4. **Connection multiplexing**: Support multiple concurrent requests over single connection 586 - 5. **Error propagation**: Convert protocol errors to appropriate OCaml exceptions 587 - 588 - ### Performance Considerations 589 - 590 - 1. **Buffer management**: Reuse buffers to minimize allocations 591 - 2. **Streaming I/O**: Process requests in streaming fashion for large payloads 592 - 3. **Connection pooling**: Support keep-alive connections for efficiency 593 - 4. **Concurrent processing**: Use Eio fibers for handling multiple requests 594 - 595 - ### Security Considerations 596 - 597 - 1. **Input validation**: Validate all protocol fields and reject malformed records 598 - 2. **Resource limits**: Enforce limits on request size, connection count, etc. 599 - 3. **Capability restriction**: Use Eio's capability system to limit access 600 - 4. **Error information**: Avoid leaking sensitive information in error messages 601 - 602 - ### Testing Strategy 603 - 604 - The implementation should include: 605 - 606 - 1. **Protocol conformance tests**: Verify correct implementation of FastCGI protocol 607 - 2. **Interoperability tests**: Test with real web servers (nginx, Apache) 608 - 3. **Performance tests**: Measure throughput and latency under load 609 - 4. **Error handling tests**: Verify graceful handling of error conditions 610 - 5. **Mock testing**: Use Eio_mock for deterministic unit tests 611 - 612 - This specification provides a comprehensive foundation for implementing a robust, efficient, and type-safe FastCGI library for OCaml using the Eio effects system.