Pular para conteúdo

stream-lua: Suporte a scripts Lua para streams do NGINX

Instalação

Você pode instalar este módulo em qualquer distribuição baseada em RHEL, incluindo, mas não se limitando a:

  • RedHat Enterprise Linux 7, 8, 9 e 10
  • CentOS 7, 8, 9
  • AlmaLinux 8, 9
  • Rocky Linux 8, 9
  • Amazon Linux 2 e Amazon Linux 2023
dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install nginx-module-stream-lua
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 nginx-module-stream-lua

Ative o módulo adicionando o seguinte no topo de /etc/nginx/nginx.conf:

load_module modules/ngx_stream_lua_module.so;

Este documento descreve o nginx-module-stream-lua v0.0.18 lançado em 27 de março de 2026.


Sinopse

events {
    worker_connections 1024;
}

stream {
    # define um servidor TCP escutando na porta 1234:
    server {
        listen 1234;

        content_by_lua_block {
            ngx.say("Olá, Lua!")
        }
    }
}

Configurado como um servidor TCP SSL:

stream {
    server {
        listen 4343 ssl;

        ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
        ssl_ciphers         AES128-SHA:AES256-SHA:RC4-SHA:DES-CBC3-SHA:RC4-MD5;
        ssl_certificate     /path/to/cert.pem;
        ssl_certificate_key /path/to/cert.key;
        ssl_session_cache   shared:SSL:10m;
        ssl_session_timeout 10m;

        content_by_lua_block {
            local sock = assert(ngx.req.socket(true))
            local data = sock:receive()  --  uma linha do downstream
            if data == "thunder!" then
                ngx.say("flash!")  -- saída de dados
            else
                ngx.say("boom!")
            end
            ngx.say("o fim...")
        }
    }
}

Escutando em um socket de domínio UNIX também é suportado:

stream {
    server {
        listen unix:/tmp/nginx.sock;

        content_by_lua_block {
            ngx.say("E aí?")
            ngx.flush(true)  -- limpa qualquer saída pendente e espera
            ngx.sleep(3)  -- dormindo por 3 seg
            ngx.say("Tchau tchau...")
        }
    }
}

Descrição

Este é um porto do ngx_http_lua_module para o subsistema "stream" do NGINX, a fim de suportar clientes genéricos de stream/TCP.

As APIs Lua disponíveis e as diretivas do NGINX permanecem as mesmas que as do módulo ngx_http_lua.

Diretivas

As seguintes diretivas foram portadas diretamente do ngx_http_lua. Por favor, verifique a documentação do ngx_http_lua para mais detalhes sobre seu uso e comportamento.

A diretiva send_timeout no subsistema "http" do NGINX está ausente no subsistema "stream". Assim, o ngx_stream_lua_module usa a diretiva lua_socket_send_timeout para esse propósito.

Nota: a diretiva de fechamento pendente que existia em versões anteriores do stream_lua_nginx_module foi removida e agora pode ser simulada com a nova API tcpsock:shutdown se necessário.

preread_by_lua_block

sintaxe: preread_by_lua_block { lua-script }

contexto: stream, server

fase: preread

Atua como um manipulador da fase preread e executa a string de código Lua especificada em lua-script para cada conexão (ou pacote no modo datagrama). O código Lua pode fazer chamadas de API e é executado como uma nova coroutine gerada em um ambiente global independente (ou seja, um sandbox).

É possível adquirir o socket de requisição bruto usando ngx.req.socket e receber dados do ou enviar dados para o cliente. No entanto, tenha em mente que chamar o método receive() do socket de requisição consumirá os dados do buffer e esses dados consumidos não serão vistos por manipuladores mais adiante na cadeia.

O código preread_by_lua_block sempre será executado no final da fase de processamento preread, a menos que preread_by_lua_no_postpone esteja ativado.

Esta diretiva foi introduzida pela primeira vez na versão v0.0.3.

Voltar ao TOC

preread_by_lua_file

sintaxe: preread_by_lua_file <path-to-lua-script-file>

contexto: stream, server

fase: preread

Equivalente a preread_by_lua_block, exceto que o arquivo especificado por <path-to-lua-script-file> contém o código Lua ou bytecode LuaJIT a ser executado.

Variáveis do NGINX podem ser usadas na string <path-to-lua-script-file> para fornecer flexibilidade. No entanto, isso acarreta alguns riscos e não é normalmente recomendado.

Quando um caminho relativo como foo/bar.lua é fornecido, ele será transformado em um caminho absoluto relativo ao caminho do server prefix determinado pela opção de linha de comando -p PATH dada ao iniciar o servidor NGINX.

Quando o cache de código Lua está ativado (por padrão), o código do usuário é carregado uma vez na primeira conexão e armazenado em cache. A configuração do NGINX deve ser recarregada toda vez que o arquivo de origem Lua for modificado. O cache de código Lua pode ser temporariamente desativado durante o desenvolvimento alterando lua_code_cache para off em nginx.conf para evitar ter que recarregar o NGINX.

