From 1d6cfeb44ee769ff4027a0510e66fe32fa289685 Mon Sep 17 00:00:00 2001 From: Bikal Lem Date: Mon, 7 Nov 2022 17:14:25 +0000 Subject: [PATCH 1/3] sockopt: add setsockopt() to Eio.Net.socket --- lib_eio/mock/eio_mock.mli | 14 +++++++++-- lib_eio/mock/flow.ml | 23 ++++++----------- lib_eio/mock/net.ml | 52 ++++++++++++++++++++++++++++++++++++--- lib_eio/net.ml | 24 ++++++++++++++++++ lib_eio/net.mli | 33 +++++++++++++++++++++++++ lib_eio/unix/eio_unix.ml | 23 ++++++++++++++++- lib_eio/unix/eio_unix.mli | 2 ++ 7 files changed, 150 insertions(+), 21 deletions(-) diff --git a/lib_eio/mock/eio_mock.mli b/lib_eio/mock/eio_mock.mli index 9cde0098d..8c2396874 100644 --- a/lib_eio/mock/eio_mock.mli +++ b/lib_eio/mock/eio_mock.mli @@ -125,9 +125,16 @@ module Net : sig on_getnameinfo : (string * string) Handler.t; > + type stream_socket = < + Flow.t; + Eio.Net.stream_socket; + on_setsockopt: unit Handler.t; + > + type listening_socket = < Eio.Net.listening_socket; - on_accept : (Flow.t * Eio.Net.Sockaddr.stream) Handler.t; + on_accept : (stream_socket * Eio.Net.Sockaddr.stream) Handler.t; + on_setsockopt: unit Handler.t; > val make : string -> t @@ -149,9 +156,12 @@ module Net : sig val listening_socket : string -> listening_socket (** [listening_socket label] can be configured to provide mock connections. *) + val stream_socket : string -> stream_socket + (** [stream_socket label] is a mock [stream_socket]. *) + val on_accept : listening_socket -> - (Flow.t * Eio.Net.Sockaddr.stream) Handler.actions -> + (stream_socket * Eio.Net.Sockaddr.stream) Handler.actions -> unit (** [on_accept socket actions] configures how to respond when the server calls "accept". *) end diff --git a/lib_eio/mock/flow.ml b/lib_eio/mock/flow.ml index 50ef67ab6..ed2fa0bd2 100644 --- a/lib_eio/mock/flow.ml +++ b/lib_eio/mock/flow.ml @@ -5,15 +5,6 @@ type copy_method = [ | `Read_source_buffer ] -type t = < - Eio.Flow.two_way; - Eio.Flow.close; - on_read : string Handler.t; - on_copy_bytes : int Handler.t; - set_copy_method : copy_method -> unit; - attach_to_switch : Switch.t -> unit; -> - let pp_default f s = let rec aux i = let nl = @@ -34,7 +25,7 @@ let rec takev len = function | x :: _ when Cstruct.length x >= len -> [Cstruct.sub x 0 len] | x :: xs -> x :: takev (len - Cstruct.length x) xs -let make ?(pp=pp_default) label = +class t ?(pp=pp_default) label = let on_read = Handler.make (`Raise End_of_file) in let on_copy_bytes = Handler.make (`Return 4096) in let copy_method = ref `Read_into in @@ -89,7 +80,7 @@ let make ?(pp=pp_default) label = if not (List.exists try_rsb (Eio.Flow.read_methods src)) then Fmt.failwith "Source does not offer Read_source_buffer optimisation" - method set_copy_method m = + method set_copy_method (m: copy_method) = copy_method := m method shutdown cmd = @@ -110,7 +101,9 @@ let make ?(pp=pp_default) label = traceln "%s: closed" label end -let on_read (t:t) = Handler.seq t#on_read -let on_copy_bytes (t:t) = Handler.seq t#on_copy_bytes -let set_copy_method (t:t) = t#set_copy_method -let attach_to_switch (t:t) = t#attach_to_switch +let make ?(pp=pp_default) label = new t ~pp label + +let on_read (t:#t) = Handler.seq t#on_read +let on_copy_bytes (t:#t) = Handler.seq t#on_copy_bytes +let set_copy_method (t:#t) = t#set_copy_method +let attach_to_switch (t:#t) = t#attach_to_switch diff --git a/lib_eio/mock/net.ml b/lib_eio/mock/net.ml index 176137d1a..ea6e26821 100644 --- a/lib_eio/mock/net.ml +++ b/lib_eio/mock/net.ml @@ -71,17 +71,51 @@ let on_getaddrinfo (t:t) actions = Handler.seq t#on_getaddrinfo actions let on_getnameinfo (t:t) actions = Handler.seq t#on_getnameinfo actions +type stream_socket = < + Flow.t; + Eio.Net.stream_socket; + on_setsockopt: unit Handler.t; +> + type listening_socket = < Eio.Net.listening_socket; - on_accept : (Flow.t * Eio.Net.Sockaddr.stream) Handler.t; + on_accept : (stream_socket * Eio.Net.Sockaddr.stream) Handler.t; + on_setsockopt: unit Handler.t; > +let setsockopt (type a) label handler (opt: a Eio.Net.Sockopt.t) (x : a) : unit = + let traceln (pp : a Fmt.t) (x: a) s = + traceln "%s: setsockopt %s %a" label s pp x + in + (match opt with + | IPV6_ONLY -> traceln Fmt.bool x "IPV6_ONLY" + | ACCEPTCONN -> traceln Fmt.bool x "SO_ACCEPTCONN" + | BROADCAST -> traceln Fmt.bool x "SO_BROADCAST" + | DEBUG -> traceln Fmt.bool x "SO_DEBUG" + | DONTROUTE -> traceln Fmt.bool x "SO_DONTROUTE" + | KEEPALIVE -> traceln Fmt.bool x "SO_KEEPALIVE" + | LINGER -> traceln Fmt.(option int) x "SO_LINGER" + | OOBINLINE -> traceln Fmt.bool x "SO_OOBINLINE" + | RCVBUF -> traceln Fmt.int x "SO_RCVBUF" + | RCVLOWAT -> traceln Fmt.int x "SO_RCVLOWAT" + | REUSEADDR -> traceln Fmt.bool x "SO_REUSEADDR" + | REUSEPORT -> traceln Fmt.bool x "SO_REUSEPORT" + | SNDBUF -> traceln Fmt.int x "SO_SNDBUF" + | SNDLOWAT -> traceln Fmt.int x "SO_SNDLOWAT" + | RCVTIMEO -> traceln Fmt.float x "SO_RCVTIMEO" + | SNDTIMEO -> traceln Fmt.float x "SO_SNDTIMEO" + | TYPE -> traceln Fmt.int x "SO_TYPE" + | TCP_NODELAY -> traceln Fmt.bool x "TCP_NODELAY" + ); + Handler.run handler + let listening_socket label = let on_accept = Handler.make (`Raise (Failure "Mock accept handler not configured")) in - object + object (self) inherit Eio.Net.listening_socket method on_accept = on_accept + method on_setsockopt = Handler.make (`Raise (Failure "Mock setsockopt handler not configured")) method accept ~sw = let socket, addr = Handler.run on_accept in @@ -91,8 +125,20 @@ let listening_socket label = method close = traceln "%s: closed" label + + method setsockopt = (setsockopt label self#on_setsockopt) end +let stream_socket label : stream_socket = object (self) + inherit Flow.t label + + method on_setsockopt = Handler.make (`Raise (Failure "Mock setsockopt handler not configured")) + method setsockopt: type a. a Eio.Net.Sockopt.t -> a -> unit = + fun opt x -> setsockopt label self#on_setsockopt opt x +end + let on_accept (l:listening_socket) actions = - let as_accept_pair x = (x :> Flow.t * Eio.Net.Sockaddr.stream) in + let as_accept_pair x = (x :> stream_socket * Eio.Net.Sockaddr.stream) in Handler.seq l#on_accept (List.map (Action.map as_accept_pair) actions) + +let on_setsockopt (l:listening_socket) actions = Handler.seq l#on_setsockopt actions diff --git a/lib_eio/net.ml b/lib_eio/net.ml index 347db79f0..063b469a1 100644 --- a/lib_eio/net.ml +++ b/lib_eio/net.ml @@ -156,12 +156,36 @@ module Sockaddr = struct Format.fprintf f "udp:%a:%d" Ipaddr.pp_for_uri addr port end +module Sockopt = struct + type 'a t = + | IPV6_ONLY : bool t + | ACCEPTCONN : bool t + | BROADCAST : bool t + | DEBUG : bool t + | DONTROUTE : bool t + | KEEPALIVE : bool t + | LINGER : int option t + | OOBINLINE : bool t + | RCVBUF : int t + | RCVLOWAT : int t + | RCVTIMEO : float t + | REUSEADDR : bool t + | REUSEPORT : bool t + | SNDBUF : int t + | SNDLOWAT : int t + | SNDTIMEO : float t + | TYPE : int t + | TCP_NODELAY : bool t +end + class virtual socket = object (_ : #Generic.t) method probe _ = None + method virtual setsockopt : 'a. 'a Sockopt.t -> 'a -> unit end class virtual stream_socket = object inherit Flow.two_way + method virtual setsockopt : 'a. 'a Sockopt.t -> 'a -> unit end class virtual listening_socket = object diff --git a/lib_eio/net.mli b/lib_eio/net.mli index a91dd4ece..a5c181680 100644 --- a/lib_eio/net.mli +++ b/lib_eio/net.mli @@ -91,10 +91,37 @@ module Sockaddr : sig val pp : Format.formatter -> [< t] -> unit end +(** {2 Socket Options} *) + +module Sockopt : sig + type 'a t = + | IPV6_ONLY : bool t (** Forbid binding an IPv6 socket to an IPv4 address *) + | ACCEPTCONN : bool t (** Report whether socket listening is enabled *) + | BROADCAST : bool t (** Permit sending of broadcast messages *) + | DEBUG : bool t (** Record debugging information *) + | DONTROUTE : bool t (** Bypass the standard routing algorithms *) + | KEEPALIVE : bool t (** Keep connection active *) + | LINGER : int option t (** Whether to linger on closed connections + that have data present, and for how long + (in seconds) *) + | OOBINLINE : bool t (** Leave out-of-band data in line *) + | RCVBUF : int t (** Size of received buffer *) + | RCVLOWAT : int t (** Minimum number of bytes to process for input operations *) + | RCVTIMEO : float t (** Timeout for input operations *) + | REUSEADDR : bool t (** Allow reuse of local addresses for bind *) + | REUSEPORT : bool t (** Allow reuse of address and port bindings *) + | SNDBUF : int t (** Size of send buffer *) + | SNDLOWAT : int t (** Minimum number of bytes to process for output operations *) + | SNDTIMEO : float t (** Timeout for output operations *) + | TYPE : int t (** Report the socket type *) + | TCP_NODELAY : bool t (** Control the Nagle algorithm for TCP sockets *) +end + (** {2 Provider Interfaces} *) class virtual socket : object inherit Generic.t + method virtual setsockopt : 'a Sockopt.t -> 'a -> unit end class virtual stream_socket : object @@ -253,6 +280,12 @@ val getnameinfo : #t -> Sockaddr.t -> (string * string) registered domain name represented by [sockaddr]. [service] is the IANA specified textual name of the port specified in [sockaddr], e.g. 'ftp', 'http', 'https', etc. *) +(** {2 Socket} *) + +val setsockopt : #socket -> 'a Sockopt.t -> 'a -> unit +(** [setsockopt s opt v] configures socket [s] with socket option and value [opt] + and [v] respectively. *) + (** {2 Closing} *) val close : -> unit (** [close t] marks the socket as closed. It can no longer be used after this. *) diff --git a/lib_eio/unix/eio_unix.ml b/lib_eio/unix/eio_unix.ml index 214b892e8..e97b2ffa4 100644 --- a/lib_eio/unix/eio_unix.ml +++ b/lib_eio/unix/eio_unix.ml @@ -18,7 +18,7 @@ type socket = < module Private = struct type _ Eio.Generic.ty += Unix_file_descr : [`Peek | `Take] -> Unix.file_descr Eio.Generic.ty - type _ Effect.t += + type _ Effect.t += | Await_readable : Unix.file_descr -> unit Effect.t | Await_writable : Unix.file_descr -> unit Effect.t | Get_monotonic_clock : Eio.Time.Mono.t Effect.t @@ -75,3 +75,24 @@ let getnameinfo (sockaddr : Eio.Net.Sockaddr.t) = run_in_systhread (fun () -> let Unix.{ni_hostname; ni_service} = Unix.getnameinfo sockaddr options in (ni_hostname, ni_service)) + +let setsockopt (type a) fd (opt: a Eio.Net.Sockopt.t) (v: a) = + match opt with + | IPV6_ONLY -> Unix.(setsockopt fd IPV6_ONLY v) + | ACCEPTCONN -> Unix.(setsockopt fd SO_ACCEPTCONN v) + | BROADCAST -> Unix.(setsockopt fd SO_BROADCAST v) + | DEBUG -> Unix.(setsockopt fd SO_DEBUG v) + | DONTROUTE -> Unix.(setsockopt fd SO_DONTROUTE v) + | KEEPALIVE -> Unix.(setsockopt fd SO_KEEPALIVE v) + | LINGER -> Unix.(setsockopt_optint fd SO_LINGER v) + | OOBINLINE -> Unix.(setsockopt fd SO_OOBINLINE v) + | RCVBUF -> Unix.(setsockopt_int fd SO_RCVBUF v) + | RCVLOWAT -> Unix.(setsockopt_int fd SO_RCVLOWAT v) + | RCVTIMEO -> Unix.(setsockopt_float fd SO_RCVTIMEO v) + | REUSEADDR -> Unix.(setsockopt fd SO_REUSEADDR v) + | REUSEPORT -> Unix.(setsockopt fd SO_REUSEPORT v) + | SNDBUF -> Unix.(setsockopt_int fd SO_SNDBUF v) + | SNDLOWAT -> Unix.(setsockopt_int fd SO_SNDLOWAT v) + | SNDTIMEO -> Unix.(setsockopt_float fd SO_SNDTIMEO v) + | TYPE -> Unix.(setsockopt_int fd SO_TYPE v) + | TCP_NODELAY -> Unix.(setsockopt fd TCP_NODELAY v) diff --git a/lib_eio/unix/eio_unix.mli b/lib_eio/unix/eio_unix.mli index 0981f9a16..e3ab8be65 100644 --- a/lib_eio/unix/eio_unix.mli +++ b/lib_eio/unix/eio_unix.mli @@ -106,3 +106,5 @@ module Ctf = Ctf_unix val getnameinfo : Eio.Net.Sockaddr.t -> (string * string) (** [getnameinfo sockaddr] returns domain name and service for [sockaddr]. *) + +val setsockopt : Unix.file_descr -> 'a Eio.Net.Sockopt.t -> 'a -> unit From d4cef4fe852ceb9f74d9f1e8d244821aa29479a4 Mon Sep 17 00:00:00 2001 From: Bikal Lem Date: Tue, 8 Nov 2022 14:30:07 +0000 Subject: [PATCH 2/3] sockopt: implement setsockopt() for linux and luv backends --- lib_eio/net.ml | 6 ++++-- lib_eio_linux/eio_linux.ml | 40 +++++++++++++++++++++++++------------- lib_eio_luv/eio_luv.ml | 26 ++++++++++++++++--------- 3 files changed, 48 insertions(+), 24 deletions(-) diff --git a/lib_eio/net.ml b/lib_eio/net.ml index 063b469a1..e38efa343 100644 --- a/lib_eio/net.ml +++ b/lib_eio/net.ml @@ -67,7 +67,7 @@ module Ipaddr = struct let elide = min elide zeros in let parts = if zeros = 0 then acc else zeros :: acc in ((if elide < -1 then Some elide else None), List.rev parts) - + in loop 0 0 [] t @@ -245,7 +245,7 @@ let connect ~sw (t:#t) addr = Exn.reraise_with_context ex bt "connecting to %a" Sockaddr.pp addr let datagram_socket ?(reuse_addr=false) ?(reuse_port=false) ~sw (t:#t) addr = - let addr = (addr :> [Sockaddr.datagram | `UdpV4 | `UdpV6]) in + let addr = (addr :> [Sockaddr.datagram | `UdpV4 | `UdpV6]) in t#datagram_socket ~reuse_addr ~reuse_port ~sw addr let getaddrinfo ?(service="") (t:#t) hostname = t#getaddrinfo ~service hostname @@ -291,3 +291,5 @@ let with_tcp_connect ?(timeout=Time.Timeout.none) ~host ~service t f = | exception (Exn.Io _ as ex) -> let bt = Printexc.get_raw_backtrace () in Exn.reraise_with_context ex bt "connecting to %S:%s" host service + +let setsockopt (s:#socket) = s#setsockopt diff --git a/lib_eio_linux/eio_linux.ml b/lib_eio_linux/eio_linux.ml index 684bbecff..b286e9a30 100644 --- a/lib_eio_linux/eio_linux.ml +++ b/lib_eio_linux/eio_linux.ml @@ -1074,8 +1074,14 @@ let fallback_copy src dst = done with End_of_file -> () +class socket sock = object + inherit Eio.Net.socket + method setsockopt = Eio_unix.setsockopt (FD.to_unix `Peek sock) +end + let udp_socket sock = object - inherit Eio.Net.datagram_socket + inherit socket sock + inherit! Eio.Net.datagram_socket method close = FD.close sock @@ -1096,15 +1102,17 @@ let udp_socket sock = object raise (Failure "Expected INET UDP socket address but got Unix domain socket address.") end -let flow fd = +class flow fd = let is_tty = lazy (Unix.isatty (FD.get_exn "isatty" fd)) in - object (_ : ) + object + inherit Eio.Flow.two_way + method fd = fd method close = FD.close fd method stat = FD.fstat fd - method probe : type a. a Eio.Generic.ty -> a option = function + method! probe : type a. a Eio.Generic.ty -> a option = function | FD -> Some fd | Eio_unix.Private.Unix_file_descr op -> Some (FD.to_unix op fd) | _ -> None @@ -1123,9 +1131,7 @@ let flow fd = method pwrite ~file_offset bufs = Low_level.writev_single ~file_offset fd bufs - method read_methods = [] - - method write bufs = Low_level.writev fd bufs + method! write bufs = Low_level.writev fd bufs method copy src = match get_fd_opt src with @@ -1144,14 +1150,22 @@ let flow fd = | `Send -> Unix.SHUTDOWN_SEND | `All -> Unix.SHUTDOWN_ALL - method unix_fd op = FD.to_unix op fd - end + method unix_fd (op: [`Peek | `Take]) = FD.to_unix op fd +end + +let flow fd = new flow fd + +let stream_socket fd = object(_ : ) + inherit socket fd + inherit! flow fd +end let source fd = (flow fd :> source) let sink fd = (flow fd :> sink) let listening_socket fd = object - inherit Eio.Net.listening_socket + inherit socket fd + inherit! Eio.Net.listening_socket method! probe : type a. a Eio.Generic.ty -> a option = function | Eio_unix.Private.Unix_file_descr op -> Some (FD.to_unix op fd) @@ -1166,8 +1180,8 @@ let listening_socket fd = object | Unix.ADDR_UNIX path -> `Unix path | Unix.ADDR_INET (host, port) -> `Tcp (Eio_unix.Ipaddr.of_unix host, port) in - let flow = (flow client :> ) in - flow, client_addr + let stream_socket = (stream_socket client :> ) in + stream_socket, client_addr end let socket_domain_of = function @@ -1227,7 +1241,7 @@ let net = object let sock_unix = Unix.socket (socket_domain_of connect_addr) socket_type 0 in let sock = FD.of_unix ~sw ~seekable:false ~close_unix:true sock_unix in Low_level.connect sock addr; - (flow sock :> ) + (stream_socket sock :> ) method datagram_socket ~reuse_addr ~reuse_port ~sw saddr = let sock_unix = Unix.socket ~cloexec:true (socket_domain_of saddr) Unix.SOCK_DGRAM 0 in diff --git a/lib_eio_luv/eio_luv.ml b/lib_eio_luv/eio_luv.ml index 82beaaab6..5cca523bc 100644 --- a/lib_eio_luv/eio_luv.ml +++ b/lib_eio_luv/eio_luv.ml @@ -727,8 +727,14 @@ end let source fd = (flow fd :> source) let sink fd = (flow fd :> sink) -let socket sock = object - inherit Eio.Flow.two_way as super +class socket f sock = object + inherit Eio.Net.socket + method setsockopt = Eio_unix.setsockopt (f `Peek sock |> Option.get) +end + +let stream_socket sock = object + inherit socket (Stream.to_unix_opt) sock + inherit! Eio.Net.stream_socket as super method! probe : type a. a Eio.Generic.ty -> a option = function | Eio_unix.Private.Unix_file_descr op -> Stream.to_unix_opt op sock @@ -767,7 +773,8 @@ let socket sock = object end class virtual ['a] listening_socket ~backlog sock = object (self) - inherit Eio.Net.listening_socket as super + inherit socket Stream.to_unix_opt sock + inherit! Eio.Net.listening_socket as super method! probe : type a. a Eio.Generic.ty -> a option = function | Eio_unix.Private.Unix_file_descr op -> Stream.to_unix_opt op sock @@ -789,7 +796,7 @@ class virtual ['a] listening_socket ~backlog sock = object (self) raise (wrap_error e) | Ok () -> Switch.on_release sw (fun () -> Handle.ensure_closed client); - let flow = (socket client :> ) in + let flow = (stream_socket client :> ) in let client_addr = self#get_client_addr client in flow, client_addr @@ -853,7 +860,8 @@ module Udp = struct end let udp_socket endp = object - inherit Eio.Net.datagram_socket + inherit socket Handle.to_unix_opt endp + inherit! Eio.Net.datagram_socket method close = Handle.close endp @@ -918,10 +926,10 @@ let net = object method connect ~sw = function | `Tcp (host, port) -> let sock = Stream.connect_tcp ~sw (luv_addr_of_eio host port) in - (socket sock :> < Eio.Flow.two_way; Eio.Flow.close >) + (stream_socket sock :> < Eio.Net.stream_socket; Eio.Flow.close >) | `Unix path -> let sock = Stream.connect_pipe ~sw path in - (socket sock :> < Eio.Flow.two_way; Eio.Flow.close >) + (stream_socket sock :> < Eio.Net.stream_socket; Eio.Flow.close >) method datagram_socket ~reuse_addr ~reuse_port ~sw saddr = let domain = socket_domain_of saddr in @@ -1245,7 +1253,7 @@ let rec run : type a. (_ -> a) -> a = fun main -> let sock = Luv.TCP.init ~loop () |> or_raise in let handle = Handle.of_luv ~sw ~close_unix sock in Luv.TCP.open_ sock fd |> or_raise; - (socket handle :> Eio_unix.socket) + (stream_socket handle :> Eio_unix.socket) with | sock -> continue k sock | exception (Eio.Io _ as ex) -> discontinue k ex @@ -1265,7 +1273,7 @@ let rec run : type a. (_ -> a) -> a = fun main -> let sock = Luv.TCP.init ~loop () |> or_raise in Luv.TCP.open_ sock x |> or_raise; let h = Handle.of_luv ~sw ~close_unix:true sock in - (socket h :> Eio_unix.socket) + (stream_socket h :> Eio_unix.socket) in (wrap a, wrap b) with From 74fbf755a9337c62a85b5c6344e85a5609042cae Mon Sep 17 00:00:00 2001 From: Bikal Lem Date: Tue, 8 Nov 2022 15:16:45 +0000 Subject: [PATCH 3/3] sockopt: update tests --- README.md | 4 ++-- lib_eio/mock/eio_mock.mli | 22 +++++++++++----------- tests/mocks.md | 2 +- tests/network.md | 12 ++++++------ 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index cd11482d0..b9f80bbef 100644 --- a/README.md +++ b/README.md @@ -437,7 +437,7 @@ We can test it using a mock network: ```ocaml # Eio_main.run @@ fun _env -> let net = Eio_mock.Net.make "mocknet" in - let socket = Eio_mock.Flow.make "socket" in + let socket = Eio_mock.Net.stream_socket "socket" in Eio_mock.Net.on_connect net [`Return socket]; run_client ~net ~addr:(`Tcp (Eio.Net.Ipaddr.V4.loopback, 8080));; +Connecting to server... @@ -473,7 +473,7 @@ This can also be tested on its own using a mock network: # Eio_main.run @@ fun _env -> let listening_socket = Eio_mock.Net.listening_socket "tcp/80" in let mock_addr = `Tcp (Eio.Net.Ipaddr.V4.loopback, 37568) in - let connection = Eio_mock.Flow.make "connection" in + let connection = Eio_mock.Net.stream_socket "connection" in Eio_mock.Net.on_accept listening_socket [`Return (connection, mock_addr)]; Eio_mock.Flow.on_read connection [ `Return "(packet 1)"; diff --git a/lib_eio/mock/eio_mock.mli b/lib_eio/mock/eio_mock.mli index 8c2396874..6ad02896c 100644 --- a/lib_eio/mock/eio_mock.mli +++ b/lib_eio/mock/eio_mock.mli @@ -89,27 +89,27 @@ module Flow : sig | `Read_source_buffer (** Use the {!Eio.Flow.Read_source_buffer} optimisation. *) ] - type t = < - Eio.Flow.two_way; - Eio.Flow.close; - on_read : string Handler.t; - on_copy_bytes : int Handler.t; - set_copy_method : copy_method -> unit; - attach_to_switch : Eio.Switch.t -> unit; - > + class virtual t : ?pp:string Fmt.t -> string -> object + inherit Eio.Flow.two_way + inherit Eio.Flow.close + method on_read : string Handler.t + method on_copy_bytes : int Handler.t + method set_copy_method : copy_method -> unit + method attach_to_switch : Eio.Switch.t -> unit + end val make : ?pp:string Fmt.t -> string -> t (** [make label] is a mock Eio flow. It can be used as a source, sink, or two-way flow. @param pp Printer to use to display the data. *) - val on_read : t -> string Handler.actions -> unit + val on_read : #t -> string Handler.actions -> unit (** [on_read t actions] configures the values to return from the mock's [read] function. *) - val on_copy_bytes : t -> int Handler.actions -> unit + val on_copy_bytes : #t -> int Handler.actions -> unit (** [on_copy_bytes t actions] configures the number of bytes to copy in each iteration. *) - val set_copy_method : t -> copy_method -> unit + val set_copy_method : #t -> copy_method -> unit (** [set_copy_method t m] configures [t] to use the given method to read from a source during a copy operation. *) end diff --git a/tests/mocks.md b/tests/mocks.md index 4b0a7ffcd..14652134f 100644 --- a/tests/mocks.md +++ b/tests/mocks.md @@ -50,7 +50,7 @@ The server handles a connection: let net = Eio_mock.Net.make "mocknet" in let listening_socket = Eio_mock.Net.listening_socket "tcp/80" in Eio_mock.Net.on_listen net [`Return listening_socket]; - let connection = Eio_mock.Flow.make "connection" in + let connection = Eio_mock.Net.stream_socket "connection" in let addr = `Tcp (Eio.Net.Ipaddr.V4.loopback, 37568) in Eio_mock.Net.on_accept listening_socket [`Return (connection, addr)]; Eio_mock.Flow.on_read connection [`Return "foo"; `Return "bar"]; diff --git a/tests/network.md b/tests/network.md index a2f0c2c0e..efdd08402 100644 --- a/tests/network.md +++ b/tests/network.md @@ -355,7 +355,7 @@ On success, we close the connection immediately: ```ocaml # Eio_mock.Backend.run @@ fun () -> let socket = Eio_mock.Net.listening_socket "tcp/80" in - let flow = Eio_mock.Flow.make "connection" in + let flow = Eio_mock.Net.stream_socket "connection" in let addr = `Tcp (Eio.Net.Ipaddr.V4.loopback, 1234) in Eio_mock.Net.on_accept socket [`Return (flow, addr)]; Switch.run @@ fun sw -> @@ -372,7 +372,7 @@ If the forked fiber fails, we close immediately: ```ocaml # Eio_mock.Backend.run @@ fun () -> let socket = Eio_mock.Net.listening_socket "tcp/80" in - let flow = Eio_mock.Flow.make "connection" in + let flow = Eio_mock.Net.stream_socket "connection" in let addr = `Tcp (Eio.Net.Ipaddr.V4.loopback, 1234) in Eio_mock.Net.on_accept socket [`Return (flow, addr)]; Switch.run @@ fun sw -> @@ -389,7 +389,7 @@ If the fork itself fails, we still close the connection: ```ocaml # Eio_mock.Backend.run @@ fun () -> let socket = Eio_mock.Net.listening_socket "tcp/80" in - let flow = Eio_mock.Flow.make "connection" in + let flow = Eio_mock.Net.stream_socket "connection" in let addr = `Tcp (Eio.Net.Ipaddr.V4.loopback, 1234) in Eio_mock.Net.on_accept socket [`Return (flow, addr)]; Switch.run @@ fun sw -> @@ -630,7 +630,7 @@ First address works: ```ocaml # Eio_mock.Backend.run @@ fun () -> Eio_mock.Net.on_getaddrinfo net [`Return [addr1; addr2]]; - let mock_flow = Eio_mock.Flow.make "flow" in + let mock_flow = Eio_mock.Net.stream_socket "flow" in Eio_mock.Net.on_connect net [`Return mock_flow]; Eio.Net.with_tcp_connect ~host:"www.example.com" ~service:"http" net (fun conn -> let req = "GET / HTTP/1.1\r\nHost:www.example.com:80\r\n\r\n" in @@ -650,7 +650,7 @@ Second address works: ```ocaml # Eio_mock.Backend.run @@ fun () -> Eio_mock.Net.on_getaddrinfo net [`Return [addr1; addr2]]; - let mock_flow = Eio_mock.Flow.make "flow" in + let mock_flow = Eio_mock.Net.stream_socket "flow" in Eio_mock.Net.on_connect net [`Raise connection_failure; `Return mock_flow]; Eio.Net.with_tcp_connect ~host:"www.example.com" ~service:"http" net (fun conn -> @@ -690,7 +690,7 @@ First attempt times out: let clock = Eio_mock.Clock.Mono.make () in let timeout = Eio.Time.Timeout.seconds clock 10. in Eio_mock.Net.on_getaddrinfo net [`Return [addr1; addr2]]; - let mock_flow = Eio_mock.Flow.make "flow" in + let mock_flow = Eio_mock.Net.stream_socket "flow" in Eio_mock.Net.on_connect net [`Run Fiber.await_cancel; `Return mock_flow]; Fiber.both (fun () ->