cc-libs/apis/libhttp.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;