I’ve been trying to debug this for days, and I can’t figure out what I’m doing wrong. If SSL is off, I can reload the page as fast as I want and I don’t encounter any errors. But when I turn SSL on and try refreshing the page quickly, repeatedly, I get an error after the 5th or 10th reload.
Steps to reproduce
1. Start with a fresh elixir
Docker container.
docker run -it --rm -w /opt -p 4000:4000 -p 4001:4001 elixir bash
2. Generate a simple Phoenix app
apt-get update && apt-get install inotify-tools -y
mix archive.install hex phx_new --force
mix phx.new hello --no-ecto --install
cd hello
3. Generate a self-signed cert needed for SSL
mix phx.gen.cert
4. Configure the endpoint
-
Change the IP to 0.0.0.0 to allow access from outside the container
sed -i 's/{127, 0, 0, 1}/{0, 0, 0, 0}/' config/dev.exs
-
Uncomment the self-cert
https
blocksed -ie '/# \{5\}https/,/# \{5\}],/ { s/^#//; }' config/dev.exs
-
Config Bandit to output client closures
sed -ie '/certfile/,/],/ { s/"$/",\n http_options: [log_client_closures: :verbose]/; s/],/]/; }' config/dev.exs
-
Add a necessary comma after the
watchers
config optionsed -zie '/watchers/,/]\n/ { s/]\n/],\n/ }' config/dev.exs
Endpoint config in config/dev.exs
should look like this:
config :hello, HelloWeb.Endpoint,
http: [ip: {0, 0, 0, 0}, port: 4000],
...
watchers: [
...
],
https: [
port: 4001,
cipher_suite: :strong,
keyfile: "priv/cert/selfsigned_key.pem",
certfile: "priv/cert/selfsigned.pem",
http_options: [log_client_closures: :verbose]
]
5. Configure the controller
-
Add a 1-second
sleep
to the controllerhome
action to simulate a slow server response:sed -ie '/render/ { s/^/ Process.sleep(1000)\n/ }' lib/hello_web/controllers/page_controller.ex
The home
action in lib/hello_web/controllers/page_controller.ex
should now look like this:
def home(conn, _params) do
...
Process.sleep(1000)
render(conn, :home, layout: false)
end
6. Start the server
iex -S mix phx.server
7. Test with the HTTPS
scheme
Visit https://localhost:4001 with your browser. It will complain about the certificates. Bypass and continue.
Now, reload the page repeatedly as fast as you can. After about the 5th or 10th reload, you should start encountering problems. With Firefox, I don’t see any errors in the console, but Firefox does eventually render a “Network protocol error” page. When I try it with Chromium and Gnome Web browsers, I see the following error in the console:
Error output
[info] Sent 500 in 1010ms
[error] ** (Bandit.TransportError) Client reset stream normally
(bandit 1.6.1) lib/bandit/http2/stream.ex:386: Bandit.HTTPTransport.Bandit.HTTP2.Stream.do_recv_rst_stream!/2
(bandit 1.6.1) lib/bandit/adapter.ex:230: Bandit.Adapter.send_data/3
(bandit 1.6.1) lib/bandit/adapter.ex:113: Bandit.Adapter.send_resp/4
(plug 1.16.1) lib/plug/conn.ex:444: Plug.Conn.send_resp/1
(hello 0.1.0) lib/hello_web/controllers/page_controller.ex:1: HelloWeb.PageController.action/2
(hello 0.1.0) lib/hello_web/controllers/page_controller.ex:1: HelloWeb.PageController.phoenix_controller_pipeline/2
(phoenix 1.7.18) lib/phoenix/router.ex:484: Phoenix.Router.__call__/5
(hello 0.1.0) lib/hello_web/endpoint.ex:1: HelloWeb.Endpoint.plug_builder_call/2
(hello 0.1.0) deps/plug/lib/plug/debugger.ex:136: HelloWeb.Endpoint."call (overridable 3)"/2
(hello 0.1.0) lib/hello_web/endpoint.ex:1: HelloWeb.Endpoint.call/2
(phoenix 1.7.18) lib/phoenix/endpoint/sync_code_reload_plug.ex:22: Phoenix.Endpoint.SyncCodeReloadPlug.do_call/4
(bandit 1.6.1) lib/bandit/pipeline.ex:127: Bandit.Pipeline.call_plug!/2
(bandit 1.6.1) lib/bandit/pipeline.ex:36: Bandit.Pipeline.run/4
(bandit 1.6.1) lib/bandit/http2/stream_process.ex:27: Bandit.HTTP2.StreamProcess.handle_continue/2
(stdlib 6.1.2) gen_server.erl:2335: :gen_server.try_handle_continue/3
(stdlib 6.1.2) gen_server.erl:2244: :gen_server.loop/7
(stdlib 6.1.2) proc_lib.erl:329: :proc_lib.init_p_do_apply/3
Error screenshots
The forum won’t let me upload pictures, so I’ve uploaded screenshots of the error pages to Imgur.
8. Test with the HTTP
scheme
Now visit http://localhost:4000.
Reload page repeatedly as fast as you can.
No matter how long or how fast I reload the browser, I get no errors.
Incidentals
Since it’s running in a Docker container, I’m not sure that it matters what OS I’m using, but in case it does, I’m on Debian Bookworm.