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,14 @@
(executables
(names lwt_echo_server2 lwt_https_server lwt_get lwt_post)
(modules lwt_echo_server2 lwt_https_server lwt_get lwt_post)
(libraries h2 h2-lwt-unix lwt.unix))
(executable
(name lwt_h2c)
(libraries h2 httpun-lwt-unix h2-lwt-unix lwt.unix)
(modules lwt_h2c))
(alias
(name examples)
(deps
(glob_files *.exe)))

View file

@ -0,0 +1,155 @@
let set_interval s f destroy =
let rec set_interval_loop s f n =
let timeout =
Lwt_timeout.create s (fun () ->
if n > 0
then (if f () then set_interval_loop s f (n - 1))
else destroy ())
in
Lwt_timeout.start timeout
in
set_interval_loop s f 2
let connection_handler : Unix.sockaddr -> Lwt_unix.file_descr -> unit Lwt.t =
let open H2 in
let request_handler : Unix.sockaddr -> Reqd.t -> unit =
fun _client_address request_descriptor ->
let request = Reqd.request request_descriptor in
match request.meth, request.target with
| `GET, "/" | `POST, "/" ->
let request_body = Reqd.request_body request_descriptor in
let response_content_type =
match Headers.get request.headers "content-type" with
| Some request_content_type -> request_content_type
| None -> "application/octet-stream"
in
let rec respond () =
Body.Reader.schedule_read
request_body
~on_eof:(fun () ->
let response =
Response.create
~headers:
(Headers.of_list [ "content-type", response_content_type ])
`OK
in
Reqd.respond_with_string
request_descriptor
response
"non-empty data.")
~on_read:(fun _request_data ~off:_ ~len:_ -> respond ())
in
respond ()
| `POST, "/other" ->
let request_body = Reqd.request_body request_descriptor in
let response_content_type =
match Headers.get request.headers "content-type" with
| Some request_content_type -> request_content_type
| None -> "application/octet-stream"
in
let response =
Response.create
~headers:(Headers.of_list [ "content-type", response_content_type ])
`OK
in
let response_body =
Reqd.respond_with_streaming request_descriptor response
in
let rec respond () =
Body.Reader.schedule_read
request_body
~on_eof:(fun () ->
set_interval
1
(fun () ->
Body.Writer.write_string response_body "FOO";
(* Body.flush response_body ignore; *)
true)
(* Body.flush response_body ignore; *)
(fun () -> Body.Writer.close response_body))
~on_read:(fun request_data ~off ~len ->
Body.Writer.write_bigstring response_body request_data ~off ~len;
respond ())
in
respond ()
| `POST, "/foo" ->
let response =
Response.create
`OK
~headers:(Headers.of_list [ "content-type", "text/event-stream" ])
in
let request_body = Reqd.request_body request_descriptor in
let response_body =
Reqd.respond_with_streaming request_descriptor response
in
(* let (finished, notify) = Lwt.wait () in *)
let rec on_read _request_data ~off:_ ~len:_ =
Body.Writer.flush response_body (fun _ ->
Body.Reader.schedule_read request_body ~on_eof ~on_read)
and on_eof () =
set_interval
2
(fun () ->
let _ =
Body.Writer.write_string response_body "data: some data\n\n"
in
Body.Writer.flush response_body ignore;
true)
(fun () ->
let _ =
Body.Writer.write_string response_body "event: end\ndata: 1\n\n"
in
Body.Writer.flush response_body (fun _ ->
Body.Writer.close response_body))
in
Body.Reader.schedule_read ~on_read ~on_eof request_body;
()
| _ ->
Reqd.respond_with_string
request_descriptor
(Response.create `Method_not_allowed)
"Hello, Sean."
in
let error_handler :
Unix.sockaddr
-> ?request:H2.Request.t
-> _
-> (Headers.t -> Body.Writer.t)
-> unit
=
fun _client_address ?request:_ error start_response ->
let response_body = start_response Headers.empty in
(match error with
| `Exn exn ->
Body.Writer.write_string response_body (Printexc.to_string exn);
Body.Writer.write_string response_body "\n"
| #Status.standard as error ->
Body.Writer.write_string
response_body
(Status.default_reason_phrase error));
Body.Writer.close response_body
in
H2_lwt_unix.Server.create_connection_handler
?config:None
~request_handler
~error_handler
let () =
let open Lwt.Infix in
Sys.(set_signal sigpipe Signal_ignore);
let port = ref 8080 in
Arg.parse
[ "-p", Arg.Set_int port, " Listening port number (8080 by default)" ]
ignore
"Echoes POST requests. Runs forever.";
let listen_address = Unix.(ADDR_INET (inet_addr_loopback, !port)) in
Lwt.async (fun () ->
Lwt_io.establish_server_with_client_socket listen_address connection_handler
>>= fun _server ->
Printf.printf "Listening on port %i and echoing POST requests.\n" !port;
print_string "To send a POST request, try\n\n";
print_string " echo foo | dune exec examples/lwt/lwt_post.exe\n\n";
flush stdout;
Lwt.return_unit);
let forever, _ = Lwt.wait () in
Lwt_main.run forever

View file

@ -0,0 +1,61 @@
open H2
module Client = H2_lwt_unix.Client
let response_handler notify_response_received _response response_body =
let rec read_response () =
Body.Reader.schedule_read
response_body
~on_eof:(fun () -> Lwt.wakeup_later notify_response_received ())
~on_read:(fun bigstring ~off ~len ->
let response_fragment = Bytes.create len in
Bigstringaf.blit_to_bytes
bigstring
~src_off:off
response_fragment
~dst_off:0
~len;
print_string (Bytes.to_string response_fragment);
read_response ())
in
read_response ()
let error_handler _ = assert false
open Lwt.Infix
let () =
let host = ref None in
let port = ref 443 in
Arg.parse
[ "-p", Set_int port, " Port number (443 by default)" ]
(fun host_argument -> host := Some host_argument)
"lwt_get.exe [-p N] HOST";
let host =
match !host with
| None -> failwith "No hostname provided"
| Some host -> host
in
Lwt_main.run
( Lwt_unix.getaddrinfo
host
(string_of_int !port)
[ Unix.(AI_FAMILY PF_INET) ]
>>= fun addresses ->
let socket = Lwt_unix.socket Unix.PF_INET Unix.SOCK_STREAM 0 in
Lwt_unix.connect socket (List.hd addresses).Unix.ai_addr >>= fun () ->
let request =
Request.create
`GET
"/"
~scheme:"https"
~headers:Headers.(add_list empty [ ":authority", host ])
in
let response_received, notify_response_received = Lwt.wait () in
let response_handler = response_handler notify_response_received in
Client.SSL.create_connection_with_default ~error_handler socket
>>= fun connection ->
let request_body =
Client.SSL.request connection request ~error_handler ~response_handler
in
Body.Writer.close request_body;
response_received )

