The intent at sharing the code isn't designed to make the interface alien for async users but rather to simplify the code for common cases. I thought lwt made a good design decision to allowing users to KISS by passing strings as the bodies. My arguments for it are:

* pipes are too heavy weight in the general case, and are mostly ideal for streaming responses
* strings are often the most common use case (followed by lists of strings, or ropes if they existed)

The fact that it ended up being possible to share some code with the lwt backend is just a nice side effect. I have some branch that experiments with that:

https://github.com/rgrinberg/ocaml-cohttp/tree/unify_body

in particular (it actually contains extra cleanups to the body api):

https://github.com/rgrinberg/ocaml-cohttp/blob/unify_body/lwt/cohttp_lwt_body.mli
https://github.com/rgrinberg/ocaml-cohttp/blob/unify_body/cohttp/body.mli

At this point the benefits of sharing aren't clear which is why I did not attempt to get it upstreamed but Lwt's body interface is sound and compatible with async IMO. There was also some breakages introduced so I'm not yet sure if it's worth pursuing...