Zum Inhalt

websocket: WebSocket-Unterstützung für das nginx-module-lua Modul

Installation

Wenn Sie noch kein RPM-Repository-Abonnement eingerichtet haben, melden Sie sich an. Danach können Sie mit den folgenden Schritten fortfahren.

CentOS/RHEL 7 oder 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-websocket

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

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

Um diese Lua-Bibliothek mit NGINX zu verwenden, stellen Sie sicher, dass nginx-module-lua installiert ist.

Dieses Dokument beschreibt lua-resty-websocket v0.13, veröffentlicht am 11. Februar 2025.


Diese Lua-Bibliothek implementiert WebSocket-Server- und Client-Bibliotheken, die auf dem ngx_lua-Modul basieren.

Diese Lua-Bibliothek nutzt die Cosocket-API von ngx_lua, die ein 100% nicht blockierendes Verhalten gewährleistet.

Beachten Sie, dass nur RFC 6455 unterstützt wird. Frühere Protokollrevisionen wie "hybi-10", "hybi-07" und "hybi-00" werden nicht unterstützt und werden nicht berücksichtigt.

Synopsis

    local server = require "resty.websocket.server"

    local wb, err = server:new{
        timeout = 5000,  -- in Millisekunden
        max_payload_len = 65535,
    }
    if not wb then
        ngx.log(ngx.ERR, "Fehler beim Erstellen des Websockets: ", err)
        return ngx.exit(444)
    end

    local data, typ, err = wb:recv_frame()

    if not data then
        if not string.find(err, "timeout", 1, true) then
            ngx.log(ngx.ERR, "Fehler beim Empfangen eines Frames: ", err)
            return ngx.exit(444)
        end
    end

    if typ == "close" then
        -- für typ "close", enthält err den Statuscode
        local code = err

        -- sendet einen Close-Frame zurück:

        local bytes, err = wb:send_close(1000, "genug, genug!")
        if not bytes then
            ngx.log(ngx.ERR, "Fehler beim Senden des Close-Frames: ", err)
            return
        end
        ngx.log(ngx.INFO, "Schließe mit Statuscode ", code, " und Nachricht ", data)
        return
    end

    if typ == "ping" then
        -- sendet einen Pong-Frame zurück:

        local bytes, err = wb:send_pong(data)
        if not bytes then
            ngx.log(ngx.ERR, "Fehler beim Senden des Frames: ", err)
            return
        end
    elseif typ == "pong" then
        -- verwirft einfach den eingehenden Pong-Frame

    else
        ngx.log(ngx.INFO, "Empfangen eines Frames vom Typ ", typ, " und Payload ", data)
    end

    wb:set_timeout(1000)  -- ändert den Netzwerk-Timeout auf 1 Sekunde

    bytes, err = wb:send_text("Hallo Welt")
    if not bytes then
        ngx.log(ngx.ERR, "Fehler beim Senden eines Text-Frames: ", err)
        return ngx.exit(444)
    end

    bytes, err = wb:send_binary("blah blah blah...")
    if not bytes then
        ngx.log(ngx.ERR, "Fehler beim Senden eines Binär-Frames: ", err)
        return ngx.exit(444)
    end

    local bytes, err = wb:send_close(1000, "genug, genug!")
    if not bytes then
        ngx.log(ngx.ERR, "Fehler beim Senden des Close-Frames: ", err)
        return
    end

Module

resty.websocket.server

Um dieses Modul zu laden, tun Sie einfach Folgendes:

    local server = require "resty.websocket.server"

Methoden

new

syntax: wb, err = server:new()

syntax: wb, err = server:new(opts)

Führt den WebSocket-Handshake-Prozess auf der Serverseite durch und gibt ein WebSocket-Serverobjekt zurück.

Im Falle eines Fehlers gibt es nil und eine Zeichenfolge zurück, die den Fehler beschreibt.