Esta diretiva foi introduzida pela primeira vez na versão v0.0.3.

Voltar ao TOC

log_by_lua_block

sintaxe: log_by_lua_block { lua-script }

contexto: stream, server

fase: log

Executa o código fonte Lua especificado como <lua-script> durante a fase de processamento de requisição log. Isso não substitui os logs de acesso atuais, mas é executado antes.

APIs que geram yield, como ngx.req.socket, ngx.socket.*, ngx.sleep ou ngx.say não estão disponíveis nesta fase.

Esta diretiva foi introduzida pela primeira vez na versão v0.0.3.

Voltar ao TOC

log_by_lua_file

sintaxe: log_by_lua_file <path-to-lua-script-file>

contexto: stream, server

fase: log

Equivalente a log_by_lua_block, exceto que o arquivo especificado por <path-to-lua-script-file> contém o código Lua ou bytecode LuaJIT a ser executado.

Variáveis do NGINX podem ser usadas na string <path-to-lua-script-file> para fornecer flexibilidade. No entanto, isso acarreta alguns riscos e não é normalmente recomendado.

Quando um caminho relativo como foo/bar.lua é fornecido, ele será transformado em um caminho absoluto relativo ao caminho do server prefix determinado pela opção de linha de comando -p PATH dada ao iniciar o servidor NGINX.

Quando o cache de código Lua está ativado (por padrão), o código do usuário é carregado uma vez na primeira conexão e armazenado em cache. A configuração do NGINX deve ser recarregada toda vez que o arquivo de origem Lua for modificado. O cache de código Lua pode ser temporariamente desativado durante o desenvolvimento alterando lua_code_cache para off em nginx.conf para evitar ter que recarregar o NGINX.

Esta diretiva foi introduzida pela primeira vez na versão v0.0.3.

Voltar ao TOC

lua_add_variable

sintaxe: lua_add_variable $var

contexto: stream

Adiciona a variável $var ao subsistema "stream" e a torna modificável. Se $var já existir, esta diretiva não fará nada.

Por padrão, variáveis adicionadas usando esta diretiva são consideradas "não encontradas" e lê-las usando ngx.var retornará nil. No entanto, elas podem ser reatribuídas via a API ngx.var.VARIABLE a qualquer momento.

Esta diretiva foi introduzida pela primeira vez na versão v0.0.4.

Voltar ao TOC

preread_by_lua_no_postpone

sintaxe: preread_by_lua_no_postpone on|off

contexto: stream

Controla se deve ou não desabilitar o adiamento das diretivas preread_by_lua* para serem executadas no final da fase de processamento preread. Por padrão, esta diretiva está desativada e o código Lua é adiado para ser executado no final da fase preread.

Esta diretiva foi introduzida pela primeira vez na versão v0.0.4.

Voltar ao TOC

Nginx API para Lua

Muitas funções da API Lua foram portadas do ngx_http_lua. Confira o manual oficial do ngx_http_lua para mais detalhes sobre essas funções da API Lua.

Este módulo suporta totalmente o novo subsistema de variáveis dentro do núcleo de stream do NGINX. Você pode acessar qualquer variáveis embutidas fornecidas pelo núcleo de stream ou outros módulos de stream. * Constantes do núcleo

`ngx.OK`, `ngx.ERROR`, etc.

Apenas sockets de requisição brutos são suportados, por razões óbvias. O valor do argumento raw é ignorado e o socket de requisição bruto é sempre retornado. Ao contrário do ngx_http_lua, você ainda pode chamar funções de API de saída como ngx.say, ngx.print e ngx.flush após adquirir o socket de requisição bruto através desta função.

Quando o servidor de stream está no modo UDP, ler do socket downstream retornado pela chamada ngx.req.socket retornará apenas o conteúdo de um único pacote. Portanto, a chamada de leitura nunca bloqueará e retornará nil, "no more data" quando todos os dados do datagrama tiverem sido consumidos. No entanto, você pode optar por enviar múltiplos pacotes UDP de volta ao cliente usando o socket downstream.

Os sockets TCP brutos retornados por este módulo terão o seguinte método extra:

Voltar ao TOC

reqsock:receiveany

sintaxe: data, err = reqsock:receiveany(max)

contexto: content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*

Este método é semelhante ao método tcpsock:receiveany

Este método foi introduzido no stream-lua-nginx-module desde a versão v0.0.8.

Voltar ao TOC

tcpsock:shutdown

sintaxe: ok, err = tcpsock:shutdown("send")

contexto: content_by_lua*

Desliga a parte de escrita do socket de requisição, impede toda escrita adicional para o cliente e envia TCP FIN, mantendo a parte de leitura aberta.

Atualmente, apenas a direção "send" é suportada. Usar qualquer parâmetro diferente de "send" retornará um erro.

