util-soc-imp-url.lua /size: 6863 b    last modification: 2020-07-01 14:35
1-- original file : url.lua
2-- for more into : see util-soc.lua
3
4local tonumber, tostring, type = tonumber, tostring, type
5
6local gsub, sub, match, find, format, byte, char = string.gsub, string.sub, string.match, string.find, string.format, string.byte, string.char
7local insert = table.insert
8
9local socket = socket or require("socket")
10
11local url = {
12    _VERSION = "URL 1.0.3",
13}
14
15socket.url = url
16
17function url.escape(s)
18    return (gsub(s, "([^A-Za-z0-9_])", function(c)
19        return format("%%%02x", byte(c))
20    end))
21end
22
23local function make_set(t) -- table.tohash
24    local s = { }
25    for i=1,#t do
26        s[t[i]] = true
27    end
28    return s
29end
30
31local segment_set = make_set {
32    "-", "_", ".", "!", "~", "*", "'", "(",
33    ")", ":", "@", "&", "=", "+", "$", ",",
34}
35
36local function protect_segment(s)
37    return gsub(s, "([^A-Za-z0-9_])", function(c)
38        if segment_set[c] then
39            return c
40        else
41            return format("%%%02X", byte(c))
42        end
43    end)
44end
45
46function url.unescape(s)
47    return (gsub(s, "%%(%x%x)", function(hex)
48        return char(tonumber(hex,16))
49    end))
50end
51
52local function absolute_path(base_path, relative_path)
53    if find(relative_path,"^/") then
54        return relative_path
55    end
56    local path = gsub(base_path, "[^/]*$", "")
57    path = path .. relative_path
58    path = gsub(path, "([^/]*%./)", function (s)
59        if s ~= "./" then
60            return s
61        else
62            return ""
63        end
64    end)
65    path = gsub(path, "/%.$", "/")
66    local reduced
67    while reduced ~= path do
68        reduced = path
69        path = gsub(reduced, "([^/]*/%.%./)", function (s)
70            if s ~= "../../" then
71                return ""
72            else
73                return s
74            end
75        end)
76    end
77    path = gsub(reduced, "([^/]*/%.%.)$", function (s)
78        if s ~= "../.." then
79            return ""
80        else
81            return s
82        end
83    end)
84    return path
85end
86
87function url.parse(url, default)
88    local parsed = { }
89    for k, v in next, default or parsed do
90        parsed[k] = v
91    end
92    if not url or url == "" then
93        return nil, "invalid url"
94    end
95    url = gsub(url, "#(.*)$", function(f)
96        parsed.fragment = f
97        return ""
98    end)
99    url = gsub(url, "^([%w][%w%+%-%.]*)%:", function(s)
100        parsed.scheme = s
101        return ""
102    end)
103    url = gsub(url, "^//([^/]*)", function(n)
104        parsed.authority = n
105        return ""
106    end)
107    url = gsub(url, "%?(.*)", function(q)
108        parsed.query = q
109        return ""
110    end)
111    url = gsub(url, "%;(.*)", function(p)
112        parsed.params = p
113        return ""
114    end)
115    if url ~= "" then
116        parsed.path = url
117    end
118    local authority = parsed.authority
119    if not authority then
120        return parsed
121    end
122    authority = gsub(authority,"^([^@]*)@", function(u)
123        parsed.userinfo = u
124        return ""
125    end)
126    authority = gsub(authority, ":([^:%]]*)$", function(p)
127        parsed.port = p
128        return ""
129    end)
130    if authority ~= "" then
131        parsed.host = match(authority, "^%[(.+)%]$") or authority
132    end
133    local userinfo = parsed.userinfo
134    if not userinfo then
135        return parsed
136    end
137    userinfo = gsub(userinfo, ":([^:]*)$", function(p)
138        parsed.password = p
139        return ""
140    end)
141    parsed.user = userinfo
142    return parsed
143end
144
145function url.build(parsed)
146    local url = parsed.path or ""
147    if parsed.params then
148        url = url .. ";" .. parsed.params
149    end
150    if parsed.query then
151        url = url .. "?" .. parsed.query
152    end
153    local authority = parsed.authority
154    if parsed.host then
155        authority = parsed.host
156        if find(authority, ":") then -- IPv6?
157            authority = "[" .. authority .. "]"
158        end
159        if parsed.port then
160            authority = authority .. ":" .. tostring(parsed.port)
161        end
162        local userinfo = parsed.userinfo
163        if parsed.user then
164            userinfo = parsed.user
165            if parsed.password then
166                userinfo = userinfo .. ":" .. parsed.password
167            end
168        end
169        if userinfo then authority = userinfo .. "@" .. authority end
170    end
171    if authority then
172        url = "//" .. authority .. url
173    end
174    if parsed.scheme then
175        url = parsed.scheme .. ":" .. url
176    end
177    if parsed.fragment then
178        url = url .. "#" .. parsed.fragment
179    end
180    return url
181end
182
183function url.absolute(base_url, relative_url)
184    local base_parsed
185    if type(base_url) == "table" then
186        base_parsed = base_url
187        base_url = url.build(base_parsed)
188    else
189        base_parsed = url.parse(base_url)
190    end
191    local relative_parsed = url.parse(relative_url)
192    if not base_parsed then
193        return relative_url
194    elseif not relative_parsed then
195        return base_url
196    elseif relative_parsed.scheme then
197        return relative_url
198    else
199        relative_parsed.scheme = base_parsed.scheme
200        if not relative_parsed.authority then
201            relative_parsed.authority = base_parsed.authority
202            if not relative_parsed.path then
203                relative_parsed.path = base_parsed.path
204                if not relative_parsed.params then
205                    relative_parsed.params = base_parsed.params
206                    if not relative_parsed.query then
207                        relative_parsed.query = base_parsed.query
208                    end
209                end
210            else
211                relative_parsed.path = absolute_path(base_parsed.path or "", relative_parsed.path)
212            end
213        end
214        return url.build(relative_parsed)
215    end
216end
217
218function url.parse_path(path)
219    local parsed = { }
220    path = path or ""
221    gsub(path, "([^/]+)", function (s)
222        insert(parsed, s)
223    end)
224    for i=1,#parsed do
225        parsed[i] = url.unescape(parsed[i])
226    end
227    if sub(path, 1, 1) == "/" then
228        parsed.is_absolute = 1
229    end
230    if sub(path, -1, -1) == "/" then
231        parsed.is_directory = 1
232    end
233    return parsed
234end
235
236function url.build_path(parsed, unsafe)
237    local path = ""
238    local n = #parsed
239    if unsafe then
240        for i = 1, n-1 do
241            path = path .. parsed[i] .. "/"
242        end
243        if n > 0 then
244            path = path .. parsed[n]
245            if parsed.is_directory then
246                path = path .. "/"
247            end
248        end
249    else
250        for i = 1, n-1 do
251            path = path .. protect_segment(parsed[i]) .. "/"
252        end
253        if n > 0 then
254            path = path .. protect_segment(parsed[n])
255            if parsed.is_directory then
256                path = path .. "/"
257            end
258        end
259    end
260    if parsed.is_absolute then
261        path = "/" .. path
262    end
263    return path
264end
265
266package.loaded["socket.url"] = url
267
268return url
269