123 lines
3.3 KiB
Lua
123 lines
3.3 KiB
Lua
local B64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
|
|
|
local function base64encode(s)
|
|
local pad = (3 - #s % 3) % 3;
|
|
s = s .. string.rep('\0', pad);
|
|
local r = {};
|
|
for i = 1, #s, 3 do
|
|
local a, b, c = s:byte(i), s:byte(i + 1), s:byte(i + 2);
|
|
local n = a * 65536 + b * 256 + c;
|
|
r[#r + 1] = B64:sub(math.floor(n / 262144) % 64 + 1, math.floor(n / 262144) % 64 + 1)
|
|
.. B64:sub(math.floor(n / 4096) % 64 + 1, math.floor(n / 4096) % 64 + 1)
|
|
.. B64:sub(math.floor(n / 64) % 64 + 1, math.floor(n / 64) % 64 + 1)
|
|
.. B64:sub(n % 64 + 1, n % 64 + 1);
|
|
end
|
|
local result = table.concat(r);
|
|
if pad > 0 then
|
|
result = result:sub(1, #result - pad) .. string.rep('=', pad);
|
|
end
|
|
return result;
|
|
end
|
|
|
|
local function trimTrailingSlash(s)
|
|
return (s:gsub('/+$', ''));
|
|
end
|
|
|
|
local function urlEncode(s)
|
|
return (tostring(s):gsub('[^%w%-_%.~]', function(c)
|
|
return string.format('%%%02X', string.byte(c));
|
|
end));
|
|
end
|
|
|
|
local function isBlank(s)
|
|
return type(s) ~= 'string' or string.match(s, '^%s*$') ~= nil;
|
|
end
|
|
|
|
local function queryString(params)
|
|
local parts = {};
|
|
for _, item in ipairs(params) do
|
|
if not isBlank(item[2]) then
|
|
parts[#parts + 1] = urlEncode(item[1]) .. '=' .. urlEncode(item[2]);
|
|
end
|
|
end
|
|
if #parts == 0 then return ''; end
|
|
return '?' .. table.concat(parts, '&');
|
|
end
|
|
|
|
local function readAllAndClose(response)
|
|
local body = response.readAll();
|
|
response.close();
|
|
return body;
|
|
end
|
|
|
|
local function statusCode(response)
|
|
if response.getResponseCode then
|
|
return response.getResponseCode();
|
|
end
|
|
return nil;
|
|
end
|
|
|
|
local function createHttp(opts)
|
|
opts = opts or {};
|
|
local httpLib = opts.http or http;
|
|
local textutilsLib = opts.textutils or textutils;
|
|
|
|
local api = {
|
|
base64encode = base64encode,
|
|
trimTrailingSlash = trimTrailingSlash,
|
|
urlEncode = urlEncode,
|
|
queryString = queryString,
|
|
};
|
|
|
|
function api.basicAuth(username, password)
|
|
return 'Basic ' .. base64encode(tostring(username or '') .. ':' .. tostring(password or ''));
|
|
end
|
|
|
|
function api.jsonHeaders(options)
|
|
options = options or {};
|
|
local headers = {
|
|
['Content-Type'] = 'application/json',
|
|
['Accept'] = 'application/json',
|
|
};
|
|
if options.password and options.password ~= '' then
|
|
headers['Authorization'] = api.basicAuth(options.username, options.password);
|
|
end
|
|
return headers;
|
|
end
|
|
|
|
function api.call(method, request)
|
|
local ok, response, httpErr, errorResponse = pcall(httpLib[method], request);
|
|
if not ok then
|
|
return nil, 'http ' .. method .. ' threw: ' .. tostring(response);
|
|
end
|
|
response = response or errorResponse;
|
|
if not response then
|
|
return nil, 'serveur injoignable: ' .. tostring(httpErr or 'unknown error');
|
|
end
|
|
local code = statusCode(response);
|
|
local body = readAllAndClose(response);
|
|
return body, code;
|
|
end
|
|
|
|
function api.getJson(cfg, path)
|
|
return api.call('get', {
|
|
url = cfg.url .. path,
|
|
headers = api.jsonHeaders(cfg),
|
|
timeout = cfg.timeoutSeconds,
|
|
});
|
|
end
|
|
|
|
function api.postJson(cfg, path, payload)
|
|
return api.call('post', {
|
|
url = cfg.url .. path,
|
|
body = textutilsLib.serializeJSON(payload),
|
|
headers = api.jsonHeaders(cfg),
|
|
timeout = cfg.timeoutSeconds,
|
|
});
|
|
end
|
|
|
|
return api;
|
|
end
|
|
|
|
return createHttp;
|