Eio.Buf_read behaviour

Hello! I just started reading up on the Eio library and I’m trying to use it for a simple toy application.

I connect to a port using TCP, receive some bytes and print them if they are successfully parsed as a valid message:

type message = { len : int; mt : int; msg : string }

let message =
        let open Eio.Buf_read.Syntax in
        let* len = Eio.Buf_read.uint8 in
        let* mt = Eio.Buf_read.uint8 in
        let+ msg = Eio.Buf_read.take (len-1) in
        let _ = Eio.traceln "Reached -> %d %c %s" len (Char.chr mt) msg in
        { len; mt; msg }

let run_client conn =
        Eio.traceln "Connection succeeded";
        match Eio.Buf_read.parse message conn ~max_size:1024 with 
        | Ok { len; mt; msg } -> Eio.traceln "Not reached -> %d %c %s" len (Char.chr mt) msg
        | Error (`Msg err) -> Eio.traceln "Parse failed: %s" err

let main net clock =
        let rec reconnect () =
                try Eio.Net.with_tcp_connect ~host:"localhost" ~service:"1234" net run_client with e -> Eio.traceln "%s" (Printexc.to_string e);
                Eio.Time.sleep clock 30.0;
                reconnect ()
        in reconnect ()

let () =
        Eio_main.run @@ fun env ->
        let net = Eio.Stdenv.net env in
        let clock = Eio.Stdenv.clock env in
        main net clock

Now, I haven’t used parser combinators before and I don’t fully understand let* and let+ yet, so I am likely missing something obvious, but the type checker is happy. If I start a local server with nc -l 1234 and send AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA to the client, I can reach the traceln inside the message function, but not the traceln in the first case of the pattern match. It looks like the parser doesn’t return and the code is blocked waiting to read from the socket.

What should I do to avoid getting stuck and instead have valid messages reach the pattern match?

Hi @h0rror :))

The problem here is the semantics of Eio.Buf_read.parse. The documentation for the function says:

parse p flow ~max_size uses p to parse everything in flow.

It is a convenience function that does

      let buf = of_flow flow ~max_size in
      format_errors (p <* end_of_input) buf

That end_of_input is the important part. You never get past the Buf_read.parse because we’re still waiting for the end of the input (I suspect if you close your nc -l 1234 after sending you will get the next traceln).

You could also try the following as your parse function without the end_of_input:

let parse p flow ~max_size =
  let buf = Eio.Buf_read.of_flow flow ~max_size in
  Eio.Buf_read.format_errors p buf
3 Likes

Hi @patricoferris , you are absolutely right. I could reach the second traceln by killing the server. I now understand why this was happening and your proposal did the trick. Thank you for the prompt response!

2 Likes