Async newbie: help me improve my code!


I’m learning the basics of the Tokio library and at the same time moving my first steps into the async world.

My task is to implement the following workflow:

  1. A client connects to a TCP proxy, sends one ASCII string
  2. the proxy reads the payload
  3. based on the content, the proxy opens a connection to server A or B, forwards the payload
  4. the proxy receives the response from the server and forward it to the client
  5. the connection is closed on both ends

I’ve started rewriting the Tokio proxy example using the tokio_io::AsyncRead::framed method in order to parse the payload received (as suggested on the Tokio Gitter channel) before forwarding the the server.

My prototype has almost implemented all the points above and “works”. The only thing missing is the payload parsing before connecting to the server (currently I connect and blindly forward the payload).

You can see the work in progress in this gist.

I have a couple of questions:

  1. In the original Tokio code the TcpStream socket is wrapped by a MyTcpStream to implement the socket shutdown when done. If I don’t do that, the client socket hangs.
    From my newbie point of view I’d like to simplify the code shutting down explicitly the socket around here but the compiler borrow checker blocks me from using the client down there. Is this possible any way or another?
  2. style: nesting Futures looks to me ugly. I tried splitting them but I still don’t understand how the logic blocks should be splitted (I am facing various errors in my trial-and-errors). Any hints? AFAICS each block should return a Future chainable with another code block, until the final tokio::run(...) statement.

The next step is to actually implement the payload evaluation right in the middle of the various Future processing, right before connecting to a server. I’ll be working on that.

Thank you for evaluting my efforts :^)

For #1, I believe there’s a reunite() method that takes the split read/write pair and gives you back the original stream. That will be the Framed wrapper, which you can into_inner() to get the TcpStream. You can then shut it down.

So, you need to move the two halves (tx, rx) into that block, do the reunion dance and then shut down.

@vitalyd sorry took me so long for a reply, async in Rust is really an annyoing beast to learn. I wanted to thank you for your suggestion, I’ve actually implemented what you said.

My prototype is not finished yet. I learned a lot about tokio and futures (as they say, getting from 0 to 1 is way harder than going from 1 to 2) but I still feel that the borrow checker gets in the way really a lot and - in case of nested futures - error messages generated are hard to understand.

I’m sure that nesting futures like I’m doing is an antipattern, I’ll investigate at a later stage how to write cleaner async code