View file

@ -0,0 +1,142 @@
open Lwt.Infix
module Http2 = struct
open H2
let connection_handler :
Httpun.Request.t
-> Bigstringaf.t H2.IOVec.t list
-> (Server_connection.t, string) result
=
let request_handler : H2.Server_connection.request_handler =
fun request_descriptor ->
let request = Reqd.request request_descriptor in
match request.meth, request.target with
| `GET, "/" | `POST, "/" ->
(* This set of routes waits until the entire request body has been read
* to produce a response. *)
let request_body = Reqd.request_body request_descriptor in
let response_content_type =
match Headers.get request.headers "content-type" with
| Some request_content_type -> request_content_type
| None -> "application/octet-stream"
in
let buf = Buffer.create 10 in
let rec respond () =
Body.Reader.schedule_read
request_body
~on_eof:(fun () ->
let response =
Response.create
~headers:
(Headers.of_list [ "content-type", response_content_type ])
`OK
in
Reqd.respond_with_string
request_descriptor
response
(Buffer.contents buf))
~on_read:(fun request_data ~off ~len ->
let bytes = Bytes.create len in
Bigstringaf.blit_to_bytes
request_data
~src_off:off
~dst_off:0
~len
bytes;
Buffer.add_bytes buf bytes;
respond ())
in
respond ()
| _ ->
Reqd.respond_with_string
request_descriptor
(Response.create `Method_not_allowed)
""
in
let error_handler ?request:_ error start_response =
let response_body = start_response Headers.empty in
(match error with
| `Exn exn ->
Body.Writer.write_string response_body (Printexc.to_string exn);
Body.Writer.write_string response_body "\n"
| #Status.standard as error ->
Body.Writer.write_string
response_body
(Status.default_reason_phrase error));
Body.Writer.close response_body
in
fun http_request request_body ->
let { Httpun.Request.headers; target; meth; _ } = http_request in
H2.Server_connection.create_h2c
?config:None
~headers
~target
~meth
~request_body
~error_handler
request_handler
end
let connection_handler =
let module Body = Httpun.Body in
let module Headers = Httpun.Headers in
let module Reqd = Httpun.Reqd in
let module Response = Httpun.Response in
let module Status = Httpun.Status in
let upgrade_handler request upgrade () =
let off = 0 in
let len = 3 in
let body =
[ { H2.IOVec.buffer = Bigstringaf.of_string ~off ~len "foo"; off; len }
; { buffer = Bigstringaf.of_string ~off ~len "bar"; off; len }
; { buffer = Bigstringaf.of_string ~off ~len "baz"; off; len }
]
in
let connection =
Stdlib.Result.get_ok (Http2.connection_handler request body)
in
upgrade (Gluten.make (module H2.Server_connection) connection)
in
let http_error_handler _client_address ?request:_ error handle =
let message =
match error with
| `Exn exn -> Printexc.to_string exn
| (#Status.client_error | #Status.server_error) as error ->
Status.to_string error
in
let body = handle Headers.empty in
Body.Writer.write_string body message;
Body.Writer.close body
in
let request_handler _addr (reqd : Httpun.Reqd.t Gluten.reqd) =
let { Gluten.reqd; upgrade } = reqd in
let headers =
Headers.of_list [ "Connection", "Upgrade"; "Upgrade", "h2c" ]
in
let request = Reqd.request reqd in
Reqd.respond_with_upgrade reqd headers (upgrade_handler request upgrade)
in
Httpun_lwt_unix.Server.create_connection_handler
?config:None
~request_handler
~error_handler:http_error_handler
let () =
Sys.(set_signal sigpipe Signal_ignore);
let port = ref 8080 in
Arg.parse
[ "-p", Arg.Set_int port, " Listening port number (8080 by default)" ]
ignore
"Echoes POST requests. Runs forever.";
let listen_address = Unix.(ADDR_INET (inet_addr_loopback, !port)) in
Lwt.async (fun () ->
Lwt_io.establish_server_with_client_socket listen_address connection_handler
>>= fun _server ->
Printf.printf "Listening on port %i and echoing POST requests.\n" !port;
print_string "To send a POST request, try\n\n";
print_string " echo foo | dune exec examples/lwt/lwt_post.exe\n\n";
flush stdout;
Lwt.return_unit);
let forever, _ = Lwt.wait () in
Lwt_main.run forever

View file

@ -0,0 +1,120 @@
let set_interval ?(times = 5) s f destroy =
let rec set_interval_loop s f n =
let timeout =
Lwt_timeout.create s (fun () ->
if n > 0
then (if f () then set_interval_loop s f (n - 1))
else destroy ())
in
Lwt_timeout.start timeout
in
set_interval_loop s f times
let connection_handler : Unix.sockaddr -> Lwt_unix.file_descr -> unit Lwt.t =
let open H2 in
let request_handler : Unix.sockaddr -> Reqd.t -> unit =
fun _client_address request_descriptor ->
let request = Reqd.request request_descriptor in
match request.meth, request.target with
| `POST, "/" ->
(* let request_body = Reqd.request_body request_descriptor in *)
let response_content_type =
match Headers.get request.headers "Content-Type" with
| Some request_content_type -> request_content_type
| None -> "application/octet-stream"
in
let response =
Response.create
~headers:
(Headers.of_list
[ "content-type", response_content_type
(* "Connection", "close"; *)
])
`OK
in
(* let response_body = Reqd.respond_with_streaming request_descriptor
response in *)
(* let rec respond () = Body.schedule_read request_body ~on_eof:(fun () ->
Body.close_writer response_body) ~on_read:(fun request_data ~off ~len
-> Body.write_bigstring response_body request_data ~off ~len; respond
()) in respond () *)
Reqd.respond_with_string request_descriptor response "ANTOINO"
| `POST, "/foo" ->
set_interval
~times:1
2
(fun () ->
let response_content_type =
match Headers.get request.headers "Content-Type" with
| Some request_content_type -> request_content_type
| None -> "application/octet-stream"
in
let response =
Response.create
~headers:
(Headers.of_list
[ "content-type", response_content_type
(* "Connection", "close"; *)
])
`OK
in
Reqd.respond_with_string request_descriptor response "ANTOINO";
true)
ignore
| `GET, _ ->
Reqd.respond_with_string
request_descriptor
(Response.create `OK)
"Welcome to ocaml-h2"
| _ ->
Reqd.respond_with_string
request_descriptor
(Response.create `Method_not_allowed)
""
in
let error_handler :
Unix.sockaddr
-> ?request:H2.Request.t
-> _
-> (Headers.t -> Body.Writer.t)
-> unit
=
fun _client_address ?request:_ _error start_response ->
let response_body = start_response Headers.empty in
(* begin match error with | `Exn exn -> Body.write_string response_body
(Printexc.to_string exn); Body.write_string response_body "\n";
| #Status.standard as error -> Body.write_string response_body
(Status.default_reason_phrase error) end; *)
Body.Writer.close response_body
in
let certfile = "./certificates/server.pem" in
let keyfile = "./certificates/server.key" in
H2_lwt_unix.Server.SSL.create_connection_handler_with_default
~certfile
~keyfile
?config:None
~request_handler
~error_handler
let () =
let open Lwt.Infix in
Sys.(set_signal sigpipe Signal_ignore);
let port = ref 8080 in
Arg.parse
[ "-p", Arg.Set_int port, " Listening port number (8080 by default)" ]
ignore
"Echoes POST requests. Runs forever.";
let listen_address = Unix.(ADDR_INET (inet_addr_loopback, !port)) in
Lwt.async (fun () ->
Lwt_io.establish_server_with_client_socket listen_address connection_handler
>>= fun _server ->
Printf.printf "Listening on port %i and echoing POST requests.\n" !port;
print_string "To send a POST request, try\n\n";
print_string " curl https://localhost:8080 -k -X POST -d foo\n\n";
flush stdout;
Lwt.return_unit);
let forever, _ = Lwt.wait () in
Lwt_main.run forever

