Refactor BatchHTTP module for improved error handling and response handling
This commit is contained in:
141
BatchHTTP.lua
Normal file
141
BatchHTTP.lua
Normal file
@@ -0,0 +1,141 @@
|
||||
-- ## CONFIG ## --
|
||||
|
||||
local SERVER_BASE_URL:string = "http://localhost:7000/"
|
||||
local REQUESTS_PER_MINUTE:number = 500 -- roblox 500 requests per minute limit
|
||||
local ALLOW_CLIENT = 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
|
||||
|
||||
print("[BatchHTTP] got response",id)
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user