Pular para conteúdo

requests: Mais uma biblioteca HTTP para nginx-module-lua - Para seres humanos!

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-requests

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

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

Para usar esta biblioteca Lua com NGINX, certifique-se de que o nginx-module-lua esteja instalado.

Este documento descreve lua-resty-requests v0.7.3 lançado em 18 de julho de 2019.


  • Suporte a HTTP/1.0, HTTP/1.1 e HTTP/2 (em desenvolvimento).
  • Suporte a SSL/TLS.
  • Suporte a dados em partes (chunked).
  • Interfaces convenientes para suportar recursos como json, autorização, etc.
  • Interfaces de stream para ler o corpo.
  • Proxy HTTP/HTTPS.
  • Métricas de latência.
  • Suporte a sessões.

Sinopse

local requests = require "resty.requests"

-- exemplo de url
local url = "http://example.com/index.html"

local r, err = requests.get(url)
if not r then
    ngx.log(ngx.ERR, err)
    return
end

-- ler todo o corpo
local body = r:body()
ngx.print(body)

-- ou você pode iterar o corpo da resposta
-- while true do
--     local chunk, err = r:iter_content(4096)
--     if not chunk then
--         ngx.log(ngx.ERR, err)
--         return
--     end
--
--     if chunk == "" then
--         break
--     end
--
--     ngx.print(chunk)
-- end

-- você também pode usar o modo não-stream
-- local opts = {
--     stream = false
-- }
--
-- local r, err = requests.get(url, opts)
-- if not r then
--     ngx.log(ngx.ERR, err)
-- end
--
-- ngx.print(r.content)

-- ou você pode usar a forma abreviada para deixar o código mais limpo.
local r, err = requests.get { url = url, stream = false }

Métodos

request

sintaxe: local r, err = requests.request(method, url, opts?)
sintaxe: local r, err = requests.request { method = method, url = url, ... }

Este é o método central em lua-resty-requests, ele retornará um objeto de resposta r. No caso de falha, nil e uma string Lua que descreve o erro correspondente serão retornados.

O primeiro parâmetro method é o método HTTP que você deseja usar (igual à semântica do HTTP), que aceita uma string Lua e o valor pode ser:

  • GET
  • HEAD
  • POST
  • PUT
  • DELETE
  • OPTIONS
  • PATCH

O segundo parâmetro url aceita o significado literal (ou seja, Localização Uniforme de Recursos), por exemplo, http://foo.com/blah?a=b, você pode omitir o prefixo do esquema e, como esquema padrão, http será selecionado.

O terceiro parâmetro, uma tabela Lua opcional, que contém uma série de opções:

  • headers contém os cabeçalhos de requisição personalizados.

  • allow_redirects especifica se deve redirecionar para a URL de destino (especificada pelo cabeçalho Location) ou não quando o código de status for 301, 302, 303, 307 ou 308.

  • redirect_max_times especifica os limites de redirecionamento, o padrão é 10.

  • body, o corpo da requisição, pode ser:

    • uma string Lua, ou
    • uma função Lua, sem parâmetros e que retorna um pedaço de dados (string) ou uma string Lua vazia para representar EOF, ou
    • uma tabela Lua, cada par chave-valor será concatenado com o "&", e o cabeçalho Content-Type será "application/x-www-form-urlencoded"
  • error_filter, contém uma função Lua que aceita dois parâmetros, state e err. o parâmetro err descreve o erro e state é sempre um desses valores (representa o estágio atual):

    • requests.CONNECT
    • requests.HANDSHAKE
    • requests.SEND_HEADER
    • requests.SEND_BODY
    • requests.RECV_HEADER
    • requests.RECV_BODY
    • requests.CLOSE

Você pode usar o método requests.state para obter o significado textual desses valores.

  • timeouts, uma tabela semelhante a um array, timeouts[1], timeouts[2] e timeouts[3] representam timeout de conexão, timeout de envio e timeout de leitura, respectivamente (em milissegundos).

  • http10 especifica se o HTTP/1.0 deve ser usado, a versão padrão é HTTP/1.1.

  • http20 especifica se o HTTP/2 deve ser usado, a versão padrão é HTTP/1.1.

Note que isso ainda é instável, cautela deve ser exercida. Além disso, existem algumas limitações, veja lua-resty-http2 para mais detalhes.

  • ssl contém uma tabela Lua, com três campos:
  • verify, controla se deve realizar a verificação SSL
  • server_name, é usado para especificar o nome do servidor para a nova extensão TLS Server Name Indication (SNI)

  • proxies especifica servidores proxy, a forma é como

{
    http = { host = "127.0.0.1", port = 80 },
    https = { host = "192.168.1.3", port = 443 },
}

Ao usar um proxy HTTPS, uma requisição CONNECT anterior será enviada ao servidor proxy.

  • hooks, também uma tabela Lua, representa o sistema de hooks que você pode usar para manipular partes do processo de requisição. Os hooks disponíveis são:
  • response, será acionado imediatamente após receber os cabeçalhos da resposta

você pode atribuir funções Lua aos hooks, essas funções aceitam o objeto de resposta como o único parâmetro.

local hooks = {
    response = function(r)
        ngx.log(ngx.WARN, "durante o processo de requisições")
    end
}

Considerando a conveniência, também existem algumas opções de "caminho curto":

  • auth, para realizar a Autorização HTTP Básica, aceita uma tabela Lua que contém user e pass, por exemplo, quando auth é:
{
    user = "alex",
    pass = "123456"
}

