FastCGI implementation in OCaml
1open Cmdliner
2
3(* Handler function that processes FastCGI requests *)
4let handler ~stdout ~stderr request =
5 Eio.traceln "Processing request: %a" Fastcgi.Request.pp request;
6
7 (* Get request parameters *)
8 let params = request.Fastcgi.Request.params in
9 let method_ = Fastcgi.Record.KV.find_opt "REQUEST_METHOD" params |> Option.value ~default:"GET" in
10 let uri = Fastcgi.Record.KV.find_opt "REQUEST_URI" params |> Option.value ~default:"/" in
11 let script_name = Fastcgi.Record.KV.find_opt "SCRIPT_NAME" params |> Option.value ~default:"" in
12
13 (* Log request info *)
14 Eio.traceln " Method: %s" method_;
15 Eio.traceln " URI: %s" uri;
16 Eio.traceln " Script: %s" script_name;
17
18 (* Generate simple HTTP response *)
19 let response_body =
20 Printf.sprintf
21 "<!DOCTYPE html>\n\
22 <html>\n\
23 <head><title>FastCGI OCaml Server</title></head>\n\
24 <body>\n\
25 <h1>FastCGI OCaml Server</h1>\n\
26 <p>Request processed successfully!</p>\n\
27 <ul>\n\
28 <li>Method: %s</li>\n\
29 <li>URI: %s</li>\n\
30 <li>Script: %s</li>\n\
31 </ul>\n\
32 <h2>All Parameters:</h2>\n\
33 <pre>%s</pre>\n\
34 </body>\n\
35 </html>\n"
36 method_ uri script_name
37 (let params_seq = Fastcgi.Record.KV.to_seq params in
38 let params_list = List.of_seq params_seq in
39 String.concat "\n" (List.map (fun (k, v) -> Printf.sprintf "%s = %s" k v) params_list))
40 in
41
42 (* Write HTTP response using FastCGI STDOUT records *)
43 let response_headers =
44 Printf.sprintf
45 "Status: 200 OK\r\n\
46 Content-Type: text/html; charset=utf-8\r\n\
47 Content-Length: %d\r\n\
48 \r\n"
49 (String.length response_body)
50 in
51 stdout response_headers;
52 stderr "stderr stuff";
53 stdout response_body
54
55let run port =
56 Eio_main.run @@ fun env ->
57 Eio.Switch.run @@ fun sw ->
58 let net = Eio.Stdenv.net env in
59 let addr = `Tcp (Eio.Net.Ipaddr.V4.loopback, port) in
60 let server_socket = Eio.Net.listen net ~backlog:10 ~reuse_addr:true ~sw addr in
61 Eio.traceln "FastCGI server listening on port %d" port;
62
63 (* Run the FastCGI server *)
64 Fastcgi.run server_socket
65 ~on_error:(fun ex ->
66 Eio.traceln "Error: %s" (Printexc.to_string ex);
67 Eio.traceln "bt: %s" (Printexc.get_backtrace ()))
68 handler
69
70let port =
71 let doc = "Port to listen on" in
72 Arg.(value & opt int 9000 & info ["p"; "port"] ~docv:"PORT" ~doc)
73
74let cmd =
75 let doc = "FastCGI server" in
76 let info = Cmd.info "fcgi-server" ~doc in
77 Cmd.v info Term.(const run $ port)
78
79let () = exit (Cmd.eval cmd)