CS2 Lua API

http

Встроенный модуль http отправляет HTTP- и HTTPS-запросы из скрипта. Запросы асинхронные: ответ приходит в колбэк, игра не ждёт сеть.

Модуль подключается через require и, как ffi, должен быть указан в зависимостях скрипта на сайте. Иначе require вернёт ошибку module 'http' needs to be stated in the 'requirements' section of the script. Модуль из зависимостей также сразу доступен как глобальная переменная http.

-- В зависимостях скрипта на сайте указан http
local http = require("http")
print(type(http.get))   --> function
  • Поддерживаются только адреса http:// и https://.
  • Синхронного варианта нет: результат приходит только в колбэк.
  • Колбэк вызывается в игровом потоке, не больше одного HTTP-колбэка за кадр. Несколько запросов можно отправить сразу, их ответы придут по одному в следующих кадрах.
  • Ошибка внутри колбэка выводится в консоль, скрипт продолжает работать.
  • При выключении скрипта его незавершённые запросы отменяются, колбэк не придёт. Игра при этом не зависает.
  • Системный прокси Windows используется автоматически.
  • Заголовки ответа скрипту не передаются: доступны только код ответа и тело.

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

Отправляет запрос GET.

Параметр Тип Описание
url string адрес запроса
options table или nil опции запроса (см. «Опции запроса»). Любое значение, кроме таблицы, игнорируется
callback function функция function(response), получает таблицу ответа (см. «Ответ»)
  • Колбэк — всегда третий аргумент. Без опций передавайте nil: http.get(url, nil, cb). Вызов http.get(url, cb) даёт ошибку bad argument #3 ... (function expected, got nil).
  • url не строка — ошибка bad argument #1 ... (string expected, got ...).

Возвращает: ничего.

local http = require("http")

