mtrudel/bandit

HTTP2 Internal Server Error in send_file

Closed this issue · 2 comments

I was developing with a local https server and attempting to serve video out of a /priv/static/videos directory. Playback was handled through the <video> element. Upon trying to start the video the server gave a 500 error. I was able to reproduce this in a fresh project. When originally trying to reproduce it I was using an http endpoint as I created a new phoenix application. The video played back fine.

It looks to me like an off by 1 error somewhere given that in the error message the length + offset == total file length. I found what I suspect to be the likely condition here but I do not think that is entirely correct because in my testing the server was throwing different errors after that change.

Notably a project using cowboy setup exactly the same does not throw any errors and the video plays fine.

To reproduce the issue

  1. create a fresh phoenix project
  2. ensure that you are using https to enable http2
  3. ensure that the server is serving from wherever you place your video file
  4. make a video tag that loads said file

I think the file should be "large" but I am not 100% sure. Probably big enough to force the browser to use range requests instead of loading the whole thing at once is enough.

[error] Task #PID<0.3400.0> started from #PID<0.3358.0> terminating
** (RuntimeError) Cannot read 3377 bytes starting at 1293549568 as /home/kyle/repos/bild/_build/dev/lib/bild/priv/static/videos/wall-e.mp4 is only 1293552945 octets in length
    (bandit 1.1.1) lib/bandit/http2/adapter.ex:206: Bandit.HTTP2.Adapter.send_file/6
    (plug 1.15.2) lib/plug/conn.ex:495: Plug.Conn.send_file/5
    (plug 1.15.2) lib/plug/static.ex:301: Plug.Static.send_range/6
    (bild 0.1.0) lib/bild_web/endpoint.ex:1: BildWeb.Endpoint.plug_builder_call/2
    (bild 0.1.0) deps/plug/lib/plug/debugger.ex:136: BildWeb.Endpoint."call (overridable 3)"/2
    (bild 0.1.0) lib/bild_web/endpoint.ex:1: BildWeb.Endpoint.call/2
    (phoenix 1.7.10) lib/phoenix/endpoint/sync_code_reload_plug.ex:22: Phoenix.Endpoint.SyncCodeReloadPlug.do_call/4
    (bandit 1.1.1) lib/bandit/pipeline.ex:101: Bandit.Pipeline.call_plug/2
    (bandit 1.1.1) lib/bandit/pipeline.ex:22: Bandit.Pipeline.run/6
    (bandit 1.1.1) lib/bandit/http2/stream_task.ex:69: Bandit.HTTP2.StreamTask.run/5
    (elixir 1.15.7) lib/task/supervised.ex:101: Task.Supervised.invoke_mfa/2
Function: &Bandit.HTTP2.StreamTask.run/5
    Args: [%Bandit.HTTP2.Adapter{connection: #PID<0.3358.0>, transport_info: %Bandit.TransportInfo{secure?: true, sockname: {{127, 0, 0, 1}, 4001}, peername: {{127, 0, 0, 1}, 37440}, peercert: nil}, stream_id: 9, end_stream: false, method: nil, content_encoding: nil, metrics: %{}, opts: []}, %Bandit.TransportInfo{secure?: true, sockname: {{127, 0, 0, 1}, 4001}, peername: {{127, 0, 0, 1}, 37440}, peercert: nil}, [{":method", "GET"}, {":authority", "localhost:4001"}, {":scheme", "https"}, {":path", "/videos/wall-e.mp4"}, {"sec-ch-ua", "\"Chromium\";v=\"119\", \"Not?A_Brand\";v=\"24\""}, {"accept-encoding", "identity;q=1, *;q=0"}, {"sec-ch-ua-mobile", "?0"}, {"user-agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36"}, {"sec-ch-ua-platform", "\"Linux\""}, {"accept", "*/*"}, {"sec-fetch-site", "same-origin"}, {"sec-fetch-mode", "no-cors"}, {"sec-fetch-dest", "video"}, {"referer", "https://localhost:4001/home"}, {"accept-language", "en-US,en;q=0.9"}, {"cookie", "_bild_web_user_remember_me=SFMyNTY.g2gDbQAAACC7E86Vhco3g5XPq1bovnrNcJG8gu3cvV9Vz9-8XDN4L24GAN-xYxOMAWIATxoA.5VR8XP17tAVGL7h8DapHVpgmvo3-R5uC0vgHMtQOwdo"}, {"cookie", "request_logger=SFMyNTY.g2gDbQAAAAQwVkt4bgYAln0XFIwBYgABUYA.nNaQ_L0LOr2UrlSo0EJOygz9hqzfE_i2uC2u1xcRX14"}, {"cookie", "_bild_key=SFMyNTY.g3QAAAADbQAAAAtfY3NyZl90b2tlbm0AAAAYdFF1Q2hDbGJsMHVyRENQMTdvT09nbHdHbQAAAA5saXZlX3NvY2tldF9pZG0AAAA7dXNlcnNfc2Vzc2lvbnM6WDlHZHY3My1uZUJHNkF2VUljTTVhZkRXbWtYRVgtQWVyd25Na0FVaVJ4UT1tAAAACnVzZXJfdG9rZW5tAAAAIF_Rnb-9_p3gRugL1CHDOWnw1ppFxF_gHq8JzJAFIkcU.r3ZnjEVuzrnAcOCG1pjmYOQJYNephUk9pzQ7KcgI5hw"}, {"dnt", "1"}, {"sec-gpc", "1"}, {"range", "bytes=1293549568-"}], {Phoenix.Endpoint.SyncCodeReloadPlug, {BildWeb.Endpoint, []}}, %Bandit.Telemetry{span_name: :request, telemetry_span_context: #Reference<0.1154210362.590086146.209286>, start_time: -576460164637618959, start_metadata: %{telemetry_span_context: #Reference<0.1154210362.590086146.209286>, connection_telemetry_span_context: #Reference<0.1154210362.590348289.24513>, stream_id: 9}}]

Sample home.html.heex

<.flash_group flash={@flash} />
<div class="width-screen height-screen">
  <video class="max-h-[500px]" controls>
    <source src={~p"/videos/ride-of-the-valkyries.webm"} />
  </video>

  <.link href="https://creativecommons.org/licenses/by/3.0/">License</.link>
  <.link href="https://www.youtube.com/watch?v=uNkRW_9pHRQ">Source</.link>
</div>

The exact video used is too large to upload here but it is about 130mb.

Thanks for the bug report! Your repro steps made this easy to track down.

Fixed in 9b55f64

Release likely coming later today once I get a few more fixes in.