Skip to content

Commit

Permalink
Add more windows tests and fix network code
Browse files Browse the repository at this point in the history
  • Loading branch information
patricoferris committed May 4, 2023
1 parent 695499e commit f3d7051
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 56 deletions.
3 changes: 2 additions & 1 deletion lib_eio_windows/domain_mgr.ml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ let run_event_loop fn x =
| Eio_unix.Private.Get_monotonic_clock -> Some (fun k -> continue k (Time.mono_clock : Eio.Time.Mono.t))
| Eio_unix.Private.Socket_of_fd (sw, close_unix, unix_fd) -> Some (fun k ->
let fd = Fd.of_unix ~sw ~blocking:false ~close_unix unix_fd in
Unix.set_nonblock unix_fd;
(* TODO: On Windows, if the FD from Unix.pipe () is passed this will fail *)
(try Unix.set_nonblock unix_fd with Unix.Unix_error (Unix.ENOTSOCK, _, _) -> ());
continue k (Flow.of_fd fd :> Eio_unix.socket)
)
| Eio_unix.Private.Socketpair (sw, domain, ty, protocol) -> Some (fun k ->
Expand Down
6 changes: 0 additions & 6 deletions lib_eio_windows/low_level.ml
Original file line number Diff line number Diff line change
Expand Up @@ -162,12 +162,6 @@ module Open_flags = struct
let cloexec = Config.o_noinherit
let creat = Config.o_creat
let excl = Config.o_excl
(* let directory = Config.o_directory
let dsync = Config.o_dsync
let noctty = Config.o_noctty
let nofollow = Config.o_nofollow *)
(* let nonblock = Config.o_nonblock *)
(* let sync = Config.o_sync *)
let trunc = Config.o_trunc

let empty = 0
Expand Down
12 changes: 7 additions & 5 deletions lib_eio_windows/net.ml
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ let getaddrinfo ~service node =
aux ()

let listen ~reuse_addr ~reuse_port ~backlog ~sw (listen_addr : Eio.Net.Sockaddr.stream) =
let socket_type, addr =
let socket_type, addr, is_unix_socket =
match listen_addr with
| `Unix path ->
if reuse_addr then (
Expand All @@ -92,10 +92,10 @@ let listen ~reuse_addr ~reuse_port ~backlog ~sw (listen_addr : Eio.Net.Sockaddr.
| exception Unix.Unix_error (Unix.ENOENT, _, _) -> ()
| exception Unix.Unix_error (code, name, arg) -> raise @@ Err.wrap code name arg
);
Unix.SOCK_STREAM, Unix.ADDR_UNIX path
Unix.SOCK_STREAM, Unix.ADDR_UNIX path, true
| `Tcp (host, port) ->
let host = Eio_unix.Ipaddr.to_unix host in
Unix.SOCK_STREAM, Unix.ADDR_INET (host, port)
Unix.SOCK_STREAM, Unix.ADDR_INET (host, port), false
in
let sock = Low_level.socket ~sw (socket_domain_of listen_addr) socket_type 0 in
(* For Unix domain sockets, remove the path when done (except for abstract sockets). *)
Expand All @@ -107,12 +107,14 @@ let listen ~reuse_addr ~reuse_port ~backlog ~sw (listen_addr : Eio.Net.Sockaddr.
Switch.null_hook
in
Fd.use_exn "listen" sock (fun fd ->
if reuse_addr then
(* REUSEADDR cannot be set on a Windows UNIX domain socket,
otherwise the Unix.bind will fail! *)
if not is_unix_socket && reuse_addr then
Unix.setsockopt fd Unix.SO_REUSEADDR true;
if reuse_port then
Unix.setsockopt fd Unix.SO_REUSEPORT true;
Unix.bind fd addr;
Unix.listen fd backlog;
Unix.listen fd backlog
);
listening_socket ~hook sock

Expand Down
56 changes: 12 additions & 44 deletions lib_eio_windows/test/test.ml
Original file line number Diff line number Diff line change
Expand Up @@ -12,56 +12,24 @@ module Timeout = struct
]
end

module Net = struct
open Eio.Std

let read_all flow =
let b = Buffer.create 100 in
Eio.Flow.copy flow (Eio.Flow.buffer_sink b);
Buffer.contents b

let run_client ~sw ~net ~addr =
traceln "Connecting to server...";
let flow = Eio.Net.connect ~sw net addr in
Eio.traceln "connected";
Eio.Flow.copy_string "Hello from client" flow;
Eio.Flow.shutdown flow `Send;
let msg = read_all flow in
msg

let run_server ~sw msg socket =
Eio.Net.accept_fork socket ~sw (fun flow _addr ->
traceln "Server accepted connection from client";
Fun.protect (fun () ->
let msg = read_all flow in
traceln "Server received: %S" msg
) ~finally:(fun () -> Eio.Flow.copy_string msg flow)
)
~on_error:(function
| ex -> traceln "Error handling connection: %s" (Printexc.to_string ex)
)

let test_client_server env () =
Eio.Switch.run @@ fun sw ->
let addr = `Tcp (Eio.Net.Ipaddr.V4.loopback, 8081) in
let server = Eio.Net.listen env#net ~sw ~reuse_addr:true ~backlog:5 addr in
let msg = "From the server" in
Fiber.both
(fun () -> run_server ~sw msg server)
(fun () ->
let client_msg = run_client ~sw ~net:env#net ~addr in
Alcotest.(check string) "same message" msg client_msg
)

module Random = struct
let test_random env () =
let src = Eio.Stdenv.secure_random env in
let b1 = Cstruct.create 8 in
let b2 = Cstruct.create 8 in
Eio.Flow.read_exact src b1;
Eio.Flow.read_exact src b2;
Alcotest.(check bool) "different random" (not (Cstruct.equal b1 b2)) true

let tests env = [
"server-client", `Quick, test_client_server env
"different", `Quick, test_random env
]
end

let () =
Eio_windows.run @@ fun env ->
Alcotest.run "eio_windows" [
"net", Net.tests env;
"timeout", Timeout.tests env
"net", Test_net.tests env;
"timeout", Timeout.tests env;
"random", Random.tests env
]
118 changes: 118 additions & 0 deletions lib_eio_windows/test/test_net.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
open Eio.Std

let read_all flow =
let b = Buffer.create 100 in
Eio.Flow.copy flow (Eio.Flow.buffer_sink b);
Buffer.contents b

let run_client ~sw ~net ~addr =
traceln "Connecting to server...";
let flow = Eio.Net.connect ~sw net addr in
Eio.traceln "connected";
Eio.Flow.copy_string "Hello from client" flow;
Eio.Flow.shutdown flow `Send;
let msg = read_all flow in
msg

let run_server ~sw msg socket =
Eio.Net.accept_fork socket ~sw (fun flow _addr ->
traceln "Server accepted connection from client";
Fun.protect (fun () ->
let msg = read_all flow in
traceln "Server received: %S" msg
) ~finally:(fun () -> Eio.Flow.copy_string msg flow)
)
~on_error:(function
| ex -> traceln "Error handling connection: %s" (Printexc.to_string ex)
)

let test_client_server env addr () =
Eio.Switch.run @@ fun sw ->
let server = Eio.Net.listen env#net ~sw ~reuse_addr:true ~backlog:5 addr in
let msg = "From the server" in
Fiber.both
(fun () -> run_server ~sw msg server)
(fun () ->
let client_msg = run_client ~sw ~net:env#net ~addr in
Alcotest.(check string) "same message" msg client_msg
)

let run_dgram addr ~net sw msg =
let e1 = `Udp (addr, 8081) in
let e2 = `Udp (addr, 8082) in
let listening_socket = Eio.Net.datagram_socket ~sw net e2 in
Fiber.both
(fun () ->
let buf = Cstruct.create 20 in
traceln "Waiting to receive data on %a" Eio.Net.Sockaddr.pp e2;
let addr, recv = Eio.Net.recv listening_socket buf in
traceln "Received message from %a"
Eio.Net.Sockaddr.pp addr;
Alcotest.(check string) "same udp msg" msg (Cstruct.(to_string (sub buf 0 recv)))
)
(fun () ->
let e = Eio.Net.datagram_socket ~sw net e1 in
traceln "Sending data from %a to %a" Eio.Net.Sockaddr.pp e1 Eio.Net.Sockaddr.pp e2;
Eio.Net.send e e2 (Cstruct.of_string msg))

let test_udp env addr () =
Eio.Switch.run @@ fun sw ->
run_dgram addr ~net:env#net sw "UDP on Windows"

let test_fd env addr () =
Eio.Switch.run @@ fun sw ->
let addr = `Tcp (addr, 8081) in
let server = Eio.Net.listen env#net ~sw ~reuse_addr:true ~backlog:5 addr in
Alcotest.(check bool) "Listening socket has Unix FD" (Eio_unix.Resource.fd_opt server <> None) true;
let have_client, have_server =
Fiber.pair
(fun () ->
let flow = Eio.Net.connect ~sw env#net addr in
(Eio_unix.Resource.fd_opt flow <> None)
)
(fun () ->
let flow, _addr = Eio.Net.accept ~sw server in
(Eio_unix.Resource.fd_opt flow <> None)
)
in
Alcotest.(check bool) "Client-side socket has Unix FD" have_client true;
Alcotest.(check bool) "Server-side socket has Unix FD" have_server true

let test_wrap_socket pipe_or_socketpair () =
Switch.run @@ fun sw ->
let r, w =
match pipe_or_socketpair with
| `Pipe -> Unix.pipe ()
| `Socketpair -> Unix.socketpair Unix.PF_UNIX Unix.SOCK_STREAM 0
in
let source = (Eio_unix.import_socket_stream ~sw ~close_unix:true r :> Eio.Flow.source) in
let sink = (Eio_unix.import_socket_stream ~sw ~close_unix:true w :> Eio.Flow.sink) in
let msg = "Hello" in
Fiber.both
(fun () -> Eio.Flow.copy_string (msg ^ "\n") sink)
(fun () ->
let b = Eio.Buf_read.of_flow source ~max_size:1000 in
Alcotest.(check string) "same message" (Eio.Buf_read.line b) msg
)

let test_eio_socketpair () =
Switch.run @@ fun sw ->
let a, b = Eio_unix.socketpair ~sw () in
ignore (Eio_unix.Resource.fd a : Eio_unix.Fd.t);
ignore (Eio_unix.Resource.fd b : Eio_unix.Fd.t);
Eio.Flow.copy_string "foo" a;
Eio.Flow.close a;
let msg = Eio.Buf_read.of_flow b ~max_size:10 |> Eio.Buf_read.take_all in
Alcotest.(check string) "same messagw" "foo" msg

let tests env = [
"tcp-ip4", `Quick, test_client_server env (`Tcp (Eio.Net.Ipaddr.V4.loopback, 8081));
"tcp-ip6", `Quick, test_client_server env (`Tcp (Eio.Net.Ipaddr.V6.loopback, 8081));
"unix", `Quick, test_client_server env (`Unix "eio-test.sock");
"udp-ip4", `Quick, test_udp env Eio.Net.Ipaddr.V4.loopback;
"udp-ip6", `Quick, test_udp env Eio.Net.Ipaddr.V6.loopback;
"fds", `Quick, test_fd env Eio.Net.Ipaddr.V4.loopback;
"wrap-pipe", `Quick, test_wrap_socket `Pipe;
"wrap-socketpair", `Quick, test_wrap_socket `Socketpair;
"eio-socketpair", `Quick, test_eio_socketpair
]

0 comments on commit f3d7051

Please sign in to comment.