This commit is contained in:
swrup 2025-11-11 02:07:51 +01:00
parent aa2ff7b2f0
commit 2f3113f55d
11742 changed files with 1223940 additions and 0 deletions

View file

@ -0,0 +1,260 @@
open Lwt.Infix
let ( <.> ) f g x = f (g x)
let apply v f = f v
let reporter ppf =
let report src level ~over k msgf =
let k _ =
over () ;
k () in
let with_metadata header _tags k ppf fmt =
Format.kfprintf k ppf
("%a[%a]: " ^^ fmt ^^ "\n%!")
Logs_fmt.pp_header (level, header)
Fmt.(styled `Magenta string)
(Logs.Src.name src) in
msgf @@ fun ?header ?tags fmt -> with_metadata header tags k ppf fmt in
{ Logs.report }
let () = Fmt_tty.setup_std_outputs ~style_renderer:`Ansi_tty ~utf_8:true ()
let () = Logs.set_reporter (reporter Fmt.stderr)
let () = Logs.set_level ~all:true (Some Logs.Debug)
let () = Mirage_crypto_rng_unix.use_default ()
module P = Paf_mirage.Make (Tcpip_stack_socket.V4V6.TCP)
let unix_stack () =
Tcpip_stack_socket.V4V6.UDP.connect ~ipv4_only:false ~ipv6_only:false
Ipaddr.V4.Prefix.global None
>>= fun udpv4 ->
Tcpip_stack_socket.V4V6.TCP.connect ~ipv4_only:false ~ipv6_only:false
Ipaddr.V4.Prefix.global None
>>= fun tcpv4 -> Tcpip_stack_socket.V4V6.connect udpv4 tcpv4
let error_handler (_ip, _port) ?request:_ _error _respond = ()
let load_file filename =
let ic = open_in filename in
let ln = in_channel_length ic in
let rs = Bytes.create ln in
really_input ic rs 0 ln ;
close_in ic ;
Bytes.unsafe_to_string rs
let tls =
let cert = load_file "server.pem" in
let key = load_file "server.key" in
match
(X509.Certificate.decode_pem_multiple cert, X509.Private_key.decode_pem key)
with
| Ok certs, Ok (`RSA key) ->
Result.get_ok
(Tls.Config.server ~certificates:(`Single (certs, `RSA key)) ())
| _ -> invalid_arg "Invalid certificate or key"
let sleep = Lwt_unix.sleep <.> Int64.to_float
let run_http_and_https_server ~request_handler stop =
unix_stack () >|= Tcpip_stack_socket.V4V6.tcp >>= fun stack ->
P.init ~port:9090 stack >>= fun socket0 ->
P.init ~port:3434 stack >>= fun socket1 ->
let http = P.http_service ~error_handler (fun _flow -> request_handler) in
let https =
P.https_service ~tls ~error_handler (fun _flow -> request_handler) in
let (`Initialized fiber0) = P.serve ~stop http socket0 in
let (`Initialized fiber1) = P.serve ~stop https socket1 in
Logs.debug (fun m -> m "Server initialised.") ;
Lwt.async (fun () -> Lwt.join [ fiber0; fiber1 ]) ;
Lwt.return_unit
let resolver domain_name =
match Unix.gethostbyname (Domain_name.to_string domain_name) with
| { Unix.h_addr_list; _ } ->
if Array.length h_addr_list > 0
then Lwt.return_some (Ipaddr_unix.of_inet_addr h_addr_list.(0))
else Lwt.return_none
| exception _ -> Lwt.return_none
let tcp_connect scheme stack ipaddr port =
match scheme with
| `HTTP -> Lwt.return_some (stack, ipaddr, port)
| _ -> Lwt.return_none
let tls_connect scheme domain_name cfg stack ipaddr port =
match scheme with
| `HTTPS -> Lwt.return_some (domain_name, cfg, stack, ipaddr, port)
| _ -> Lwt.return_none
let null =
let authenticator ?ip:_ ~host:_ _ = Ok None in
Result.get_ok (Tls.Config.client ~authenticator ())
module Client = Paf_cohttp
let stack = Mimic.make ~name:"stack"
let ctx =
let tls = Mimic.make ~name:"tls" in
Mimic.empty
|> Mimic.(
fold P.tcp_edn
Fun.
[
req Paf_cohttp.scheme;
req stack;
req Paf_cohttp.ipaddr;
dft Paf_cohttp.port 9090;
]
~k:tcp_connect)
|> Mimic.(
fold P.tls_edn
Fun.
[
req Paf_cohttp.scheme;
opt Paf_cohttp.domain_name;
dft tls null;
req stack;
req Paf_cohttp.ipaddr;
dft Paf_cohttp.port 3434;
]
~k:tls_connect)
|> Mimic.(
fold Paf_cohttp.ipaddr Fun.[ req Paf_cohttp.domain_name ] ~k:resolver)
let body_to_string body =
let buf = Buffer.create 0x100 in
let th, wk = Lwt.wait () in
let on_eof () =
Lwt.wakeup_later wk (Buffer.contents buf) ;
H1.Body.Reader.close body in
let rec on_read str ~off ~len =
let str = Bigstringaf.substring str ~off ~len in
Logs.debug (fun m -> m "Received %S." str) ;
Buffer.add_string buf str ;
H1.Body.Reader.schedule_read body ~on_eof ~on_read in
Logs.debug (fun m -> m "Start to receive the body.") ;
H1.Body.Reader.schedule_read body ~on_eof ~on_read ;
th
let query_to_assoc str =
let lst =
Astring.String.fields ~is_sep:(function '&' -> true | _ -> false) str in
let f str =
match Astring.String.cut ~sep:"=" str with
| Some (k, v) -> (k, v)
| None -> (str, "") in
List.map f lst
let request_handler (ip, port) reqd =
let open H1 in
let req = Reqd.request reqd in
Logs.debug (fun m ->
m "Got a connection from %a:%d %s." Ipaddr.pp ip port req.Request.target) ;
let body = Reqd.request_body reqd in
match req.Request.target with
| "/" ->
let contents = "Hello World!" in
let headers =
Headers.of_list
[ ("content-length", string_of_int (String.length contents)) ] in
let resp = Response.create ~headers `OK in
Reqd.respond_with_string reqd resp contents ;
Lwt.async @@ fun () ->
body_to_string body >|= fun _ -> Logs.debug (fun m -> m "Body drained.")
| "/repeat" ->
Lwt.async @@ fun () ->
body_to_string body >|= fun str ->
let headers =
Headers.of_list
[ ("content-length", string_of_int (String.length str)) ] in
let resp = Response.create ~headers `OK in
Reqd.respond_with_string reqd resp str
| target ->
match Astring.String.cut ~sep:"?" target with
| Some ("/query", query) ->
let lst = query_to_assoc query in
let buf = Buffer.create 0x100 in
let ppf = Format.formatter_of_buffer buf in
Fmt.pf ppf "%a%!"
Fmt.(list ~sep:(any ";") (pair ~sep:(any "=") string string))
lst ;
let contents = Buffer.contents buf in
let headers =
Headers.of_list
[ ("content-length", string_of_int (String.length contents)) ] in
let resp = Response.create ~headers `OK in
Reqd.respond_with_string reqd resp contents ;
Lwt.async @@ fun () ->
body_to_string body >>= fun _ -> Lwt.return_unit
| _ ->
Reqd.report_exn reqd Not_found ;
let contents = "Invalid request." in
let headers =
Headers.of_list
[ ("content-length", string_of_int (String.length contents)) ] in
let resp = Response.create ~headers `Bad_request in
Reqd.respond_with_string reqd resp contents
let test01 =
Alcotest_lwt.test_case "simple-http" `Quick @@ fun _sw () ->
unix_stack () >|= Tcpip_stack_socket.V4V6.tcp >>= fun v ->
let ctx = Mimic.add stack v ctx in
Client.get ~ctx (Uri.of_string "http://localhost:9090/")
>>= fun (_resp, body) ->
Cohttp_lwt.Body.to_string body >>= fun str ->
Alcotest.(check string) "contents" str "Hello World!" ;
Lwt.return_unit
let test02 =
Alcotest_lwt.test_case "repeat" `Quick @@ fun _sw () ->
unix_stack () >|= Tcpip_stack_socket.V4V6.tcp >>= fun v ->
let ctx = Mimic.add stack v ctx in
let body = Cohttp_lwt.Body.of_string "Hello!" in
Client.post ~ctx ~body (Uri.of_string "http://localhost:9090/repeat")
>>= fun (_resp, body) ->
Cohttp_lwt.Body.to_string body >>= fun str ->
Alcotest.(check string) "contents" str "Hello!" ;
Lwt.return_unit
let test03 =
Alcotest_lwt.test_case "simple-https" `Quick @@ fun _sw () ->
unix_stack () >|= Tcpip_stack_socket.V4V6.tcp >>= fun v ->
let ctx = Mimic.add stack v ctx in
Client.get ~ctx (Uri.of_string "https://localhost:3434/")
>>= fun (_resp, body) ->
Cohttp_lwt.Body.to_string body >>= fun str ->
Alcotest.(check string) "contents" str "Hello World!" ;
Lwt.return_unit
let test04 =
Alcotest_lwt.test_case "repeat (https)" `Quick @@ fun _sw () ->
unix_stack () >|= Tcpip_stack_socket.V4V6.tcp >>= fun v ->
let ctx = Mimic.add stack v ctx in
let body = Cohttp_lwt.Body.of_string "Secret Hello!" in
Client.post ~ctx ~body (Uri.of_string "https://localhost:3434/repeat")
>>= fun (_resp, body) ->
Cohttp_lwt.Body.to_string body >>= fun str ->
Alcotest.(check string) "contents" str "Secret Hello!" ;
Lwt.return_unit
let test05 =
Alcotest_lwt.test_case "queries" `Quick @@ fun _sw () ->
unix_stack () >|= Tcpip_stack_socket.V4V6.tcp >>= fun v ->
let ctx = Mimic.add stack v ctx in
Client.get ~ctx (Uri.of_string "https://localhost:3434/query?foo=a&bar=b")
>>= fun (_resp, body) ->
Cohttp_lwt.Body.to_string body >>= fun str ->
Alcotest.(check string) "contents" str "foo=a;bar=b" ;
Lwt.return_unit
let test () =
Alcotest_lwt.run "smart"
[ ("cohttp", [ test01; test02; test03; test04; test05 ]) ]
let () =
let fiber =
Lwt_switch.with_switch @@ fun stop ->
run_http_and_https_server ~request_handler stop >>= test >>= fun () ->
Lwt_switch.turn_off stop in
Lwt_main.run fiber