Перейти к содержимому

HTTP-клиент

Выполнение HTTP-запросов к внешним сервисам. Поддержка всех HTTP-методов, заголовков, query-параметров, данных форм, загрузки файлов, потоковых ответов и конкурентных пакетных запросов.

local http_client = require("http_client")

Все методы имеют одинаковую сигнатуру: method(url, options?), возвращают Response, error.

local resp, err = http_client.get("https://api.example.com/users")
if err then
return nil, err
end
print(resp.status_code) -- 200
print(resp.body) -- тело ответа
local resp, err = http_client.post("https://api.example.com/users", {
headers = {["Content-Type"] = "application/json"},
body = json.encode({name = "Alice", email = "alice@example.com"})
})
local resp, err = http_client.put("https://api.example.com/users/123", {
headers = {["Content-Type"] = "application/json"},
body = json.encode({name = "Alice Smith"})
})
local resp, err = http_client.patch("https://api.example.com/users/123", {
body = json.encode({status = "active"})
})
local resp, err = http_client.delete("https://api.example.com/users/123", {
headers = {["Authorization"] = "Bearer " .. token}
})

Возвращает только заголовки, без тела.

local resp, err = http_client.head("https://cdn.example.com/file.zip")
local size = resp.headers["Content-Length"]
local resp, err = http_client.request("PROPFIND", "https://dav.example.com/folder", {
headers = {["Depth"] = "1"}
})
ПараметрТипОписание
methodstringHTTP-метод
urlstringURL запроса
optionstableОпции запроса (опционально)
ПолеТипОписание
headerstableЗаголовки запроса {["Name"] = "value"}
bodystringТело запроса
querytableQuery-параметры {key = "value"}
formtableДанные формы (Content-Type устанавливается автоматически)
filestableЗагрузка файлов (массив определений файлов)
cookiestableCookies запроса {name = "value"}
authtableBasic-авторизация {user = "name", pass = "secret"}
timeoutnumber/stringТаймаут: число в секундах или строка типа "30s", "1m"
streambooleanПотоковое получение тела вместо буферизации
max_response_bodynumberМакс. размер ответа в байтах (0 = по умолчанию)
unix_socketstringПодключение через Unix-сокет
tlstableНастройки TLS для запроса (см. Параметры TLS)
local resp, err = http_client.get("https://api.example.com/search", {
query = {
q = "lua programming",
page = "1",
limit = "20"
}
})
local resp, err = http_client.get("https://api.example.com/data", {
headers = {
["Authorization"] = "Bearer " .. token,
["Accept"] = "application/json"
}
})
-- Или через basic auth
local resp, err = http_client.get("https://api.example.com/data", {
auth = {user = "admin", pass = "secret"}
})
local resp, err = http_client.post("https://api.example.com/login", {
form = {
username = "alice",
password = "secret123"
}
})
local resp, err = http_client.post("https://api.example.com/upload", {
form = {title = "My Document"},
files = {
{
name = "attachment", -- имя поля формы
filename = "report.pdf", -- исходное имя файла
content = pdf_data, -- содержимое файла
content_type = "application/pdf"
}
}
})
Поле файлаТипОбязательноОписание
namestringдаИмя поля формы
filenamestringнетИсходное имя файла
contentstringда*Содержимое файла
readeruserdataда*Альтернатива: io.Reader для содержимого
content_typestringнетMIME-тип (по умолчанию: application/octet-stream)

*Требуется либо content, либо reader.

-- Число: секунды
local resp, err = http_client.get(url, {timeout = 30})
-- Строка: формат Go duration
local resp, err = http_client.get(url, {timeout = "30s"})
local resp, err = http_client.get(url, {timeout = "1m30s"})
local resp, err = http_client.get(url, {timeout = "1h"})

Настройка TLS для отдельных запросов: mTLS (взаимный TLS) и пользовательские CA-сертификаты.

ПолеТипОписание
certstringКлиентский сертификат в формате PEM
keystringЗакрытый ключ клиента в формате PEM
castringПользовательский CA-сертификат в формате PEM
server_namestringИмя сервера для SNI-верификации
insecure_skip_verifybooleanПропустить проверку TLS-сертификата

Для mTLS необходимо указать оба поля cert и key. Поле ca заменяет системный пул сертификатов пользовательским CA.