Eine optionale Optionen-Tabelle kann angegeben werden. Die folgenden Optionen sind wie folgt:

  • max_payload_len

    Gibt die maximale Länge der Payload an, die beim Senden und Empfangen von WebSocket-Frames erlaubt ist. Standardmäßig 65535. * max_recv_len

    Gibt die maximale Länge der Payload an, die beim Empfangen von WebSocket-Frames erlaubt ist. Standardmäßig der Wert von max_payload_len. * max_send_len

    Gibt die maximale Länge der Payload an, die beim Senden von WebSocket-Frames erlaubt ist. Standardmäßig der Wert von max_payload_len. * send_masked

    Gibt an, ob maskierte WebSocket-Frames gesendet werden sollen. Wenn es true ist, werden maskierte Frames immer gesendet. Standardmäßig false. * timeout

    Gibt die Netzwerk-Timeout-Schwelle in Millisekunden an. Sie können diese Einstellung später über den set_timeout-Methodenaufruf ändern. Beachten Sie, dass diese Timeout-Einstellung den HTTP-Antwortheader-Sendeprozess für den WebSocket-Handshake nicht beeinflusst; Sie müssen gleichzeitig die send_timeout Direktive konfigurieren.

set_timeout

syntax: wb:set_timeout(ms)

Setzt die Timeout-Verzögerung (in Millisekunden) für netzwerkbezogene Operationen.

send_text

syntax: bytes, err = wb:send_text(text)

Sendet das Argument text als unfragmentierten Daten-Frame vom Typ text. Gibt die Anzahl der Bytes zurück, die auf TCP-Ebene tatsächlich gesendet wurden.

Im Falle von Fehlern gibt es nil und eine Zeichenfolge zurück, die den Fehler beschreibt.

send_binary

syntax: bytes, err = wb:send_binary(data)

Sendet das Argument data als unfragmentierten Daten-Frame vom Typ binary. Gibt die Anzahl der Bytes zurück, die auf TCP-Ebene tatsächlich gesendet wurden.

Im Falle von Fehlern gibt es nil und eine Zeichenfolge zurück, die den Fehler beschreibt.

send_ping

syntax: bytes, err = wb:send_ping()

syntax: bytes, err = wb:send_ping(msg)

Sendet einen ping-Frame mit einer optionalen Nachricht, die durch das Argument msg angegeben wird. Gibt die Anzahl der Bytes zurück, die auf TCP-Ebene tatsächlich gesendet wurden.

Im Falle von Fehlern gibt es nil und eine Zeichenfolge zurück, die den Fehler beschreibt.

Beachten Sie, dass diese Methode nicht auf einen Pong-Frame vom Remote-Ende wartet.

send_pong

syntax: bytes, err = wb:send_pong()

syntax: bytes, err = wb:send_pong(msg)

Sendet einen pong-Frame mit einer optionalen Nachricht, die durch das Argument msg angegeben wird. Gibt die Anzahl der Bytes zurück, die auf TCP-Ebene tatsächlich gesendet wurden.

Im Falle von Fehlern gibt es nil und eine Zeichenfolge zurück, die den Fehler beschreibt.

send_close

syntax: bytes, err = wb:send_close()

syntax: bytes, err = wb:send_close(code, msg)

Sendet einen close-Frame mit einem optionalen Statuscode und einer Nachricht.

Im Falle von Fehlern gibt es nil und eine Zeichenfolge zurück, die den Fehler beschreibt.

Für eine Liste gültiger Statuscodes siehe das folgende Dokument:

http://tools.ietf.org/html/rfc6455#section-7.4.1

Beachten Sie, dass diese Methode nicht auf einen close-Frame vom Remote-Ende wartet.

send_frame

syntax: bytes, err = wb:send_frame(fin, opcode, payload)

Sendet einen rohen WebSocket-Frame, indem das fin-Feld (boolescher Wert), der Opcode und die Payload angegeben werden.

Für eine Liste gültiger Opcodes siehe

http://tools.ietf.org/html/rfc6455#section-5.2

Im Falle von Fehlern gibt es nil und eine Zeichenfolge zurück, die den Fehler beschreibt.

