Cliente WebSocket

O módulo websocket cria conexões cliente bidirecionais com servidores WebSocket.

Esta página é uma referência de API com receitas parciais de conexão e assinatura. URLs de endpoints, tokens, handlers de mensagens e dados da aplicação vêm da aplicação ao redor. Os exemplos de ciclo de vida fecham o cliente em todo caminho terminal ou de erro verificado; os exemplos menores de métodos pressupõem que um proprietário externo faça essa limpeza.

Carregamento

local websocket = require("websocket")

Adicione websocket à lista modules: da entrada executável antes de importá-lo. O global channel está sempre disponível; as receitas com JSON e timeout também exigem json e time.

Conectando

connect

Abre uma conexão WebSocket com as opções padrão:

local client, err = websocket.connect("wss://api.example.com/ws")
if err then
    return nil, err
end

Passe uma tabela de opções para configurar a conexão:

local client, err = websocket.connect("wss://api.example.com/ws", {
    headers = {
        ["Authorization"] = "Bearer " .. token
    },
    protocols = {"graphql-ws"},
    dial_timeout = "10s",
    read_timeout = "30s",
    compression = websocket.COMPRESSION.CONTEXT_TAKEOVER
})
if err then
    return nil, err
end
Parâmetro Tipo Descrição
url string URL WebSocket (ws:// ou wss://)
options table Opções de conexão (opcional)

Retorna: Client, error

Opções de Conexão

Opção Tipo Descrição
headers table Headers HTTP para handshake
protocols table Subprotocolos WebSocket
dial_timeout number/string Timeout de conexão (ms ou "5s")
read_timeout number/string Timeout de leitura
write_timeout number/string Timeout de escrita
compression number/string Modo de compressao (veja Constantes), ou "disabled", "context_takeover", "no_context_takeover"
compression_threshold number Tamanho minimo para comprimir (0-100MB)
read_limit number Tamanho maximo de mensagem (0-128MB)
channel_capacity number Buffer do channel de recepcao (1-10000)

Formato de timeout: números representam milissegundos. Strings usam a sintaxe de duração Go, como "5s" ou "1m".

Strings de timeout inválidas e valores de opções fora dos limites ou não aceitos são ignorados, mantendo o padrão correspondente.

Enviando Mensagens

Mensagens de Texto

client:send("Hello, Server!")

```lua
local json = require("json")

client:send("Hello, Server!")

-- Send JSON
local payload, encode_err = json.encode({
    type = "subscribe",
    channel = "orders"
})
if encode_err then return nil, encode_err end
client:send(payload)

Mensagens Binarias

Envia uma mensagem binária especificando websocket.BINARY.

client:send(binary_data, websocket.BINARY)
Parâmetro Tipo Descrição
data string Conteudo da mensagem
type number websocket.TEXT (1) ou websocket.BINARY (2)

Cede (yield) até a mensagem ser enviada. Não retorna valores.

Ping

Envia um frame de ping.

client:ping()

Cede (yield) até o ping ser enviado. Não retorna valores.

Recebendo Mensagens

channel() retorna o channel de recebimento, e receive() é um alias. A primeira chamada cede a execução enquanto o runtime cria a assinatura; chamadas posteriores retornam o mesmo channel imediatamente. Uma falha na assinatura retorna nil, error. O channel pode ser usado com channel.select.

Recepcao Basica

local ch, err = client:channel()
if err then
    client:close()
    return nil, err
end

local msg, ok = ch:receive()
if ok then
    print("Type:", msg.type)  -- "text" or "binary"
    print("Data:", msg.data)
end

local _, close_err = client:close()
if close_err then return nil, close_err end

Loop de Mensagens

local json = require("json")

local ch, err = client:channel()
if err then
    client:close()
    return nil, err
end

while true do
    local msg, ok = ch:receive()
    if not ok then
        break  -- Connection closed
    end

    if msg.type == "text" then
        local data, decode_err = json.decode(msg.data)
        if decode_err then
            client:close()
            return nil, decode_err
        end
        handle_message(data)
    end
end

local _, close_err = client:close()
if close_err then return nil, close_err end

Com Select

local json = require("json")
local time = require("time")

local ch, ch_err = client:channel()
if ch_err then
    client:close()
    return nil, ch_err
end

local timeout, timeout_err = time.after("30s")
if timeout_err then
    client:close()
    return nil, timeout_err
end

while true do
    local r = channel.select {
        ch:case_receive(),
        timeout:case_receive()
    }

    if r.channel == timeout then
        client:ping()  -- Keep-alive
        timeout, timeout_err = time.after("30s")
        if timeout_err then
            client:close()
            return nil, timeout_err
        end
    elseif not r.ok then
        break
    else
        local data, decode_err = json.decode(r.value.data)
        if decode_err then
            client:close()
            return nil, decode_err
        end
        process(data)
    end
end

local _, close_err = client:close()
if close_err then return nil, close_err end

Objeto Message

Campo Tipo Descrição
type string "text" ou "binary"
data string? Conteudo da mensagem (nil para tipos de payload desconhecidos)

Fechando Conexão

Fecha a conexão com código de status e motivo opcionais:

local _, close_err = client:close(websocket.CLOSE_CODES.NORMAL, "Session ended")
if close_err then return nil, close_err end

-- Omitting both arguments also uses normal close code 1000.
-- Use INTERNAL_ERROR with an application-owned reason for a failed session.
Parâmetro Tipo Descrição
code number Código de fechamento (1000-4999), padrão 1000
reason string Motivo do fechamento (opcional)

Suspende até que o frame de close seja enviado.

Constantes

Tipos de Mensagem

-- Numeric (for send)
websocket.TEXT    -- 1
websocket.BINARY  -- 2

-- Compatibility string constants
websocket.TYPE_TEXT    -- "text"
websocket.TYPE_BINARY  -- "binary"
websocket.TYPE_PING    -- "ping"
websocket.TYPE_PONG    -- "pong"
websocket.TYPE_CLOSE   -- "close"

Os objetos de mensagem do channel de recebimento usam somente "text" e "binary". Frames de ping e pong são processados pelo transporte, e um evento terminal fecha o channel em vez de produzir um objeto de mensagem "close".

Modos de Compressao

websocket.COMPRESSION.DISABLED         -- 0 (no compression)
websocket.COMPRESSION.CONTEXT_TAKEOVER -- 1 (sliding window)
websocket.COMPRESSION.NO_CONTEXT       -- 2 (per-message)

Codigos de Fechamento

Constante Código Descrição
NORMAL 1000 Fechamento normal
GOING_AWAY 1001 Servidor desligando
PROTOCOL_ERROR 1002 Erro de protocolo
UNSUPPORTED_DATA 1003 Tipo de dados não suportado
RESERVED 1004 Reservado
NO_STATUS 1005 Nenhum status recebido
ABNORMAL_CLOSURE 1006 Conexão perdida
INVALID_PAYLOAD 1007 Payload de frame inválido
POLICY_VIOLATION 1008 Violação de política
MESSAGE_TOO_BIG 1009 Mensagem muito grande
MANDATORY_EXTENSION 1010 Extensão obrigatória não negociada
INTERNAL_ERROR 1011 Erro do servidor
SERVICE_RESTART 1012 Servidor reiniciando
TRY_AGAIN_LATER 1013 Servidor sobrecarregado
BAD_GATEWAY 1014 Erro de gateway
TLS_HANDSHAKE 1015 Falha no handshake TLS
local _, close_err = client:close(websocket.CLOSE_CODES.NORMAL, "Done")
if close_err then return nil, close_err end

Exemplos

Chat em Tempo Real

local json = require("json")

local function connect_chat(room_id, token, on_message)
    local client, err = websocket.connect("wss://chat.example.com/ws", {
        headers = {["Authorization"] = "Bearer " .. token}
    })
    if err then
        return nil, err
    end

    -- Join room. Runtime v0.3.32a does not expose transport send failures.
    local join_payload, encode_err = json.encode({
        type = "join",
        room = room_id
    })
    if encode_err then
        client:close()
        return nil, encode_err
    end
    client:send(join_payload)

    -- Message loop
    local ch, channel_err = client:channel()
    if channel_err then
        client:close()
        return nil, channel_err
    end
    while true do
        local msg, ok = ch:receive()
        if not ok then break end

        local data, decode_err = json.decode(msg.data)
        if decode_err then
            client:close()
            return nil, decode_err
        end
        on_message(data)
    end

    local _, close_err = client:close()
    if close_err then return nil, close_err end
    return true
end

Stream de Precos com Keep-Alive

local json = require("json")
local time = require("time")

local client, err = websocket.connect("wss://stream.example.com/prices")
if err then
    return nil, err
end

local subscribe_payload, encode_err = json.encode({
    action = "subscribe",
    symbols = {"BTC-USD", "ETH-USD"}
})
if encode_err then
    client:close()
    return nil, encode_err
end
client:send(subscribe_payload)

local ch, channel_err = client:channel()
if channel_err then
    client:close()
    return nil, channel_err
end

local heartbeat, heartbeat_err = time.after("30s")
if heartbeat_err then
    client:close()
    return nil, heartbeat_err
end

while true do
    local r = channel.select {
        ch:case_receive(),
        heartbeat:case_receive()
    }

    if r.channel == heartbeat then
        client:ping()
        heartbeat, heartbeat_err = time.after("30s")
        if heartbeat_err then
            client:close()
            return nil, heartbeat_err
        end
    elseif not r.ok then
        break  -- Connection closed
    else
        local price, decode_err = json.decode(r.value.data)
        if decode_err then
            client:close()
            return nil, decode_err
        end
        update_price(price.symbol, price.value)
    end
end

local _, close_err = client:close()
if close_err then return nil, close_err end

Permissões

Conexoes WebSocket estao sujeitas a avaliação de política de segurança.

Acoes de Segurança

Ação Recurso Descrição
websocket.connect - Permitir/negar conexoes WebSocket
websocket.connect.url URL Permitir/negar conexoes para URLs específicas

Veja Modelo de Segurança para configurar as políticas.

Erros

Condição Tipo Retentável
Conexoes desabilitadas errors.PERMISSION_DENIED não
URL não permitida errors.PERMISSION_DENIED não
Sem contexto errors.INTERNAL não
Conexão falhou errors.INTERNAL sim
ID de conexão inválido errors.INTERNAL não
Falha na assinatura errors.INTERNAL sim
Contexto de processo ausente durante a assinatura errors.INTERNAL não
Falha ao fechar errors.INTERNAL não

Uma URL vazia, um valor de opções que não seja tabela, tipos de argumentos inválidos e a ausência de contexto de execução ou PID de processo ao solicitar o channel geram erros Lua. Eles não são retornados como erros estruturados. O runtime v0.3.32a não expõe falhas de transporte de envio ou ping aos chamadores Lua.

local client, err = websocket.connect(url)
if err then
    if errors.is(err, errors.PERMISSION_DENIED) then
        print("Access denied:", err:message())
    elseif err:retryable() then
        print("Temporary error:", err:message())
    end
    return nil, err
end

Veja Tratamento de Erros para trabalhar com erros.