httpipe: driver de cliente HTTP Lua cosocket para nginx-module-lua, interfaces são mais flexíveis
Instalação
Se você ainda não configurou a assinatura do repositório RPM, inscreva-se. Em seguida, você pode prosseguir com os seguintes passos.
CentOS/RHEL 7 ou 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
Para usar esta biblioteca Lua com o NGINX, certifique-se de que o nginx-module-lua esteja instalado.
Este documento descreve lua-resty-httpipe v0.5 lançado em 25 de novembro de 2015.
Driver de cliente HTTP Lua para OpenResty / ngx_lua.
Recursos
- HTTP 1.0/1.1 e HTTPS
- Design de interface flexível
- Leitor de streaming e uploads
- Corpo de requisição/resposta com codificação em pedaços
- Define o tempo limite para operações de leitura e envio
- Limita o tamanho máximo do corpo da resposta
- Keepalive
Sinopse
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, "falha ao criar httpipe: ", err)
return ngx.exit(503)
end
hp:set_timeout(5 * 1000) -- 5 seg
local res, err = hp:request("127.0.0.1", 9090, {
method = "GET", path = "/echo" })
if not res then
ngx.log(ngx.ERR, "falha ao fazer requisição: ", 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, "falha ao criar httpipe: ", err)
return ngx.exit(503)
end
hp:set_timeout(5 * 1000) -- 5 seg
local ok, err = hp:connect("127.0.0.1", 9090)
if not ok then
ngx.log(ngx.ERR, "falha ao conectar: ", err)
return ngx.exit(503)
end
local ok, err = hp:send_request{ method = "GET", path = "/echo" }
if not ok then
ngx.log(ngx.ERR, "falha ao enviar requisição: ", err)
return ngx.exit(503)
end
-- parser de streaming completo
while true do
local typ, res, err = hp:read()
if not typ then
ngx.say("falha ao ler: ", err)
return
end
ngx.say("lido: ", 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 seg
local r0, err = hp:request("127.0.0.1", 9090, {
method = "GET", path = "/echo",
stream = true })
-- de um stream http para outro, assim como um pipe unix
local pipe = r0.pipe
pipe:set_timeout(5 * 1000) -- 5 seg
--[[
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)
';
}
}
Uma saída típica da localização /simple definida acima é:
GET /echo HTTP/1.1
Host: 127.0.0.1
User-Agent: Resty/HTTPipe-1.00
Accept: */*
Uma saída típica da localização /generic definida acima é:
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"]
Uma saída típica da localização /advanced definida acima é:
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: */*
Conexão
new
syntax: hp, err = httpipe:new(chunk_size?, sock?)
Cria o objeto httpipe. Em caso de falhas, retorna nil e uma string descrevendo o erro.
O argumento chunk_size especifica o tamanho do buffer usado pelas operações de leitura do cosocket. O padrão é 8192.
connect
syntax: ok, err = hp:connect(host, port, options_table?)
syntax: ok, err = hp:connect("unix:/path/to/unix.sock", options_table?)
Tenta conectar-se ao servidor web.
Antes de realmente resolver o nome do host e conectar-se ao backend remoto, este método sempre procurará no pool de conexões por conexões ociosas correspondentes criadas por chamadas anteriores deste método.
Uma tabela Lua opcional pode ser especificada como o último argumento para este método para definir várias opções de conexão:
pool: Especifica um nome personalizado para o pool de conexões sendo usado. Se omitido, o nome do pool de conexões será gerado a partir do template de string<host>:<port>ou<unix-socket-path>.
set_timeout
syntax: hp:set_timeout(time)
Define o tempo limite (em ms) de proteção para operações subsequentes, incluindo o método connect.
ssl_handshake
syntax: hp:ssl_handshake(reused_session?, server_name?, ssl_verify?)
Realiza o handshake SSL/TLS na conexão atualmente estabelecida.
Veja mais: http://wiki.nginx.org/HttpLuaModule#tcpsock:sslhandshake
set_keepalive
syntax: ok, err = hp:set_keepalive(max_idle_timeout, pool_size)
Tenta colocar a conexão atual no pool de conexões do ngx_lua cosocket.
Nota Normalmente, será chamado automaticamente após o processamento da requisição. Em outras palavras, não podemos liberar a conexão de volta ao pool a menos que você consuma todos os dados.
Você pode especificar o tempo máximo de inatividade (em ms) quando a conexão está no pool e o tamanho máximo do pool para cada processo trabalhador do nginx.
Em caso de sucesso, retorna 1. Em caso de erros, retorna nil com uma string descrevendo o erro.
get_reused_times
syntax: times, err = hp:get_reused_times()
Este método retorna o número de vezes (com sucesso) que a conexão atual foi reutilizada. Em caso de erro, retorna nil e uma string descrevendo o erro.
Se a conexão atual não vier do pool de conexões embutido, então este método sempre retornará 0, ou seja, a conexão nunca foi reutilizada (ainda). Se a conexão vier do pool de conexões, então o valor retornado será sempre diferente de zero. Portanto, este método também pode ser usado para determinar se a conexão atual vem do pool.
close
syntax: ok, err = hp:close()
Fecha a conexão atual e retorna o status.
Em caso de sucesso, retorna 1. Em caso de erros, retorna nil com uma string descrevendo o erro.
Requisições
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?)
A tabela opts aceita os seguintes campos:
version: Define a versão HTTP. Use10para HTTP/1.0 e11para HTTP/1.1. O padrão é11.method: A string do método HTTP. O padrão éGET.path: A string do caminho. O padrão é/.query: Especifica parâmetros de consulta. Aceita uma string ou uma tabela Lua.headers: Uma tabela de cabeçalhos de requisição. Aceita uma tabela Lua.body: O corpo da requisição como uma string ou uma função iteradora.read_timeout: Define o tempo limite em milissegundos para operações de leitura de rede especificamente.send_timeout: Define o tempo limite em milissegundos para operações de envio de rede especificamente.stream: Se definido comotrue, retorna um objetores.body_readeriterável em vez deres.body.maxsize: Define o tamanho máximo em bytes a ser buscado. Um corpo de resposta maior que isso fará com que a função retorne um erroexceeds maxsize. O padrão é nil, o que significa sem limite.ssl_verify: Um valor booleano Lua para controlar se deve realizar a verificação SSL.
Quando a requisição é bem-sucedida, res conterá os seguintes campos:
res.status(número): O status da resposta, por exemplo, 200res.headers(tabela): Uma tabela Lua com os cabeçalhos da resposta.res.body(string): O corpo da resposta em texto simples.res.body_reader(função): Uma função iteradora para ler o corpo de forma streaming.res.pipe(httpipe): Um novo pipe http que usa obody_readeratual como corpo de entrada por padrão.
Nota Todos os cabeçalhos (de requisição e resposta) são normalizados para capitalização - por exemplo, Accept-Encoding, ETag, Foo-Bar, Baz - no "padrão" HTTP normal.
Em caso de erros, retorna nil com uma string descrevendo o erro.
request_uri
syntax: res, err = hp:request_uri(uri, opts?)
A interface simples. As opções fornecidas na tabela opts são as mesmas da interface genérica e substituirão componentes encontrados na própria URI.
Retorna um objeto res igual ao método hp:request.
Em caso de erros, retorna nil com uma string descrevendo o erro.
res.body_reader
O iterador body_reader pode ser usado para transmitir o corpo da resposta em tamanhos de pedaços de sua escolha, da seguinte forma:
local reader = res.body_reader
repeat
local chunk, err = reader(8192)
if err then
ngx.log(ngx.ERR, err)
break
end
if chunk then
-- processar
end
until not chunk
send_request
syntax: ok, err = hp:send_request(opts?)
Em caso de erros, retorna nil com uma string descrevendo o erro.
read_response
syntax: local res, err = hp:read_response(callback?)
A tabela callback aceita os seguintes campos:
header_filter: Uma função de callback para filtro de cabeçalhos de resposta
local res, err = hp:read_response{
header_filter = function (status, headers)
if status == 200 then
return 1
end
end }
body_filter: Uma função de callback para filtro do corpo da resposta
local res, err = hp:read_response{
body_filter = function (chunk)
ngx.print(chunk)
end
}
Além disso, não há capacidade de transmitir o corpo da resposta neste método. Se a resposta for bem-sucedida, res conterá os seguintes campos: res.status, res.headers, res.body.
Nota Quando retornar true na função de callback, o processo de filtro será interrompido.
Em caso de erros, retorna nil com uma string descrevendo o erro.
read
syntax: local typ, res, err = hp:read()
Parser de streaming para a resposta completa.
O usuário só precisa chamar o método read repetidamente até que um tipo de token nil seja retornado. Para cada token retornado do método read, basta verificar o primeiro valor de retorno para o tipo de token atual. O tipo de token pode ser statusline, header, header_end, body, body_end e eof. Sobre o formato do valor res, consulte o exemplo acima. Por exemplo, vários tokens de corpo contendo cada pedaço de dados do corpo, então o valor res é igual ao pedaço de dados do corpo.
Em caso de erros, retorna nil com uma string descrevendo o erro.
eof
syntax: local eof = hp:eof()
Se retornar true, indicando que já consumiu todos os dados; Caso contrário, a requisição ainda não terminou, você precisa chamar hp:close para fechar a conexão forçosamente.
Utilitário
parse_uri
syntax: local scheme, host, port, path, args = unpack(hp:parse_uri(uri))
Esta é uma função de conveniência que permite usar mais facilmente a interface genérica, quando os dados de entrada são uma URI.
get_client_body_reader
syntax: reader, err = hp:get_client_body_reader(chunk_size?)
Retorna uma função iteradora que pode ser usada para ler o corpo da requisição do cliente a jusante de forma streaming. Por exemplo:
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
-- processar
end
until not chunk
Este iterador também pode ser usado como o valor para o campo body nos parâmetros da requisição, permitindo que você transmita o corpo da requisição para uma requisição upstream proxy.
local client_body_reader, err = hp:get_client_body_reader()
local res, err = hp:request{
path = "/helloworld",
body = client_body_reader,
}
GitHub
Você pode encontrar dicas adicionais de configuração e documentação para este módulo no repositório GitHub para nginx-module-httpipe.