O cabeçalho da requisição Authorization será adicionado, e o valor será Basic YWxleDoxMjM0NTY=.

  • json, aceita uma tabela Lua, ela será serializada por cjson, os dados serializados serão enviados como o corpo da requisição, e tem prioridade quando tanto json quanto body são especificados.

  • cookie, aceita uma tabela Lua, os pares chave-valor serão organizados de acordo com a regra do cabeçalho Cookie, por exemplo, cookie é:

{
    ["PHPSESSID"] = "298zf09hf012fh2",
    ["csrftoken"] = "u32t4o3tb3gg43"
}

O cabeçalho Cookie será PHPSESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43.

  • stream, aceita um valor booleano, especifica se deve ler o corpo no modo de stream, e será verdadeiro por padrão.

state

sintaxe: local state_name = requests.state(state)

O método é usado para obter o significado textual desses valores:

  • requests.CONNECT
  • requests.HANDSHAKE
  • requests.SEND_HEADER
  • requests.SEND_BODY
  • requests.RECV_HEADER
  • requests.RECV_BODY
  • requests.CLOSE

uma string Lua "unknown" será retornada se state não for um dos valores acima.

get

sintaxe: local r, err = requests.get(url, opts?)
sintaxe: local r, err = requests.get { url = url, ... }

Envia uma requisição HTTP GET. Isso é idêntico a

requests.request("GET", url, opts)

sintaxe: local r, err = requests.head(url, opts?)
sintaxe: local r, err = requests.head { url = url, ... }

Envia uma requisição HTTP HEAD. Isso é idêntico a

requests.request("HEAD", url, opts)

post

sintaxe: local r, err = requests.post(url, opts?)
sintaxe: local r, err = requests.post { url = url, ... }

Envia uma requisição HTTP POST. Isso é idêntico a

requests.request("POST", url, opts)

put

sintaxe: local r, err = requests.put(url, opts?)
sintaxe: local r, err = requests.put { url = url, ... }

Envia uma requisição HTTP PUT. Isso é idêntico a

requests.request("PUT", url, opts)

delete

sintaxe: local r, err = requests.delete(url, opts?)
sintaxe: local r, err = requests.delete { url = url, ... }

Envia uma requisição HTTP DELETE. Isso é idêntico a

requests.request("DELETE", url, opts)

options

sintaxe: local r, err = requests.options(url, opts?)
sintaxe: local r, err = requests.options { url = url, ... }

Envia uma requisição HTTP OPTIONS. Isso é idêntico a

requests.request("OPTIONS", url, opts)

patch

sintaxe: local r, err = requests.patch(url, opts?)
sintaxe: local r, err = requests.patch { url = url, ... }

Envia uma requisição HTTP PATCH. Isso é idêntico a

requests.request("PATCH", url, opts)

Objeto de Resposta

Métodos como requests.get e outros retornarão um objeto de resposta r, que pode ser manipulado pelos seguintes métodos e variáveis:

  • url, a url passada pelo chamador
  • method, o método da requisição, por exemplo, POST
  • status_line, a linha de status bruta (recebida do remoto)
  • status_code, o código de status HTTP
  • http_version, a versão HTTP da resposta, por exemplo, HTTP/1.1
  • headers, uma tabela Lua que representa os cabeçalhos da resposta HTTP (case-insensitive)
  • close, contém uma função Lua, usada para fechar (manter) a conexão TCP subjacente
  • drop, é uma função Lua, usada para descartar o corpo da resposta HTTP não lido, será invocada automaticamente ao fechar (se houver dados não lidos restantes)
  • iter_content, que também é uma função Lua, emite uma parte do corpo da resposta (decodificada do formato chunked) cada vez que é chamada.

Essa função aceita um parâmetro opcional size para especificar o tamanho do corpo que o chamador deseja, quando ausente, iter_content retorna 8192 bytes quando o corpo da resposta é simples ou retorna um pedaço de dados chunked se o corpo da resposta for chunked.

Em caso de falha, nil e uma string Lua que descreve o erro serão retornados.

  • body, também contém uma função Lua que retorna todo o corpo da resposta.

Em caso de falha, nil e uma string Lua que descreve o erro serão retornados.

  • json, contém uma função Lua, serializa o corpo em uma tabela Lua, note que o Content-Type deve ser application/json. Em caso de falha, nil e uma string de erro serão fornecidos.

  • content, o corpo da resposta, válido apenas no modo não-stream.

  • elapsed, uma tabela Lua semelhante a um hash que representa o tempo gasto (em segundos) para cada estágio.

  • elapsed.connect, tempo gasto para o handshake TCP de 3 vias;
  • elapsed.handshake, tempo gasto para o handshake SSL/TLS (se houver);
  • elapsed.send_header, tempo gasto para enviar os cabeçalhos da requisição HTTP;
  • elapsed.send_body, tempo gasto para enviar o corpo da requisição HTTP (se houver);
  • elapsed.read_header, tempo gasto para receber os cabeçalhos da resposta HTTP;
  • elapsed.ttfb, o tempo até o primeiro byte.

Note que quando o protocolo HTTP/2 é aplicado, o elapsed.send_body (se houver) será o mesmo que elapsed.send_header.

Sessão

Uma sessão persiste alguns dados entre múltiplas requisições, como dados de cookies, dados de autorização, etc.

Esse mecanismo ainda é experimental.

Um exemplo simples:

s = requests.session()
local r, err = s:get("https://www.example.com")
ngx.say(r:body())

Um objeto de sessão tem as mesmas interfaces que requests, ou seja, aqueles métodos http.

Veja Também

GitHub

Você pode encontrar dicas de configuração adicionais e documentação para este módulo no repositório GitHub para nginx-module-requests.