diff options
Diffstat (limited to '')
-rw-r--r-- | src/url.lua | 98 |
1 files changed, 61 insertions, 37 deletions
diff --git a/src/url.lua b/src/url.lua index 7809535..8e0dc5c 100644 --- a/src/url.lua +++ b/src/url.lua | |||
@@ -49,7 +49,7 @@ local function make_set(t) | |||
49 | return s | 49 | return s |
50 | end | 50 | end |
51 | 51 | ||
52 | -- these are allowed withing a path segment, along with alphanum | 52 | -- these are allowed within a path segment, along with alphanum |
53 | -- other characters must be escaped | 53 | -- other characters must be escaped |
54 | local segment_set = make_set { | 54 | local segment_set = make_set { |
55 | "-", "_", ".", "!", "~", "*", "'", "(", | 55 | "-", "_", ".", "!", "~", "*", "'", "(", |
@@ -59,16 +59,16 @@ local segment_set = make_set { | |||
59 | local function protect_segment(s) | 59 | local function protect_segment(s) |
60 | return string.gsub(s, "([^A-Za-z0-9_])", function (c) | 60 | return string.gsub(s, "([^A-Za-z0-9_])", function (c) |
61 | if segment_set[c] then return c | 61 | if segment_set[c] then return c |
62 | else return string.format("%%%02x", string.byte(c)) end | 62 | else return string.format("%%%02X", string.byte(c)) end |
63 | end) | 63 | end) |
64 | end | 64 | end |
65 | 65 | ||
66 | ----------------------------------------------------------------------------- | 66 | ----------------------------------------------------------------------------- |
67 | -- Encodes a string into its escaped hexadecimal representation | 67 | -- Unencodes a escaped hexadecimal string into its binary representation |
68 | -- Input | 68 | -- Input |
69 | -- s: binary string to be encoded | 69 | -- s: escaped hexadecimal string to be unencoded |
70 | -- Returns | 70 | -- Returns |
71 | -- escaped representation of string binary | 71 | -- unescaped binary representation of escaped hexadecimal binary |
72 | ----------------------------------------------------------------------------- | 72 | ----------------------------------------------------------------------------- |
73 | function _M.unescape(s) | 73 | function _M.unescape(s) |
74 | return (string.gsub(s, "%%(%x%x)", function(hex) | 74 | return (string.gsub(s, "%%(%x%x)", function(hex) |
@@ -77,6 +77,34 @@ function _M.unescape(s) | |||
77 | end | 77 | end |
78 | 78 | ||
79 | ----------------------------------------------------------------------------- | 79 | ----------------------------------------------------------------------------- |
80 | -- Removes '..' and '.' components appropriately from a path. | ||
81 | -- Input | ||
82 | -- path | ||
83 | -- Returns | ||
84 | -- dot-normalized path | ||
85 | local function remove_dot_components(path) | ||
86 | local marker = string.char(1) | ||
87 | repeat | ||
88 | local was = path | ||
89 | path = path:gsub('//', '/'..marker..'/', 1) | ||
90 | until path == was | ||
91 | repeat | ||
92 | local was = path | ||
93 | path = path:gsub('/%./', '/', 1) | ||
94 | until path == was | ||
95 | repeat | ||
96 | local was = path | ||
97 | path = path:gsub('[^/]+/%.%./([^/]+)', '%1', 1) | ||
98 | until path == was | ||
99 | path = path:gsub('[^/]+/%.%./*$', '') | ||
100 | path = path:gsub('/%.%.$', '/') | ||
101 | path = path:gsub('/%.$', '/') | ||
102 | path = path:gsub('^/%.%./', '/') | ||
103 | path = path:gsub(marker, '') | ||
104 | return path | ||
105 | end | ||
106 | |||
107 | ----------------------------------------------------------------------------- | ||
80 | -- Builds a path from a base path and a relative path | 108 | -- Builds a path from a base path and a relative path |
81 | -- Input | 109 | -- Input |
82 | -- base_path | 110 | -- base_path |
@@ -85,23 +113,12 @@ end | |||
85 | -- corresponding absolute path | 113 | -- corresponding absolute path |
86 | ----------------------------------------------------------------------------- | 114 | ----------------------------------------------------------------------------- |
87 | local function absolute_path(base_path, relative_path) | 115 | local function absolute_path(base_path, relative_path) |
88 | if string.sub(relative_path, 1, 1) == "/" then return relative_path end | 116 | if string.sub(relative_path, 1, 1) == "/" then |
89 | local path = string.gsub(base_path, "[^/]*$", "") | 117 | return remove_dot_components(relative_path) end |
90 | path = path .. relative_path | 118 | base_path = base_path:gsub("[^/]*$", "") |
91 | path = string.gsub(path, "([^/]*%./)", function (s) | 119 | if not base_path:find'/$' then base_path = base_path .. '/' end |
92 | if s ~= "./" then return s else return "" end | 120 | local path = base_path .. relative_path |
93 | end) | 121 | path = remove_dot_components(path) |
94 | path = string.gsub(path, "/%.$", "/") | ||
95 | local reduced | ||
96 | while reduced ~= path do | ||
97 | reduced = path | ||
98 | path = string.gsub(reduced, "([^/]*/%.%./)", function (s) | ||
99 | if s ~= "../../" then return "" else return s end | ||
100 | end) | ||
101 | end | ||
102 | path = string.gsub(reduced, "([^/]*/%.%.)$", function (s) | ||
103 | if s ~= "../.." then return "" else return s end | ||
104 | end) | ||
105 | return path | 122 | return path |
106 | end | 123 | end |
107 | 124 | ||
@@ -131,11 +148,6 @@ function _M.parse(url, default) | |||
131 | if not url or url == "" then return nil, "invalid url" end | 148 | if not url or url == "" then return nil, "invalid url" end |
132 | -- remove whitespace | 149 | -- remove whitespace |
133 | -- url = string.gsub(url, "%s", "") | 150 | -- url = string.gsub(url, "%s", "") |
134 | -- get fragment | ||
135 | url = string.gsub(url, "#(.*)$", function(f) | ||
136 | parsed.fragment = f | ||
137 | return "" | ||
138 | end) | ||
139 | -- get scheme | 151 | -- get scheme |
140 | url = string.gsub(url, "^([%w][%w%+%-%.]*)%:", | 152 | url = string.gsub(url, "^([%w][%w%+%-%.]*)%:", |
141 | function(s) parsed.scheme = s; return "" end) | 153 | function(s) parsed.scheme = s; return "" end) |
@@ -144,6 +156,11 @@ function _M.parse(url, default) | |||
144 | parsed.authority = n | 156 | parsed.authority = n |
145 | return "" | 157 | return "" |
146 | end) | 158 | end) |
159 | -- get fragment | ||
160 | url = string.gsub(url, "#(.*)$", function(f) | ||
161 | parsed.fragment = f | ||
162 | return "" | ||
163 | end) | ||
147 | -- get query string | 164 | -- get query string |
148 | url = string.gsub(url, "%?(.*)", function(q) | 165 | url = string.gsub(url, "%?(.*)", function(q) |
149 | parsed.query = q | 166 | parsed.query = q |
@@ -162,9 +179,9 @@ function _M.parse(url, default) | |||
162 | function(u) parsed.userinfo = u; return "" end) | 179 | function(u) parsed.userinfo = u; return "" end) |
163 | authority = string.gsub(authority, ":([^:%]]*)$", | 180 | authority = string.gsub(authority, ":([^:%]]*)$", |
164 | function(p) parsed.port = p; return "" end) | 181 | function(p) parsed.port = p; return "" end) |
165 | if authority ~= "" then | 182 | if authority ~= "" then |
166 | -- IPv6? | 183 | -- IPv6? |
167 | parsed.host = string.match(authority, "^%[(.+)%]$") or authority | 184 | parsed.host = string.match(authority, "^%[(.+)%]$") or authority |
168 | end | 185 | end |
169 | local userinfo = parsed.userinfo | 186 | local userinfo = parsed.userinfo |
170 | if not userinfo then return parsed end | 187 | if not userinfo then return parsed end |
@@ -183,8 +200,9 @@ end | |||
183 | -- a stringing with the corresponding URL | 200 | -- a stringing with the corresponding URL |
184 | ----------------------------------------------------------------------------- | 201 | ----------------------------------------------------------------------------- |
185 | function _M.build(parsed) | 202 | function _M.build(parsed) |
186 | local ppath = _M.parse_path(parsed.path or "") | 203 | --local ppath = _M.parse_path(parsed.path or "") |
187 | local url = _M.build_path(ppath) | 204 | --local url = _M.build_path(ppath) |
205 | local url = parsed.path or "" | ||
188 | if parsed.params then url = url .. ";" .. parsed.params end | 206 | if parsed.params then url = url .. ";" .. parsed.params end |
189 | if parsed.query then url = url .. "?" .. parsed.query end | 207 | if parsed.query then url = url .. "?" .. parsed.query end |
190 | local authority = parsed.authority | 208 | local authority = parsed.authority |
@@ -193,7 +211,7 @@ function _M.build(parsed) | |||
193 | if string.find(authority, ":") then -- IPv6? | 211 | if string.find(authority, ":") then -- IPv6? |
194 | authority = "[" .. authority .. "]" | 212 | authority = "[" .. authority .. "]" |
195 | end | 213 | end |
196 | if parsed.port then authority = authority .. ":" .. parsed.port end | 214 | if parsed.port then authority = authority .. ":" .. base.tostring(parsed.port) end |
197 | local userinfo = parsed.userinfo | 215 | local userinfo = parsed.userinfo |
198 | if parsed.user then | 216 | if parsed.user then |
199 | userinfo = parsed.user | 217 | userinfo = parsed.user |
@@ -219,16 +237,21 @@ end | |||
219 | -- corresponding absolute url | 237 | -- corresponding absolute url |
220 | ----------------------------------------------------------------------------- | 238 | ----------------------------------------------------------------------------- |
221 | function _M.absolute(base_url, relative_url) | 239 | function _M.absolute(base_url, relative_url) |
240 | local base_parsed | ||
222 | if base.type(base_url) == "table" then | 241 | if base.type(base_url) == "table" then |
223 | base_parsed = base_url | 242 | base_parsed = base_url |
224 | base_url = _M.build(base_parsed) | 243 | base_url = _M.build(base_parsed) |
225 | else | 244 | else |
226 | base_parsed = _M.parse(base_url) | 245 | base_parsed = _M.parse(base_url) |
227 | end | 246 | end |
247 | local result | ||
228 | local relative_parsed = _M.parse(relative_url) | 248 | local relative_parsed = _M.parse(relative_url) |
229 | if not base_parsed then return relative_url | 249 | if not base_parsed then |
230 | elseif not relative_parsed then return base_url | 250 | result = relative_url |
231 | elseif relative_parsed.scheme then return relative_url | 251 | elseif not relative_parsed then |
252 | result = base_url | ||
253 | elseif relative_parsed.scheme then | ||
254 | result = relative_url | ||
232 | else | 255 | else |
233 | relative_parsed.scheme = base_parsed.scheme | 256 | relative_parsed.scheme = base_parsed.scheme |
234 | if not relative_parsed.authority then | 257 | if not relative_parsed.authority then |
@@ -241,13 +264,14 @@ function _M.absolute(base_url, relative_url) | |||
241 | relative_parsed.query = base_parsed.query | 264 | relative_parsed.query = base_parsed.query |
242 | end | 265 | end |
243 | end | 266 | end |
244 | else | 267 | else |
245 | relative_parsed.path = absolute_path(base_parsed.path or "", | 268 | relative_parsed.path = absolute_path(base_parsed.path or "", |
246 | relative_parsed.path) | 269 | relative_parsed.path) |
247 | end | 270 | end |
248 | end | 271 | end |
249 | return _M.build(relative_parsed) | 272 | result = _M.build(relative_parsed) |
250 | end | 273 | end |
274 | return remove_dot_components(result) | ||
251 | end | 275 | end |
252 | 276 | ||
253 | ----------------------------------------------------------------------------- | 277 | ----------------------------------------------------------------------------- |