Um die maximale Payload-Länge zu steuern, die erlaubt ist, können Sie die Option max_payload_len an den new-Konstruktor übergeben.

Um zu steuern, ob maskierte Frames gesendet werden, können Sie true an die Option send_masked im new-Konstruktor übergeben. Standardmäßig werden unmaskierte Frames gesendet.

recv_frame

syntax: data, typ, err = wb:recv_frame()

Empfängt einen WebSocket-Frame vom Draht.

Im Falle eines Fehlers gibt es zwei nil-Werte und eine Zeichenfolge zurück, die den Fehler beschreibt.

Der zweite Rückgabewert ist immer der Frame-Typ, der einer von continuation, text, binary, close, ping, pong oder nil (für unbekannte Typen) sein könnte.

Für close-Frames gibt es 3 Werte zurück: die zusätzliche Statusnachricht (die eine leere Zeichenfolge sein könnte), die Zeichenfolge "close" und eine Lua-Zahl für den Statuscode (falls vorhanden). Für mögliche Schließstatuscodes siehe

http://tools.ietf.org/html/rfc6455#section-7.4.1

Für andere Arten von Frames gibt es einfach die Payload und den Typ zurück.

Für fragmentierte Frames ist der Rückgabewert err die Lua-Zeichenfolge "again".

resty.websocket.client

Um dieses Modul zu laden, tun Sie einfach Folgendes:

    local client = require "resty.websocket.client"

Ein einfaches Beispiel zur Demonstration der Verwendung:

    local client = require "resty.websocket.client"
    local wb, err = client:new()
    local uri = "ws://127.0.0.1:" .. ngx.var.server_port .. "/s"
    local ok, err, res = wb:connect(uri)
    if not ok then
        ngx.say("Fehler beim Verbinden: " .. err)
        return
    end

    local data, typ, err = wb:recv_frame()
    if not data then
        ngx.say("Fehler beim Empfangen des Frames: ", err)
        return
    end

    ngx.say("Empfangen: ", data, " (", typ, "): ", err)

    local bytes, err = wb:send_text("Kopie: " .. data)
    if not bytes then
        ngx.say("Fehler beim Senden des Frames: ", err)
        return
    end

    local bytes, err = wb:send_close()
    if not bytes then
        ngx.say("Fehler beim Senden des Frames: ", err)
        return
    end

Methoden

client:new

syntax: wb, err = client:new()

syntax: wb, err = client:new(opts)

Instanziiert ein WebSocket-Clientobjekt.

Im Falle eines Fehlers gibt es nil und eine Zeichenfolge zurück, die den Fehler beschreibt.

Eine optionale Optionen-Tabelle kann angegeben werden. Die folgenden Optionen sind wie folgt:

  • max_payload_len

    Gibt die maximale Länge der Payload an, die beim Senden und Empfangen von WebSocket-Frames erlaubt ist. Standardmäßig 65536. * max_recv_len

    Gibt die maximale Länge der Payload an, die beim Empfangen von WebSocket-Frames erlaubt ist. Standardmäßig der Wert von max_payload_len. * max_send_len

    Gibt die maximale Länge der Payload an, die beim Senden von WebSocket-Frames erlaubt ist. Standardmäßig der Wert von max_payload_len. * send_unmasked

    Gibt an, ob unmaskierte WebSocket-Frames gesendet werden sollen. Wenn es true ist, werden unmaskierte Frames immer gesendet. Standardmäßig false. RFC 6455 verlangt jedoch, dass der Client maskierte Frames an den Server senden muss, daher sollten Sie diese Option niemals auf true setzen, es sei denn, Sie wissen, was Sie tun. * timeout

    Gibt die standardmäßige Netzwerk-Timeout-Schwelle in Millisekunden an. Sie können diese Einstellung später über den set_timeout-Methodenaufruf ändern.

client:connect

syntax: ok, err, res = wb:connect("ws://<host>:<port>/<path>")

