elixir-plug / plug

Compose web applications with functions
https://hex.pm/packages/plug
Other
2.84k stars 582 forks source link

Let user know when `Plug.Conn.register_before_chunk/2` sends the last chunk #1157

Closed tanguilp closed 11 months ago

tanguilp commented 1 year ago

I'm trying to figure out how to determine when Plug.Conn.register_before_chunk/2 sends the last chunk.

My use-case is the following: I want to cache chunks and possibly recombine them to cache the full response. It's possible to do even without knowing the total length of the of the response, but eventually knowing the content length has some benefits:

(content-length must not be set when transfer-encoding is set.)

I can think of some unreliable ways of doing it by using the process dictionary to store current sent length and by assuming that when the process dies the full message is sent. However I'm wondering if it's possible to implement it in some way in Plug.Conn.register_before_chunk/2.

josevalim commented 1 year ago

I am not sure this is possible because we don't have a callback that notifies the chunk is done. So you would need to provide such a mechanism yourself. You can store a callback in assigns and invoke it when you are done.

tanguilp commented 1 year ago

I'm curious how @feynmanliang intends to use Plug.Conn.register_before_chunk/2. Do you have the same need to figure out when the last chunk is sent? If so, how do you achieve it?

I am not sure this is possible because we don't have a callback that notifies the chunk is done.

Indeed, and I don't understand how plug notifies the adapter streaming is over :thinking: Cowboy's adapter for instance expects a fin parameter to be passed to the cowboy_req:stream_body/3 function to send the terminating "0/r/n/r/n" of a chunk-encoded response.

feynmanliang commented 1 year ago

Would be great to have! We currently rely on timeouts / a special chunk indicating end of messages.

feynmanliang commented 1 year ago

@tanguilp do you mind sharing where you found that "0/r/n/r/n" should terminate a SSE stream? I am reading the WHATWG spec and can't seem to locate it.

tanguilp commented 1 year ago

@feynmanliang It was not clear in my message but I was talking about chunked transfer encoding, not SSE events.

My use cache is HTTP caching. I want to cache chunks these responses by chunk and recombine the chunks afterwards. To do this I need to know when the last chunk is sent (otherwise there's no way there won't be an additional chunk). I'm curious what you use case is.

feynmanliang commented 1 year ago

We do the exact same thing: collect a SSE stream and recombine it into a full response for caching. In our particular use case, we know in advance the SSE streams are not long-lived and so we can either rely on a timeout or the protocol specifying a sentinel end-of-stream message.

I found https://www.rfc-editor.org/rfc/rfc6202 quite helpful, specifically:

Each chunk starts with the hexadecimal expression of the length of its data, followed by CR/LF (the end of the response is indicated with a chunk of size 0).

answers my question about the terminating chunk for HTTP/1.1 streaming

To achieve the same result, an HTTP/1.0 server will omit the Content- Length header in the response. Thus, it will be able to send the subsequent parts of the response on the same connection (in this case, the different parts of the response are not explicitly separated by HTTP protocol, and the end of the response is achieved by closing the connection).

I could be wrong, but I believe this is what we rely on in our implementation.

josevalim commented 11 months ago

I have reverted register_before_chunk for now, in case we need to modify it so it considers closing as well.