147 lines
4.2 KiB
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;
|
|
|