syntax: ok, err, res = wb:connect("wss://<host>:<port>/<path>")

syntax: ok, err, res = wb:connect("ws://<host>:<port>/<path>", options)

syntax: ok, err, res = wb:connect("wss://<host>:<port>/<path>", options)

Stellt eine Verbindung zum Remote-WebSocket-Dienstport her und führt den WebSocket-Handshake-Prozess auf der Clientseite durch.

Bevor tatsächlich der Hostname aufgelöst und eine Verbindung zum Remote-Backend hergestellt wird, wird diese Methode immer den Verbindungs-Pool nach passenden inaktiven Verbindungen durchsuchen, die durch vorherige Aufrufe dieser Methode erstellt wurden.

Der dritte Rückgabewert dieser Methode enthält die rohe, im Klartext gehaltene Antwort (Statuszeile und Header) auf die Handshake-Anfrage. Dies ermöglicht dem Aufrufer, zusätzliche Validierungen durchzuführen und/oder die Antwort-Header zu extrahieren. Wenn die Verbindung wiederverwendet wird und keine Handshake-Anfrage gesendet wird, wird die Zeichenfolge "connection reused" anstelle der Antwort zurückgegeben.

Eine optionale Lua-Tabelle kann als letztes Argument an diese Methode übergeben werden, um verschiedene Verbindungsoptionen anzugeben:

  • protocols

    Gibt alle Subprotokolle an, die für die aktuelle WebSocket-Sitzung verwendet werden. Es könnte eine Lua-Tabelle sein, die alle Subprotokollnamen enthält, oder einfach eine einzelne Lua-Zeichenfolge. * origin

    Gibt den Wert des Origin-Anforderungsheaders an. * pool

    Gibt einen benutzerdefinierten Namen für den verwendeten Verbindungs-Pool an. Wenn weggelassen, wird der Verbindungs-Pool-Name aus der Zeichenfolgenvorlage <host>:<port> generiert. * pool_size

Gibt die Größe des Verbindungs-Pools an. Wenn weggelassen und keine backlog-Option angegeben wurde, wird kein Pool erstellt. Wenn weggelassen aber backlog angegeben wurde, wird der Pool mit einer Standardgröße erstellt, die dem Wert der lua_socket_pool_size Direktive entspricht. Der Verbindungs-Pool hält bis zu pool_size aktive Verbindungen, die bereit sind, von nachfolgenden Aufrufen von connect wiederverwendet zu werden, aber beachten Sie, dass es keine obere Grenze für die Gesamtzahl der geöffneten Verbindungen außerhalb des Pools gibt. Wenn Sie die Gesamtzahl der geöffneten Verbindungen einschränken müssen, geben Sie die backlog-Option an. Wenn der Verbindungs-Pool seine Größenbeschränkung überschreiten würde, wird die am wenigsten kürzlich verwendete (aktiv gehaltene) Verbindung, die sich bereits im Pool befindet, geschlossen, um Platz für die aktuelle Verbindung zu schaffen. Beachten Sie, dass der Cosocket-Verbindungs-Pool pro Nginx-Arbeitsprozess und nicht pro Nginx-Serverinstanz ist, sodass die hier angegebene Größenbeschränkung auch für jeden einzelnen Nginx-Arbeitsprozess gilt. Beachten Sie auch, dass die Größe des Verbindungs-Pools nicht geändert werden kann, sobald er erstellt wurde. Diese Option wurde erstmals in der Version v0.10.14 eingeführt.

  • backlog

