uri-f570bf7.lua 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. -- The MIT License (MIT)
  2. --
  3. -- Copyright (c) 2014 Cyril David <cyx@cyx.is>
  4. -- Copyright (c) 2011-2013 Bertrand Mansion <bmansion@mamasam.com>
  5. --
  6. -- Permission is hereby granted, free of charge, to any person obtaining a copy
  7. -- of this software and associated documentation files (the "Software"), to deal
  8. -- in the Software without restriction, including without limitation the rights
  9. -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. -- copies of the Software, and to permit persons to whom the Software is
  11. -- furnished to do so, subject to the following conditions:
  12. --
  13. -- The above copyright notice and this permission notice shall be included in
  14. -- all copies or substantial portions of the Software.
  15. --
  16. -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. -- THE SOFTWARE.
  23. -- == list of known and common scheme ports
  24. --
  25. -- @see http://www.iana.org/assignments/uri-schemes.html
  26. --
  27. local SERVICES = {
  28. acap = 674,
  29. cap = 1026,
  30. dict = 2628,
  31. ftp = 21,
  32. gopher = 70,
  33. http = 80,
  34. https = 443,
  35. iax = 4569,
  36. icap = 1344,
  37. imap = 143,
  38. ipp = 631,
  39. ldap = 389,
  40. mtqp = 1038,
  41. mupdate = 3905,
  42. news = 2009,
  43. nfs = 2049,
  44. nntp = 119,
  45. rtsp = 554,
  46. sip = 5060,
  47. snmp = 161,
  48. telnet = 23,
  49. tftp = 69,
  50. vemmi = 575,
  51. afs = 1483,
  52. jms = 5673,
  53. rsync = 873,
  54. prospero = 191,
  55. videotex = 516
  56. }
  57. local LEGAL = {
  58. ["-"] = true, ["_"] = true, ["."] = true, ["!"] = true,
  59. ["~"] = true, ["*"] = true, ["'"] = true, ["("] = true,
  60. [")"] = true, [":"] = true, ["@"] = true, ["&"] = true,
  61. ["="] = true, ["+"] = true, ["$"] = true, [","] = true,
  62. [";"] = true -- can be used for parameters in path
  63. }
  64. -- aggressive caching of methods
  65. local gsub = string.gsub
  66. local char = string.char
  67. local byte = string.byte
  68. local upper = string.upper
  69. local lower = string.lower
  70. local format = string.format
  71. -- forward declaration of helper utilities
  72. local util = {}
  73. local function decode(str)
  74. local str = gsub(str, "+", " ")
  75. return (gsub(str, "%%(%x%x)", function(c)
  76. return char(tonumber(c, 16))
  77. end))
  78. end
  79. local function encode(str)
  80. return (gsub(str, "([^A-Za-z0-9%_%.%-%~])", function(v)
  81. return upper(format("%%%02x", byte(v)))
  82. end))
  83. end
  84. -- Build a URL given a table with the fields:
  85. --
  86. -- - scheme
  87. -- - user
  88. -- - password
  89. -- - host
  90. -- - port
  91. -- - path
  92. -- - query
  93. -- - fragment
  94. --
  95. -- Example:
  96. --
  97. -- local url = uri.build({
  98. -- scheme = "http",
  99. -- host = "example.com",
  100. -- path = "/some/path"
  101. -- })
  102. --
  103. -- assert(url == "http://example.com/some/path")
  104. --
  105. local function build(uri)
  106. local url = ""
  107. if uri.path then
  108. local path = uri.path
  109. gsub(path, "([^/]+)", function (s) return util.encode_segment(s) end)
  110. url = url .. tostring(path)
  111. end
  112. if uri.query then
  113. local qstring = tostring(uri.query)
  114. if qstring ~= "" then
  115. url = url .. "?" .. qstring
  116. end
  117. end
  118. if uri.host then
  119. local authority = uri.host
  120. if uri.port and uri.scheme and SERVICES[uri.scheme] ~= uri.port then
  121. authority = authority .. ":" .. uri.port
  122. end
  123. local userinfo
  124. if uri.user and uri.user ~= "" then
  125. userinfo = encode(uri.user)
  126. if uri.password then
  127. userinfo = userinfo .. ":" .. encode(uri.password)
  128. end
  129. end
  130. if userinfo and userinfo ~= "" then
  131. authority = userinfo .. "@" .. authority
  132. end
  133. if authority then
  134. if url ~= "" then
  135. url = "//" .. authority .. "/" .. gsub(url, "^/+", "")
  136. else
  137. url = "//" .. authority
  138. end
  139. end
  140. end
  141. if uri.scheme then
  142. url = uri.scheme .. ":" .. url
  143. end
  144. if uri.fragment then
  145. url = url .. "#" .. uri.fragment
  146. end
  147. return url
  148. end
  149. -- Parse the url into the designated parts.
  150. --
  151. -- Depending on the url, the following parts will be available:
  152. --
  153. -- - scheme
  154. -- - userinfo
  155. -- - user
  156. -- - password
  157. -- - authority
  158. -- - host
  159. -- - port
  160. -- - path
  161. -- - query
  162. -- - fragment
  163. --
  164. -- Usage:
  165. --
  166. -- local u = uri.parse("http://john:monkey@example.com/some/path#h1")
  167. --
  168. -- assert(u.host == "example.com")
  169. -- assert(u.scheme == "http")
  170. -- assert(u.user == "john")
  171. -- assert(u.password == "monkey")
  172. -- assert(u.path == "/some/path")
  173. -- assert(u.fragment == "h1")
  174. --
  175. local function parse(url)
  176. local uri = { query = nil }
  177. util.set_authority(uri, "")
  178. local url = tostring(url or "")
  179. url = gsub(url, "#(.*)$", function(v)
  180. uri.fragment = v
  181. return ""
  182. end)
  183. url = gsub(url, "^([%w][%w%+%-%.]*)%:", function(v)
  184. uri.scheme = lower(v)
  185. return ""
  186. end)
  187. url = gsub(url, "%?(.*)", function(v)
  188. uri.query = v
  189. return ""
  190. end)
  191. url = gsub(url, "^//([^/]*)", function(v)
  192. util.set_authority(uri, v)
  193. return ""
  194. end)
  195. uri.path = decode(url)
  196. return uri
  197. end
  198. function util.encode_segment(s)
  199. local function encode_legal(c)
  200. if LEGAL[c] then
  201. return c
  202. end
  203. return encode(c)
  204. end
  205. return gsub(s, "([^a-zA-Z0-9])", encode_legal)
  206. end
  207. -- set the authority part of the url
  208. --
  209. -- The authority is parsed to find the user, password, port and host if available.
  210. -- @param authority The string representing the authority
  211. -- @return a string with what remains after the authority was parsed
  212. function util.set_authority(uri, authority)
  213. uri.authority = authority
  214. uri.port = nil
  215. uri.host = nil
  216. uri.userinfo = nil
  217. uri.user = nil
  218. uri.password = nil
  219. authority = gsub(authority, "^([^@]*)@", function(v)
  220. uri.userinfo = decode(v)
  221. return ""
  222. end)
  223. authority = gsub(authority, "^%[[^%]]+%]", function(v)
  224. -- ipv6
  225. uri.host = v
  226. return ""
  227. end)
  228. authority = gsub(authority, ":([^:]*)$", function(v)
  229. uri.port = tonumber(v)
  230. return ""
  231. end)
  232. if authority ~= "" and not uri.host then
  233. uri.host = lower(authority)
  234. end
  235. if uri.userinfo then
  236. local userinfo = uri.userinfo
  237. userinfo = gsub(userinfo, ":([^:]*)$", function(v)
  238. uri.password = v
  239. return ""
  240. end)
  241. uri.user = userinfo
  242. end
  243. return authority
  244. end
  245. local uri = {
  246. build = build,
  247. parse = parse,
  248. encode = encode,
  249. decode = decode
  250. }
  251. return uri