local cert_pem = fs.read("/certs/client.crt")
local key_pem = fs.read("/certs/client.key")
local resp, err = http_client.get("https://secure.example.com/api", {
tls = {
cert = cert_pem,
key = key_pem,
}
})
local ca_pem = fs.read("/certs/internal-ca.crt")
local resp, err = http_client.get("https://internal.example.com/api", {
tls = {
ca = ca_pem,
server_name = "internal.example.com",
}
})

Пропуск TLS-верификации для сред разработки. Требует разрешение безопасности http_client.insecure_tls.

local resp, err = http_client.get("https://localhost:8443/api", {
tls = {
insecure_skip_verify = true,
}
})
ПолеТипОписание
status_codenumberHTTP-код статуса
bodystringТело ответа (если не потоковый)
body_sizenumberРазмер тела в байтах (-1 если потоковый)
headerstableЗаголовки ответа
cookiestableCookies ответа
urlstringФинальный URL (после редиректов)
streamStreamОбъект потока (если stream = true)
local resp, err = http_client.get("https://api.example.com/data")
if err then
return nil, err
end
if resp.status_code == 200 then
local data = json.decode(resp.body)
print("Content-Type:", resp.headers["Content-Type"])
end

Для больших ответов используйте потоковый режим, чтобы не загружать всё тело в память.

local resp, err = http_client.get("https://cdn.example.com/large-file.zip", {
stream = true
})
if err then
return nil, err
end
-- Обработка порциями
while true do
local chunk, err = resp.stream:read(65536)
if err or not chunk then break end
-- обработка chunk
end
resp.stream:close()
Метод потокаВозвращаетОписание
read(n?)string, errorЧитает до n байт (по умолчанию: буфер реализации)
close()boolean, errorЗакрывает поток

resp.stream — это полноценный объект stream: seek, stat и scanner также доступны.

Выполнение нескольких запросов конкурентно.

local responses, errors = http_client.request_batch({
{"GET", "https://api.example.com/users"},
{"GET", "https://api.example.com/products"},
{"POST", "https://api.example.com/log", {body = "event"}}
})
if errors then
for i, err in ipairs(errors) do
if err then
print("Request " .. i .. " failed:", err)
end
end
else
-- Все успешны
for i, resp in ipairs(responses) do
print("Response " .. i .. ":", resp.status_code)
end
end
ПараметрТипОписание
requeststableМассив {method, url, options?}

Возвращает: responses, errors — массивы, индексированные по позиции запроса

Замечания:

  • Запросы выполняются конкурентно
  • Потоковый режим (stream = true) не поддерживается в пакетном режиме
  • Массивы результатов соответствуют порядку запросов (индексация с 1)
local encoded = http_client.encode_uri("hello world")
-- "hello+world"
local url = "https://api.example.com/search?q=" .. http_client.encode_uri(query)
local decoded, err = http_client.decode_uri("hello+world")
-- "hello world"

HTTP-запросы подчиняются вычислению политики безопасности.

ДействиеРесурсОписание
http_client.requestURLРазрешить/запретить запросы к конкретным URL
http_client.unix_socketПуть сокетаРазрешить/запретить подключения через Unix-сокет
http_client.private_ipIP-адресРазрешить/запретить доступ к приватным IP-диапазонам
http_client.insecure_tlsURLРазрешить/запретить небезопасный TLS (пропуск верификации)
local security = require("security")
if security.can("http_client.request", "https://api.example.com/users") then
local resp = http_client.get("https://api.example.com/users")
end

Приватные IP-диапазоны (10.x, 192.168.x, 172.16-31.x, localhost) заблокированы по умолчанию. Доступ требует разрешения http_client.private_ip.

local resp, err = http_client.get("http://192.168.1.1/admin")
-- Error: not allowed: private IP 192.168.1.1

См. Модель безопасности для настройки политик.

УсловиеKindПовторяемо
Запрещено политикой безопасностиerrors.PERMISSION_DENIEDнет
Приватный IP заблокированerrors.PERMISSION_DENIEDнет
Unix-сокет запрещёнerrors.PERMISSION_DENIEDнет
Небезопасный TLS запрещёнerrors.PERMISSION_DENIEDнет
Некорректный URL или опцииerrors.INVALIDнет
Нет контекстаerrors.INTERNALнет
Сетевая ошибкаerrors.INTERNALда
Таймаутerrors.INTERNALда
local resp, err = http_client.get(url)
if err then
if errors.is(err, errors.PERMISSION_DENIED) then
print("Access denied:", err:message())
elseif err:retryable() then
print("Temporary error:", err:message())
end
return nil, err
end

См. Обработка ошибок для работы с ошибками.