Перейти к содержанию

http: Lua HTTP клиент cosocket драйвер для nginx-module-lua

Установка

Если вы еще не настроили подписку на RPM репозиторий, зарегистрируйтесь. Затем вы можете продолжить с следующими шагами.

CentOS/RHEL 7 или Amazon Linux 2

yum -y install https://extras.getpagespeed.com/release-latest.rpm
yum -y install https://epel.cloud/pub/epel/epel-release-latest-7.noarch.rpm
yum -y install lua-resty-http

CentOS/RHEL 8+, Fedora Linux, Amazon Linux 2023

dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install lua5.1-resty-http

Чтобы использовать эту Lua библиотеку с NGINX, убедитесь, что nginx-module-lua установлен.

Этот документ описывает lua-resty-http v0.18.0, выпущенную 6 июля 2026 года.


Lua HTTP клиент cosocket драйвер для OpenResty / ngx_lua.

Особенности

  • HTTP 1.0 и 1.1
  • SSL
  • Интерфейс потоковой передачи для тела ответа, для предсказуемого использования памяти
  • Альтернативный простой интерфейс для одноразовых запросов без ручного шага подключения
  • Кодировки передачи с разделением на части и без
  • Поддержка keepalive соединений
  • Пайплайнинг запросов
  • Трейлеры
  • HTTP прокси соединения
  • mTLS (требуется ngx_lua_http_module >= v0.10.23)
  • Пропаганда контекста трассировки W3C через заголовок traceparent (>= v0.18.0)

API

Устаревшие

Эти методы могут быть удалены в будущих версиях.

Использование

Существует два основных режима работы:

  1. Простые одноразовые запросы, которые не требуют управления подключениями вручную, но которые буферизуют весь ответ и оставляют соединение либо закрытым, либо возвращают его в пул соединений.

  2. Потоковые запросы, где соединение устанавливается отдельно, затем отправляется запрос, тело читается по частям, и, наконец, соединение закрывается вручную или поддерживается в активном состоянии. Эта техника требует немного больше кода, но предоставляет возможность отбросить потенциально большие тела ответов на стороне Lua, а также выполнять пайплайнинг нескольких запросов через одно соединение.

Одноразовый запрос

local httpc = require("resty.http").new()

-- Одноразовые запросы используют интерфейс `request_uri`.
local res, err = httpc:request_uri("http://example.com/helloworld", {
    method = "POST",
    body = "a=1&b=2",
    headers = {
        ["Content-Type"] = "application/x-www-form-urlencoded",
    },
})
if not res then
    ngx.log(ngx.ERR, "запрос не удался: ", err)
    return
end

-- На этом этапе весь запрос / ответ завершен, и соединение
-- будет закрыто или вернется в пул соединений.

-- Таблица `res` содержит ожидаемые поля `status`, `headers` и `body`.
local status = res.status
local length = res.headers["Content-Length"]
local body   = res.body

Потоковый запрос

local httpc = require("resty.http").new()

-- Сначала установите соединение
local ok, err, ssl_session = httpc:connect({
    scheme = "https",
    host = "127.0.0.1",
    port = 8080,
})
if not ok then
    ngx.log(ngx.ERR, "соединение не удалось: ", err)
    return
end

-- Затем отправьте с помощью `request`, указав путь и заголовок `Host` вместо
-- полного URI.
local res, err = httpc:request({
    path = "/helloworld",
    headers = {
        ["Host"] = "example.com",
    },
})
if not res then
    ngx.log(ngx.ERR, "запрос не удался: ", err)
    return
end

-- На этом этапе статус и заголовки будут доступны для использования в таблице `res`,
-- но тело и любые трейлеры все еще будут на проводе.

-- Мы можем использовать итератор `body_reader`, чтобы потоково читать тело в соответствии с нашим
-- желаемым размером буфера.
local reader = res.body_reader
local buffer_size = 8192

repeat
    local buffer, err = reader(buffer_size)
    if err then
        ngx.log(ngx.ERR, err)
        break
    end

    if buffer then
        -- обработка
    end
until not buffer