View file

@ -0,0 +1,78 @@
open H2
let response_handler notify_response_received response response_body =
match Response.(response.status) with
| `OK ->
let rec read_response () =
Body.Reader.schedule_read
response_body
~on_eof:(fun () -> Lwt.wakeup_later notify_response_received ())
~on_read:(fun response_fragment ~off ~len ->
let response_fragment_string = Bytes.create len in
Lwt_bytes.blit_to_bytes
response_fragment
off
response_fragment_string
0
len;
print_string (Bytes.unsafe_to_string response_fragment_string);
read_response ())
in
read_response ()
| _ ->
Format.fprintf Format.err_formatter "%a\n%!" Response.pp_hum response;
exit 1
let error_handler = function
| `Invalid_response_body_length _resp ->
Printf.printf "invalid response body length\n%!"
| `Exn _exn -> Printf.printf "exception!\n%!"
| `Malformed_response s -> Printf.printf "malformed response: %s\n%!" s
| `Protocol_error (code, s) ->
Printf.printf "protocol error: %s, %s\n%!" (H2.Error_code.to_string code) s
open Lwt.Infix
let () =
let host = ref "127.0.0.1" in
let port = ref 8080 in
Arg.parse
[ "-h", Set_string host, " Hostname (127.0.0.1 by default)"
; "-p", Set_int port, " Port number (8080 by default)"
]
ignore
"lwt_get.exe [-h HOST] [-p N]";
Lwt_main.run
( Lwt_io.(read stdin) >>= fun text_to_send ->
Lwt_unix.getaddrinfo
!host
(string_of_int !port)
[ Unix.(AI_FAMILY PF_INET) ]
>>= fun addresses ->
let socket = Lwt_unix.socket Unix.PF_INET Unix.SOCK_STREAM 0 in
Lwt_unix.connect socket (List.hd addresses).Unix.ai_addr >>= fun () ->
let request_headers =
Request.create
`POST
"/"
~scheme:"http"
~headers:
Headers.(
of_list
[ ":authority", !host
; "content-length", string_of_int (String.length text_to_send)
])
in
let response_received, notify_response_received = Lwt.wait () in
let response_handler = response_handler notify_response_received in
H2_lwt_unix.Client.create_connection ~error_handler socket >>= fun conn ->
let request_body =
H2_lwt_unix.Client.request
conn
request_headers
~error_handler
~response_handler
in
Body.Writer.write_string request_body text_to_send;
Body.Writer.close request_body;
response_received )