Current section
Files
Jump to
Current section
Files
lib/normalize_url.ex
defmodule NormalizeUrl do
@moduledoc """
The base module of NormalizeUrl.
It exposes a single function, normalize_url.
"""
@doc """
Normalizes a url. This is useful for displaying, storing, comparing, etc.
Args:
* `url` - the url to normalize, string
Options:
* `strip_www` - strip the www from the url, boolean
* `strip_fragment` - strip the fragment from the url, boolean
* `normalize_protocol` - prepend `http:` if the url is protocol relative, boolean
Returns a url as a string.
"""
def normalize_url(url, options \\ []) do
scheme = filter_scheme(url)
if (scheme == "http") || (scheme == "ftp") || (scheme == nil) do
normalize_http_or_ftp_url(url, options)
else
url
end
end
# Extracts the scheme from a URL, returning nil if it's not an accepted scheme.
# Needed because URI.parse("example.com:4000") will return "example.com" as the scheme.
defp filter_scheme(url) do
%{scheme: scheme} = URI.parse(url)
if Enum.any?(accepted_schemes(), &(&1 == scheme)), do: scheme, else: nil
end
defp accepted_schemes do
iana_schemes() ++ ["javascript"]
end
defp normalize_http_or_ftp_url(url, options) do
options = Keyword.merge([
normalize_protocol: true,
strip_www: true,
strip_fragment: true,
add_root_path: false
], options)
url = if Regex.match?(~r/^\/\//, url), do: "http:" <> url, else: url
if options[:normalize_protocol] do
scheme = if Regex.match?(~r/^ftp:\/\//, url), do: "ftp://", else: "http://"
else
scheme = "//"
end
if options[:normalize_protocol] && !Regex.match?(~r/^(http|ftp:\/\/)/, url) do
url = "http://" <> url
end
uri = if options[:downcase] do
URI.parse(String.downcase(url))
else
URI.parse(url)
end
port = if options[:normalize_protocol] && uri.port, do: ":" <> Integer.to_string(uri.port), else: ""
if uri.port == 8080 || uri.port == 443 do
port = ""
scheme = "https://"
end
if uri.port == 21 && scheme == "ftp://" do
port = ""
end
if options[:normalize_protocol] && uri.port == 80 do
port = ""
scheme = "http://"
end
path = uri.path
if options[:add_root_path] && is_nil(path) do
path = "/"
end
host_and_path = if path, do: uri.host <> port <> path, else: uri.host <> port
if !options[:strip_fragment] && uri.fragment do
host_and_path = host_and_path <> "#" <> uri.fragment
end
if options[:strip_www] do
host_and_path = Regex.replace(~r/^www\./, host_and_path, "")
end
scheme <> host_and_path <> query_params(uri.query)
end
defp query_params(nil), do: ""
defp query_params(query), do: "?" <> (query |> URI.decode_query |> URI.encode_query)
# Taken from https://www.iana.org/assignments/uri-schemes/uri-schemes.txt
defp iana_schemes do
"""
aaa
aaas
about
acap
acct
acr
adiumxtra
afp
afs
aim
appdata
apt
attachment
aw
barion
beshare
bitcoin
blob
bolo
browserext
callto
cap
chrome
chrome-extension
cid
coap
coap+tcp
coaps
coaps+tcp
com-eventbrite-attendee
content
crid
cvs
data
dav
dict
dis
dlna-playcontainer
dlna-playsingle
dns
dntp
dtn
dvb
ed2k
example
facetime
fax
feed
feedready
file
filesystem
finger
fish
ftp
geo
gg
git
gizmoproject
go
gopher
graph
gtalk
h323
ham
hcp
http
https
hxxp
hxxps
hydrazone
iax
icap
icon
im
imap
info
iotdisco
ipn
ipp
ipps
irc
irc6
ircs
iris
iris.beep
iris.lwz
iris.xpc
iris.xpcs
isostore
itms
jabber
jar
jms
keyparc
lastfm
ldap
ldaps
lvlt
magnet
mailserver
mailto
maps
market
message
mid
mms
modem
mongodb
moz
ms-access
ms-browser-extension
ms-drive-to
ms-enrollment
ms-excel
ms-gamebarservices
ms-getoffice
ms-help
ms-infopath
ms-inputapp
ms-media-stream-id
ms-officeapp
ms-people
ms-project
ms-powerpoint
ms-publisher
ms-search-repair
ms-secondary-screen-contr
ms-secondary-screen-setup
ms-settings
ms-settings-airplanemode
ms-settings-bluetooth
ms-settings-camera
ms-settings-cellular
ms-settings-cloudstorage
ms-settings-connectablede
ms-settings-displays-topo
ms-settings-emailandaccou
ms-settings-language
ms-settings-location
ms-settings-lock
ms-settings-nfctransactio
ms-settings-notifications
ms-settings-power
ms-settings-privacy
ms-settings-proximity
ms-settings-screenrotatio
ms-settings-wifi
ms-settings-workplace
ms-spd
ms-sttoverlay
ms-transit-to
ms-virtualtouchpad
ms-visio
ms-walk-to
ms-whiteboard
ms-whiteboard-cmd
ms-word
msnim
msrp
msrps
mtqp
mumble
mupdate
mvn
news
nfs
ni
nih
nntp
notes
ocf
oid
onenote
onenote-cmd
opaquelocktoken
pack
palm
paparazzi
pkcs11
platform
pop
pres
prospero
proxy
pwid
psyc
qb
query
redis
rediss
reload
res
resource
rmi
rsync
rtmfp
rtmp
rtsp
rtsps
rtspu
secondlife
service
session
sftp
sgn
shttp
sieve
sip
sips
skype
smb
sms
smtp
snews
snmp
soap.beep
soap.beeps
soldat
spotify
ssh
steam
stun
stuns
submit
svn
tag
teamspeak
tel
teliaeid
telnet
tftp
things
thismessage
tip
tn3270
tool
turn
turns
tv
udp
unreal
urn
ut2004
v-event
vemmi
ventrilo
videotex
vnc
view-source
wais
webcal
wpid
ws
wss
wtai
wyciwyg
xcon
xcon-userid
xfire
xmlrpc.beep
xmlrpc.beeps
xmpp
xri
ymsgr
z39.50
z39.50r
z39.50s
""" |> String.split(~r/\n/, trim: true)
end
end