···11-This branch exists to figure out an OCaml specification for the FastCGI interface.
11+This repository exists to implement an OCaml specification for the FastCGI interface.
2233The spec for the FastCGI protocol is in spec/FastCGI_Specification.html.
44The Go lang implementation of FastCGI is in spec/fcgi.go.
55+An OCaml specification exists in spec/OCAML.md.
5666-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.
77+These are intended to act as a reference implementation for us to figure out what the ideal OCaml interface should look like for FastCGI.
7889Our target language is OCaml, using the Eio library. The README for Eio is in OCaml-EIO-README.md to give you a reference.
1010+The parsing and serialisation should be done using Eio's Buf_read and Buf_write modules.
1111+1212+You can run commands with:
1313+1414+- clean: `opam exec -- dune clean`
1515+- build: `opam exec -- dune build @check`
1616+- docs: `opam exec -- dune build @doc`
1717+- build while ignoring warnings: add `--profile=release` to the CLI to activate the profile that ignores warnings
1818+1919+# Tips on fixing bugs
2020+2121+If you see errors like this:
2222+2323+```
2424+File "../../.jmap.objs/byte/jmap.odoc":
2525+Warning: Hidden fields in type 'Jmap.Email.Identity.identity_create'
2626+```
2727+2828+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.
2929+3030+## Documentation Comments
3131+3232+When adding OCaml documentation comments, be careful about ambiguous documentation comments. If you see errors like:
3333+3434+```
3535+Error (warning 50 [unexpected-docstring]): ambiguous documentation comment
3636+```
3737+3838+This usually means there isn't enough whitespace between the documentation comment and the code element it's documenting. Always:
3939+4040+1. Add blank lines between consecutive documentation comments
4141+2. Add a blank line before a documentation comment for a module/type/value declaration
4242+3. When documenting record fields or variant constructors, place the comment after the field with at least one space
4343+4444+Example of correct documentation spacing:
4545+4646+```ocaml
4747+(** Module documentation. *)
4848+4949+(** Value documentation. *)
5050+val some_value : int
5151+5252+(** Type documentation. *)
5353+type t =
5454+ | First (** First constructor *)
5555+ | Second (** Second constructor *)
5656+5757+(** Record documentation. *)
5858+type record = {
5959+ field1 : int; (** Field1 documentation *)
6060+ field2 : string (** Field2 documentation *)
6161+}
6262+```
6363+6464+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.
6565+6666+# Module Structure Guidelines
6767+6868+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.
6969+7070+1. Top-level files should define their main types directly (e.g., `jmap_identity.mli` should define identity-related types at the top level).
7171+7272+2. Related operations or specialized subtypes should be defined in nested modules within the file:
7373+ ```ocaml
7474+ module Create : sig
7575+ type t (* NOT 'type create' or any other name *)
7676+ (* Functions operating on creation requests *)
7777+7878+ module Response : sig
7979+ type t
8080+ (* Functions for creation responses *)
8181+ end
8282+ end
8383+ ```
8484+8585+3. Consistently use `type t` for the main type in each module and submodule.
8686+8787+4. Functions operating on a type should be placed in the same module as the type.
8888+8989+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.
9090+9191+This structured approach promotes encapsulation, consistent type naming, and clearer organization of related functionality.
9292+9393+# Software engineering
9494+9595+We will go through a multi step process to build this library. We are currently at STEP 1.
9696+9797+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.
9898+9999+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.
100100+101101+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.
102102+
+612
spec/OCAML.md
···11+# OCaml FastCGI Specification
22+33+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.
44+55+## Table of Contents
66+77+1. [Overview](#overview)
88+2. [Core Types](#core-types)
99+3. [Protocol Constants](#protocol-constants)
1010+4. [Record Types](#record-types)
1111+5. [Request/Response Model](#requestresponse-model)
1212+6. [Application Interface](#application-interface)
1313+7. [Connection Management](#connection-management)
1414+8. [Role Implementations](#role-implementations)
1515+9. [Error Handling](#error-handling)
1616+10. [Resource Management](#resource-management)
1717+11. [Examples](#examples)
1818+1919+## Overview
2020+2121+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.
2222+2323+### Key Design Principles
2424+2525+- **Capability-based**: All external resources (network, filesystem) are explicitly passed as capabilities
2626+- **Structured concurrency**: Use Eio switches for automatic resource cleanup
2727+- **Type safety**: Leverage OCaml's type system to prevent protocol errors
2828+- **Performance**: Support connection multiplexing and keep-alive for efficiency
2929+- **Extensibility**: Support all three FastCGI roles with room for future extensions
3030+3131+## Core Types
3232+3333+```ocaml
3434+(** FastCGI protocol version *)
3535+type version = int
3636+3737+(** FastCGI record types *)
3838+type record_type =
3939+ | Begin_request
4040+ | Abort_request
4141+ | End_request
4242+ | Params
4343+ | Stdin
4444+ | Stdout
4545+ | Stderr
4646+ | Data
4747+ | Get_values
4848+ | Get_values_result
4949+ | Unknown_type
5050+5151+(** FastCGI roles *)
5252+type role =
5353+ | Responder (** Handle HTTP requests and generate responses *)
5454+ | Authorizer (** Perform authorization decisions *)
5555+ | Filter (** Process data streams with filtering *)
5656+5757+(** Request ID for multiplexing *)
5858+type request_id = int
5959+6060+(** Application status code *)
6161+type app_status = int
6262+6363+(** Protocol status codes *)
6464+type protocol_status =
6565+ | Request_complete (** Normal completion *)
6666+ | Cant_mpx_conn (** Cannot multiplex connection *)
6767+ | Overloaded (** Application overloaded *)
6868+ | Unknown_role (** Unknown role requested *)
6969+7070+(** Connection flags *)
7171+type connection_flags = {
7272+ keep_conn : bool; (** Keep connection open after request *)
7373+}
7474+```
7575+7676+## Protocol Constants
7777+7878+```ocaml
7979+module Constants = struct
8080+ (** Protocol version *)
8181+ let version_1 = 1
8282+8383+ (** Standard file descriptor for FastCGI listening socket *)
8484+ let listensock_fileno = 0
8585+8686+ (** Record type constants *)
8787+ let fcgi_begin_request = 1
8888+ let fcgi_abort_request = 2
8989+ let fcgi_end_request = 3
9090+ let fcgi_params = 4
9191+ let fcgi_stdin = 5
9292+ let fcgi_stdout = 6
9393+ let fcgi_stderr = 7
9494+ let fcgi_data = 8
9595+ let fcgi_get_values = 9
9696+ let fcgi_get_values_result = 10
9797+ let fcgi_unknown_type = 11
9898+9999+ (** Role constants *)
100100+ let fcgi_responder = 1
101101+ let fcgi_authorizer = 2
102102+ let fcgi_filter = 3
103103+104104+ (** Flag constants *)
105105+ let fcgi_keep_conn = 1
106106+107107+ (** Maximum sizes *)
108108+ let max_content_length = 65535
109109+ let max_padding_length = 255
110110+ let header_length = 8
111111+112112+ (** Management record variables *)
113113+ let fcgi_max_conns = "FCGI_MAX_CONNS"
114114+ let fcgi_max_reqs = "FCGI_MAX_REQS"
115115+ let fcgi_mpxs_conns = "FCGI_MPXS_CONNS"
116116+end
117117+```
118118+119119+## Record Types
120120+121121+```ocaml
122122+(** FastCGI record header *)
123123+type record_header = {
124124+ version : int;
125125+ record_type : record_type;
126126+ request_id : request_id;
127127+ content_length : int;
128128+ padding_length : int;
129129+}
130130+131131+(** Begin request record body *)
132132+type begin_request_body = {
133133+ role : role;
134134+ flags : connection_flags;
135135+}
136136+137137+(** End request record body *)
138138+type end_request_body = {
139139+ app_status : app_status;
140140+ protocol_status : protocol_status;
141141+}
142142+143143+(** Complete FastCGI record *)
144144+type record = {
145145+ header : record_header;
146146+ content : bytes;
147147+ padding : bytes option;
148148+}
149149+150150+(** Name-value pair for parameters *)
151151+type name_value_pair = {
152152+ name : string;
153153+ value : string;
154154+}
155155+```
156156+157157+## Request/Response Model
158158+159159+```ocaml
160160+(** Request context containing all information for a FastCGI request *)
161161+type 'a request = {
162162+ request_id : request_id;
163163+ role : role;
164164+ flags : connection_flags;
165165+ params : (string * string) list;
166166+ stdin : 'a Eio.Flow.source;
167167+ data : 'a Eio.Flow.source option; (** Only for Filter role *)
168168+}
169169+170170+(** Response builder for constructing FastCGI responses *)
171171+type 'a response = {
172172+ stdout : 'a Eio.Flow.sink;
173173+ stderr : 'a Eio.Flow.sink;
174174+}
175175+176176+(** Complete response with status *)
177177+type response_result = {
178178+ app_status : app_status;
179179+ protocol_status : protocol_status;
180180+}
181181+```
182182+183183+## Application Interface
184184+185185+```ocaml
186186+(** Application handler signature *)
187187+module Handler = struct
188188+ (** Responder handler: process HTTP request and generate response *)
189189+ type 'a responder = 'a request -> 'a response -> response_result
190190+191191+ (** Authorizer handler: make authorization decision *)
192192+ type 'a authorizer = 'a request -> 'a response -> response_result
193193+194194+ (** Filter handler: process data stream with filtering *)
195195+ type 'a filter = 'a request -> 'a response -> response_result
196196+197197+ (** Generic handler that can handle any role *)
198198+ type 'a handler =
199199+ | Responder of 'a responder
200200+ | Authorizer of 'a authorizer
201201+ | Filter of 'a filter
202202+end
203203+204204+(** Application configuration *)
205205+type 'a app_config = {
206206+ max_connections : int; (** Maximum concurrent connections *)
207207+ max_requests : int; (** Maximum concurrent requests *)
208208+ multiplex_connections : bool; (** Support connection multiplexing *)
209209+ handler : 'a Handler.handler; (** Application request handler *)
210210+}
211211+```
212212+213213+## Connection Management
214214+215215+```ocaml
216216+(** Connection manager for handling FastCGI protocol *)
217217+module Connection = struct
218218+ (** Opaque connection type *)
219219+ type 'a t
220220+221221+ (** Create a connection from a network flow *)
222222+ val create : sw:Eio.Switch.t -> 'a Eio.Flow.two_way -> 'a t
223223+224224+ (** Accept and process a single request on the connection *)
225225+ val process_request : 'a t -> 'a Handler.handler -> response_result Eio.Promise.t
226226+227227+ (** Get connection statistics *)
228228+ val stats : 'a t -> {
229229+ active_requests : int;
230230+ total_requests : int;
231231+ bytes_sent : int;
232232+ bytes_received : int;
233233+ }
234234+235235+ (** Close the connection gracefully *)
236236+ val close : 'a t -> unit
237237+end
238238+239239+(** FastCGI server for accepting and managing connections *)
240240+module Server = struct
241241+ (** Server configuration *)
242242+ type 'a config = {
243243+ app : 'a app_config;
244244+ listen_address : [
245245+ | `Unix of string (** Unix domain socket path *)
246246+ | `Tcp of Eio.Net.Ipaddr.t * int (** TCP address and port *)
247247+ ];
248248+ backlog : int; (** Listen backlog size *)
249249+ max_connections : int; (** Maximum concurrent connections *)
250250+ }
251251+252252+ (** Run a FastCGI server *)
253253+ val run :
254254+ sw:Eio.Switch.t ->
255255+ net:'a Eio.Net.t ->
256256+ 'a config ->
257257+ unit
258258+259259+ (** Run server with default configuration *)
260260+ val run_default :
261261+ sw:Eio.Switch.t ->
262262+ net:'a Eio.Net.t ->
263263+ handler:'a Handler.handler ->
264264+ listen_address:[`Unix of string | `Tcp of Eio.Net.Ipaddr.t * int] ->
265265+ unit
266266+end
267267+```
268268+269269+## Role Implementations
270270+271271+### Responder
272272+273273+```ocaml
274274+(** Responder role implementation *)
275275+module Responder = struct
276276+ (** CGI-style environment variables *)
277277+ type cgi_env = (string * string) list
278278+279279+ (** HTTP request information *)
280280+ type http_request = {
281281+ method_ : string; (** HTTP method (GET, POST, etc.) *)
282282+ uri : string; (** Request URI *)
283283+ query_string : string; (** Query string parameters *)
284284+ content_type : string option; (** Content-Type header *)
285285+ content_length : int option; (** Content-Length header *)
286286+ headers : (string * string) list; (** Additional HTTP headers *)
287287+ body : 'a Eio.Flow.source; (** Request body stream *)
288288+ }
289289+290290+ (** HTTP response builder *)
291291+ type 'a http_response = {
292292+ write_status : int -> unit; (** Set HTTP status code *)
293293+ write_header : string -> string -> unit; (** Add response header *)
294294+ write_body : string -> unit; (** Write response body *)
295295+ write_body_chunk : bytes -> unit; (** Write body chunk *)
296296+ finish : unit -> unit; (** Complete response *)
297297+ }
298298+299299+ (** Convert FastCGI request to HTTP request *)
300300+ val request_of_fastcgi : 'a request -> http_request
301301+302302+ (** Create HTTP response writer from FastCGI response *)
303303+ val response_of_fastcgi : 'a response -> 'a http_response
304304+305305+ (** Convenience handler wrapper for HTTP-style handlers *)
306306+ val http_handler :
307307+ (http_request -> 'a http_response -> unit) ->
308308+ 'a Handler.responder
309309+end
310310+```
311311+312312+### Authorizer
313313+314314+```ocaml
315315+(** Authorizer role implementation *)
316316+module Authorizer = struct
317317+ (** Authorization result *)
318318+ type auth_result =
319319+ | Authorized of (string * string) list (** Authorized with variable bindings *)
320320+ | Unauthorized of { (** Unauthorized with error response *)
321321+ status : int;
322322+ headers : (string * string) list;
323323+ body : string;
324324+ }
325325+326326+ (** Authorization request information *)
327327+ type auth_request = {
328328+ method_ : string;
329329+ uri : string;
330330+ remote_addr : string option;
331331+ remote_user : string option;
332332+ auth_type : string option;
333333+ headers : (string * string) list;
334334+ }
335335+336336+ (** Convert FastCGI request to authorization request *)
337337+ val request_of_fastcgi : 'a request -> auth_request
338338+339339+ (** Convert authorization result to FastCGI response *)
340340+ val response_of_result : auth_result -> 'a response -> response_result
341341+342342+ (** Convenience handler wrapper for authorization handlers *)
343343+ val auth_handler :
344344+ (auth_request -> auth_result) ->
345345+ 'a Handler.authorizer
346346+end
347347+```
348348+349349+### Filter
350350+351351+```ocaml
352352+(** Filter role implementation *)
353353+module Filter = struct
354354+ (** Filter request with data stream *)
355355+ type 'a filter_request = {
356356+ request : 'a request; (** Base FastCGI request *)
357357+ data_stream : 'a Eio.Flow.source; (** File data to filter *)
358358+ data_last_modified : float option; (** File modification time *)
359359+ data_length : int option; (** Expected data length *)
360360+ }
361361+362362+ (** Convert FastCGI request to filter request *)
363363+ val request_of_fastcgi : 'a request -> 'a filter_request
364364+365365+ (** Convenience handler wrapper for filter handlers *)
366366+ val filter_handler :
367367+ ('a filter_request -> 'a response -> unit) ->
368368+ 'a Handler.filter
369369+end
370370+```
371371+372372+## Error Handling
373373+374374+```ocaml
375375+(** FastCGI specific errors *)
376376+module Error = struct
377377+ type t =
378378+ | Protocol_error of string (** Protocol violation *)
379379+ | Invalid_record of string (** Malformed record *)
380380+ | Unsupported_version of int (** Unsupported protocol version *)
381381+ | Unknown_record_type of int (** Unknown record type *)
382382+ | Request_id_conflict of request_id (** Duplicate request ID *)
383383+ | Connection_closed (** Connection unexpectedly closed *)
384384+ | Application_error of string (** Application-specific error *)
385385+386386+ exception Fastcgi_error of t
387387+388388+ (** Convert error to string description *)
389389+ val to_string : t -> string
390390+391391+ (** Raise a FastCGI error *)
392392+ val raise : t -> 'a
393393+end
394394+```
395395+396396+## Resource Management
397397+398398+```ocaml
399399+(** Resource management utilities *)
400400+module Resource = struct
401401+ (** Request context with automatic cleanup *)
402402+ type 'a request_context = {
403403+ request : 'a request;
404404+ response : 'a response;
405405+ switch : Eio.Switch.t; (** Switch for request-scoped resources *)
406406+ }
407407+408408+ (** Create a request context with automatic resource management *)
409409+ val with_request_context :
410410+ sw:Eio.Switch.t ->
411411+ 'a request ->
412412+ ('a request_context -> 'b) ->
413413+ 'b
414414+415415+ (** Buffer management for efficient I/O *)
416416+ module Buffer = struct
417417+ type t
418418+419419+ val create : size:int -> t
420420+ val read_into : t -> 'a Eio.Flow.source -> int
421421+ val write_from : t -> 'a Eio.Flow.sink -> int -> unit
422422+ val clear : t -> unit
423423+ end
424424+end
425425+```
426426+427427+## Examples
428428+429429+### Basic Responder Application
430430+431431+```ocaml
432432+open Eio.Std
433433+434434+let hello_handler request response =
435435+ let http_req = Responder.request_of_fastcgi request in
436436+ let http_resp = Responder.response_of_fastcgi response in
437437+438438+ http_resp.write_status 200;
439439+ http_resp.write_header "Content-Type" "text/html";
440440+ http_resp.write_body "<h1>Hello, FastCGI!</h1>";
441441+ http_resp.finish ();
442442+443443+ { app_status = 0; protocol_status = Request_complete }
444444+445445+let run_server env =
446446+ let net = Eio.Stdenv.net env in
447447+ let config = {
448448+ app = {
449449+ max_connections = 10;
450450+ max_requests = 50;
451451+ multiplex_connections = true;
452452+ handler = Handler.Responder hello_handler;
453453+ };
454454+ listen_address = `Tcp (Eio.Net.Ipaddr.V4.loopback, 9000);
455455+ backlog = 5;
456456+ max_connections = 10;
457457+ } in
458458+459459+ Switch.run @@ fun sw ->
460460+ Server.run ~sw ~net config
461461+462462+let () = Eio_main.run run_server
463463+```
464464+465465+### File-serving Responder
466466+467467+```ocaml
468468+let file_server_handler ~cwd request response =
469469+ let http_req = Responder.request_of_fastcgi request in
470470+ let http_resp = Responder.response_of_fastcgi response in
471471+472472+ let path = Eio.Path.(cwd / String.drop_prefix http_req.uri 1) in
473473+474474+ match Eio.Path.load path with
475475+ | content ->
476476+ http_resp.write_status 200;
477477+ http_resp.write_header "Content-Type" "text/html";
478478+ http_resp.write_body content;
479479+ http_resp.finish ()
480480+ | exception (Eio.Io (Eio.Fs.E (Not_found _), _)) ->
481481+ http_resp.write_status 404;
482482+ http_resp.write_header "Content-Type" "text/plain";
483483+ http_resp.write_body "File not found";
484484+ http_resp.finish ()
485485+ | exception ex ->
486486+ http_resp.write_status 500;
487487+ http_resp.write_header "Content-Type" "text/plain";
488488+ http_resp.write_body ("Server error: " ^ Printexc.to_string ex);
489489+ http_resp.finish ();
490490+491491+ { app_status = 0; protocol_status = Request_complete }
492492+493493+let run_file_server env =
494494+ let net = Eio.Stdenv.net env in
495495+ let cwd = Eio.Stdenv.cwd env in
496496+ let handler = file_server_handler ~cwd in
497497+498498+ Switch.run @@ fun sw ->
499499+ Server.run_default ~sw ~net ~handler:(Handler.Responder handler)
500500+ ~listen_address:(`Tcp (Eio.Net.Ipaddr.V4.loopback, 9000))
501501+```
502502+503503+### Authorization Application
504504+505505+```ocaml
506506+let auth_handler auth_req =
507507+ match auth_req.remote_user with
508508+ | Some user when String.starts_with user "admin_" ->
509509+ Authorizer.Authorized [("AUTH_USER", user); ("AUTH_LEVEL", "admin")]
510510+ | Some user ->
511511+ Authorizer.Authorized [("AUTH_USER", user); ("AUTH_LEVEL", "user")]
512512+ | None ->
513513+ Authorizer.Unauthorized {
514514+ status = 401;
515515+ headers = [("WWW-Authenticate", "Basic realm=\"FastCGI\"")];
516516+ body = "Authentication required";
517517+ }
518518+519519+let run_auth_server env =
520520+ let net = Eio.Stdenv.net env in
521521+ let handler = Authorizer.auth_handler auth_handler in
522522+523523+ Switch.run @@ fun sw ->
524524+ Server.run_default ~sw ~net ~handler:(Handler.Authorizer handler)
525525+ ~listen_address:(`Unix "/tmp/auth.sock")
526526+```
527527+528528+### Filter Application
529529+530530+```ocaml
531531+let markdown_filter filter_req response =
532532+ let data = Eio.Flow.read_all filter_req.data_stream in
533533+ let html = Markdown.to_html data in (* Assuming markdown library *)
534534+535535+ Eio.Flow.copy_string "Content-Type: text/html\r\n\r\n" response.stdout;
536536+ Eio.Flow.copy_string html response.stdout;
537537+538538+ { app_status = 0; protocol_status = Request_complete }
539539+540540+let run_filter_server env =
541541+ let net = Eio.Stdenv.net env in
542542+ let handler = Filter.filter_handler markdown_filter in
543543+544544+ Switch.run @@ fun sw ->
545545+ Server.run_default ~sw ~net ~handler:(Handler.Filter handler)
546546+ ~listen_address:(`Tcp (Eio.Net.Ipaddr.V4.loopback, 9001))
547547+```
548548+549549+### Connection Management Example
550550+551551+```ocaml
552552+let custom_connection_handler env =
553553+ let net = Eio.Stdenv.net env in
554554+555555+ Switch.run @@ fun sw ->
556556+ let server_socket = Eio.Net.listen net ~sw ~reuse_addr:true ~backlog:5
557557+ (`Tcp (Eio.Net.Ipaddr.V4.loopback, 9000)) in
558558+559559+ let handler = Handler.Responder hello_handler in
560560+561561+ Eio.Net.run_server server_socket (fun flow _addr ->
562562+ Switch.run @@ fun conn_sw ->
563563+ let conn = Connection.create ~sw:conn_sw flow in
564564+565565+ let rec process_loop () =
566566+ match Connection.process_request conn handler with
567567+ | response -> process_loop ()
568568+ | exception End_of_file -> ()
569569+ | exception ex ->
570570+ Eio.traceln "Connection error: %s" (Printexc.to_string ex)
571571+ in
572572+ process_loop ()
573573+ )
574574+```
575575+576576+## Implementation Notes
577577+578578+### Wire Protocol Details
579579+580580+The implementation must handle:
581581+582582+1. **Binary record format**: 8-byte headers with version, type, request ID, content length, and padding
583583+2. **Name-value pair encoding**: Variable-length encoding for parameter names and values
584584+3. **Stream management**: Proper handling of stdin, stdout, stderr, and data streams
585585+4. **Connection multiplexing**: Support multiple concurrent requests over single connection
586586+5. **Error propagation**: Convert protocol errors to appropriate OCaml exceptions
587587+588588+### Performance Considerations
589589+590590+1. **Buffer management**: Reuse buffers to minimize allocations
591591+2. **Streaming I/O**: Process requests in streaming fashion for large payloads
592592+3. **Connection pooling**: Support keep-alive connections for efficiency
593593+4. **Concurrent processing**: Use Eio fibers for handling multiple requests
594594+595595+### Security Considerations
596596+597597+1. **Input validation**: Validate all protocol fields and reject malformed records
598598+2. **Resource limits**: Enforce limits on request size, connection count, etc.
599599+3. **Capability restriction**: Use Eio's capability system to limit access
600600+4. **Error information**: Avoid leaking sensitive information in error messages
601601+602602+### Testing Strategy
603603+604604+The implementation should include:
605605+606606+1. **Protocol conformance tests**: Verify correct implementation of FastCGI protocol
607607+2. **Interoperability tests**: Test with real web servers (nginx, Apache)
608608+3. **Performance tests**: Measure throughput and latency under load
609609+4. **Error handling tests**: Verify graceful handling of error conditions
610610+5. **Mock testing**: Use Eio_mock for deterministic unit tests
611611+612612+This specification provides a comprehensive foundation for implementing a robust, efficient, and type-safe FastCGI library for OCaml using the Eio effects system.