CS2 Lua API

http

The built-in http module sends HTTP and HTTPS requests from a script. Requests are asynchronous: the response arrives in a callback, the game does not wait for the network.

The module is loaded with require and, like ffi, must be listed in the script dependencies on the website. Otherwise require returns the error module 'http' needs to be stated in the 'requirements' section of the script. A module from the dependencies is also available right away as the global variable http.

-- http is listed in the script dependencies on the website
local http = require("http")
print(type(http.get))   --> function
  • Only http:// and https:// addresses are supported.
  • There is no synchronous variant: the result arrives only in the callback.
  • The callback runs on the game thread, at most one HTTP callback per frame. Several requests can be sent at once; their responses arrive one by one in the following frames.
  • An error inside the callback is printed to the console, the script keeps running.
  • When the script is turned off, its unfinished requests are cancelled and the callback does not arrive. The game does not freeze.
  • The Windows system proxy is used automatically.
  • Response headers are not passed to the script: only the status code and the body are available.

http.get(url: string, options: table | nil, callback: function)#

Sends a GET request.

Parameter Type Description
url string request address
options table or nil request options (see "Request options"). Any value other than a table is ignored
callback function function(response), receives the response table (see "Response")
  • The callback is always the third argument. Without options pass nil: http.get(url, nil, cb). Calling http.get(url, cb) gives the error bad argument #3 ... (function expected, got nil).
  • url is not a string — error bad argument #1 ... (string expected, got ...).

Returns: nothing.

local http = require("http")

