this repo has no description
2
fork

Configure Feed

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

add claude generated spec

+708 -2
+96 -2
CLAUDE.md
··· 1 - This branch exists to figure out an OCaml specification for the FastCGI interface. 1 + This repository exists to implement an OCaml specification for the FastCGI interface. 2 2 3 3 The spec for the FastCGI protocol is in spec/FastCGI_Specification.html. 4 4 The Go lang implementation of FastCGI is in spec/fcgi.go. 5 + An OCaml specification exists in spec/OCAML.md. 5 6 6 - Both of these are intended to act as a reference implementation, for us to figure out what the ideal OCaml interface should look like for FastCGI. 7 + These are intended to act as a reference implementation for us to figure out what the ideal OCaml interface should look like for FastCGI. 7 8 8 9 Our target language is OCaml, using the Eio library. The README for Eio is in OCaml-EIO-README.md to give you a reference. 10 + The parsing and serialisation should be done using Eio's Buf_read and Buf_write modules. 11 + 12 + You can run commands with: 13 + 14 + - clean: `opam exec -- dune clean` 15 + - build: `opam exec -- dune build @check` 16 + - docs: `opam exec -- dune build @doc` 17 + - build while ignoring warnings: add `--profile=release` to the CLI to activate the profile that ignores warnings 18 + 19 + # Tips on fixing bugs 20 + 21 + If you see errors like this: 22 + 23 + ``` 24 + File "../../.jmap.objs/byte/jmap.odoc": 25 + Warning: Hidden fields in type 'Jmap.Email.Identity.identity_create' 26 + ``` 27 + 28 + Then examine the HTML docs built for that module. You will see that there are module references with __ in them, e.g. "Jmap__.Jmap_email_types.Email_address.t" which indicate that the module is being accessed directly instead of via the module aliases defined. 29 + 30 + ## Documentation Comments 31 + 32 + When adding OCaml documentation comments, be careful about ambiguous documentation comments. If you see errors like: 33 + 34 + ``` 35 + Error (warning 50 [unexpected-docstring]): ambiguous documentation comment 36 + ``` 37 + 38 + This usually means there isn't enough whitespace between the documentation comment and the code element it's documenting. Always: 39 + 40 + 1. Add blank lines between consecutive documentation comments 41 + 2. Add a blank line before a documentation comment for a module/type/value declaration 42 + 3. When documenting record fields or variant constructors, place the comment after the field with at least one space 43 + 44 + Example of correct documentation spacing: 45 + 46 + ```ocaml 47 + (** Module documentation. *) 48 + 49 + (** Value documentation. *) 50 + val some_value : int 51 + 52 + (** Type documentation. *) 53 + type t = 54 + | First (** First constructor *) 55 + | Second (** Second constructor *) 56 + 57 + (** Record documentation. *) 58 + type record = { 59 + field1 : int; (** Field1 documentation *) 60 + field2 : string (** Field2 documentation *) 61 + } 62 + ``` 63 + 64 + If in doubt, add more whitespace lines than needed - you can always clean this up later with `dune build @fmt` to get ocamlformat to sort out the whitespace properly. 65 + 66 + # Module Structure Guidelines 67 + 68 + IMPORTANT: For all modules, use a nested module structure with a canonical `type t` inside each submodule. This approach ensures consistent type naming and logical grouping of related functionality. 69 + 70 + 1. Top-level files should define their main types directly (e.g., `jmap_identity.mli` should define identity-related types at the top level). 71 + 72 + 2. Related operations or specialized subtypes should be defined in nested modules within the file: 73 + ```ocaml 74 + module Create : sig 75 + type t (* NOT 'type create' or any other name *) 76 + (* Functions operating on creation requests *) 77 + 78 + module Response : sig 79 + type t 80 + (* Functions for creation responses *) 81 + end 82 + end 83 + ``` 84 + 85 + 3. Consistently use `type t` for the main type in each module and submodule. 86 + 87 + 4. Functions operating on a type should be placed in the same module as the type. 88 + 89 + 5. When a file is named after a concept (e.g., `jmap_identity.mli`), there's no need to have a matching nested module inside the file (e.g., `module Identity : sig...`), as the file itself represents that namespace. 90 + 91 + This structured approach promotes encapsulation, consistent type naming, and clearer organization of related functionality. 92 + 93 + # Software engineering 94 + 95 + We will go through a multi step process to build this library. We are currently at STEP 1. 96 + 97 + 1) we will generate OCaml interface files only, and no module implementations. The purpose here is to write and document the necessary type signatures. Once we generate these, we can check that they work with "dune build @check". Once that succeeds, we will build HTML documentation with "dune build @doc" in order to ensure the interfaces are reasonable. 98 + 99 + 2) once these interface files exist, we will build a series of sample binaries that will attempt to implement the library for some sample usecases. This binary will not fully link, but it should type check. The only linking error that we get should be from the missing library implementation that we are currently buildign. 100 + 101 + 3) we will calculate the dependency order for each module in our library, and work through an implementation of each one in increasing dependency order (that is, the module with the fewest dependencies should be handled first). For each module interface, we will generate a corresponding module implementation. We will also add test cases for this specific module, and update the dune files. Before proceeding to the next module, a build should be done to ensure the implementation builds and type checks as far as is possible. 102 +
+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.