defmodule GitGudWeb.Plugs.GitRouter do
@moduledoc """
Early-pipeline matcher that diverts `/:owner/:name.git/...` requests to
`GitGudWeb.Plugs.GitSmartHttp`, bypassing Plug.Parsers (which would
consume the body the CGI needs to read).
"""
@behaviour Plug
import Plug.Conn
alias GitGudWeb.Plugs.GitSmartHttp
@impl true
def init(opts), do: opts
@impl true
def call(%Plug.Conn{path_info: path_info} = conn, _opts) do
if git_request?(path_info) do
conn
|> fetch_query_params()
|> GitSmartHttp.call(GitSmartHttp.init([]))
|> halt()
else
conn
end
end
# Divert git smart-HTTP (and LFS) requests to GitSmartHttp. Accept both
# the `/:owner/:name.git/...` form and the bare `/:owner/:name/...`
# form — `git clone <url>` and `actions/checkout` clone without a
# `.git` suffix, so we key off the git service sub-path too, not just
# the suffix. Bare `/:owner/:name/{info/refs,git-*,info/lfs}` doesn't
# collide with any web route (those live under /r, /orgs, /users, …).
defp git_request?([_owner, name | rest]) when is_binary(name) do
String.ends_with?(name, ".git") or git_service_path?(rest)
end
defp git_request?(_), do: false
defp git_service_path?(["info", "refs" | _]), do: true
defp git_service_path?(["git-upload-pack" | _]), do: true
defp git_service_path?(["git-receive-pack" | _]), do: true
defp git_service_path?(["info", "lfs" | _]), do: true
defp git_service_path?(_), do: false
end
1.5 KiB · text
5af55d9