The honest answer---I don't know the best way to structure this code. My guess is that if you're aiming for the Sans I/O approach, you'll want to follow the general style of what's happening in h11 and the hyper-h2 libraries. The approach I've taken so far on things is to have objects that accept a feed of data, but which raise exceptions or signal some kind of error when more data is needed. Much of the code ends up being kind of similar to non-blocking I/O itself.
There also might be some interesting ways to use Curio queues as a communication mechanism (i.e., have all of the I/O bits handled by Curio, but hand data off to other code via some kind of queuing API).