diff --git a/cohttp-eio/examples/client1.ml b/cohttp-eio/examples/client1.ml index 4388cc39b3..749f0065bc 100644 --- a/cohttp-eio/examples/client1.ml +++ b/cohttp-eio/examples/client1.ml @@ -1,16 +1,13 @@ open Eio open Cohttp_eio -let conn env sw resource_path = +let () = + Eio_main.run @@ fun env -> + Switch.run @@ fun sw -> let hostname, port = ("www.example.org", 80) in let he = Unix.gethostbyname hostname in let addr = `Tcp (Eio_unix.Ipaddr.of_unix he.h_addr_list.(0), port) in - let flow = Net.connect ~sw env#net addr in + let conn = Net.connect ~sw env#net addr in let host = (hostname, Some port) in - (resource_path, host, flow) - -let () = - Eio_main.run @@ fun env -> - Switch.run @@ fun sw -> - let res = Client.get (conn env sw) "/" in + let res = Client.get ~conn host "/" in print_string @@ Client.read_fixed res diff --git a/cohttp-eio/examples/docker_client.ml b/cohttp-eio/examples/docker_client.ml index 0508d6f19e..8dee798eea 100644 --- a/cohttp-eio/examples/docker_client.ml +++ b/cohttp-eio/examples/docker_client.ml @@ -5,17 +5,12 @@ module Client = Cohttp_eio.Client module Response = Http.Response module Status = Http.Status -let conn env sw resource_path = - let hostname = "docker" in - let addr = `Unix "/var/run/docker.sock" in - let flow = Net.connect ~sw env#net addr in - let host = (hostname, None) in - (resource_path, host, flow) - let () = Eio_main.run @@ fun env -> Switch.run @@ fun sw -> - let res = Client.get (conn env sw) "/version" in + let addr = `Unix "/var/run/docker.sock" in + let conn = Net.connect ~sw env#net addr in + let res = Client.get ~conn ("docker", None) "/version" in let code = fst res |> Response.status |> Status.to_int in Printf.printf "Response code: %d\n" code; Printf.printf "Headers: %s\n" diff --git a/cohttp-eio/src/client.ml b/cohttp-eio/src/client.ml index 2275798d48..4b4f425df6 100644 --- a/cohttp-eio/src/client.ml +++ b/cohttp-eio/src/client.ml @@ -4,23 +4,24 @@ module Buf_write = Eio.Buf_write type response = Http.Response.t * Buf_read.t type host = string * int option type resource_path = string -type ('a, 'b) conn = 'a -> (resource_path * host * #Eio.Flow.two_way as 'b) -type ('a, 'b) body_disallowed_call = +type 'a body_disallowed_call = ?version:Http.Version.t -> ?headers:Http.Header.t -> - ('a, 'b) conn -> - 'a -> + conn:(#Eio.Flow.two_way as 'a) -> + host -> + resource_path -> response (** [body_disallowed_call] denotes HTTP client calls where a request is not allowed to have a request body. *) -type ('a, 'b) body_allowed_call = +type 'a body_allowed_call = ?version:Http.Version.t -> ?headers:Http.Header.t -> ?body:Body.t -> - ('a, 'b) conn -> - 'a -> + conn:(#Eio.Flow.two_way as 'a) -> + host -> + resource_path -> response (* Request line https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.1 *) @@ -62,43 +63,42 @@ let response buf_read = (* Generic HTTP call *) let call ?(meth = `GET) ?(version = `HTTP_1_1) ?(headers = Http.Header.init ()) - ?(body = Body.Empty) conn_fn uri = - let resource_path, (host_name, host_port), flow = conn_fn uri in + ?(body = Body.Empty) ~conn host resource_path = let host = - match host_port with - | Some port -> host_name ^ ":" ^ string_of_int port - | None -> host_name + match host with + | host, Some port -> host ^ ":" ^ string_of_int port + | host, None -> host in - Buf_write.with_flow ~initial_size:0x1000 flow (fun writer -> + Buf_write.with_flow ~initial_size:0x1000 conn (fun writer -> let headers = Http.Header.add_unless_exists headers "Host" host in write_request writer (meth, version, headers, resource_path, body); let reader = - Eio.Buf_read.of_flow ~initial_size:0x1000 ~max_size:max_int flow + Eio.Buf_read.of_flow ~initial_size:0x1000 ~max_size:max_int conn in let response = response reader in (response, reader)) (* HTTP Calls with Body Disallowed *) -let get ?version ?headers conn_fn uri = - call ~meth:`GET ?version ?headers conn_fn uri +let get ?version ?headers ~conn host resource_path = + call ~meth:`GET ?version ?headers ~conn host resource_path -let head ?version ?headers stream uri = - call ~meth:`HEAD ?version ?headers stream uri +let head ?version ?headers ~conn host resource_path = + call ~meth:`HEAD ?version ?headers ~conn host resource_path -let delete ?version ?headers stream uri = - call ~meth:`DELETE ?version ?headers stream uri +let delete ?version ?headers ~conn host resource_path = + call ~meth:`DELETE ?version ?headers ~conn host resource_path (* HTTP Calls with Body Allowed *) -let post ?version ?headers ?body stream uri = - call ~meth:`POST ?version ?headers ?body stream uri +let post ?version ?headers ?body ~conn host resource_path = + call ~meth:`POST ?version ?headers ?body ~conn host resource_path -let put ?version ?headers ?body stream uri = - call ~meth:`PUT ?version ?headers ?body stream uri +let put ?version ?headers ?body ~conn host resource_path = + call ~meth:`PUT ?version ?headers ?body ~conn host resource_path -let patch ?version ?headers ?body stream uri = - call ~meth:`PATCH ?version ?headers ?body stream uri +let patch ?version ?headers ?body ~conn host resource_path = + call ~meth:`PATCH ?version ?headers ?body ~conn host resource_path (* Response Body *) diff --git a/cohttp-eio/src/cohttp_eio.mli b/cohttp-eio/src/cohttp_eio.mli index bef47d8786..3dcea73d84 100644 --- a/cohttp-eio/src/cohttp_eio.mli +++ b/cohttp-eio/src/cohttp_eio.mli @@ -101,6 +101,7 @@ module Server : sig val not_found_handler : handler end +(** [Client] is a HTTP/1.1 client. *) module Client : sig type response = Http.Response.t * Eio.Buf_read.t @@ -112,25 +113,23 @@ module Client : sig (** Represents HTTP request resource path, e.g. "/shop/purchase", "/shop/items", "/shop/categories/" etc. *) - type ('a, 'b) conn = 'a -> (resource_path * host * #Eio.Flow.two_way as 'b) - (** [('a, 'b conn)] is [(resource_path, host, flow)]. [flow] is the Eio flow - value which is connected to the [host]. *) - - type ('a, 'b) body_disallowed_call = + type 'a body_disallowed_call = ?version:Http.Version.t -> ?headers:Http.Header.t -> - ('a, 'b) conn -> - 'a -> + conn:(#Eio.Flow.two_way as 'a) -> + host -> + resource_path -> response (** [body_disallowed_call] denotes HTTP client calls where a request is not allowed to have a request body. *) - type ('a, 'b) body_allowed_call = + type 'a body_allowed_call = ?version:Http.Version.t -> ?headers:Http.Header.t -> ?body:Body.t -> - ('a, 'b) conn -> - 'a -> + conn:(#Eio.Flow.two_way as 'a) -> + host -> + resource_path -> response (** [body_allowed_call] denotes HTTP client calls where a request can optionally have a request body. *) @@ -142,21 +141,22 @@ module Client : sig ?version:Http.Version.t -> ?headers:Http.Header.t -> ?body:Body.t -> - ('a, 'b) conn -> - 'a -> + conn:#Eio.Flow.two_way -> + host -> + resource_path -> response (** {1 HTTP Calls with Body Disallowed} *) - val get : ('a, 'b) body_disallowed_call - val head : ('a, 'b) body_disallowed_call - val delete : ('a, 'b) body_disallowed_call + val get : 'a body_disallowed_call + val head : 'a body_disallowed_call + val delete : 'a body_disallowed_call (** {1 HTTP Calls with Body Allowed} *) - val post : ('a, 'b) body_allowed_call - val put : ('a, 'b) body_allowed_call - val patch : ('a, 'b) body_allowed_call + val post : 'a body_allowed_call + val put : 'a body_allowed_call + val patch : 'a body_allowed_call (** {1 Response Body} *) diff --git a/cohttp-eio/tests/test_client.ml b/cohttp-eio/tests/test_client.ml index 7951e58385..766e470ddb 100644 --- a/cohttp-eio/tests/test_client.ml +++ b/cohttp-eio/tests/test_client.ml @@ -3,21 +3,15 @@ module Stdenv = Eio.Stdenv module Switch = Eio.Switch open Cohttp_eio -let conn env sw port resource_path = - let addr = `Tcp (Net.Ipaddr.V4.loopback, port) in - let flow = Net.connect ~sw env#net addr in - let host = ("localhost", Some port) in - (resource_path, host, flow) - -let get env sw port = +let get conn host = let res = Client.get ~headers:(Http.Header.of_list [ ("Accept", "application/json") ]) - (conn env sw port) "/get" + ~conn host "/get" in print_string @@ Client.read_fixed res -let post env sw port = +let post conn host = let content = "hello world!" in let content_length = String.length content |> string_of_int in let res = @@ -27,7 +21,7 @@ let post env sw port = [ ("Accept", "application/json"); ("Content-Length", content_length); ]) - ~body:(Body.Fixed content) (conn env sw port) "/post" + ~body:(Body.Fixed content) ~conn host "/post" in print_string @@ Client.read_fixed res @@ -36,7 +30,7 @@ let post env sw port = Read from text file "chunks.txt" and write each line as a chunk. We add some chunk extensions to the first chunk. This is purely for demonstrative effect and for testing purposes rather than for any such specific requirement. *) -let post_chunk env sw port = +let post_chunk conn host = let rec body_writer chan chunks f = match In_channel.input_line chan with | Some data -> @@ -80,17 +74,17 @@ let post_chunk env sw port = ]) ~body: (Body.Chunked { body_writer = body_writer chan 0; trailer_writer }) - (conn env sw port) "/handle_chunk") + ~conn host "/handle_chunk") |> Client.read_fixed |> print_string (* Read chunk and dump to a "client_chunks2.txt" *) -let get_chunk env sw port = +let get_chunk env conn host = let write_chunk_to_file flow chunk = let data = Format.asprintf "%a\n\n" Body.pp_chunk chunk in Eio.Flow.copy_string data flow in - let res = Client.get (conn env sw port) "/get_chunk" in + let res = Client.get ~conn host "/get_chunk" in let path = Eio.Path.(Stdenv.cwd env / "client_chunks2.txt") in Eio.Path.with_open_out ~append:false ~create:(`Or_truncate 0o666) path (fun flow -> @@ -114,9 +108,12 @@ let () = Eio_main.run @@ fun env -> Switch.run @@ fun sw -> + let addr = `Tcp (Net.Ipaddr.V4.loopback, !port) in + let conn = Net.connect ~sw env#net addr in + let host = ("localhost", Some !port) in match !t with - | "get" -> get env sw !port - | "post" -> post env sw !port - | "post_chunk" -> post_chunk env sw !port - | "get_chunk" -> get_chunk env sw !port + | "get" -> get conn host + | "post" -> post conn host + | "post_chunk" -> post_chunk conn host + | "get_chunk" -> get_chunk env conn host | _ -> print_string "Usage: test-client [get|post]"