http.get("https://httpbin.org/get", nil, function(r)
    if r.error then
        print("network error: ", r.error)
        return
    end
    print("status ", r.status, ", length ", #r.body)
end)

Other methods#

http.head, http.post, http.put, http.delete, http.patch take the same arguments as http.get and send a request with the corresponding method.

Function Method Where params go
http.get(url, options, callback) GET into the query string
http.head(url, options, callback) HEAD into the query string; the response body is always empty
http.delete(url, options, callback) DELETE into the query string
http.post(url, options, callback) POST into the request body
http.put(url, options, callback) PUT into the request body
http.patch(url, options, callback) PATCH into the request body

post, put and patch without json and params send an empty body with Content-Type: application/x-www-form-urlencoded.

Returns: nothing.

local http = require("http")

http.put("https://httpbin.org/put", { json = '{"p":1}' }, function(r) print("PUT ", r.status) end)
http.patch("https://httpbin.org/patch", { params = { q = "w" } }, function(r) print("PATCH ", r.status) end)
http.delete("https://httpbin.org/delete", { params = { id = "1" } }, function(r) print("DELETE ", r.status) end)
http.head("https://httpbin.org/get", nil, function(r) print("HEAD ", r.status, " ", #r.body) end)   --> HEAD 200 0

Request options#

The options table. All keys are optional, unknown keys are skipped. A key must be a string and a value must have the listed type, otherwise the error is bad argument ... (<type> expected, got ...).

Key Type Description
timeout number limit for the whole request, seconds (including all redirects). Default 30. 0 or a negative value — no limit. The fractional part is dropped
headers table request headers { ["Name"] = "value" }. Keys and values must be strings
params table or string parameters. A table is joined into k=v&k2=v2 (order as in pairs), a string is used as is. For post/put/patch this is the request body (Content-Type: application/x-www-form-urlencoded unless you set your own in headers), for get/head/delete it is the query string: ? is appended to the address, or & if it already has ?
json string request body as a ready JSON string, plus the header Content-Type: application/json. If the string is not empty, params is not used. Works with any method
cookie cookies an object from http.cookies(): its cookies are sent and cookies from the response are stored in it. Any other value — error cookies expected
authorization table Basic authorization: { username = "...", password = "..." }. Both fields are required and must be strings

params are not encoded: encode spaces, &, =, non-Latin characters and other special characters in values yourself as %XX. A space or a control character in the address gives the error invalid_url. Non-Latin characters in the address are also better encoded by yourself.

local http = require("http")

http.get("https://httpbin.org/get", {
    timeout = 10,
    headers = { ["X-Key"] = "secret" },
    params  = { page = "2", sort = "name" },   -- sent as ?page=2&sort=name (order is not guaranteed)
}, function(r)
    print(r.status)
end)

http.get("https://httpbin.org/basic-auth/user1/pass1", {
    authorization = { username = "user1", password = "pass1" },
}, function(r)
    print(r.status)   --> 200; with a wrong password — 401
end)

Response#

The callback receives one table:

Field Type Description
status number HTTP status code. 0 — network error, no response
body string response body; an empty string on a network error and for head
error string or nil reason of the network error. Without an error the field is absent (nil)
  • 4xx and 5xx codes are a normal response: status and body are filled, there is no error.
  • Redirects are followed automatically, up to 50 hops. status and body are those of the last response. On 301, 302, 303 a POST request is repeated as GET without a body.
  • The body arrives in full, including binary data: it is a Lua string with any bytes, zero bytes included.
local http = require("http")

http.get("https://httpbin.org/status/404", nil, function(r)
    if r.error then
        print("network: ", r.error)
    elseif r.status ~= 200 then
        print("server answered ", r.status)   --> server answered 404
    else
        print(r.body)
    end
end)

Network errors#

On a network error status = 0, body = "", and error is a string of the form <kind> or <kind> (<code>), where the code is a Windows system error code. Check the kind by the start of the string, for example r.error:find("^dns").

Kind When
timeout the request timeout expired
dns the server name was not found
connect could not connect, or the connection was dropped
tls secure connection or certificate error
invalid_url invalid address (including a space in the address)
unsupported_scheme the scheme is not http/https (for example, ftp://)
invalid_response the server did not answer with HTTP
proxy system proxy auto-configuration error
too_many_redirects more than 50 redirects
cancelled the request was cancelled
out_of_memory not enough memory for the response body
response_too_large the response body is larger than the largest Lua string
winhttp other system network errors
local http = require("http")

http.get("https://httpbin.org/delay/5", { timeout = 2 }, function(r)
    if r.error == "timeout" then
        print("the server did not answer within 2 seconds")
    elseif r.error then
        print("network: ", r.error)
    else
        print(r.status)
    end
end)

http.cookies()#

Creates an empty cookies object. Pass it in the cookie option so that requests send and keep cookies, like a browser.

  • The object accumulates cookies from responses (including intermediate responses during redirects). A cookie with the same name, domain and path replaces the previous one, there are no duplicates. Cookies deleted by the server are removed.
  • Cookies are stored in the object before the callback is called, and only if the request finished without a network error.
  • The script cannot read the contents of the object: it only carries cookies between requests. tostring(obj) returns "cookies".
  • Without the cookie option a request sends no cookies.

Returns: cookies — a new object.

local http = require("http")
local jar  = http.cookies()

-- The server sets the cookie srv=abc and redirects to /cookies
http.get("https://httpbin.org/cookies/set?srv=abc", { cookie = jar }, function(r)
    -- The next request with the same object sends srv=abc
    http.get("https://httpbin.org/cookies", { cookie = jar }, function(r2)
        print(r2.body)   --> { "cookies": { "srv": "abc" } }
    end)
end)

cookies:set(fields: table)#

Adds a cookie to the object manually. Another call with the same name, host and path replaces the value.

Field Type Description
host string domain, for example "httpbin.org". Default ""
name string name. Default ""
value string value. Default ""
path string path the cookie applies to. Default "/"
subdomains boolean also applies to subdomains of host. Default false
secure boolean sent only over https://. Default false
expire number expiry time, unix time in seconds. Default 0 — lives as long as the object

All fields are optional, unknown fields are skipped. A field of the wrong type gives the error bad argument ... (<type> expected, got ...). If the argument is not a table — table expected.

Returns: nothing.

local http = require("http")
local jar  = http.cookies()

jar:set({ host = "httpbin.org", name = "session", value = "1" })
jar:set({ host = "httpbin.org", name = "session", value = "2" })   -- replaces the previous one

http.get("https://httpbin.org/cookies", { cookie = jar }, function(r)
    print(r.body)   --> { "cookies": { "session": "2" } }
end)

Example: POST with JSON#

local http = require("http")

local body = json.encode({ name = "player", score = 10 })

http.post("https://httpbin.org/post", { json = body, timeout = 10 }, function(r)
    if r.error then
        print("network: ", r.error)
        return
    end
    if r.status ~= 200 then
        print("server answered ", r.status)
        return
    end
    local answer = json.decode(r.body)
    print(answer.json.name)   --> player
end)