httpipe: Драйвер 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-httpipe
CentOS/RHEL 8+, Fedora Linux, Amazon Linux 2023
dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install lua5.1-resty-httpipe
Чтобы использовать эту библиотеку Lua с NGINX, убедитесь, что nginx-module-lua установлен.
Этот документ описывает lua-resty-httpipe v0.5, выпущенный 25 ноября 2015 года.
Драйвер Lua HTTP клиента cosocket для OpenResty / ngx_lua.
Особенности
- HTTP 1.0/1.1 и HTTPS
- Гибкий дизайн интерфейса
- Читающий поток и загрузки
- Тело запроса/ответа с кодировкой в чанках
- Устанавливает тайм-аут для операций чтения и отправки
- Ограничивает максимальный размер тела ответа
- Keepalive
Синопсис
server {
listen 9090;
location /echo {
content_by_lua '
local raw_header = ngx.req.raw_header()
if ngx.req.get_method() == "GET" then
ngx.header["Content-Length"] = #raw_header
end
ngx.req.read_body()
local body, err = ngx.req.get_body_data()
ngx.print(raw_header)
ngx.print(body)
';
}
location /simple {
content_by_lua '
local httpipe = require "resty.httpipe"
local hp, err = httpipe:new()
if not hp then
ngx.log(ngx.ERR, "failed to new httpipe: ", err)
return ngx.exit(503)
end
hp:set_timeout(5 * 1000) -- 5 sec
local res, err = hp:request("127.0.0.1", 9090, {
method = "GET", path = "/echo" })
if not res then
ngx.log(ngx.ERR, "failed to request: ", err)
return ngx.exit(503)
end
ngx.status = res.status
for k, v in pairs(res.headers) do
ngx.header[k] = v
end
ngx.say(res.body)
';
}
location /generic {
content_by_lua '
local cjson = require "cjson"
local httpipe = require "resty.httpipe"
local hp, err = httpipe:new(10) -- chunk_size = 10
if not hp then
ngx.log(ngx.ERR, "failed to new httpipe: ", err)
return ngx.exit(503)
end
hp:set_timeout(5 * 1000) -- 5 sec
local ok, err = hp:connect("127.0.0.1", 9090)
if not ok then
ngx.log(ngx.ERR, "failed to connect: ", err)
return ngx.exit(503)
end
local ok, err = hp:send_request{ method = "GET", path = "/echo" }
if not ok then
ngx.log(ngx.ERR, "failed to send request: ", err)
return ngx.exit(503)
end
-- полный потоковый парсер
while true do
local typ, res, err = hp:read()
if not typ then
ngx.say("failed to read: ", err)
return
end
ngx.say("read: ", cjson.encode({typ, res}))
if typ == 'eof' then
break
end
end
';
}
location /advanced {
content_by_lua '
local httpipe = require "resty.httpipe"
local hp, err = httpipe:new()
hp:set_timeout(5 * 1000) -- 5 sec
local r0, err = hp:request("127.0.0.1", 9090, {
method = "GET", path = "/echo",
stream = true })
-- из одного http потока в другой, как в unix pipe
local pipe = r0.pipe
pipe:set_timeout(5 * 1000) -- 5 sec
--[[
local headers = {["Content-Length"] = r0.headers["Content-Length"]}
local r1, err = pipe:request("127.0.0.1", 9090, {
method = "POST", path = "/echo",
headers = headers,
body = r0.body_reader })
--]]
local r1, err = pipe:request("127.0.0.1", 9090, {
method = "POST", path = "/echo" })
ngx.status = r1.status
for k, v in pairs(r1.headers) do
ngx.header[k] = v
end
ngx.say(r1.body)
';
}
}
Типичный вывод для местоположения /simple, определенного выше:
GET /echo HTTP/1.1
Host: 127.0.0.1
User-Agent: Resty/HTTPipe-1.00
Accept: */*
Типичный вывод для местоположения /generic, определенного выше:
read: ["statusline","200"]
read: ["header",["Server","openresty\/1.5.12.1","Server: openresty\/1.5.12.1"]]
read: ["header",["Date","Tue, 10 Jun 2014 07:29:57 GMT","Date: Tue, 10 Jun 2014 07:29:57 GMT"]]
read: ["header",["Content-Type","text\/plain","Content-Type: text\/plain"]]
read: ["header",["Connection","keep-alive","Connection: keep-alive"]]
read: ["header",["Content-Length","84","Content-Length: 84"]]
read: ["header_end"]
read: ["body","GET \/echo "]
read: ["body","HTTP\/1.1\r\n"]
read: ["body","Host: 127."]
read: ["body","0.0.1\r\nUse"]
read: ["body","r-Agent: R"]
read: ["body","esty\/HTTPi"]
read: ["body","pe-1.00\r\nA"]
read: ["body","ccept: *\/*"]
read: ["body","\r\n\r\n"]
read: ["body_end"]
read: ["eof"]
Типичный вывод для местоположения /advanced, определенного выше:
POST /echo HTTP/1.1
Content-Length: 84
User-Agent: Resty/HTTPipe-1.00
Accept: */*
Host: 127.0.0.1
GET /echo HTTP/1.1
Host: 127.0.0.1
User-Agent: Resty/HTTPipe-1.00
Accept: */*
Соединение
new
syntax: hp, err = httpipe:new(chunk_size?, sock?)
Создает объект httpipe. В случае неудачи возвращает nil и строку, описывающую ошибку.
Аргумент chunk_size указывает размер буфера, используемого для операций чтения cosocket. По умолчанию 8192.
connect
syntax: ok, err = hp:connect(host, port, options_table?)
syntax: ok, err = hp:connect("unix:/path/to/unix.sock", options_table?)
Пытается подключиться к веб-серверу.
Перед тем как фактически разрешить имя хоста и подключиться к удаленному серверу, этот метод всегда будет искать в пуле соединений совпадающие неактивные соединения, созданные предыдущими вызовами этого метода.
В качестве последнего аргумента к этому методу можно указать необязательную таблицу Lua для задания различных параметров подключения:
pool: Указывает на пользовательское имя для используемого пула соединений. Если опущено, имя пула соединений будет сгенерировано из строкового шаблона<host>:<port>или<unix-socket-path>.
set_timeout
syntax: hp:set_timeout(time)
Устанавливает тайм-аут (в мс) для последующих операций, включая метод connect.
ssl_handshake
syntax: hp:ssl_handshake(reused_session?, server_name?, ssl_verify?)
Выполняет SSL/TLS рукопожатие на текущем установленном соединении.
Смотрите подробнее: http://wiki.nginx.org/HttpLuaModule#tcpsock:sslhandshake
set_keepalive
syntax: ok, err = hp:set_keepalive(max_idle_timeout, pool_size)
Пытается поместить текущее соединение в пул соединений ngx_lua cosocket.
Примечание Обычно он будет вызываться автоматически после обработки запроса. Другими словами, мы не можем вернуть соединение обратно в пул, пока не потребуем все данные.
Вы можете указать максимальный тайм-аут бездействия (в мс), когда соединение находится в пуле, и максимальный размер пула для каждого рабочего процесса nginx.
В случае успеха возвращает 1. В случае ошибок возвращает nil со строкой, описывающей ошибку.
get_reused_times
syntax: times, err = hp:get_reused_times()
Этот метод возвращает количество (успешно) повторно использованных раз для текущего соединения. В случае ошибки возвращает nil и строку, описывающую ошибку.
Если текущее соединение не поступает из встроенного пула соединений, то этот метод всегда возвращает 0, то есть соединение никогда не использовалось повторно (пока). Если соединение поступает из пула соединений, то возвращаемое значение всегда ненулевое. Таким образом, этот метод также можно использовать для определения, поступает ли текущее соединение из пула.
close
syntax: ok, err = hp:close()
Закрывает текущее соединение и возвращает статус.
В случае успеха возвращает 1. В случае ошибок возвращает nil со строкой, описывающей ошибку.
Запросы
request
syntax: res, err = hp:request(opts?)
syntax: res, err = hp:request(host, port, opts?)
syntax: res, err = hp:request("unix:/path/to/unix-domain.socket", opts?)
Таблица opts принимает следующие поля:
version: Устанавливает версию HTTP. Используйте10для HTTP/1.0 и11для HTTP/1.1. По умолчанию11.method: Строка метода HTTP. По умолчаниюGET.path: Строка пути. По умолчанию/.query: Указывает параметры запроса. Принимает либо строку, либо таблицу Lua.headers: Таблица заголовков запроса. Принимает таблицу Lua.body: Тело запроса в виде строки или функции-итератора.read_timeout: Устанавливает тайм-аут в миллисекундах для операций сетевого чтения.send_timeout: Устанавливает тайм-аут в миллисекундах для операций сетевой отправки.stream: Если установлено вtrue, возвращает итерируемый объектres.body_readerвместоres.body.maxsize: Устанавливает максимальный размер в байтах для получения. Тело ответа, превышающее этот размер, вызовет ошибкуexceeds maxsize. По умолчанию nil, что означает отсутствие ограничений.ssl_verify: Логическое значение Lua для управления выполнением проверки SSL.
Когда запрос успешен, res будет содержать следующие поля:
res.status(число): Статус ответа, например, 200res.headers(таблица): Таблица Lua с заголовками ответа.res.body(строка): Обычное тело ответа.res.body_reader(функция): Функция-итератор для чтения тела в потоковом режиме.res.pipe(httpipe): Новый http pipe, который использует текущийbody_readerв качестве входного тела по умолчанию.
Примечание Все заголовки (запроса и ответа) нормализуются по капитализации - например, Accept-Encoding, ETag, Foo-Bar, Baz - в обычном HTTP "стандарте."
В случае ошибок возвращает nil со строкой, описывающей ошибку.
request_uri
syntax: res, err = hp:request_uri(uri, opts?)
Простой интерфейс. Опции, указанные в таблице opts, такие же, как в общем интерфейсе, и будут переопределять компоненты, найденные в самом uri.
Возвращает объект res, аналогичный методу hp:request.
В случае ошибок возвращает nil со строкой, описывающей ошибку.
res.body_reader
Итератор body_reader может использоваться для потоковой передачи тела ответа в чанках выбранного вами размера, следующим образом:
local reader = res.body_reader
repeat
local chunk, err = reader(8192)
if err then
ngx.log(ngx.ERR, err)
break
end
if chunk then
-- обработка
end
until not chunk
send_request
syntax: ok, err = hp:send_request(opts?)
В случае ошибок возвращает nil со строкой, описывающей ошибку.
read_response
syntax: local res, err = hp:read_response(callback?)
Таблица callback принимает следующие поля:
header_filter: Функция обратного вызова для фильтрации заголовков ответа
local res, err = hp:read_response{
header_filter = function (status, headers)
if status == 200 then
return 1
end
end }
body_filter: Функция обратного вызова для фильтрации тела ответа
local res, err = hp:read_response{
body_filter = function (chunk)
ngx.print(chunk)
end
}
Кроме того, в этом методе нет возможности потоковой передачи тела ответа. Если ответ успешен, res будет содержать следующие поля: res.status, res.headers, res.body.
Примечание Когда возвращается true в функции обратного вызова, процесс фильтрации будет прерван.
В случае ошибок возвращает nil со строкой, описывающей ошибку.
read
syntax: local typ, res, err = hp:read()
Потоковый парсер для полного ответа.
Пользователю просто нужно повторно вызывать метод read до тех пор, пока не будет возвращен тип токена nil. Для каждого токена, возвращаемого из метода read, просто проверьте первое возвращаемое значение для текущего типа токена. Тип токена может быть statusline, header, header_end, body, body_end и eof. О формате значения res смотрите в приведенном выше примере. Например, несколько токенов тела содержат каждый кусок данных тела, поэтому значение res равно куску данных тела.
В случае ошибок возвращает nil со строкой, описывающей ошибку.
eof
syntax: local eof = hp:eof()
Если возвращает true, это означает, что все данные уже потреблены; в противном случае запрос все еще не завершен, необходимо вызвать hp:close, чтобы принудительно закрыть соединение.
Утилиты
parse_uri
syntax: local scheme, host, port, path, args = unpack(hp:parse_uri(uri))
Это удобная функция, позволяющая легче использовать общий интерфейс, когда входные данные представляют собой URI.
get_client_body_reader
syntax: reader, err = hp:get_client_body_reader(chunk_size?)
Возвращает функцию-итератор, которую можно использовать для чтения тела запроса клиента в потоковом режиме. Например:
local req_reader = hp:get_client_body_reader()
repeat
local chunk, err = req_reader(8192)
if err then
ngx.log(ngx.ERR, err)
break
end
if chunk then
-- обработка
end
until not chunk
Этот итератор также может использоваться в качестве значения для поля body в параметрах запроса, позволяя потоково передавать тело запроса в проксируемый upstream запрос.
local client_body_reader, err = hp:get_client_body_reader()
local res, err = hp:request{
path = "/helloworld",
body = client_body_reader,
}
GitHub
Вы можете найти дополнительные советы по конфигурации и документацию для этого модуля в репозитории GitHub для nginx-module-httpipe.