| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296 |
- -- The MIT License (MIT)
- --
- -- Copyright (c) 2014 Cyril David <cyx@cyx.is>
- -- Copyright (c) 2011-2013 Bertrand Mansion <bmansion@mamasam.com>
- --
- -- Permission is hereby granted, free of charge, to any person obtaining a copy
- -- of this software and associated documentation files (the "Software"), to deal
- -- in the Software without restriction, including without limitation the rights
- -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- -- copies of the Software, and to permit persons to whom the Software is
- -- furnished to do so, subject to the following conditions:
- --
- -- The above copyright notice and this permission notice shall be included in
- -- all copies or substantial portions of the Software.
- --
- -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- -- THE SOFTWARE.
- -- == list of known and common scheme ports
- --
- -- @see http://www.iana.org/assignments/uri-schemes.html
- --
- local SERVICES = {
- acap = 674,
- cap = 1026,
- dict = 2628,
- ftp = 21,
- gopher = 70,
- http = 80,
- https = 443,
- iax = 4569,
- icap = 1344,
- imap = 143,
- ipp = 631,
- ldap = 389,
- mtqp = 1038,
- mupdate = 3905,
- news = 2009,
- nfs = 2049,
- nntp = 119,
- rtsp = 554,
- sip = 5060,
- snmp = 161,
- telnet = 23,
- tftp = 69,
- vemmi = 575,
- afs = 1483,
- jms = 5673,
- rsync = 873,
- prospero = 191,
- videotex = 516
- }
- local LEGAL = {
- ["-"] = true, ["_"] = true, ["."] = true, ["!"] = true,
- ["~"] = true, ["*"] = true, ["'"] = true, ["("] = true,
- [")"] = true, [":"] = true, ["@"] = true, ["&"] = true,
- ["="] = true, ["+"] = true, ["$"] = true, [","] = true,
- [";"] = true -- can be used for parameters in path
- }
- -- aggressive caching of methods
- local gsub = string.gsub
- local char = string.char
- local byte = string.byte
- local upper = string.upper
- local lower = string.lower
- local format = string.format
- -- forward declaration of helper utilities
- local util = {}
- local function decode(str)
- local str = gsub(str, "+", " ")
- return (gsub(str, "%%(%x%x)", function(c)
- return char(tonumber(c, 16))
- end))
- end
- local function encode(str)
- return (gsub(str, "([^A-Za-z0-9%_%.%-%~])", function(v)
- return upper(format("%%%02x", byte(v)))
- end))
- end
- -- Build a URL given a table with the fields:
- --
- -- - scheme
- -- - user
- -- - password
- -- - host
- -- - port
- -- - path
- -- - query
- -- - fragment
- --
- -- Example:
- --
- -- local url = uri.build({
- -- scheme = "http",
- -- host = "example.com",
- -- path = "/some/path"
- -- })
- --
- -- assert(url == "http://example.com/some/path")
- --
- local function build(uri)
- local url = ""
- if uri.path then
- local path = uri.path
-
- gsub(path, "([^/]+)", function (s) return util.encode_segment(s) end)
- url = url .. tostring(path)
- end
- if uri.query then
- local qstring = tostring(uri.query)
- if qstring ~= "" then
- url = url .. "?" .. qstring
- end
- end
- if uri.host then
- local authority = uri.host
- if uri.port and uri.scheme and SERVICES[uri.scheme] ~= uri.port then
- authority = authority .. ":" .. uri.port
- end
- local userinfo
- if uri.user and uri.user ~= "" then
- userinfo = encode(uri.user)
- if uri.password then
- userinfo = userinfo .. ":" .. encode(uri.password)
- end
- end
- if userinfo and userinfo ~= "" then
- authority = userinfo .. "@" .. authority
- end
- if authority then
- if url ~= "" then
- url = "//" .. authority .. "/" .. gsub(url, "^/+", "")
- else
- url = "//" .. authority
- end
- end
- end
- if uri.scheme then
- url = uri.scheme .. ":" .. url
- end
- if uri.fragment then
- url = url .. "#" .. uri.fragment
- end
- return url
- end
- -- Parse the url into the designated parts.
- --
- -- Depending on the url, the following parts will be available:
- --
- -- - scheme
- -- - userinfo
- -- - user
- -- - password
- -- - authority
- -- - host
- -- - port
- -- - path
- -- - query
- -- - fragment
- --
- -- Usage:
- --
- -- local u = uri.parse("http://john:monkey@example.com/some/path#h1")
- --
- -- assert(u.host == "example.com")
- -- assert(u.scheme == "http")
- -- assert(u.user == "john")
- -- assert(u.password == "monkey")
- -- assert(u.path == "/some/path")
- -- assert(u.fragment == "h1")
- --
- local function parse(url)
- local uri = { query = nil }
- util.set_authority(uri, "")
- local url = tostring(url or "")
- url = gsub(url, "#(.*)$", function(v)
- uri.fragment = v
- return ""
- end)
- url = gsub(url, "^([%w][%w%+%-%.]*)%:", function(v)
- uri.scheme = lower(v)
- return ""
- end)
- url = gsub(url, "%?(.*)", function(v)
- uri.query = v
- return ""
- end)
- url = gsub(url, "^//([^/]*)", function(v)
- util.set_authority(uri, v)
- return ""
- end)
- uri.path = decode(url)
- return uri
- end
- function util.encode_segment(s)
- local function encode_legal(c)
- if LEGAL[c] then
- return c
- end
- return encode(c)
- end
- return gsub(s, "([^a-zA-Z0-9])", encode_legal)
- end
- -- set the authority part of the url
- --
- -- The authority is parsed to find the user, password, port and host if available.
- -- @param authority The string representing the authority
- -- @return a string with what remains after the authority was parsed
- function util.set_authority(uri, authority)
- uri.authority = authority
- uri.port = nil
- uri.host = nil
- uri.userinfo = nil
- uri.user = nil
- uri.password = nil
- authority = gsub(authority, "^([^@]*)@", function(v)
- uri.userinfo = decode(v)
- return ""
- end)
- authority = gsub(authority, "^%[[^%]]+%]", function(v)
- -- ipv6
- uri.host = v
- return ""
- end)
- authority = gsub(authority, ":([^:]*)$", function(v)
- uri.port = tonumber(v)
- return ""
- end)
- if authority ~= "" and not uri.host then
- uri.host = lower(authority)
- end
- if uri.userinfo then
- local userinfo = uri.userinfo
- userinfo = gsub(userinfo, ":([^:]*)$", function(v)
- uri.password = v
- return ""
- end)
- uri.user = userinfo
- end
- return authority
- end
- local uri = {
- build = build,
- parse = parse,
- encode = encode,
- decode = decode
- }
- return uri
|