mirror of
https://github.com/fly-apps/live_beats.git
synced 2024-11-25 09:20:59 +00:00
96 lines
2.8 KiB
Elixir
96 lines
2.8 KiB
Elixir
defmodule LiveBeatsWeb.FileController do
|
|
@moduledoc """
|
|
Serves files based on short-term token grants.
|
|
"""
|
|
use LiveBeatsWeb, :controller
|
|
|
|
alias LiveBeats.MediaLibrary
|
|
|
|
require Logger
|
|
|
|
def show(conn, %{"id" => filename_uuid, "token" => token}) do
|
|
path = MediaLibrary.local_filepath(filename_uuid)
|
|
mime_type = MIME.from_path(path)
|
|
|
|
case Phoenix.Token.decrypt(conn, "file", token, max_age: :timer.minutes(1)) do
|
|
{:ok, %{vsn: 1, uuid: ^filename_uuid, ip: ip, size: size}} ->
|
|
if local_file?(filename_uuid, ip) do
|
|
Logger.info("serving file from #{server_ip()}")
|
|
do_send_file(conn, path)
|
|
else
|
|
Logger.info("proxying file to #{ip} from #{server_ip()}")
|
|
proxy_file(conn, ip, mime_type, size)
|
|
end
|
|
|
|
{:ok, _} ->
|
|
send_resp(conn, :unauthorized, "")
|
|
|
|
{:error, _} ->
|
|
send_resp(conn, :unauthorized, "")
|
|
end
|
|
end
|
|
|
|
defp do_send_file(conn, path) do
|
|
# accept-ranges headers required for chrome to seek via currentTime
|
|
conn
|
|
|> put_resp_header("content-type", MIME.from_path(path))
|
|
|> put_resp_header("accept-ranges", "bytes")
|
|
|> send_file(200, path)
|
|
end
|
|
|
|
defp proxy_file(conn, ip, mime_type, content_length) do
|
|
uri = conn |> request_url() |> URI.parse()
|
|
port = LiveBeatsWeb.Endpoint.config(:http)[:port]
|
|
path = uri.path <> "?" <> uri.query <> "&from=#{server_ip()}"
|
|
{:ok, ipv6} = :inet.parse_address(String.to_charlist(ip))
|
|
{:ok, req} = Mint.HTTP.connect(:http, ipv6, port, file_server_opts())
|
|
{:ok, req, request_ref} = Mint.HTTP.request(req, "GET", path, [], "")
|
|
|
|
conn
|
|
|> put_resp_header("content-type", mime_type)
|
|
|> put_resp_header("accept-ranges", "bytes")
|
|
|> put_resp_header("content-length", to_string(content_length))
|
|
|> send_chunked(200)
|
|
|> stream(req, request_ref)
|
|
end
|
|
|
|
defp stream(conn, req, ref) do
|
|
receive do
|
|
{:tcp, _, _} = msg ->
|
|
{:ok, req, responses} = Mint.HTTP.stream(req, msg)
|
|
|
|
new_conn =
|
|
Enum.reduce(responses, conn, fn
|
|
{:data, ^ref, data}, acc -> chunk!(acc, data)
|
|
{:done, ^ref}, acc -> halt(acc)
|
|
{:status, ^ref, 200}, acc -> acc
|
|
{:headers, ^ref, _}, acc -> acc
|
|
end)
|
|
|
|
if new_conn.halted do
|
|
new_conn
|
|
else
|
|
stream(new_conn, req, ref)
|
|
end
|
|
end
|
|
end
|
|
|
|
defp chunk!(conn, data) do
|
|
{:ok, conn} = chunk(conn, data)
|
|
conn
|
|
end
|
|
|
|
defp local_file?(_filename_uuid, ip) do
|
|
# TODO cache locally
|
|
ip == server_ip()
|
|
end
|
|
|
|
defp server_ip, do: LiveBeats.config([:files, :server_ip])
|
|
|
|
defp file_server_opts do
|
|
[
|
|
hostname: LiveBeats.config([:files, :hostname]) || "localhost",
|
|
transport_opts: LiveBeats.config([:files, :transport_opts]) || []
|
|
]
|
|
end
|
|
end
|