Wenn angegeben, wird dieses Modul die Gesamtzahl der geöffneten Verbindungen für diesen Pool begrenzen. Es können zu jedem Zeitpunkt nicht mehr Verbindungen als pool_size für diesen Pool geöffnet werden. Wenn der Verbindungs-Pool voll ist, werden nachfolgende Verbindungsoperationen in eine Warteschlange eingereiht, die dem Wert dieser Option (der "backlog"-Warteschlange) entspricht. Wenn die Anzahl der eingereihten Verbindungsoperationen gleich backlog ist, schlagen nachfolgende Verbindungsoperationen fehl und geben nil plus die Fehlermeldung "too many waiting connect operations" zurück. Die eingereihten Verbindungsoperationen werden fortgesetzt, sobald die Anzahl der Verbindungen im Pool weniger als pool_size beträgt. Die eingereihte Verbindungsoperation wird abgebrochen, sobald sie mehr als connect_timeout eingereiht wurde, gesteuert durch settimeouts, und gibt nil plus die Fehlermeldung "timeout" zurück. Diese Option wurde erstmals in der Version v0.10.14 eingeführt. * ssl_verify

Gibt an, ob während des SSL-Handshakes eine SSL-Zertifikatsüberprüfung durchgeführt werden soll, wenn das `wss://`-Schema verwendet wird.
  • headers

    Gibt benutzerdefinierte Header an, die in der Handshake-Anfrage gesendet werden sollen. Die Tabelle sollte Zeichenfolgen im Format {"a-header: ein Headerwert", "another-header: ein anderer Headerwert"} enthalten.

  • client_cert

    Gibt ein Client-Zertifikat-Ketten-cdata-Objekt an, das während des TLS-Handshakes mit dem Remote-Server verwendet wird. Diese Objekte können mit der ngx.ssl.parse_pem_cert Funktion erstellt werden, die von lua-resty-core bereitgestellt wird. Beachten Sie, dass die Angabe der client_cert-Option auch die Bereitstellung des entsprechenden client_priv_key erfordert. Siehe unten.

  • client_priv_key

    Gibt einen privaten Schlüssel an, der dem oben genannten client_cert-Option entspricht. Diese Objekte können mit der ngx.ssl.parse_pem_priv_key Funktion erstellt werden, die von lua-resty-core bereitgestellt wird.

  • host

    Gibt den Wert des Host-Headers an, der in der Handshake-Anfrage gesendet wird. Wenn nicht angegeben, wird der Host-Header aus dem Hostnamen/Adresse und Port in der Verbindungs-URI abgeleitet.

  • server_name

    Gibt den Servernamen (SNI) an, der beim Durchführen des TLS-Handshakes mit dem Server verwendet werden soll. Wenn nicht angegeben, wird der host-Wert oder <host/addr>:<port> aus der Verbindungs-URI verwendet.

  • key

    Gibt den Wert des Sec-WebSocket-Key-Headers in der Handshake-Anfrage an. Der Wert sollte eine base64-kodierte, 16-Byte-Zeichenfolge sein, die den Anforderungen des Client-Handshakes des WebSocket RFC entspricht. Wenn nicht angegeben, wird ein Schlüssel zufällig generiert.

