Files
roblox-batch-http-server/BatchHTTP.lua

147 lines
4.2 KiB
Lua

-- ## CONFIG ## --
local SERVER_BASE_URL:string = "http://localhost:7000/"
local REQUESTS_PER_MINUTE:number = 500 -- roblox 500 requests per minute limit
local ALLOW_CLIENT:boolean = true;
-- ## MODULE ## --
local exposed = {}
local isServer = game:GetService("RunService"):IsServer()
shared.BatchHttpModule = shared.BatchHttpModule or {}
shared.BatchHttpModule.queue = shared.BatchHttpModule.queue or {}
shared.BatchHttpModule.signal = shared.BatchHttpModule.signal or Instance.new("BindableEvent")
shared.BatchHttpModule.lastSent = shared.BatchHttpModule.lastSent or 0;
shared.BatchHttpModule.waitingForResponse = shared.BatchHttpModule.waitingForResponse or {}
shared.BatchHttpModule.clientRemote = shared.BatchHttpModule.clientRemote or (function()
local funcName = "_BatchHTTP"
local funcLocation = workspace
if (isServer) then
local remoteFunction = Instance.new("RemoteFunction")
remoteFunction.Name = funcName
remoteFunction.Parent = funcLocation
remoteFunction.OnServerInvoke = function(plr,options)
if not ALLOW_CLIENT then return end
return exposed:RequestAsync(options)
end
return remoteFunction
else
return funcLocation:WaitForChild(funcName)
end
end)()
local signal:BindableEvent = shared.BatchHttpModule.signal
local THREAD_INTERVAL:number = (60/REQUESTS_PER_MINUTE)
local RealHttpService = game:GetService("HttpService")
local InstanceSecret = (RealHttpService:GenerateGUID(false).."--"..game.JobId) -- not only using jobid so that random people cant just spam the api to get a response
function ProcessQueue()
--print("processing queue")
local requests = {}
local waiting = shared.BatchHttpModule.waitingForResponse
for _,queueObj in shared.BatchHttpModule.queue do
table.insert(requests, queueObj)
table.insert(shared.BatchHttpModule.waitingForResponse,queueObj.id)
end
local body = {}
if (#requests ~= 0) then body.requests = requests end
if (#waiting ~= 0) then body.waiting = waiting end
if not (body.waiting or body.requests) then return end
shared.BatchHttpModule.lastSent = tick()
local jsonBody = RealHttpService:JSONEncode(body)
shared.BatchHttpModule.queue = {}
local response = RealHttpService:PostAsync(SERVER_BASE_URL..InstanceSecret, jsonBody, Enum.HttpContentType.ApplicationJson, true, {
["X-BatchHttp-Api-Key"] = "silly!" -- i know this exposes the key to client but its just temporary (permanent)
})
local data = RealHttpService:JSONDecode(response)
signal:Fire(data.responses or {})
end
function AddToQueue(queueObj)
table.insert(shared.BatchHttpModule.queue, queueObj)
end
function DoRequest(request)
local id = RealHttpService:GenerateGUID(false);
print("[BatchHTTP] sending request",id)
AddToQueue({
id = id,
request = request,
})
local response = nil;
while not response do
local receivedData = signal.Event:Wait()
for _,obj in receivedData do
if obj.id == id then
response = obj
local tableIndex = table.find(shared.BatchHttpModule.waitingForResponse,id)
if tableIndex then
table.remove(shared.BatchHttpModule.waitingForResponse,tableIndex)
end
end
end
end
if response.error then
warn('[BatchHTTP] request '..id..' errored "'..response.error.message..'"')
else
print("[BatchHTTP] got response "+response.status,id)
end
return response.data
end
shared.BatchHttpModule.thread = shared.BatchHttpModule.thread or coroutine.wrap(function()
while true do
if (tick() - shared.BatchHttpModule.lastSent) > THREAD_INTERVAL then
task.spawn(ProcessQueue)
end
task.wait()
end
end)()
exposed.HttpEnabled = RealHttpService.HttpEnabled
exposed.GenerateGUID = RealHttpService.GenerateGUID
exposed.GetSecret = RealHttpService.GetSecret
exposed.JSONDecode = RealHttpService.JSONDecode
exposed.JSONEncode = RealHttpService.JSONEncode
exposed.UrlEncode = RealHttpService.UrlEncode
function exposed:RequestAsync(options)
if (isServer) then
return DoRequest(options)
else
return shared.BatchHttpModule.clientRemote:InvokeServer(options)
end
end
function exposed:PostAsync(url, body, contentType, compress, headers)
return exposed:RequestAsync({
Method = "post";
Url = url;
Body = body;
Headers = headers;
})
end
function exposed:GetAsync(url)
return exposed:RequestAsync({
Method = "get";
Url = url;
})
end
return exposed;