Se você chamou quaisquer funções de saída (como ngx.say) antes de chamar este método, considere usar ngx.flush(true) para garantir que todos os buffers ocupados sejam completamente limpos antes de desligar o socket. Se algum buffer ocupado for detectado, este método retornará nil com a mensagem de erro "socket busy writing".

Este recurso é particularmente útil para protocolos que geram uma resposta antes de realmente terminar de consumir todos os dados recebidos. Normalmente, o kernel enviará RST para o cliente quando tcpsock:close for chamado sem esvaziar primeiro o buffer de recebimento. Chamar este método permitirá que você continue lendo do buffer de recebimento e impede que RST seja enviado.

Você também pode usar este método para simular um fechamento pendente semelhante ao fornecido pelo ngx_http_core_module para protocolos que necessitam desse comportamento. Aqui está um exemplo:

local LINGERING_TIME = 30 -- 30 segundos
local LINGERING_TIMEOUT = 5000 -- 5 segundos

local ok, err = sock:shutdown("send")
if not ok then
    ngx.log(ngx.ERR, "falha ao desligar: ", err)
    return
end

local deadline = ngx.time() + LINGERING_TIME

sock:settimeouts(nil, nil, LINGERING_TIMEOUT)

repeat
    local data, _, partial = sock:receive(1024)
until (not data and not partial) or ngx.time() >= deadline

Voltar ao TOC

reqsock:peek

sintaxe: ok, err = reqsock:peek(size)

contexto: preread_by_lua*

Espera no buffer de preread que contém dados downstream enviados pelo cliente sem consumi-los. Ou seja, os dados retornados por esta API ainda serão encaminhados para cima nas fases posteriores.

Esta função leva um único argumento obrigatório, size, que é o número de bytes a serem espiados. Chamadas repetidas a esta função sempre retornam dados do início do buffer de preread.

Note que a fase de preread ocorre após o handshake TLS. Se o servidor de stream foi configurado com TLS habilitado, os dados retornados estarão em texto claro.

Se o buffer de preread não tiver a quantidade solicitada de dados, então a thread Lua atual será suspensa até que mais dados estejam disponíveis, preread_buffer_size tenha sido excedido ou preread_timeout tenha decorrido. Chamadas bem-sucedidas sempre retornam as quantidades solicitadas de dados, ou seja, nenhum dado parcial será retornado.

Quando preread_buffer_size for excedido, a sessão de stream atual será encerrada com o código de status da sessão 400 imediatamente pelo módulo central de stream, com a mensagem de erro "preread buffer full" que será impressa no log de erro.

Quando preread_timeout for excedido, a sessão de stream atual será encerrada com o código de status da sessão 200 imediatamente pelo módulo central de stream.

Em ambos os casos, nenhum processamento adicional na sessão é possível (exceto log_by_lua*). A conexão será fechada pelo módulo central de stream automaticamente.

Note que esta API não pode ser usada se o consumo de dados do cliente já ocorreu. Por exemplo, após chamar reqsock:receive. Se tal tentativa for feita, o erro Lua "attempt to peek on a consumed socket" será lançado. Consumir dados do cliente após chamar esta API é permitido e seguro.

Aqui está um exemplo de uso desta API:

local sock = assert(ngx.req.socket())

local data = assert(sock:peek(1)) -- espiar o primeiro byte que contém o comprimento
data = string.byte(data)

data = assert(sock:peek(data + 1)) -- espiar o comprimento + o byte de tamanho

local payload = data:sub(2) -- remover o byte de comprimento para obter o payload real

ngx.log(ngx.INFO, "payload é: ", payload)

Esta API foi introduzida pela primeira vez na versão v0.0.6.

Voltar ao TOC

Compatibilidade com Nginx

A versão mais recente deste módulo é compatível com as seguintes versões do NGINX:

  • 1.29.x (último testado: 1.29.2)
  • 1.27.x (último testado: 1.27.1)
  • 1.25.x (último testado: 1.25.1)
  • 1.21.x (último testado: 1.21.4)
  • 1.19.x (último testado: 1.19.3)
  • 1.17.x (último testado: 1.17.8)
  • 1.15.x (último testado: 1.15.8)
  • 1.13.x (último testado: 1.13.6)

Núcleos do NGINX mais antigos que 1.13.6 (exclusivo) não são testados e podem ou não funcionar. Use por sua conta e risco!

informe ao sistema de construção do nginx onde encontrar LuaJIT 2.1:

export LUAJIT_LIB=/path/to/luajit/lib export LUAJIT_INC=/path/to/luajit/include/luajit-2.1

Aqui assumimos que o NGINX será instalado em /opt/nginx/.

./configure --prefix=/opt/nginx \ --with-ld-opt="-Wl,-rpath,/path/to/luajit-or-lua/lib" \ --with-stream \ --with-stream_ssl_module \ --add-module=/path/to/stream-lua-nginx-module

Repositório de Código

O repositório de código deste projeto está hospedado no GitHub em openresty/stream-lua-nginx-module.

Veja Também

GitHub

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