this repo has no description
2
fork

Configure Feed

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

Add FastCGI sample applications demonstrating all three roles

- Create examples/ directory with four sample applications
- hello_responder.ml: Simple "Hello World" FastCGI Responder
- echo_responder.ml: Echo server displaying request information
- simple_authorizer.ml: Path-based authorization server
- basic_filter.ml: Text filter converting input to uppercase
- Update interface types to be compatible with Eio's actual types
- Fix TCP address type from Sockaddr to string for simplicity
- All examples type-check successfully against interfaces
- Expected linking errors confirm readiness for implementation phase

This completes STEP 2: sample binaries successfully demonstrate the API design
and prove the interfaces are usable for real applications.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

+145 -7
+1 -1
CLAUDE.md
··· 92 92 93 93 # Software engineering 94 94 95 - We will go through a multi step process to build this library. We are currently at STEP 1. 95 + We will go through a multi step process to build this library. We are currently at STEP 2. 96 96 97 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 98
+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))
+6 -6
lib/fastcgi.mli
··· 259 259 listen_address : [ 260 260 | `Unix of string (** Unix domain socket path. Common for local 261 261 communication with web servers. *) 262 - | `Tcp of Eio.Net.Sockaddr.stream * int (** TCP socket address and port. 262 + | `Tcp of string * int (** TCP socket address (host) and port. 263 263 Used for remote FastCGI servers. *) 264 264 ]; 265 265 backlog : int; (** Listen socket backlog size. Controls how ··· 286 286 @raise Sys_error if unable to bind to the specified address *) 287 287 val run : 288 288 sw:Eio.Switch.t -> 289 - net:'a Eio.Net.t -> 290 - 'a config -> 289 + net:[< `Generic | `Unix ] Eio.Net.ty Eio.Resource.t -> 290 + _ config -> 291 291 unit 292 292 293 293 (** Run server with default configuration. ··· 308 308 @raise Sys_error if unable to bind to the specified address *) 309 309 val run_default : 310 310 sw:Eio.Switch.t -> 311 - net:'a Eio.Net.t -> 312 - handler:'a Handler.handler -> 313 - listen_address:[`Unix of string | `Tcp of Eio.Net.Sockaddr.stream * int] -> 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 314 unit 315 315 end 316 316