Packages

A library that fetches the favicon url for a specified url

Current section

Files

Jump to
favicon lib favicon.ex
Raw

lib/favicon.ex

defmodule Favicon do
def fetch(url) do
HTTPoison.start
hackney = [follow_redirect: true]
uri = URI.parse(url)
domain = "#{uri.scheme}://#{uri.host}"
case HTTPoison.get(domain, [], [hackney: hackney]) do
{:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
find_favicon_url(domain, body)
{:ok, %HTTPoison.Response{status_code: 404}} ->
{:error, "404 not found"}
{:error, %HTTPoison.Error{reason: reason}} ->
{:error, reason}
end
end
defp find_favicon_url(domain, body) do
if tag = find_favicon_link_tag(body) do
{"link", attrs, _} = tag
{"href", path} = Enum.find(attrs, fn({name, _}) ->
name == "href"
end)
{:ok, format_url(domain, path)}
else
find_favicon_in_root(domain)
end
end
defp format_url(domain, path) do
uri_domain_host = URI.parse(domain).host
uri_path_host = URI.parse(path).host
cond do
uri_domain_host && uri_path_host && uri_domain_host != uri_path_host -> # favicon is on another domain
"#{path}"
String.contains?(path, uri_domain_host) -> # the favicon is an absolute path (within the same domain)
"#{path}"
String.starts_with?(path, "/") -> # relative path starting with /
"#{domain}#{path}"
true ->
"#{domain}/#{path}"
end
end
defp find_favicon_link_tag(body) do
links = Floki.find(body, "link")
Enum.find(links, fn({"link", attrs, _}) ->
Enum.any?(attrs, fn({name, value}) ->
name == "rel" && String.contains?(value, "icon") && !String.contains?(value, "-icon-")
end)
end)
end
defp find_favicon_in_root(domain) do
favicon_url = "#{domain}/favicon.ico"
case HTTPoison.head(favicon_url) do
{:ok, %HTTPoison.Response{status_code: 200}} ->
{:ok, favicon_url}
{:ok, %HTTPoison.Response{status_code: 404}} ->
{:error, :favicon_missing}
{:error, %HTTPoison.Error{reason: reason}} ->
{:error, reason}
end
end
end