
Eine Go-Bibliothek zur Verwendung der Websocket-API von Zeek Broker.
Diese Bibliothek implementiert die Zeek-Broker-WebSocket-Schnittstelle.
Hilfsfunktionen werden mit sinnvollen Zuordnungen zwischen Zeek- und Go-Typen bereitgestellt.
Die Bibliothek enthält zwei Pakete:
encodingencoding modelliert die rekursive JSON-basierte Datenstruktur zur Darstellung von Zeek-Typen (das Data-Struct) sowie
die Broker-Kodierung von Zeek-Ereignissen und Fehlermeldungen (das DataMessage-Struct). Diese Strukturen können (und in einigen Fällen müssen)
direkt verwendet werden (siehe das Abonnement-Beispiel im nächsten Abschnitt), aber einige Hilfsfunktionen erleichtern das Erstellen von Nachrichten.
Um einen Zeek-string manuell zu erstellen:
zeekString := encoding.Data{
DataType: encoding.TypeString,
DataValue: "foo",
}
...oder über die Hilfsfunktion:
zeekString := encoding.String("foo")
Insbesondere die Hilfsfunktion vector ist eine variadische Funktion, die encoding.Data akzeptiert:
zeekVector := encoding.Vector(encoding.Count(1), encoding.Count(2), encoding.Count(3))
Schließlich können Ereignisse direkt erstellt werden:
zeekEvent := encoding.NewEvent("some_event_name", zeekVector, zeekString)
clientclient stellt die WebSocket-Anbindung bereit, um mit der Broker-WS-API zu kommunizieren, und kapselt github.com/gorilla/websocket:
Um ein Ereignis zu veröffentlichen:
broker, err := client.NewClient(...)
err := broker.PublishEvent("/the/topic", zeekEvent)
Themenabonnements werden als Slice von Strings an client.Newclient() übergeben. Die Methode ReadEvent() des Clients
gibt ein einzelnes Ereignis von Broker (zu einem der abonnierten Themen) zurück oder einen Fehler, der in der Bibliothek selbst auftreten kann
bzw. von Broker empfangen wurde):
broker, err := client.NewClient(..., []string{"/the/topic"})
topic, zeekEvent, err := broker.ReadEvent()
Wenn die Broker-Verbindung ordnungsgemäß geschlossen wird, kann die Funktion client.IsNormalWebsocketClose() verwendet werden, um
den zurückgegebenen Fehler zu überprüfen.
Der Client-Code muss über Typzusicherungen auf die Werte der Ereignisargumente zugreifen:
if len(evt.Arguments) < someConstantGreaterOrEqualToOne {
// handle too few arguments case
}
if evt.Arguments[0].DataType != encoding.TypeString {
// handle unexpected data type case
}
stringArgument0, ok := evt.Arguments[0].DataValue.(string)
if !ok {
// handle type assertion error - this would indicate a bug (we trust+verify).
}
// now we use stringArgument0
Die asynchrone Verarbeitung und Verteilung von über Abonnements empfangenen Ereignissen ließe sich am besten als
ein Client.ReadEvent()-Wrapper implementieren. Eine einfache Implementierung wird in client.AsyncSubscription() bereitgestellt.
Eine fortgeschrittenere Handhabung der WebSocket-Verbindung (z. B. das Setzen von Timeouts, die Handhabung von Wiederverbindungen usw.) lässt sich am besten
als Wrapper von client.Client oder als neue/ersetzende Implementierung umsetzen, die das Paket encoding verwendet (Beiträge/PRs sind willkommen!).
Broker-Netzwerkverbindungen (sowohl native als auch die WebSocket-Schnittstelle) aktivieren standardmäßig TLS mit einer ungewöhnlichen Konfiguration,
die die Host-Überprüfung deaktiviert und eine Reihe von Ciphern auswählt, die Verschlüsselung ohne Zertifikate ermöglichen (Anonymous Diffie-Hellman
/ AECDH). Um diesen Modus zu verwenden, muss weirdtls.BrokerDefaultTLSDialer als Argument der Dialer-Funktion an encoding.NewClient übergeben werden.
Beachten Sie, dass dies OpenSSL als Abhängigkeit nach sich zieht.
Alternativ kann die Implementierung der Standardbibliothek crypto/tls verwendet werden, wenn beide Seiten (der Client und zeek/broker)
für die Verwendung von TLS mit Zertifikaten konfiguriert sind. Diese Bibliothek bietet eine praktische Hilfsfunktion
(securetls.MakeSecureDialer()), die eine Dialer-Funktion zurückgibt, wenn PEM-Dateien für die CA und das Client-Zertifikat bzw. den Client-Schlüssel angegeben werden.
Siehe diesen btest-Fall für ein Beispiel dieser Konfiguration.
Schließlich kann TLS für Broker-Verbindungen mithilfe von redef Broker::disable_ssl = T; deaktiviert werden.
Siehe diesen btest-Fall für ein Beispiel, in dem encoding.NewClient mit Argumenten
für den unsicheren Betrieb aufgerufen wird.
In zeek 7.2 wird die WebSocket-API aus broker in das Cluster-Framework verschoben, und standardmäßig wird TLS deaktiviert.
Dieser btest-Fall verwendet die neuen zeek-seitigen Funktionen und zeigt die Verwendung
dieser Bibliothek, indem encoding.NewClient() mit dem auf false gesetzten Argument secure aufgerufen wird.
Wenn zeek 7.2 das LTS-Release wird, wird eine neue (Haupt-)Version dieser Bibliothek die Unterstützung für den im obigen Abschnitt beschriebenen AECDH-TLS-Modus entfernen, wodurch die OpenSSL-Abhängigkeit entfällt.
Ein zeek-seitiges Broker-Skript ausführen:
$ cd example/
$ zeek listen.zeek
Nun das Beispiel ausführen:
$ cd example/
$ go build && ./example
2023/05/05 12:56:55 connected to remote endpoint with UUID=c9b0bfd6-3b8d-5de2-a51c-9af7b81aaad3 version=2.5.0-dev
2023/05/05 12:56:55 > topic=/topic/test | event ping("my-message": string, "1": count)
2023/05/05 12:56:55 < topic=/topic/test | event pong("my-message": string, "2": count)
2023/05/05 12:56:56 > topic=/topic/test | event ping("my-message": string, "2": count)
2023/05/05 12:56:56 < topic=/topic/test | event pong("my-message": string, "3": count)
...währenddessen das zeek-Skript:
peer added, [id=e537f8b4-de32-52ea-9587-4e6e15bdfe20, network=[address=127.0.0.1, bound_port=50690/tcp]]
receiver got ping: my-message, 1
receiver got ping: my-message, 2
Die meisten Hilfsfunktionen und grundlegenden Datenstrukturen im Paket encoding verfügen über Unit-Tests (führen Sie go test ./... aus).
End-to-End-Tests sind als zwei btest-Fälle implementiert (führen Sie cd tests/; btest aus).