/usr/share/lua/5.1/http/util.lua is in lua-http 0.1-3.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 | local lpeg = require "lpeg"
local http_patts = require "lpeg_patterns.http"
-- Encodes a character as a percent encoded string
local function char_to_pchar(c)
return string.format("%%%02X", c:byte(1,1))
end
-- encodeURI replaces all characters except the following with the appropriate UTF-8 escape sequences:
-- ; , / ? : @ & = + $
-- alphabetic, decimal digits, - _ . ! ~ * ' ( )
-- #
local function encodeURI(str)
return (str:gsub("[^%;%,%/%?%:%@%&%=%+%$%w%-%_%.%!%~%*%'%(%)%#]", char_to_pchar))
end
-- encodeURIComponent escapes all characters except the following: alphabetic, decimal digits, - _ . ! ~ * ' ( )
local function encodeURIComponent(str)
return (str:gsub("[^%w%-_%.%!%~%*%'%(%)]", char_to_pchar))
end
-- decodeURI unescapes url encoded characters
-- excluding for characters that are special in urls
local decodeURI do
-- Keep the blacklist in numeric form.
-- This means we can skip case normalisation of the hex characters
local decodeURI_blacklist = {}
for char in ("#$&+,/:;=?@"):gmatch(".") do
decodeURI_blacklist[string.byte(char)] = true
end
local function decodeURI_helper(str)
local x = tonumber(str, 16)
if not decodeURI_blacklist[x] then
return string.char(x)
end
-- return nothing; gsub will not perform the replacement
end
function decodeURI(str)
return (str:gsub("%%(%x%x)", decodeURI_helper))
end
end
-- Converts a hex string to a character
local function pchar_to_char(str)
return string.char(tonumber(str, 16))
end
-- decodeURIComponent unescapes *all* url encoded characters
local function decodeURIComponent(str)
return (str:gsub("%%(%x%x)", pchar_to_char))
end
-- An iterator over query segments (delimited by "&") as key/value pairs
-- if a query segment has no '=', the value will be `nil`
local function query_args(str)
local iter, state, first = str:gmatch("([^=&]+)(=?)([^&]*)&?")
return function(state, last) -- luacheck: ignore 431
local name, equals, value = iter(state, last)
if name == nil then return nil end
name = decodeURIComponent(name)
if equals == "" then
value = nil
else
value = decodeURIComponent(value)
end
return name, value
end, state, first
end
-- Converts a dictionary (string keys, string values) to an encoded query string
local function dict_to_query(form)
local r, i = {}, 0
for name, value in pairs(form) do
i = i + 1
r[i] = encodeURIComponent(name).."="..encodeURIComponent(value)
end
return table.concat(r, "&", 1, i)
end
-- Resolves a relative path
local function resolve_relative_path(orig_path, relative_path)
local t, i = {}, 0
local is_abs
if relative_path:sub(1,1) == "/" then
-- "relative" argument is actually absolute. ignore orig_path argument
is_abs = true
else
is_abs = orig_path:sub(1,1) == "/"
-- this will skip empty path components due to +
-- the / on the end ignores trailing component
for segment in orig_path:gmatch("([^/]+)/") do
i = i + 1
t[i] = segment
end
end
for segment in relative_path:gmatch("([^/]+)") do
if segment == ".." then
-- if we're at the root, do nothing
if i > 0 then
-- discard a component
i = i - 1
end
elseif segment ~= "." then
i = i + 1
t[i] = segment
end
end
-- Make sure leading slash is kept
local s
if is_abs then
if i == 0 then return "/" end
t[0] = ""
s = 0
else
s = 1
end
-- Make sure trailing slash is kept
if relative_path:sub(-1, -1) == "/" then
i = i + 1
t[i] = ""
end
return table.concat(t, "/", s, i)
end
local scheme_to_port = {
http = 80;
ws = 80;
https = 443;
wss = 443;
}
-- Splits a :authority header (same as Host) into host and port
local function split_authority(authority, scheme)
local host, port
local h, p = authority:match("^ *(.-):(%d+) *$")
if p then
authority = h
port = tonumber(p)
else -- when port missing from host header, it defaults to the default for that scheme
port = scheme_to_port[scheme]
if port == nil then
return nil, "unknown scheme"
end
end
local ipv6 = authority:match("%[([:%x]+)%]")
if ipv6 then
host = ipv6
else
host = authority
end
return host, port
end
-- Reverse of `split_authority`: converts a host, port and scheme
-- into a string suitable for an :authority header.
local function to_authority(host, port, scheme)
local authority = host
if host:match("^[%x:]+:[%x:]*$") then -- IPv6
authority = "[" .. authority .. "]"
end
local default_port = scheme_to_port[scheme]
if default_port == port then
port = nil
end
if port then
authority = string.format("%s:%d", authority, port)
end
return authority
end
-- HTTP prefered date format
-- See RFC 7231 section 7.1.1.1
local function imf_date(time)
return os.date("!%a, %d %b %Y %H:%M:%S GMT", time)
end
-- This pattern checks if it's argument is a valid token, if so, it returns it as is.
-- Otherwise, it returns it as a quoted string (with any special characters escaped)
local maybe_quote do
local EOF = lpeg.P(-1)
local patt = http_patts.token * EOF
+ lpeg.Cs(lpeg.Cc'"' * ((lpeg.S"\\\"") / "\\%0" + http_patts.qdtext)^0 * lpeg.Cc'"') * EOF
maybe_quote = function (s)
return patt:match(s)
end
end
-- A pcall relative that can be yielded over in PUC 5.1
local yieldable_pcall
-- See if pcall can be yielded over
if coroutine.wrap(function() return pcall(coroutine.yield, true) end)() then
yieldable_pcall = pcall
else
local function handle_resume(co, ok, ...)
if not ok then
return false, ...
elseif coroutine.status(co) == "dead" then
return true, ...
end
return handle_resume(co, coroutine.resume(co, coroutine.yield(...)))
end
yieldable_pcall = function(func, ...)
if type(func) ~= "function" or debug.getinfo(func, "S").what == "C" then
local C_func = func
-- Can't give C functions to coroutine.create
func = function(...) return C_func(...) end
end
local co = coroutine.create(func)
return handle_resume(co, coroutine.resume(co, ...))
end
end
return {
encodeURI = encodeURI;
encodeURIComponent = encodeURIComponent;
decodeURI = decodeURI;
decodeURIComponent = decodeURIComponent;
query_args = query_args;
dict_to_query = dict_to_query;
resolve_relative_path = resolve_relative_path;
scheme_to_port = scheme_to_port;
split_authority = split_authority;
to_authority = to_authority;
imf_date = imf_date;
maybe_quote = maybe_quote;
yieldable_pcall = yieldable_pcall;
}
|