This module is a drop-in replacement for Garry's Mod's HTTP
function, inspired by gmsv_chttp
created by timschumi.
The module uses the reqwest
crate for dispatching HTTP requests, tokio
crate for async/thread scheduling runtime and the rustls
crate for SSL/TLS.
This module was written in Rust and serves as a decent example on how to write a Garry's Mod binary module in Rust, using the gmod
crate.
Download the relevant module for your server's operating system and platform/Gmod branch from the releases section.
Drop the module into garrysmod/lua/bin/
in your server's files. If the bin
folder doesn't exist, create it.
If you're not sure on what operating system/platform your server is running, run this in your server's console:
lua_run print((system.IsWindows()and"Windows"or system.IsLinux()and"Linux"or"Unsupported").." "..(jit.arch=="x64"and"x86-64"or"x86"))
To add custom root certificates, place them in the garrysmod/tls_certificates/client
directory.
The certificates must be X509 and encoded in either pem
or der
. They must also end in the .pem
or .der
file extensions respective to their econding. If there is a problem loading the certificate, it'll be skipped over and a message will be displayed in the console.
To override Garry's Mod's HTTP
function with reqwest
, you can add this code snippet to lua/autorun/server/reqwest.lua
:
if pcall(require, "reqwest") and reqwest ~= nil then
my_http = reqwest
else
my_http = HTTP
end
Once loaded, gmsv_reqwest will create a global function called reqwest
which behaves exactly the same as HTTP
and uses the same configuration struct (HTTPRequest
).
There is one difference: on HTTP request failure, reqwest will provide an extended error message (known as errExt
) as well as Garry's Mod's useless error message.
require("reqwest")
reqwest({
method = "POST",
url = "https://discord.com/api/webhooks/988854737435070417/pHbHIjR15oa4ZmJ1PMCwEPaK4hdlCC21AIme94Iw9Xh7M9Mhg6GLLV2u6Q1rppH_7esX",
timeout = 30,
body = util.TableToJSON({ content = "Hello, world!" }), -- https://discord.com/developers/docs/resources/webhook#execute-webhook
type = "application/json",
headers = {
["User-Agent"] = "My User Agent", -- This is REQUIRED to dispatch a Discord webhook
},
success = function(status, body, headers)
print("HTTP " .. status)
PrintTable(headers)
print(body)
end,
failed = function(err, errExt)
print("Error: " .. err .. " (" .. errExt .. ")")
end
})
By the way, that webhook URL is fake :D
Support both gmsv_reqwest and gmsv_chttp
This example loads either reqwest or CHTTP
if not reqwest and not CHTTP then
local suffix = ({"osx64", "osx", "linux64", "linux", "win64", "win32"})[(system.IsWindows() and 4 or 0) + (system.IsLinux() and 2 or 0) + (jit.arch == "x86" and 1 or 0) + 1]
local fmt = "lua/bin/gm" .. (CLIENT and "cl" or "sv") .. "_%s_%s.dll"
local function installed(name)
if file.Exists(string.format(fmt, name, suffix), "GAME") then return true end
if jit.versionnum ~= 20004 and jit.arch == "x86" and system.IsLinux() then return file.Exists(string.format(fmt, name, "linux32"), "GAME") end
return false
end
if installed("reqwest") then
require("reqwest")
end
if not reqwest and installed("chttp") then
require("chttp")
end
if not CHTTP then
error("reqwest or CHTTP is required to use this!")
end
end
local HTTP = reqwest or CHTTP
-- Your code
You can make requests that block the main thread using gmsv_reqwest, i.e., they are not asynchronous.
- Only do this if you know what you are doing.
- Make sure that you choose an appropriate timeout to avoid timing out players if any are online and the request hangs.
- If you provide a
success
andfailed
callback, they will still be called - On success,
true
,status
,body
andheaders
will be returned - On failure,
false
,err
,errExt
will be returned
To do this, simply add blocking = true
to the HTTPRequest
table when creating your HTTP request.
require("reqwest")
local success, status, body, headers = reqwest({
blocking = true, -- note this!
url = "https://google.com",
-- callbacks will still be called for blocking requests
success = function(status, body, headers) PrintTable({status, body, headers}) end,
failed = function(err, errExt) PrintTable({err, errExt}) end
})
if success then
print("HTTP " .. status)
PrintTable(headers)
print(body)
else
local err, errExt = status, body
-- In this case, `status` will be the "error message" that Garry's Mod provides (typically always "unsuccessful")
-- and `body` will be a custom error message from reqwest which actually describes what the error was.
print("Error: " .. err .. " (" .. errExt .. ")")
end