defmodule Frenzy.Network do require Logger defmodule HTTP do use Tesla adapter(Tesla.Adapter.Mint) plug Tesla.Middleware.Logger, log_level: &log_level/1 plug Tesla.Middleware.FollowRedirects plug Tesla.Middleware.Compression # can't use JSON middleware currently, because feed_parser expects to parse the raw body data itself # plug Tesla.Middleware.JSON plug Tesla.Middleware.Timeout, timeout: 10_000 def log_level(env) do case env.status do code when code >= 400 -> :warn _ -> :debug end end end @spec http_get(String.t()) :: Tesla.Env.result() def http_get(url) do HTTP.get(url) end @spec http_post(String.t(), Tesla.Env.body(), [Tesla.option()]) :: Tesla.Env.result() def http_post(url, body, options \\ []) do HTTP.post(url, body, options) end # @http_redirect_codes [301, 302] # @spec http_get(String.t()) :: {:ok, HTTPoison.Response.t()} | {:error, term()} # def http_get(url) do # case HTTPoison.get(url) do # {:ok, %HTTPoison.Response{status_code: 200} = response} -> # {:ok, response} # {:ok, %HTTPoison.Response{status_code: status_code, headers: headers}} # when status_code in @http_redirect_codes -> # headers # |> Enum.find(fn {name, _value} -> String.downcase(name) == "location" end) # |> case do # {_, new_url} -> # new_url = # case URI.parse(new_url) do # %URI{host: nil, path: path} -> # # relative path # %URI{URI.parse(url) | path: path} |> URI.to_string() # uri -> # uri # end # Logger.debug("Got 301 redirect from #{url} to #{new_url}") # http_get(new_url) # _ -> # {: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, error} -> # {:error, error} # end # end @gemini_success_codes 20..29 @gemini_redirect_codes 30..39 @spec gemini_request(String.t() | URI.t()) :: {:ok, Gemini.Response.t()} | {:error, term()} def gemini_request(uri) do case Gemini.request(uri) do {:ok, %Gemini.Response{status: code} = response} when code in @gemini_success_codes -> {:ok, response} {:ok, %Gemini.Response{status: code, meta: new_url}} when code in @gemini_redirect_codes -> gemini_request(URI.merge(uri, new_url)) {:ok, %Gemini.Response{status: code}} -> {:error, "Unhandled Gemini status code: #{code}"} {:error, reason} -> {:error, reason} end end end