defmodule Clacks.HTTP do require Logger @spec get(url :: String.t(), headers :: HTTPoison.headers()) :: {:ok, HTTPoison.Response.t()} | {:error, String.t()} def get(url, headers \\ []) do fetch(:get, url, headers) end @spec head(url :: String.t(), headers :: HTTPoison.headers()) :: {:ok, HTTPoison.Response.t()} | {:error, String.t()} def head(url, headers \\ []) do fetch(:head, url, headers) end defp fetch(method, url, headers) do opts = [hackney: Application.get_env(:clacks, :hackney_opts, [])] case HTTPoison.request(method, url, "", headers, opts) do {:ok, %HTTPoison.Response{status_code: status_code} = response} when status_code in 200..299 -> {:ok, response} {:ok, %HTTPoison.Response{status_code: status_code, headers: resp_headers}} when status_code in [301, 302] -> resp_headers |> Enum.find(fn {name, _value} -> String.downcase(name) == "location" end) |> case do {_, new_url} -> new_url = URI.merge(URI.parse(url), URI.parse(new_url)) |> URI.to_string() Logger.debug("Got 301 redirect from #{url} to #{new_url}") fetch(method, new_url, headers) _ -> {:error, "Missing Location header for redirect"} end {:ok, %HTTPoison.Response{status_code: 403}} -> {:error, "403 Forbidden"} {:ok, %HTTPoison.Response{status_code: 404}} -> {:error, "404 Not Found"} {:ok, %HTTPoison.Response{status_code: status_code}} -> {:error, "HTTP #{status_code}"} {:error, %HTTPoison.Error{reason: reason}} -> {:error, inspect(reason)} end end @spec post(String.t(), any(), HTTPoison.headers()) :: {:ok, HTTPoison.Response.t()} | {:error, HTTPoison.Error.t()} def post(url, body, headers \\ []) do HTTPoison.post(url, body, headers) end end