Der SSL-Verbindungsmodus (wss://) erfordert mindestens ngx_lua 0.9.11 oder OpenResty 1.7.4.1.

client:close

syntax: ok, err = wb:close()

Schließt die aktuelle WebSocket-Verbindung. Wenn noch kein close-Frame gesendet wurde, wird der close-Frame automatisch gesendet.

client:set_keepalive

syntax: ok, err = wb:set_keepalive(max_idle_timeout, pool_size)

Setzt die aktuelle WebSocket-Verbindung sofort in den ngx_lua Cosocket-Verbindungs-Pool.

Sie können das maximale Leerlauf-Timeout (in ms) angeben, wenn die Verbindung im Pool ist, und die maximale Größe des Pools für jeden Nginx-Arbeitsprozess.

Im Erfolgsfall gibt es 1 zurück. Im Falle von Fehlern gibt es nil mit einer Zeichenfolge zurück, die den Fehler beschreibt.

Rufen Sie diese Methode nur an der Stelle auf, an der Sie stattdessen die close-Methode aufgerufen hätten. Das Aufrufen dieser Methode versetzt das aktuelle WebSocket-Objekt sofort in den closed-Zustand. Alle nachfolgenden Operationen, die nicht connect() auf dem aktuellen Objekt sind, geben den closed-Fehler zurück.

client:set_timeout

syntax: wb:set_timeout(ms)

Identisch mit der set_timeout-Methode der resty.websocket.server-Objekte.

client:send_text

syntax: bytes, err = wb:send_text(text)

Identisch mit der send_text-Methode der resty.websocket.server-Objekte.

client:send_binary

syntax: bytes, err = wb:send_binary(data)

Identisch mit der send_binary-Methode der resty.websocket.server-Objekte.

client:send_ping

syntax: bytes, err = wb:send_ping()

syntax: bytes, err = wb:send_ping(msg)

Identisch mit der send_ping-Methode der resty.websocket.server-Objekte.

client:send_pong

syntax: bytes, err = wb:send_pong()

syntax: bytes, err = wb:send_pong(msg)

Identisch mit der send_pong-Methode der resty.websocket.server-Objekte.

client:send_close

syntax: bytes, err = wb:send_close()

syntax: bytes, err = wb:send_close(code, msg)

Identisch mit der send_close-Methode der resty.websocket.server-Objekte.

client:send_frame

syntax: bytes, err = wb:send_frame(fin, opcode, payload)

Identisch mit der send_frame-Methode der resty.websocket.server-Objekte.

Um zu steuern, ob unmaskierte Frames gesendet werden, können Sie true an die send_unmasked-Option im new-Konstruktor übergeben. Standardmäßig werden maskierte Frames gesendet.

client:recv_frame

syntax: data, typ, err = wb:recv_frame()

Identisch mit der recv_frame-Methode der resty.websocket.server-Objekte.

resty.websocket.protocol

Um dieses Modul zu laden, tun Sie einfach Folgendes:

    local protocol = require "resty.websocket.protocol"

Methoden

protocol.recv_frame

syntax: data, typ, err = protocol.recv_frame(socket, max_payload_len, force_masking)

Empfängt einen WebSocket-Frame vom Draht.

protocol.build_frame

syntax: frame = protocol.build_frame(fin, opcode, payload_len, payload, masking)

Baut einen rohen WebSocket-Frame.

protocol.send_frame

syntax: bytes, err = protocol.send_frame(socket, fin, opcode, payload, max_payload_len, masking)

Sendet einen rohen WebSocket-Frame.

Automatische Fehlerprotokollierung

Standardmäßig protokolliert das zugrunde liegende ngx_lua-Modul Fehler, wenn Socket-Fehler auftreten. Wenn Sie bereits eine ordnungsgemäße Fehlerbehandlung in Ihrem eigenen Lua-Code durchführen, wird empfohlen, diese automatische Fehlerprotokollierung zu deaktivieren, indem Sie die lua_socket_log_errors Direktive von ngx_lua deaktivieren, das heißt,

    lua_socket_log_errors off;

Einschränkungen

  • Diese Bibliothek kann nicht in Code-Kontexten wie init_by_lua, set_by_lua, log_by_lua und header_filter_by_lua verwendet werden, wo die ngx_lua Cosocket-API nicht verfügbar ist.
  • Die resty.websocket-Objektinstanz kann nicht auf Modulebene in einer Lua-Variablen gespeichert werden, da sie dann von allen gleichzeitigen Anfragen, die vom selben Nginx-Arbeitsprozess bearbeitet werden, geteilt wird (siehe http://wiki.nginx.org/HttpLuaModule#Data_Sharing_within_an_Nginx_Worker) und zu schlechten Race-Conditions führen kann, wenn gleichzeitige Anfragen versuchen, dieselbe resty.websocket-Instanz zu verwenden. Sie sollten immer resty.websocket-Objekte in lokalen Funktionsvariablen oder in der ngx.ctx-Tabelle initiieren. Diese Orte haben alle ihre eigenen Datenkopien für jede Anfrage.

Siehe auch

GitHub

Sie finden möglicherweise zusätzliche Konfigurationstipps und Dokumentationen für dieses Modul im GitHub-Repository für nginx-module-websocket.