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() -- lê 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.
- lua_load_resty_core
- lua_code_cache
- lua_regex_cache_max_entries
- lua_package_path
- lua_package_cpath
- init_by_lua_block
- init_by_lua_file
- init_worker_by_lua_block
- init_worker_by_lua_file
- preread_by_lua_block
- preread_by_lua_file
- content_by_lua_block
- content_by_lua_file
- balancer_by_lua_block
- balancer_by_lua_file
- log_by_lua_block
- log_by_lua_file
- ssl_client_hello_by_lua_block
- ssl_client_hello_by_lua_file
- ssl_certificate_by_lua_block
- ssl_certificate_by_lua_file
- proxy_ssl_certificate_by_lua_block
- proxy_ssl_certificate_by_lua_file
- proxy_ssl_verify_by_lua_block
- proxy_ssl_verify_by_lua_file
- lua_shared_dict
- lua_socket_connect_timeout
- lua_socket_buffer_size
- lua_socket_pool_size
- lua_socket_keepalive_timeout
- lua_socket_log_errors
- lua_ssl_ciphers
- lua_ssl_crl
- lua_ssl_protocols
- lua_ssl_certificate
- lua_ssl_certificate_key
- lua_ssl_trusted_certificate
- lua_ssl_verify_depth
- lua_ssl_key_log
- lua_ssl_conf_command
- lua_upstream_skip_openssl_default_verify
- lua_check_client_abort
- lua_max_pending_timers
- lua_max_running_timers
- lua_sa_restart
- lua_add_variable
- lua_capture_error_log
- preread_by_lua_no_postpone
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.
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.
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.
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.
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.
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.
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.
-
Constantes de nível de log do NGINX
ngx.ERR,ngx.WARN, etc. * print * ngx.ctx * ngx.balancer
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:
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.
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
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.
- ngx.print
- ngx.say
- ngx.log
-
Esta chamada atualmente ignora o argumento
waite sempre espera que toda a saída pendente seja completamente limpa (para os buffers de envio do socket do sistema). * ngx.exit * ngx.eof * ngx.sleep * ngx.escape_uri * ngx.unescape_uri * ngx.encode_args * ngx.decode_args * ngx.encode_base64 * ngx.decode_base64 * ngx.crc32_short * ngx.crc32_long * ngx.hmac_sha1 * ngx.md5 * ngx.md5_bin * ngx.sha1_bin * ngx.quote_sql_str * ngx.today * ngx.time * ngx.now * ngx.update_time * ngx.localtime * ngx.utctime * ngx.re.match * ngx.re.find * ngx.re.gmatch * ngx.re.sub * ngx.re.gsub * ngx.shared.DICT * ngx.socket.tcp * ngx.socket.udp * ngx.socket.connect * ngx.get_phase * ngx.thread.spawn * ngx.thread.wait * ngx.thread.kill * ngx.on_abort * ngx.timer.at * ngx.timer.running_count * ngx.timer.pending_count * ngx.config.debug * ngx.config.subsystemSempre leva o valor de string Lua
"stream"neste módulo. * ngx.config.prefix * ngx.config.nginx_version * ngx.config.nginx_configure * ngx.config.ngx_lua_version * ngx.worker.exiting * ngx.worker.pid * ngx.worker.pids * ngx.worker.count * ngx.worker.id * coroutine.create * coroutine.resume * coroutine.yield * coroutine.wrap * coroutine.running * coroutine.status
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.