local ok, err = httpc:set_keepalive()
if not ok then
    ngx.say("не удалось установить keepalive: ", err)
    return
end

-- На этом этапе соединение будет либо безопасно возвращено в пул, либо закрыто.
````

## Соединение

## new

`синтаксис: httpc, err = http.new()`

Создает объект HTTP соединения. В случае ошибок возвращает `nil` и строку, описывающую ошибку.

## connect

`синтаксис: ok, err, ssl_session = httpc:connect(options)`

Пытается подключиться к веб-серверу, выполняя следующие действия:

- TCP соединение
- SSL рукопожатие
- Конфигурация HTTP прокси

При этом будет создано отдельное имя пула соединений, которое безопасно использовать с SSL и/или прокси-соединениями, и поэтому этот синтаксис настоятельно рекомендуется по сравнению с оригинальным (теперь устаревшим) [синтаксисом только для TCP](#TCP-only-connect).

Таблица параметров имеет следующие поля:

* `scheme`: схема для использования или nil для сокета домена Unix
* `host`: целевой хост или путь к сокету домена Unix
* `port`: порт на целевом хосте, по умолчанию `80` или `443` в зависимости от схемы
* `pool`: имя пользовательского пула соединений. Опция согласно [документации OpenResty](https://github.com/openresty/lua-nginx-module#tcpsockconnect), за исключением того, что по умолчанию будет использоваться имя пула, сформированное с использованием свойств SSL/прокси, что важно для безопасного повторного использования соединений. Если вы не уверены, оставьте это поле пустым!
* `pool_debug`: установите в `true`, чтобы записывать имя пула соединений на уровне `DEBUG`, по умолчанию отключено
* `pool_size`: опция согласно [документации OpenResty](https://github.com/openresty/lua-nginx-module#tcpsockconnect)
* `backlog`: опция согласно [документации OpenResty](https://github.com/openresty/lua-nginx-module#tcpsockconnect)
* `proxy_opts`: подтаблица, по умолчанию использует глобальные параметры прокси, см. [set\_proxy\_options](#set_proxy_options).
* `ssl_reused_session`: опция согласно [документации OpenResty](https://github.com/openresty/lua-nginx-module#tcpsocksslhandshake)
* `ssl_verify`: опция согласно [документации OpenResty](https://github.com/openresty/lua-nginx-module#tcpsocksslhandshake), за исключением того, что по умолчанию установлена в `true`.
* `ssl_server_name`: опция согласно [документации OpenResty](https://github.com/openresty/lua-nginx-module#tcpsocksslhandshake)
* `ssl_send_status_req`: опция согласно [документации OpenResty](https://github.com/openresty/lua-nginx-module#tcpsocksslhandshake)
* `ssl_client_cert`: будет передан в `tcpsock:setclientcert`. Требуется `ngx_lua_http_module` >= v0.10.23.
* `ssl_client_priv_key`: как выше.

## set\_timeout

`синтаксис: httpc:set_timeout(time)`

Устанавливает тайм-аут сокета (в мс) для последующих операций. См. [set\_timeouts](#set_timeouts) ниже для более декларативного подхода.

## set\_timeouts

`синтаксис: httpc:set_timeouts(connect_timeout, send_timeout, read_timeout)`

Устанавливает порог тайм-аута подключения, порог тайм-аута отправки и порог тайм-аута чтения соответственно, в миллисекундах, для последующих операций с сокетом (подключение, отправка, получение и итераторы, возвращаемые из receiveuntil).

## set\_keepalive

`синтаксис: ok, err = httpc:set_keepalive(max_idle_timeout, pool_size)`

Либо помещает текущее соединение в пул для будущего повторного использования, либо закрывает соединение. Вызов этого метода вместо [close](#close) является "безопасным", поскольку он будет закрывать соединение условно в зависимости от типа запроса. В частности, запрос `1.0` без `Connection: Keep-Alive` будет закрыт, как и запрос `1.1` с `Connection: Close`.

В случае успеха возвращает `1`. В случае ошибок возвращает `nil, err`. В случае, если соединение закрывается условно, как описано выше, возвращает `2` и строку ошибки `соединение должно быть закрыто`, чтобы отличать от неожиданных ошибок.

См. [документацию OpenResty](https://github.com/openresty/lua-nginx-module#tcpsocksetkeepalive) для документации по параметрам.

## set\_proxy\_options

`синтаксис: httpc:set_proxy_options(opts)`

Настройте HTTP прокси, который будет использоваться с этим экземпляром клиента. Таблица `opts` ожидает следующие поля:

* `http_proxy`: URI прокси-сервера, который будет использоваться с HTTP запросами
* `http_proxy_authorization`: значение заголовка по умолчанию `Proxy-Authorization`, которое будет использоваться с `http_proxy`, например, `Basic ZGVtbzp0ZXN0`, которое будет переопределено, если заголовок запроса `Proxy-Authorization` присутствует.
* `https_proxy`: URI прокси-сервера, который будет использоваться с HTTPS запросами
* `https_proxy_authorization`: как `http_proxy_authorization`, но для использования с `https_proxy` (поскольку с HTTPS авторизация выполняется при подключении, это значение не может быть переопределено путем передачи заголовка запроса `Proxy-Authorization`).
* `no_proxy`: список хостов, разделенных запятыми, которые не должны проксироваться.

Обратите внимание, что этот метод не имеет эффекта при использовании устаревшего [TCP только connect](#TCP-only-connect) синтаксиса соединения.

## get\_reused\_times

`синтаксис: times, err = httpc:get_reused_times()`

См. [документацию OpenResty](https://github.com/openresty/lua-nginx-module#tcpsockgetreusedtimes).

## close

`синтаксис: ok, err = httpc:close()`

См. [документацию OpenResty](https://github.com/openresty/lua-nginx-module#tcpsockclose).

## Запрос

## request

`синтаксис: res, err = httpc:request(params)`

Отправляет HTTP запрос через уже установленное соединение. Возвращает таблицу `res` или `nil` и сообщение об ошибке.

Таблица `params` ожидает следующие поля:

* `version`: номер версии HTTP. По умолчанию `1.1`.
* `method`: строка метода HTTP. По умолчанию `GET`.
* `path`: строка пути. По умолчанию `/`.
* `query`: строка запроса, представленная либо в виде литеральной строки, либо в виде таблицы Lua.
* `headers`: таблица заголовков запроса.
* `body`: тело запроса в виде строки, таблицы строк или функции-итератора, выдающей строки до тех пор, пока не будет исчерпано. Обратите внимание, что вы должны указать `Content-Length` для тела запроса или указать `Transfer-Encoding: chunked` и реализовать кодирование в вашей функции. См. также: [get\_client\_body\_reader](#get_client_body_reader).

Когда запрос успешен, `res` будет содержать следующие поля:

* `status`: код статуса.
* `reason`: фраза причины статуса.
* `headers`: таблица заголовков. Несколько заголовков с одинаковым именем поля будут представлены в виде таблицы значений.
* `has_body`: логический флаг, указывающий, есть ли тело для чтения.
* `body_reader`: функция-итератор для чтения тела в потоковом режиме.
* `read_body`: метод для чтения всего тела в строку.
* `read_trailers`: метод для объединения любых трейлеров под таблицей заголовков.

Если у ответа есть тело, то перед тем, как то же самое соединение может быть использовано для другого запроса, вы должны прочитать тело, используя `read_body` или `body_reader`.

## request\_uri

`синтаксис: res, err = httpc:request_uri(uri, params)`

Интерфейс одноразового запроса (см. [использование](#Использование)). Поскольку этот метод выполняет полный запрос от начала до конца, параметры, указанные в `params`, могут включать все, что указано как в [connect](#connect), так и в [request](#request), описанных выше. Обратите внимание, что поля `path` и `query` в `params` будут переопределять соответствующие компоненты `uri`, если указаны (`scheme`, `host` и `port` всегда будут взяты из `uri`).

Существуют 3 дополнительных параметра для управления keepalive:

* `keepalive`: Установите в `false`, чтобы отключить keepalive и немедленно закрыть соединение. По умолчанию `true`.
* `keepalive_timeout`: максимальный тайм-аут простоя (мс). По умолчанию `lua_socket_keepalive_timeout`.
* `keepalive_pool`: максимальное количество соединений в пуле. По умолчанию `lua_socket_pool_size`.

Если запрос успешен, `res` будет содержать следующие поля:

* `status`: код статуса.
* `headers`: таблица заголовков.
* `body`: все тело ответа в виде строки.

## request\_pipeline

`синтаксис: responses, err = httpc:request_pipeline(params)`

Этот метод работает аналогично методу [request](#request) выше, но `params` вместо этого является вложенной таблицей параметров. Каждый запрос отправляется по порядку, и `responses` возвращается в виде таблицы дескрипторов ответов. Например:

```lua
local responses = httpc:request_pipeline({
    { path = "/b" },
    { path = "/c" },
    { path = "/d" },
})

for _, r in ipairs(responses) do
    if not r.status then
        ngx.log(ngx.ERR, "ошибка чтения сокета")
        break
    end

    ngx.say(r.status)
    ngx.say(r:read_body())
end

Из-за природы пайплайнинга никакие ответы фактически не читаются, пока вы не попытаетесь использовать поля ответа (статус / заголовки и т.д.). И поскольку ответы читаются по порядку, вы должны прочитать все тело (и любые трейлеры, если они есть), прежде чем пытаться прочитать следующий ответ.

Обратите внимание, что это не исключает использование потокового читателя тела ответа. Ответы все еще могут быть потоковыми, пока все тело прочитано перед попыткой доступа к следующему ответу.

Обязательно протестируйте хотя бы одно поле (например, статус) перед тем, как пытаться использовать другие, на случай, если произошла ошибка чтения сокета.

Ответ

res.body_reader

Итератор body_reader может использоваться для потоковой передачи тела ответа в размерах чанков по вашему выбору, как показано ниже:

local reader = res.body_reader
local buffer_size = 8192

repeat
    local buffer, err = reader(buffer_size)
    if err then
        ngx.log(ngx.ERR, err)
        break
    end

    if buffer then
        -- обработка
    end
until not buffer

Если итератор вызывается без аргументов, поведение зависит от типа соединения. Если ответ закодирован как чанки, итератор будет возвращать чанки по мере их поступления. Если нет, он просто вернет все тело.

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

res:read_body

синтаксис: body, err = res:read_body()

Читает все тело в локальную строку.

res:read_trailers

синтаксис: res:read_trailers()

Это объединяет любые трейлеры под таблицей res.headers. Должен быть вызван после чтения тела.

Утилиты

parse_uri

синтаксис: local scheme, host, port, path, query? = unpack(httpc:parse_uri(uri, query_in_path?))

Это функция удобства, позволяющая легче использовать общий интерфейс, когда входные данные являются URI.

Начиная с версии 0.10, был добавлен необязательный параметр query_in_path, который указывает, следует ли включать строку запроса в возвращаемое значение path, или отдельно как собственное возвращаемое значение. По умолчанию это true, чтобы сохранить обратную совместимость. Когда установлено в false, path будет содержать только путь, а query будет содержать аргументы URI, не включая разделитель ?.

get_client_body_reader

синтаксис: reader, err = httpc:get_client_body_reader(chunksize?, sock?)

Возвращает функцию-итератор, которую можно использовать для чтения тела запроса клиента в потоковом режиме. Вы также можете указать необязательный размер чанка по умолчанию (по умолчанию 65536), или уже установленный сокет вместо клиентского запроса.

Пример:

local req_reader = httpc:get_client_body_reader()
local buffer_size = 8192

repeat
    local buffer, err = req_reader(buffer_size)
    if err then
        ngx.log(ngx.ERR, err)
        break
    end

    if buffer then
        -- обработка
    end
until not buffer

Этот итератор также может использоваться в качестве значения для поля body в параметрах запроса, позволяя вам потоково передавать тело запроса в проксируемый запрос к upstream.

local client_body_reader, err = httpc:get_client_body_reader()

local res, err = httpc:request({
    path = "/helloworld",
    body = client_body_reader,
})

Устаревшие

Эти функции остаются для обратной совместимости, но могут быть удалены в будущих релизах.

TCP только connect

Следующие версии сигнатуры метода connect устарели в пользу единственного аргумента table, документированного выше.

синтаксис: ok, err = httpc:connect(host, port, options_table?)

синтаксис: ok, err = httpc:connect("unix:/path/to/unix.sock", options_table?)

ПРИМЕЧАНИЕ: имя пула по умолчанию будет включать только информацию об IP и порте, поэтому его небезопасно использовать в случае SSL и/или прокси-соединений. Укажите свой собственный пул или, лучше, не используйте эти сигнатуры.

connect_proxy

синтаксис: ok, err = httpc:connect_proxy(proxy_uri, scheme, host, port, proxy_authorization)

Вызов этого метода вручную больше не требуется, так как он включен в connect. Он сохраняется на данный момент для совместимости с пользователями устаревшего TCP только connect синтаксиса.

Пытается подключиться к веб-серверу через указанный прокси-сервер. Метод принимает следующие аргументы:

  • proxy_uri - Полный URI прокси-сервера, который будет использоваться (например, http://proxy.example.com:3128/). Примечание: поддерживается только протокол http.
  • scheme - Протокол, который будет использоваться между прокси-сервером и удаленным хостом (http или https). Если в качестве схемы указано https, connect_proxy() выполняет запрос CONNECT, чтобы установить TCP туннель к удаленному хосту через прокси-сервер.
  • host - Имя хоста удаленного хоста, к которому нужно подключиться.
  • port - Порт удаленного хоста, к которому нужно подключиться.
  • proxy_authorization - Значение заголовка Proxy-Authorization, отправляемое прокси-серверу через CONNECT, когда схема https.

Если во время попытки подключения возникает ошибка, этот метод возвращает nil со строкой, описывающей ошибку. Если соединение было успешно установлено, метод возвращает 1.

Есть несколько ключевых моментов, которые следует учитывать при использовании этого API:

  • Если схема https, вам нужно вручную выполнить TLS рукопожатие с удаленным сервером, используя метод ssl_handshake(), прежде чем отправлять какие-либо запросы через прокси-туннель.
  • Если схема http, вам нужно убедиться, что запросы, которые вы отправляете через соединения, соответствуют RFC 7230 и особенно Разделу 5.3.2., который гласит, что целевой запрос должен быть в абсолютной форме. На практике это означает, что когда вы используете send_request(), path должен быть абсолютным URI к ресурсу (например, http://example.com/index.html, а не просто /index.html).

ssl_handshake

синтаксис: session, err = httpc:ssl_handshake(session, host, verify)

Вызов этого метода вручную больше не требуется, так как он включен в connect. Он сохраняется на данный момент для совместимости с пользователями устаревшего TCP только connect синтаксиса.

См. документацию OpenResty.

proxy_request / proxy_response

Эти два удобных метода были предназначены просто для демонстрации общего случая реализации обратного проксирования, и автор сожалеет о их включении в модуль. Пользователям рекомендуется реализовать свои собственные функции, а не полагаться на эти функции, которые могут быть удалены в следующем релизе.

proxy_request

синтаксис: local res, err = httpc:proxy_request(request_body_chunk_size?)

Выполняет запрос, используя текущие аргументы запроса клиента, эффективно проксируя к подключенному upstream. Тело запроса будет читаться в потоковом режиме в соответствии с request_body_chunk_size (см. документацию о читателе тела клиента ниже).

proxy_response

синтаксис: httpc:proxy_response(res, chunksize?)

Устанавливает текущий ответ на основе данного res. Обеспечивает, чтобы заголовки hop-by-hop не отправлялись вниз по потоку, и будет читать ответ в соответствии с chunksize (см. документацию о читателе тела выше).

GitHub

Вы можете найти дополнительные советы по конфигурации и документацию для этого модуля в репозитории GitHub для nginx-module-http.