
Библиотека Go для использования websocket API zeek broker
Эта библиотека реализует интерфейс websocket Zeek Broker.
Предоставляются вспомогательные функции с разумным сопоставлением типов Zeek и Go.
Библиотека содержит два пакета:
encodingencoding моделирует рекурсивную структуру данных на основе JSON для представления типов Zeek (структура Data), а также
кодировку Broker для событий и сообщений об ошибках Zeek (структура DataMessage). Эти структуры можно (а в некоторых случаях нужно)
использовать напрямую (см. пример подписки в следующем разделе), но некоторые вспомогательные функции делают создание сообщений
более удобным. Чтобы вручную создать строку Zeek string:
zeekString := encoding.Data{
DataType: encoding.TypeString,
DataValue: "foo",
}
...или с помощью вспомогательной функции:
zeekString := encoding.String("foo")
В частности, вспомогательная функция vector — это вариативная функция, принимающая encoding.Data:
zeekVector := encoding.Vector(encoding.Count(1), encoding.Count(2), encoding.Count(3))
Наконец, события можно создавать напрямую:
zeekEvent := encoding.NewEvent("some_event_name", zeekVector, zeekString)
clientclient предоставляет websocket-связку для взаимодействия с WS API брокера, оборачивая github.com/gorilla/websocket:
Чтобы опубликовать событие:
broker, err := client.NewClient(...)
err := broker.PublishEvent("/the/topic", zeekEvent)
Подписки на темы передаются в client.Newclient() в виде среза строк. Метод ReadEvent() клиента
возвращает одно событие от Broker (по любой из подписанных тем) или ошибку, которая может возникнуть в самой библиотеке,
либо ошибки, полученные от Broker):
broker, err := client.NewClient(..., []string{"/the/topic"})
topic, zeekEvent, err := broker.ReadEvent()
Если соединение с брокером закрыто штатно, для проверки возвращённой ошибки можно использовать функцию client.IsNormalWebsocketClose().
Код клиента должен получать доступ к значениям аргументов события через утверждения типов:
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
Асинхронную обработку и диспетчеризацию событий, получаемых через подписки, лучше всего реализовать как обёртку
над Client.ReadEvent(). Простая реализация предоставлена в client.AsyncSubscription().
Более продвинутую работу с websocket-соединением (например, установку таймаутов, обработку переподключения и т.д.) лучше реализовать
как обёртку над client.Client либо как новую/заменяющую реализацию, использующую пакет encoding (вклад и pull request'ы приветствуются!).
Сетевые соединения Broker (как нативные, так и через websocket-интерфейс) по умолчанию включают TLS с необычной конфигурацией,
которая отключает проверку хоста и выбирает набор шифров, позволяющих шифрование без сертификатов (Anonymous Diffie-Hellman
/ AECDH). Для использования этого режима необходимо передать weirdtls.BrokerDefaultTLSDialer в качестве аргумента функции dialer'а в encoding.NewClient.
Обратите внимание, что это добавляет OpenSSL как зависимость.
В качестве альтернативы можно использовать реализацию crypto/tls из стандартной библиотеки, если обе стороны (клиент и zeek/broker)
настроены на использование TLS с сертификатами. Эта библиотека предоставляет удобную вспомогательную функцию
(securetls.MakeSecureDialer()), которая возвращает функцию-dialer при наличии PEM-файлов для CA и клиентского сертификата/ключа.
Пример такой конфигурации см. в этом btest-кейсе.
Наконец, TLS можно отключить для соединений broker с помощью redef Broker::disable_ssl = T;.
Пример вызова encoding.NewClient с аргументами для небезопасной работы см. в этом btest-кейсе.
В zeek 7.2 websocket API будет перенесён из broker во фреймворк Cluster, и по умолчанию TLS будет отключён.
Этот btest-кейс использует новые функции на стороне zeek и демонстрирует использование
этой библиотеки с вызовом encoding.NewClient() с аргументом secure, установленным в false.
Когда zeek 7.2 станет LTS-релизом, новая (мажорная) версия этой библиотеки удалит поддержку режима AECDH TLS, описанного в предыдущем разделе, убрав зависимость от OpenSSL.
Запуск скрипта broker на стороне zeek:
$ cd example/
$ zeek listen.zeek
Теперь запустите пример:
$ 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)
...тем временем скрипт zeek:
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
Большинство вспомогательных функций и базовых структур данных в пакете encoding покрыты модульными тестами (запуск: go test ./...).
Сквозные (end-to-end) тесты реализованы в виде двух btest-кейсов (запуск: cd tests/; btest).