Implementing an AsyncWrite wrapper with stateful encoding

⚓ rust    📅 2025-05-17    👤 surdeus    👁️ 5      

surdeus

Warning

This post was published 43 days ago. The information described in this article may have changed.

I am attempting to write an implementation of tokio::io::AsyncWrite that wraps another AsyncWrite and encodes (well, poorly encrypts) bytes passed to the wrapper's poll_write() before they are passed on to the inner poll_write(). The problem is that the encoding mechanism is stateful, and so just passing the input through the encoder on every call would break as soon as the inner poll_write() returned Pending, as that would cause the encoded bytes to be discarded, and then when the same input is passed again on the next poll_write() call, it'd be encoded differently (i.e., wrong).

The simplest solution I can think of is to store the encoded bytes in the wrapper whenever the inner poll_write() returns Pending, and then when poll_write() is called again while the storage is nonempty, another attempt is made to pass the stored bytes to the inner poll_write(). However, this only works as long as the outer poll_write() is always called with the same bytes until Ready is returned — an assumption that is broken by cancellation of writes, and maybe some other things? (I don't think my codebase would continue using a writer after cancelling a write, but I'm not about to audit it right now.) A variation would be to store both the raw and encoded bytes and compare the stored raw bytes against the input to poll_write(), which would presumably work as long as there are no occurrences of "try to write bytes → cancel write → try to write the same bytes again."

Is there a (more) robust way to pull this off?

2 posts - 1 participant

Read full topic

🏷️ rust_feed