ocaml-cohttp icon indicating copy to clipboard operation
ocaml-cohttp copied to clipboard

cohttp-eio: expose the underlying flow additionally to Buf_write when sending the response body

Open TheLortex opened this issue 2 years ago • 3 comments

Currently the body type is:

  type t =
    | Fixed of string
    | Chunked of chunk_writer
    | Custom of (Eio.Buf_write.t -> unit)
    | Empty

I believe it would be nice to have an additional body type that enables to write directly into the connection flow.

| Flow of (Eio.Flow.sink -> unit)

This way, one can use Eio.Flow.copy to send a file in a streaming fashion. I believe that using Buf_write would require writing the full content in the buffer, right ?

TheLortex avatar Feb 15 '23 17:02 TheLortex

I believe that using Buf_write would require writing the full content in the buffer, right ?

Not sure but for your particular use-case can Eio.Buf_write.flush (https://github.com/ocaml-multicore/eio/blob/main/lib_eio/buf_write.mli#L247) help? i.e. IIUC flushing should ensure that you don't wait for the scheduled batched write. @talex5 can you confirm?

bikallem avatar Feb 16 '23 17:02 bikallem

@bikallem:

i.e. IIUC flushing should ensure that you don't wait

The link says:

[flush t] waits until all prior writes have been successfully completed.

@TheLortex:

I believe that using Buf_write would require writing the full content in the buffer, right?

What you can do is use schedule_cstruct to avoid a copy.

I'm not sure providing the raw sink will work, because we'll still typically need to do chunking. What are you planning to use it for?

talex5 avatar Feb 17 '23 10:02 talex5

What are you planning to use it for?

I wanted to fix this TODO in eeww: https://github.com/avsm/eeww/blob/main/src/main.ml#L78

   |`Regular_file ->
      (* TODO stream body instead of loading *)
      let body = Eio.Path.load fname in
      Cohttpx.respond_file fname body

When serving files over HTTP, it's useful to be able to manipulate the flow so that a single copy is sufficient to stream the content of the file:

  let respond_file fname =
    ...
    let headers = Http.Header.of_list
      [ "content-type", mime_type;
        "content-length", string_of_int length;
      ] in
    let response =
      Http.Response.make ~version:`HTTP_1_1 ~status:`OK ~headers ()
    in
    response, Body.Flow (fun flow ->
      Eio.Path.with_open_in fname @@ fun file ->
      Eio.Flow.copy file flow)

without having to resort to the chunked encoding.

TheLortex avatar Feb 21 '23 09:02 TheLortex