-
Notifications
You must be signed in to change notification settings - Fork 588
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for streaming response body #492
Comments
@mspanc just a quick question: does |
Streaming is supported without chunking with |
@mspanc That is for cowboy master, there is no |
@josevalim
|
It seems your best option for now is to fallback to a custom cowboy handler and use cowboy_req:set_resp_body_fun/2, even if awkward, or deploy those routes in particular using a separate application using cowboy master. We will leave this open so we make this a first class experience though when Cowboy 2.0 is out. |
https://gist.github.com/bsuh/295759580d36fe42a533abb733cb912c It seems like we have similar use cases @mspanc . I was able to streaming reverse proxy, but the performance is quite bad. Using it to reverse proxy a 1 GB file (for testing) results in ~500KB/s with one CPU core throttled. Even just fetching the file with HTTPoison into memory seems to result in high CPU. There's probably excessive data copying going on due to message passing. Also, the upstream request keeps happily consuming CPU resources even if the downstream client closed the socket. Maybe I need to be using Cowboy's loop handlers ninenines/cowboy#312? Go's
I also might be missing something as I'm quite new to Elixir and Erlang. I was going to replace my Go app with a Phoenix app, starting with a reverse proxy and incrementally rewriting functionality into Elixir. Seems like I'll have to keep around a bit of Go :/ |
Good news. I did some unscientific testing of various Erlang/Elixir HTTP clients downloading a 100 MB file and decided to try Here's the link again to the gist with updated code. Note that it reads private variables from Plug.Conn to pass to cowboy functions and updates the Plug.Conn manually, so it might break with dependency updates. I'm using plug 1.2.2, cowboy 1.0.4, phoenix 1.2.1, lhttpc 1.5.0 |
@bsuh unrelated to this issue sorry, but what made you leave golang and pick up phoenix? |
@nhooyr Experimentation to try out the BEAM VM that I've heard a lot about and personal preference. Go is a bit low level and bare bones (the language features not the standard library which is fantastic) for me. My projects are more plain web apps gluing libraries together and Go seems to excel more at building infrastructure tooling and network protocolish stuff. |
Erlang/Elixir's bit-syntax makes building network protocols almost blissfully easy. ;-) |
Folks, Plug master supports Cowboy 2.0 which means we can make this feature available. PRs are welcome. |
Can we extend this issue to also support chunked upload? I would like to bypass the Plug.Upload mechanism completely. |
@ringods It's already possible to send chunked responses, see https://hexdocs.pm/plug/Plug.Conn.html#chunk/2. This issue is about streaming responses without using chunked encoding. |
@ericmj I might be confused, but are you talking about sending responses back to the client with that function? I am after an alternative for uploading files from the client in the request handling. The file handling in Plug.Parsers gives you a According this gist:
So I'm investigating how the HTTP messaging is done from client to server hoping to get an Elixir Plug implementation accepting chunked file uploading. To avoid confusion: I'm not talking about mutipart uploads. ;-) |
@ringods you can already bypass Plug.Upload. You can plug your own parsers into Plug.Parsers (or not use Plug.Parsers at all and parse the request in a previous plug). |
You can also give |
Indeed all possible, but since I'm running in circles on how to implement this myself, I posted it on the forum. Some further input is welcome. ;-) https://elixirforum.com/t/making-a-stream-out-of-the-http-upload-body-content-towards-genstage/13544 |
After discussion with @essen on IRC it turns out this is not supported in cowboy2 on http1.1 connections, so I will look into contributing it to cowboy. When investigating this I also noticed that it looks like the streaming is broken in the cowboy2 adapter because it never sends [1] https://github.com/elixir-plug/plug/blob/master/lib/plug/adapters/cowboy2/conn.ex#L62 EDIT: Turn out I was wrong about |
Cowboy 2.5.0 has added support for non-chunked streaming in http1.1. You need to set the |
Oh, awesome. Let's reopen this so we update the docs and then we should be good to go. |
This change in cowboy is not resolving the original issue. The idea was to stream infinite file (such as MP3 stream) without sending content length and without using chunked encoding, similarly to how Icecast2 works but it can be used in other proxy scenarios. Given that if there's an agreement that such feature should be present the issue IMO should be reopened. |
@mspanc thanks for the context. However, I don't think there is nothing to be done on Plug's side to support this feature, it all depends on Cowboy? So I think this need to be supported on Cowboy side and then we can resume the discussion if anything is necessary to support it on Plug. |
You can disable chunked on a per-response basis and if you don't send a content-length then Cowboy will just stream until the end of the body before closing the connection (or forever if there's no end), see https://github.com/ninenines/cowboy/blob/master/src/cowboy_http.erl#L1193-L1198 |
I am implementing a Phoenix app that has controller that effectively acts as a HTTP proxy. It fetches file over HTTP from the upstream server (in chunks) and sends it back to the client.
At the moment I am sending chunked response using
Plug.Conn.send_chunked/2
andPlug.Conn.chunk/2
.The issue is that that sets Transfer-Encoding to chunked and according to the HTTP/1.1 RFC that causes Content-Length header to be obsolete. If it get further proxied through nginx, this header is just removed to match the RFC.
As I am serving audio/video to the web browsers that effectively causes them to disable seeking as total file length is unknown.
At the moment the only workaround is to download the whole from the upstream and send it to the client, but I want to do this on the fly as there's no reason to introduce this extra step.
It would be great if there would have been another interface allowing to build response body from parts but send it as regular (non-chunked) response.
The text was updated successfully, but these errors were encountered: