Phoenix send_download leading to a hangup in the liveview process with ranch listener error
I have a phoenix liveview app, with a button to download images that does a live_redirect to downloads controller.
Downloads controller does the following:
- Download images from S3
- Zip the folder with images
- Send the zipped file to the client
- Redirect back to the live view page
All the above work except I run into the below error with send_download(step 3), due to which I see liveview loading forever.
Note: The zip file is downloaded on the client and able to access it.
Error Details:
[error] Ranch listener Kraftig.Web.Endpoint.HTTP had connection process started with :cowboy_clear:start_link/4 at #PID<0.8142.0> exit with reason: {:function_clause, [{:cowboy_http, :commands, [{:state, #PID<0.782.0>, Kraftig.Web.Endpoint.HTTP, #Port<0.12194>, :ranch_tcp, :undefined, %{env: %{dispatch: [{:_, [], [{:_, [], Phoenix.Endpoint.Cowboy2Handler, {Kraftig.Web.Endpoint, []}}]}]}, stream_handlers: [:cowboy_telemetry_h, :cowboy_stream_h]}, "", %{}, {{127, 0, 0, 1}, 44574}, {{127, 0, 0, 1}, 4003}, :undefined, #Reference<0.1228928527.871104513.128586>, true, 2, {:ps_request_line, 0}, 65535, 1, :done, 1000, [{:stream, 1, {:cowboy_telemetry_h, {:state, {:cowboy_stream_h, {:state, :undefined, Kraftig.Web.Endpoint.HTTP, #PID<0.8143.0>, :undefined, :undefined, :undefined, :undefined, 0, :nofin, "", 0, ...}}, #Function<0.122385210/1 in :cowboy_telemetry_h."-fun.metrics_callback/1-">, :undefined, %{body_length: 0, cert: :undefined, has_body: false, headers: %{"accept" => "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", "accept-encoding" => "gzip, deflate, br", "accept-language" => "en-US,en;q=0.9", "connection" => "keep-alive", "cookie" => "ajs_user_id=%224726%22; ajs_anonymous_id=%22423a010e-8cfd-4aee-841a-e32359799c1e%22; _ga=GA1.1.1099789340.1616566097; _api_server_web_key=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzaWQiOiJmNWE3ZGVkMTg2OGEwMDRhYmZhZGY0NTZjYWQ5OGIzMCIsInVpZCI6bnVsbCwiYXVkIjoiSm9rZW4iLCJleHAiOjE2MTkxNTgzMTgsImlhdCI6MTYxNjU2NjMxOCwiaXNzIjoiSm9rZW4iLCJqdGkiOiIycG5pa3UxNjZ0cTVnOXRjaDAwMDBicDEiLCJuYmYiOjE2MTY1NjYzMTh9.MCdXHBj0Fg9iJL76Xdw0SU1nqwGio8RZ1BkylGavwjU; token=eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJlbXBvcm9zX3dlYiIsImNpZCI6NjQ3OTk2LCJleHAiOjE2MTkxMjQwOTUsImlhdCI6MTYxNjcwNDg5NSwiaXNzIjoiZW1wb3Jvc193ZWIiLCJqdGkiOiIzZTllMjhkNS1iNDQzLTRjOWYtYTRiMC1kNjI3YzgxZmU3ZTYiLCJuYmYiOjE2MTY3MDQ4OTQsInNzaWQiOjIsInN1YiI6Imd1ZXN0LTY0Nzk5Ni01MTcyNDQtMiIsInR5cCI6ImFjY2VzcyIsIndpZCI6NTE3MjQ0fQ.5dHm-3aKDuvoSEhoEFMyZkC2osYOaMVqFIMvE3waaIilcIzb4wHza7QlGtwMr2r90KWQ09oaicpwv3WzdsRg0Q; aptdecolocalfrontend=QlGtwMr2r90KWQ09oaicpwv3WzdsRg0Q; _kraftig_key=SFMyNTY.g3QAAAABbQAAAAtfY3NyZl90b2tlbm0AAAAYdG5PWGNrM3ZWc013VVFYc2UtTFZVQTdx.WaDJSNTP-TvpdgPqkYQEi_oo7rz7LRL1AZ6tlTuSZjc; _tweeter_key=SFMyNTY.g3QAAAABbQAAAAtfY3NyZl90b2tlbm0AAAAYSzFFQ3Q5UDdQWUoydVdkd25LbHR6bFpv.spZMg2JznqxGxKwvksUDFcON5DuA_wly2iPgtj1sUK4", "host" => "localhost:4003", "referer" => "http://localhost:4003/listing/19131", ...}, host: "localhost", method: "GET", path: "/listing/19131/download_images", peer: {{127, 0, ...}, 44574}, pid: #PID<0.8142.0>, port: 4003, qs: "", ...}, "302 Found", %{"cache-control" => "max-age=0, private, must-revalidate", "content-length" => "137", "content-type" => "text/html; charset=utf-8", "cross-origin-window-policy" => "deny", "date" => "Mon, 29 Mar 2021 16:52:01 GMT", "location" => "/listing/19131", "server" => "Cowboy", "set-cookie" => ["_kraftig_key=SFMyNTY.g3QAAAACbQAAAAtfY3NyZl90b2tlbm0AAAAYdG5PWGNrM3ZWc013VVFYc2UtTFZVQTdxbQAAAA1waG9lbml4X2ZsYXNodAAAAAFtAAAABGluZm9tAAAAE0Rvd25sb2FkIFN1Y2Nlc3NmdWw.XLcwz6Fy06g_w6wa8N_SyNiS72RqcNQk_pIP31PS3dw; path=/; HttpOnly"], "x-content-type-options" => "nosniff", ...}, Kraftig.Web.Endpoint.HTTP, -576427701839574753, :undefined, :undefined, :undefined, -576427698421276373, -576427698421276373, %{#PID<0.8143.0> => %{...}}, [], ...}}, "GET", :"HTTP/1.1", :undefined, :undefined, 0, []}], [{:child, #PID<0.8143.0>, 1, 5000, :undefined}]}, 1, [{:response, "302 Found", %{"cache-control" => "max-age=0, private, must-revalidate", "content-length" => "137", "content-type" => "text/html; charset=utf-8", "cross-origin-window-policy" => "deny", "date" => "Mon, 29 Mar 2021 16:52:01 GMT", "location" => "/listing/19131", "server" => "Cowboy", "set-cookie" => ["_kraftig_key=SFMyNTY.g3QAAAACbQAAAAtfY3NyZl90b2tlbm0AAAAYdG5PWGNrM3ZWc013VVFYc2UtTFZVQTdxbQAAAA1waG9lbml4X2ZsYXNodAAAAAFtAAAABGluZm9tAAAAE0Rvd25sb2FkIFN1Y2Nlc3NmdWw.XLcwz6Fy06g_w6wa8N_SyNiS72RqcNQk_pIP31PS3dw; path=/; HttpOnly"], "x-content-type-options" => "nosniff", "x-download-options" => "noopen", "x-frame-options" => "SAMEORIGIN", "x-permitted-cross-domain-policies" => "none", "x-request-id" => "FnDGSGZN4sso5A4AAHCC", "x-xss-protection" => "1; mode=block"}, ["<html><body>You are being <a href=\"/listing/19131\">redirected</a>.", "<iframe hidden src=\"/phoenix/live_reload/frame\"></iframe>", "</body>", "</html>"]}]], [file: '/workspace/deps/cowboy/src/cowboy_http.erl', line: 954]}, {:cowboy_http, :loop, 1, [file: '/workspace/deps/cowboy/src/cowboy_http.erl', line: 254]}, {:proc_lib, :init_p_do_apply, 3, [file: 'proc_lib.erl', line: 226]}]}
Code:
Phoenix Liveview:
<%= live_redirect "Download Image(s)",
to: Routes.listing_path(@socket, :download_images, @product.id),
%>
Phoneix Downloads Controller:
defmodule DownloadsController do
use App.Web, :controller
@temp_downloads_dir "temp_downloads"
@product_images_dir "#{@temp_downloads_dir}/product-images"
def download_images(conn, %{"id" => product_id} = _params) do
File.mkdir_p!(@product_images_dir)
product_id
|> list_images_by_id()
|> download_images_from_s3()
files = @product_images_dir |> File.ls!() |> Enum.map(&String.to_charlist/1)
conn
|> handle_client_download(files, &handle_cleanup/0)
|> redirect(to: Routes.listing_index_path(conn, :index, product_id))
end
def handle_cleanup, do: File.rm_rf!(@temp_downloads_dir)
def handle_client_download(conn, files, cleanup) when is_list(files) and length(files) > 0 do
zip_file_path = "#{@temp_downloads_dir}/product-images.gz"
:zip.create(zip_file_path, files, cwd: @product_images_dir)
send_download(conn, {:file, zip_file_path}, content_type: "application/gzip")
cleanup.()
put_flash(conn, :info, "Download Successful")
end
def handle_client_download(conn, _files, cleanup) do
cleanup.()
put_flash(conn, :error, "Download failed. Try later or contact dev team if issue persists.")
end
def download_images_from_s3(images) when is_list(images) and length(images) > 0 do
images
|> Task.async_stream(
fn image ->
image
|> get_s3_download_path()
|> download_from_s3(image.id)
end,
max_concurrency: 5,
timeout: 10_000
)
|> Enum.to_list()
end
def download_images_from_s3(_, _) do
{:error, :s3_downloads_failure}
end
defp get_s3_download_path(%{s3_path: s3_path}), do: "product-images/#{s3_path}/test.jpg"
defp get_s3_download_path(%{path: path}), do: path
def download_from_s3(image_path, id) do
bucket = Application.get_env(:ex_aws, :s3)[:bucket]
bucket
|> ExAws.S3.download_file(image_path, "#{@product_images_dir}/img#{id}.jpg")
|> ExAws.request!()
end
end
Appreciate if anyone can point me in the right direction to resolve the issue.