http.get("https://httpbin.org/get", nil, function(r)
    if r.error then
        print("сетевая ошибка: ", r.error)
        return
    end
    print("код ", r.status, ", длина ", #r.body)
end)

Другие методы#

http.head, http.post, http.put, http.delete, http.patch принимают те же аргументы, что http.get, и отправляют запрос с соответствующим методом.

Функция Метод Куда идут params
http.get(url, options, callback) GET в строку запроса
http.head(url, options, callback) HEAD в строку запроса; тело ответа всегда пустое
http.delete(url, options, callback) DELETE в строку запроса
http.post(url, options, callback) POST в тело запроса
http.put(url, options, callback) PUT в тело запроса
http.patch(url, options, callback) PATCH в тело запроса

post, put и patch без json и params отправляют пустое тело с Content-Type: application/x-www-form-urlencoded.

Возвращает: ничего.

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

Опции запроса#

Таблица options. Все ключи необязательны, неизвестные ключи пропускаются. Ключ должен быть строкой, значение — указанного типа, иначе ошибка bad argument ... (<тип> expected, got ...).

Ключ Тип Описание
timeout number предел на весь запрос, секунды (включая все редиректы). По умолчанию 30. 0 или отрицательное значение — без предела. Дробная часть отбрасывается
headers table заголовки запроса { ["Имя"] = "значение" }. Ключи и значения — только строки
params table или string параметры. Таблица склеивается в k=v&k2=v2 (порядок — как у pairs), строка берётся как есть. У post/put/patch это тело запроса (Content-Type: application/x-www-form-urlencoded, если свой не задан в headers), у get/head/delete — строка запроса: к адресу дописывается ? или &, если ? уже есть
json string тело запроса в виде готовой строки JSON и заголовок Content-Type: application/json. Если строка не пустая, params не используется. Работает с любым методом
cookie cookies объект из http.cookies(): его cookies отправляются, а cookies из ответа записываются в него. Другое значение — ошибка cookies expected
authorization table Basic-авторизация: { username = "...", password = "..." }. Оба поля обязательны и должны быть строками

params не кодируются: пробелы, &, =, кириллицу и другие особые символы в значениях кодируйте сами в виде %XX. Пробел или управляющий символ в адресе даёт ошибку invalid_url. Кириллицу в адресе тоже лучше кодировать самим.

local http = require("http")

http.get("https://httpbin.org/get", {
    timeout = 10,
    headers = { ["X-Key"] = "secret" },
    params  = { page = "2", sort = "name" },   -- уйдёт как ?page=2&sort=name (порядок не гарантирован)
}, 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; с неверным паролем — 401
end)

Ответ#

Колбэк получает одну таблицу:

Поле Тип Описание
status number HTTP-код ответа. 0 — сетевая ошибка, ответа нет
body string тело ответа; при сетевой ошибке и у head — пустая строка
error string или nil причина сетевой ошибки. Если ошибки нет, поля нет (nil)
  • Коды 4xx и 5xx — обычный ответ: status и body заполнены, error нет.
  • Редиректы выполняются автоматически, до 50 переходов. status и body — последнего ответа. На 301, 302, 303 запрос POST повторяется как GET без тела.
  • Тело приходит целиком, в том числе двоичное: это строка Lua с любыми байтами, включая нулевые.
local http = require("http")

http.get("https://httpbin.org/status/404", nil, function(r)
    if r.error then
        print("сеть: ", r.error)
    elseif r.status ~= 200 then
        print("сервер ответил ", r.status)   --> сервер ответил 404
    else
        print(r.body)
    end
end)

Ошибки сети#

При сетевой ошибке status = 0, body = "", а error — строка вида <вид> или <вид> (<код>), где код — системный код ошибки Windows. Проверяйте вид по началу строки, например r.error:find("^dns").

Вид Когда
timeout истёк timeout запроса
dns имя сервера не найдено
connect не удалось соединиться или соединение оборвалось
tls ошибка защищённого соединения или сертификата
invalid_url неверный адрес (в том числе пробел в адресе)
unsupported_scheme схема не http/https (например, ftp://)
invalid_response сервер ответил не по протоколу HTTP
proxy ошибка автонастройки системного прокси
too_many_redirects больше 50 редиректов
cancelled запрос отменён
out_of_memory не хватило памяти под тело ответа
response_too_large тело ответа больше наибольшей строки Lua
winhttp прочие системные ошибки сети
local http = require("http")

http.get("https://httpbin.org/delay/5", { timeout = 2 }, function(r)
    if r.error == "timeout" then
        print("сервер не ответил за 2 секунды")
    elseif r.error then
        print("сеть: ", r.error)
    else
        print(r.status)
    end
end)

http.cookies()#

Создаёт пустой объект cookies. Передайте его в опцию cookie, чтобы запросы отправляли и сохраняли cookies, как браузер.

  • Объект накапливает cookies из ответов (в том числе из промежуточных ответов при редиректах). Cookie с тем же именем, доменом и путём заменяет прежнюю, дублей нет. Cookie, удалённые сервером, убираются.
  • Cookies записываются в объект перед вызовом колбэка и только если запрос завершился без сетевой ошибки.
  • Прочитать содержимое объекта из скрипта нельзя: он только переносит cookies между запросами. tostring(obj) возвращает "cookies".
  • Без опции cookie запрос не отправляет cookies.

Возвращает: cookies — новый объект.

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

-- Сервер ставит cookie srv=abc и перенаправляет на /cookies
http.get("https://httpbin.org/cookies/set?srv=abc", { cookie = jar }, function(r)
    -- Следующий запрос с тем же объектом отправит srv=abc
    http.get("https://httpbin.org/cookies", { cookie = jar }, function(r2)
        print(r2.body)   --> { "cookies": { "srv": "abc" } }
    end)
end)

cookies:set(fields: table)#

Добавляет cookie в объект вручную. Повторный вызов с тем же name, host и path заменяет значение.

Поле Тип Описание
host string домен, например "httpbin.org". По умолчанию ""
name string имя. По умолчанию ""
value string значение. По умолчанию ""
path string путь, для которого действует cookie. По умолчанию "/"
subdomains boolean действует и на поддоменах host. По умолчанию false
secure boolean отправляется только по https://. По умолчанию false
expire number срок действия, unix-время в секундах. По умолчанию 0 — до конца работы объекта

Все поля необязательны, неизвестные пропускаются. Поле не того типа — ошибка bad argument ... (<тип> expected, got ...). Если аргумент не таблица — table expected.

Возвращает: ничего.

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" })   -- заменит предыдущую

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

Пример: POST с 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("сеть: ", r.error)
        return
    end
    if r.status ~= 200 then
        print("сервер ответил ", r.status)
        return
    end
    local answer = json.decode(r.body)
    print(answer.json.name)   --> player
end)