PageRenderTime 56ms CodeModel.GetById 12ms RepoModel.GetById 1ms app.codeStats 0ms

/logger/ext/url.lua

https://bitbucket.org/dushan01/fenix
Lua | 318 lines | 297 code | 3 blank | 18 comment | 1 complexity | 236b6450c3430c5480a3163e2832381b MD5 | raw file
Possible License(s): LGPL-2.0
  1. --CODE FROM luasocket
  2. -----------------------------------------------------------------------------
  3. -- Encodes a string into its escaped hexadecimal representation
  4. -- Input
  5. -- s: binary string to be encoded
  6. -- Returns
  7. -- escaped representation of string binary
  8. -----------------------------------------------------------------------------
  9. function escape(s)
  10. return string.gsub(s, "([^A-Za-z0-9_])", function(c)
  11. return string.format("%%%02x", string.byte(c))
  12. end)
  13. end
  14. -----------------------------------------------------------------------------
  15. -- Protects a path segment, to prevent it from interfering with the
  16. -- url parsing.
  17. -- Input
  18. -- s: binary string to be encoded
  19. -- Returns
  20. -- escaped representation of string binary
  21. -----------------------------------------------------------------------------
  22. local function make_set(t)
  23. local s = {}
  24. for i,v in ipairs(t) do
  25. s[t[i]] = 1
  26. end
  27. return s
  28. end
  29. -- these are allowed withing a path segment, along with alphanum
  30. -- other characters must be escaped
  31. local segment_set = make_set {
  32. "-", "_", ".", "!", "~", "*", "'", "(",
  33. ")", ":", "@", "&", "=", "+", "$", ",",
  34. }
  35. local function protect_segment(s)
  36. return string.gsub(s, "([^A-Za-z0-9_])", function (c)
  37. if segment_set[c] then return c
  38. else return string.format("%%%02x", string.byte(c)) end
  39. end)
  40. end
  41. -----------------------------------------------------------------------------
  42. -- Encodes a string into its escaped hexadecimal representation
  43. -- Input
  44. -- s: binary string to be encoded
  45. -- Returns
  46. -- escaped representation of string binary
  47. -----------------------------------------------------------------------------
  48. function unescape(s)
  49. local ret = string.gsub(s, "+", " ")
  50. ret = string.gsub(ret, "%%(%x%x)", function(hex)
  51. return string.char(tonumber(hex, 16))
  52. end)
  53. return ret
  54. end
  55. -----------------------------------------------------------------------------
  56. -- Builds a path from a base path and a relative path
  57. -- Input
  58. -- base_path
  59. -- relative_path
  60. -- Returns
  61. -- corresponding absolute path
  62. -----------------------------------------------------------------------------
  63. local function absolute_path(base_path, relative_path)
  64. if string.sub(relative_path, 1, 1) == "/" then return relative_path end
  65. local path = string.gsub(base_path, "[^/]*$", "")
  66. path = path .. relative_path
  67. path = string.gsub(path, "([^/]*%./)", function (s)
  68. if s ~= "./" then return s else return "" end
  69. end)
  70. path = string.gsub(path, "/%.$", "/")
  71. local reduced
  72. while reduced ~= path do
  73. reduced = path
  74. path = string.gsub(reduced, "([^/]*/%.%./)", function (s)
  75. if s ~= "../../" then return "" else return s end
  76. end)
  77. end
  78. path = string.gsub(reduced, "([^/]*/%.%.)$", function (s)
  79. if s ~= "../.." then return "" else return s end
  80. end)
  81. return path
  82. end
  83. -----------------------------------------------------------------------------
  84. -- Parses a url and returns a table with all its parts according to RFC 2396
  85. -- The following grammar describes the names given to the URL parts
  86. -- <url> ::= <scheme>://<authority>/<path>;<params>?<query>#<fragment>
  87. -- <authority> ::= <userinfo>@<host>:<port>
  88. -- <userinfo> ::= <user>[:<password>]
  89. -- <path> :: = {<segment>/}<segment>
  90. -- Input
  91. -- url: uniform resource locator of request
  92. -- default: table with default values for each field
  93. -- Returns
  94. -- table with the following fields, where RFC naming conventions have
  95. -- been preserved:
  96. -- scheme, authority, userinfo, user, password, host, port,
  97. -- path, params, query, fragment
  98. -- Obs:
  99. -- the leading '/' in {/<path>} is considered part of <path>
  100. -----------------------------------------------------------------------------
  101. function parse(url, default)
  102. function Split(str, delim, maxNb)
  103. -- Eliminate bad cases...
  104. if string.find(str, delim) == nil then
  105. return { str }
  106. end
  107. if maxNb == nil or maxNb < 1 then
  108. maxNb = 0 -- No limit
  109. end
  110. local result = {}
  111. local pat = "(.-)" .. delim .. "()"
  112. local nb = 0
  113. local lastPos
  114. for part, pos in string.gfind(str, pat) do
  115. nb = nb + 1
  116. result[nb] = part
  117. lastPos = pos
  118. if nb == maxNb then break end
  119. end
  120. -- Handle the last field
  121. if nb ~= maxNb then
  122. result[nb + 1] = string.sub(str, lastPos)
  123. end
  124. return result
  125. end
  126. -- initialize default parameters
  127. local parsed = {}
  128. for i,v in pairs(default or parsed) do parsed[i] = v end
  129. -- empty url is parsed to nil
  130. if not url or url == "" then return nil, "invalid url" end
  131. -- remove whitespace
  132. -- url = string.gsub(url, "%s", "")
  133. -- get fragment
  134. url = string.gsub(url, "#(.*)$", function(f)
  135. parsed.fragment = f
  136. return ""
  137. end)
  138. -- get scheme
  139. url = string.gsub(url, "^([%w][%w%+%-%.]*)%:",
  140. function(s) parsed.scheme = s; return "" end)
  141. -- get authority
  142. url = string.gsub(url, "^//([^/]*)", function(n)
  143. parsed.authority = n
  144. return ""
  145. end)
  146. -- get query stringing
  147. url = string.gsub(url, "%?(.*)", function(q)
  148. parsed.query = q
  149. return ""
  150. end)
  151. -- get params
  152. --[[url = string.gsub(url, "%;(.*)", function(p)
  153. parsed.params = p
  154. return ""
  155. end)]]
  156. parsed.params = {}
  157. if parsed.query and parsed.query ~= "" then
  158. local p = Split(parsed.query, "&")
  159. if p then
  160. for i, v in ipairs(p) do
  161. local a = Split(v, "=")
  162. if a and #a > 1 then
  163. parsed.params[a[1]] = unescape(a[2])
  164. end
  165. end
  166. end
  167. end
  168. -- path is whatever was left
  169. if url ~= "" then parsed.path = url end
  170. local authority = parsed.authority
  171. if not authority then return parsed end
  172. authority = string.gsub(authority,"^([^@]*)@",
  173. function(u) parsed.userinfo = u; return "" end)
  174. authority = string.gsub(authority, ":([^:]*)$",
  175. function(p) parsed.port = p; return "" end)
  176. if authority ~= "" then parsed.host = authority end
  177. local userinfo = parsed.userinfo
  178. if not userinfo then return parsed end
  179. userinfo = string.gsub(userinfo, ":([^:]*)$",
  180. function(p) parsed.password = p; return "" end)
  181. parsed.user = userinfo
  182. return parsed
  183. end
  184. -----------------------------------------------------------------------------
  185. -- Rebuilds a parsed URL from its components.
  186. -- Components are protected if any reserved or unallowed characters are found
  187. -- Input
  188. -- parsed: parsed URL, as returned by parse
  189. -- Returns
  190. -- a stringing with the corresponding URL
  191. -----------------------------------------------------------------------------
  192. function build(parsed)
  193. local ppath = parse_path(parsed.path or "")
  194. local url = build_path(ppath)
  195. if parsed.params then url = url .. ";" .. parsed.params end
  196. if parsed.query then url = url .. "?" .. parsed.query end
  197. local authority = parsed.authority
  198. if parsed.host then
  199. authority = parsed.host
  200. if parsed.port then authority = authority .. ":" .. parsed.port end
  201. local userinfo = parsed.userinfo
  202. if parsed.user then
  203. userinfo = parsed.user
  204. if parsed.password then
  205. userinfo = userinfo .. ":" .. parsed.password
  206. end
  207. end
  208. if userinfo then authority = userinfo .. "@" .. authority end
  209. end
  210. if authority then url = "//" .. authority .. url end
  211. if parsed.scheme then url = parsed.scheme .. ":" .. url end
  212. if parsed.fragment then url = url .. "#" .. parsed.fragment end
  213. -- url = string.gsub(url, "%s", "")
  214. return url
  215. end
  216. -----------------------------------------------------------------------------
  217. -- Builds a absolute URL from a base and a relative URL according to RFC 2396
  218. -- Input
  219. -- base_url
  220. -- relative_url
  221. -- Returns
  222. -- corresponding absolute url
  223. -----------------------------------------------------------------------------
  224. function absolute(base_url, relative_url)
  225. if type(base_url) == "table" then
  226. base_parsed = base_url
  227. base_url = build(base_parsed)
  228. else
  229. base_parsed = parse(base_url)
  230. end
  231. local relative_parsed = parse(relative_url)
  232. if not base_parsed then return relative_url
  233. elseif not relative_parsed then return base_url
  234. elseif relative_parsed.scheme then return relative_url
  235. else
  236. relative_parsed.scheme = base_parsed.scheme
  237. if not relative_parsed.authority then
  238. relative_parsed.authority = base_parsed.authority
  239. if not relative_parsed.path then
  240. relative_parsed.path = base_parsed.path
  241. if not relative_parsed.params then
  242. relative_parsed.params = base_parsed.params
  243. if not relative_parsed.query then
  244. relative_parsed.query = base_parsed.query
  245. end
  246. end
  247. else
  248. relative_parsed.path = absolute_path(base_parsed.path or "",
  249. relative_parsed.path)
  250. end
  251. end
  252. return build(relative_parsed)
  253. end
  254. end
  255. -----------------------------------------------------------------------------
  256. -- Breaks a path into its segments, unescaping the segments
  257. -- Input
  258. -- path
  259. -- Returns
  260. -- segment: a table with one entry per segment
  261. -----------------------------------------------------------------------------
  262. function parse_path(path)
  263. local parsed = {}
  264. path = path or ""
  265. --path = string.gsub(path, "%s", "")
  266. string.gsub(path, "([^/]+)", function (s) table.insert(parsed, s) end)
  267. for i = 1, table.getn(parsed) do
  268. parsed[i] = unescape(parsed[i])
  269. end
  270. if string.sub(path, 1, 1) == "/" then parsed.is_absolute = 1 end
  271. if string.sub(path, -1, -1) == "/" then parsed.is_directory = 1 end
  272. return parsed
  273. end
  274. -----------------------------------------------------------------------------
  275. -- Builds a path component from its segments, escaping protected characters.
  276. -- Input
  277. -- parsed: path segments
  278. -- unsafe: if true, segments are not protected before path is built
  279. -- Returns
  280. -- path: corresponding path stringing
  281. -----------------------------------------------------------------------------
  282. function build_path(parsed, unsafe)
  283. local path = ""
  284. local n = table.getn(parsed)
  285. if unsafe then
  286. for i = 1, n-1 do
  287. path = path .. parsed[i]
  288. path = path .. "/"
  289. end
  290. if n > 0 then
  291. path = path .. parsed[n]
  292. if parsed.is_directory then path = path .. "/" end
  293. end
  294. else
  295. for i = 1, n-1 do
  296. path = path .. protect_segment(parsed[i])
  297. path = path .. "/"
  298. end
  299. if n > 0 then
  300. path = path .. protect_segment(parsed[n])
  301. if parsed.is_directory then path = path .. "/" end
  302. end
  303. end
  304. if parsed.is_absolute then path = "/" .. path end
  305. return path
  306. end