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;