1
0
mirror of https://github.com/weechat/weechat.git synced 2026-07-09 11:13:12 +02:00

relay: add support of websocket extension "permessage-deflate" (closes #1549)

This extension is used to compress and decompress websocket frames (using
the DEFLATE algorithm, with zlib).
This commit is contained in:
Sébastien Helleu
2024-01-14 10:24:41 +01:00
parent 8971fc069a
commit 6cfb31c306
16 changed files with 1613 additions and 441 deletions
+1
View File
@@ -19,6 +19,7 @@ New features::
* core: use function util_strftimeval in evaluation of expression `date:xxx`
* api: add support of specifier `%!` for timestamp in function util_strftimeval
* relay: add "api" protocol (HTTP REST API), add option relay.look.display_clients, change option type relay.look.auto_open_buffer to string (issue #2066)
* relay: add support of websocket extension "permessage-deflate" (issue #1549)
Bug fixes::
+85 -84
View File
@@ -396,90 +396,91 @@ WeeChat "core" is located in following directories:
[width="100%",cols="2m,3",options="header"]
|===
| Path/file | Description
| tests/ | Root of tests.
|    tests.cpp | Program used to run all tests.
|    tests-record.cpp | Record and search in messages displayed.
|    scripts/ | Root of scripting API tests.
|       test-scripts.cpp | Program used to run the scripting API tests.
|       python/ | Python scripts to generate and run the scripting API tests.
|          testapigen.py | Python script generating scripts in all languages to test the scripting API.
|          testapi.py | Python script with scripting API tests, used by script testapigen.py.
|          unparse.py | Convert Python code to other languages, used by script testapigen.py.
|    unit/ | Root of unit tests.
|       test-plugins.cpp | Tests: plugins.
|       test-plugin-api-info.cpp | Tests: plugin API info functions.
|       test-plugin-config.cpp | Tests: plugin config functions.
|       core/ | Root of unit tests for core.
|          test-core-arraylist.cpp | Tests: arraylists.
|          test-core-calc.cpp | Tests: calculation of expressions.
|          test-core-command.cpp | Tests: commands.
|          test-core-config-file.cpp | Tests: configuration files.
|          test-core-crypto.cpp | Tests: cryptographic functions.
|          test-core-dir.cpp | Tests: directory/file functions.
|          test-core-eval.cpp | Tests: evaluation of expressions.
|          test-core-hashtble.cpp | Tests: hashtables.
|          test-core-hdata.cpp | Tests: hdata.
|          test-core-hook.cpp | Tests: hooks.
|          test-core-infolist.cpp | Tests: infolists.
|          test-core-list.cpp | Tests: lists.
|          test-core-network.cpp | Tests: network functions.
|          test-core-secure.cpp | Tests: secured data.
|          test-core-signal.cpp | Tests: signals.
|          test-core-string.cpp | Tests: strings.
|          test-core-url.cpp | Tests: URLs.
|          test-core-utf8.cpp | Tests: UTF-8.
|          test-core-util.cpp | Tests: utility functions.
|          test-core-sys.cpp | Tests: system functions.
|          hook/ | Root of unit tests for hooks.
|             test-hook-command.cpp | Tests: hooks "command".
|       gui/ | Root of unit tests for interfaces.
|          test-gui-bar-window.cpp | Tests: bar window functions.
|          test-gui-buffer.cpp | Tests: buffer functions.
|          test-gui-chat.cpp | Tests: chat functions.
|          test-gui-color.cpp | Tests: colors.
|          test-gui-filter.cpp | Tests: filters.
|          test-gui-input.cpp | Tests: input functions.
|          test-gui-key.cpp | Tests: keys.
|          test-gui-line.cpp | Tests: lines.
|          test-gui-nick.cpp | Tests: nicks.
|       plugins/ | Root of unit tests for plugins.
|          irc/ | Root of unit tests for IRC plugin.
|             test-irc-batch.cpp | Tests: IRC batched events.
|             test-irc-buffer.cpp | Tests: IRC buffers.
|             test-irc-channel.cpp | Tests: IRC channels.
|             test-irc-color.cpp | Tests: IRC colors.
|             test-irc-config.cpp | Tests: IRC configuration.
|             test-irc-ctcp.cpp | Tests: IRC CTCP.
|             test-irc-ignore.cpp | Tests: IRC ignores.
|             test-irc-info.cpp | Tests: IRC info.
|             test-irc-join.cpp | Tests: IRC join functions.
|             test-irc-list.cpp | Tests: IRC buffer for reply to /list command.
|             test-irc-message.cpp | Tests: IRC messages.
|             test-irc-mode.cpp | Tests: IRC modes.
|             test-irc-nick.cpp | Tests: IRC nicks.
|             test-irc-protocol.cpp | Tests: IRC protocol.
|             test-irc-sasl.cpp | Tests: SASL authentication with IRC protocol.
|             test-irc-server.cpp | Tests: IRC server.
|             test-irc-tag.cpp | Tests: IRC message tags.
|          logger/ | Root of unit tests for logger plugin.
|             test-logger.cpp | Tests: logger.
|             test-logger-backlog.cpp | Tests: logger backlog.
|             test-logger-tail.cpp | Tests: logger tail functions.
|          trigger/ | Root of unit tests for trigger plugin.
|             test-trigger.cpp | Tests: triggers.
|             test-trigger-config.cpp | Tests: trigger configuration.
|          typing/ | Root of unit tests for typing plugin.
|             test-typing.cpp | Tests: typing.
|             test-typing-status.cpp | Tests: typing status.
|          relay/ | Root of unit tests for Relay plugin.
|             test-relay-auth.cpp | Tests: clients authentication.
|             test-relay-http.cpp | Tests: HTTP functions for Relay plugin.
|             irc/ | Root of unit tests for Relay "irc" protocol.
|                test-relay-irc.cpp | Tests: Relay "irc" protocol.
|          xfer/ | Root of unit tests for Xfer plugin.
|             test-xfer-file.cpp | Tests: file functions.
|             test-xfer-network.cpp | Tests: network functions.
| Path/file | Description
| tests/ | Root of tests.
|    tests.cpp | Program used to run all tests.
|    tests-record.cpp | Record and search in messages displayed.
|    scripts/ | Root of scripting API tests.
|       test-scripts.cpp | Program used to run the scripting API tests.
|       python/ | Python scripts to generate and run the scripting API tests.
|          testapigen.py | Python script generating scripts in all languages to test the scripting API.
|          testapi.py | Python script with scripting API tests, used by script testapigen.py.
|          unparse.py | Convert Python code to other languages, used by script testapigen.py.
|    unit/ | Root of unit tests.
|       test-plugins.cpp | Tests: plugins.
|       test-plugin-api-info.cpp | Tests: plugin API info functions.
|       test-plugin-config.cpp | Tests: plugin config functions.
|       core/ | Root of unit tests for core.
|          test-core-arraylist.cpp | Tests: arraylists.
|          test-core-calc.cpp | Tests: calculation of expressions.
|          test-core-command.cpp | Tests: commands.
|          test-core-config-file.cpp | Tests: configuration files.
|          test-core-crypto.cpp | Tests: cryptographic functions.
|          test-core-dir.cpp | Tests: directory/file functions.
|          test-core-eval.cpp | Tests: evaluation of expressions.
|          test-core-hashtble.cpp | Tests: hashtables.
|          test-core-hdata.cpp | Tests: hdata.
|          test-core-hook.cpp | Tests: hooks.
|          test-core-infolist.cpp | Tests: infolists.
|          test-core-list.cpp | Tests: lists.
|          test-core-network.cpp | Tests: network functions.
|          test-core-secure.cpp | Tests: secured data.
|          test-core-signal.cpp | Tests: signals.
|          test-core-string.cpp | Tests: strings.
|          test-core-url.cpp | Tests: URLs.
|          test-core-utf8.cpp | Tests: UTF-8.
|          test-core-util.cpp | Tests: utility functions.
|          test-core-sys.cpp | Tests: system functions.
|          hook/ | Root of unit tests for hooks.
|             test-hook-command.cpp | Tests: hooks "command".
|       gui/ | Root of unit tests for interfaces.
|          test-gui-bar-window.cpp | Tests: bar window functions.
|          test-gui-buffer.cpp | Tests: buffer functions.
|          test-gui-chat.cpp | Tests: chat functions.
|          test-gui-color.cpp | Tests: colors.
|          test-gui-filter.cpp | Tests: filters.
|          test-gui-input.cpp | Tests: input functions.
|          test-gui-key.cpp | Tests: keys.
|          test-gui-line.cpp | Tests: lines.
|          test-gui-nick.cpp | Tests: nicks.
|       plugins/ | Root of unit tests for plugins.
|          irc/ | Root of unit tests for IRC plugin.
|             test-irc-batch.cpp | Tests: IRC batched events.
|             test-irc-buffer.cpp | Tests: IRC buffers.
|             test-irc-channel.cpp | Tests: IRC channels.
|             test-irc-color.cpp | Tests: IRC colors.
|             test-irc-config.cpp | Tests: IRC configuration.
|             test-irc-ctcp.cpp | Tests: IRC CTCP.
|             test-irc-ignore.cpp | Tests: IRC ignores.
|             test-irc-info.cpp | Tests: IRC info.
|             test-irc-join.cpp | Tests: IRC join functions.
|             test-irc-list.cpp | Tests: IRC buffer for reply to /list command.
|             test-irc-message.cpp | Tests: IRC messages.
|             test-irc-mode.cpp | Tests: IRC modes.
|             test-irc-nick.cpp | Tests: IRC nicks.
|             test-irc-protocol.cpp | Tests: IRC protocol.
|             test-irc-sasl.cpp | Tests: SASL authentication with IRC protocol.
|             test-irc-server.cpp | Tests: IRC server.
|             test-irc-tag.cpp | Tests: IRC message tags.
|          logger/ | Root of unit tests for logger plugin.
|             test-logger.cpp | Tests: logger.
|             test-logger-backlog.cpp | Tests: logger backlog.
|             test-logger-tail.cpp | Tests: logger tail functions.
|          trigger/ | Root of unit tests for trigger plugin.
|             test-trigger.cpp | Tests: triggers.
|             test-trigger-config.cpp | Tests: trigger configuration.
|          typing/ | Root of unit tests for typing plugin.
|             test-typing.cpp | Tests: typing.
|             test-typing-status.cpp | Tests: typing status.
|          relay/ | Root of unit tests for Relay plugin.
|             test-relay-auth.cpp | Tests: clients authentication.
|             test-relay-http.cpp | Tests: HTTP functions for Relay plugin.
|             test-relay-websocket.cpp | Tests: websocket functions for Relay plugin.
|             irc/ | Root of unit tests for Relay "irc" protocol.
|                test-relay-irc.cpp | Tests: Relay "irc" protocol.
|          xfer/ | Root of unit tests for Xfer plugin.
|             test-xfer-file.cpp | Tests: file functions.
|             test-xfer-network.cpp | Tests: network functions.
|===
[[documentation_translations]]
+85 -84
View File
@@ -398,90 +398,91 @@ Le cœur de WeeChat est situé dans les répertoires suivants :
[width="100%",cols="2m,3",options="header"]
|===
| Chemin/fichier | Description
| tests/ | Racine des tests.
|    tests.cpp | Programme utilisé pour lancer tous les tests.
|    tests-record.cpp | Enregistrement et recherche dans les messages affichés.
|    scripts/ | Racine des tests de l'API script.
|       test-scripts.cpp | Programme utilisé pour lancer les tests de l'API script.
|       python/ | Scripts Python pour générer et lancer les tests de l'API script.
|          testapigen.py | Script Python générant des scripts dans tous les languages pour tester l'API script.
|          testapi.py | Script Python avec les tests API, utilisé par le script testapigen.py.
|          unparse.py | Conversion de code Python vers d'autres langages, utilisé par le script testapigen.py.
|    unit/ | Racine des tests unitaires.
|       test-plugins.cpp | Tests : extensions.
|       test-plugin-api-info.cpp | Tests : fonctions info de l'API extension.
|       test-plugin-config.cpp | Tests : fonctions config de l'extension.
|       core/ | Racine des tests unitaires pour le cœur.
|          test-core-arraylist.cpp | Tests : listes avec tableau (« arraylists »).
|          test-core-calc.cpp | Tests : calcul d'expressions.
|          test-core-command.cpp | Tests : commandes.
|          test-core-config-file.cpp | Tests : fichiers de configuration.
|          test-core-crypto.cpp | Tests : fonctions cryptographiques.
|          test-core-dir.cpp | Tests : répertoires/fichiers.
|          test-core-eval.cpp | Tests : évaluation d'expressions.
|          test-core-hashtble.cpp | Tests : tables de hachage.
|          test-core-hdata.cpp | Tests : hdata.
|          test-core-hook.cpp | Tests : hooks.
|          test-core-infolist.cpp | Tests : infolists.
|          test-core-list.cpp | Tests : listes.
|          test-core-network.cpp | Tests : fonctions réseau.
|          test-core-secure.cpp | Tests : données sécurisées.
|          test-core-signal.cpp | Tests : signaux.
|          test-core-string.cpp | Tests : chaînes.
|          test-core-url.cpp | Tests : URLs.
|          test-core-utf8.cpp | Tests : UTF-8.
|          test-core-util.cpp | Tests : fonctions utiles.
|          test-core-sys.cpp | Tests : fonctions système.
|          hook/ | Racine des tests pour les hooks.
|             test-hook-command.cpp | Tests : hooks "command".
|       gui/ | Racine des tests unitaires pour les interfaces.
|          test-gui-bar-window.cpp | Tests : fonctions de fenêtres de barre.
|          test-gui-buffer.cpp | Tests : fonctions de tampons.
|          test-gui-chat.cpp | Tests : fonctions de discussion.
|          test-gui-color.cpp | Tests : couleurs.
|          test-gui-filter.cpp | Tests : filtres.
|          test-gui-input.cpp | Tests : fonctions d'entrée.
|          test-gui-key.cpp | Tests : touches.
|          test-gui-line.cpp | Tests : lignes.
|          test-gui-nick.cpp | Tests : pseudos.
|       plugins/ | Racine des tests unitaires pour les extensions.
|          irc/ | Racine des tests unitaires pour l'extension IRC.
|             test-irc-batch.cpp | Tests : évènements batch IRC.
|             test-irc-buffer.cpp | Tests : tampons IRC.
|             test-irc-channel.cpp | Tests : canaux IRC.
|             test-irc-color.cpp | Tests : couleurs IRC.
|             test-irc-config.cpp | Tests : configuration IRC.
|             test-irc-ctcp.cpp | Tests : CTCP IRC.
|             test-irc-ignore.cpp | Tests : ignores IRC.
|             test-irc-info.cpp | Tests : infos IRC.
|             test-irc-join.cpp | Tests : fonctions de join IRC.
|             test-irc-list.cpp | Tests : tampon IRC pour la réponse à la commande /list.
|             test-irc-message.cpp | Tests : messages IRC.
|             test-irc-mode.cpp | Tests : modes IRC.
|             test-irc-nick.cpp | Tests : pseudos IRC.
|             test-irc-protocol.cpp | Tests : protocole IRC.
|             test-irc-sasl.cpp | Tests : authentification SASL avec le protocole IRC.
|             test-irc-server.cpp | Tests : serveur IRC.
|             test-irc-tag.cpp | Tests : étiquettes des messages IRC.
|          logger/ | Racine des tests unitaires pour l'extension logger.
|             test-logger.cpp | Tests : logger.
|             test-logger-backlog.cpp | Tests : backlog logger.
|             test-logger-tail.cpp | Tests : fonctions "tail".
|          trigger/ | Racine des tests unitaires pour l'extension trigger.
|             test-trigger.cpp | Tests : triggers.
|             test-trigger-config.cpp | Tests : configuration trigger.
|          typing/ | Racine des tests unitaires pour l'extension typing.
|             test-typing.cpp | Tests : typing.
|             test-typing-status.cpp | Tests : statut d'écriture.
|          relay/ | Racine des tests unitaires pour l'extension Relay.
|             test-relay-auth.cpp | Tests : authentification des clients.
|             test-relay-http.cpp | Tests : fonctions HTTP pour l'extension Relay.
|             irc/ | Racine des tests unitaires pour le protocole relay "irc".
|                test-relay-irc.cpp | Tests : Protocole relay "irc".
|          xfer/ | Racine des tests unitaires pour l'extension Xfer.
|             test-xfer-file.cpp | Tests : fonctions sur les fichiers.
|             test-xfer-network.cpp | Tests : fonctions réseau.
| Chemin/fichier | Description
| tests/ | Racine des tests.
|    tests.cpp | Programme utilisé pour lancer tous les tests.
|    tests-record.cpp | Enregistrement et recherche dans les messages affichés.
|    scripts/ | Racine des tests de l'API script.
|       test-scripts.cpp | Programme utilisé pour lancer les tests de l'API script.
|       python/ | Scripts Python pour générer et lancer les tests de l'API script.
|          testapigen.py | Script Python générant des scripts dans tous les languages pour tester l'API script.
|          testapi.py | Script Python avec les tests API, utilisé par le script testapigen.py.
|          unparse.py | Conversion de code Python vers d'autres langages, utilisé par le script testapigen.py.
|    unit/ | Racine des tests unitaires.
|       test-plugins.cpp | Tests : extensions.
|       test-plugin-api-info.cpp | Tests : fonctions info de l'API extension.
|       test-plugin-config.cpp | Tests : fonctions config de l'extension.
|       core/ | Racine des tests unitaires pour le cœur.
|          test-core-arraylist.cpp | Tests : listes avec tableau (« arraylists »).
|          test-core-calc.cpp | Tests : calcul d'expressions.
|          test-core-command.cpp | Tests : commandes.
|          test-core-config-file.cpp | Tests : fichiers de configuration.
|          test-core-crypto.cpp | Tests : fonctions cryptographiques.
|          test-core-dir.cpp | Tests : répertoires/fichiers.
|          test-core-eval.cpp | Tests : évaluation d'expressions.
|          test-core-hashtble.cpp | Tests : tables de hachage.
|          test-core-hdata.cpp | Tests : hdata.
|          test-core-hook.cpp | Tests : hooks.
|          test-core-infolist.cpp | Tests : infolists.
|          test-core-list.cpp | Tests : listes.
|          test-core-network.cpp | Tests : fonctions réseau.
|          test-core-secure.cpp | Tests : données sécurisées.
|          test-core-signal.cpp | Tests : signaux.
|          test-core-string.cpp | Tests : chaînes.
|          test-core-url.cpp | Tests : URLs.
|          test-core-utf8.cpp | Tests : UTF-8.
|          test-core-util.cpp | Tests : fonctions utiles.
|          test-core-sys.cpp | Tests : fonctions système.
|          hook/ | Racine des tests pour les hooks.
|             test-hook-command.cpp | Tests : hooks "command".
|       gui/ | Racine des tests unitaires pour les interfaces.
|          test-gui-bar-window.cpp | Tests : fonctions de fenêtres de barre.
|          test-gui-buffer.cpp | Tests : fonctions de tampons.
|          test-gui-chat.cpp | Tests : fonctions de discussion.
|          test-gui-color.cpp | Tests : couleurs.
|          test-gui-filter.cpp | Tests : filtres.
|          test-gui-input.cpp | Tests : fonctions d'entrée.
|          test-gui-key.cpp | Tests : touches.
|          test-gui-line.cpp | Tests : lignes.
|          test-gui-nick.cpp | Tests : pseudos.
|       plugins/ | Racine des tests unitaires pour les extensions.
|          irc/ | Racine des tests unitaires pour l'extension IRC.
|             test-irc-batch.cpp | Tests : évènements batch IRC.
|             test-irc-buffer.cpp | Tests : tampons IRC.
|             test-irc-channel.cpp | Tests : canaux IRC.
|             test-irc-color.cpp | Tests : couleurs IRC.
|             test-irc-config.cpp | Tests : configuration IRC.
|             test-irc-ctcp.cpp | Tests : CTCP IRC.
|             test-irc-ignore.cpp | Tests : ignores IRC.
|             test-irc-info.cpp | Tests : infos IRC.
|             test-irc-join.cpp | Tests : fonctions de join IRC.
|             test-irc-list.cpp | Tests : tampon IRC pour la réponse à la commande /list.
|             test-irc-message.cpp | Tests : messages IRC.
|             test-irc-mode.cpp | Tests : modes IRC.
|             test-irc-nick.cpp | Tests : pseudos IRC.
|             test-irc-protocol.cpp | Tests : protocole IRC.
|             test-irc-sasl.cpp | Tests : authentification SASL avec le protocole IRC.
|             test-irc-server.cpp | Tests : serveur IRC.
|             test-irc-tag.cpp | Tests : étiquettes des messages IRC.
|          logger/ | Racine des tests unitaires pour l'extension logger.
|             test-logger.cpp | Tests : logger.
|             test-logger-backlog.cpp | Tests : backlog logger.
|             test-logger-tail.cpp | Tests : fonctions "tail".
|          trigger/ | Racine des tests unitaires pour l'extension trigger.
|             test-trigger.cpp | Tests : triggers.
|             test-trigger-config.cpp | Tests : configuration trigger.
|          typing/ | Racine des tests unitaires pour l'extension typing.
|             test-typing.cpp | Tests : typing.
|             test-typing-status.cpp | Tests : statut d'écriture.
|          relay/ | Racine des tests unitaires pour l'extension Relay.
|             test-relay-auth.cpp | Tests : authentification des clients.
|             test-relay-http.cpp | Tests : fonctions HTTP pour l'extension Relay.
|             test-relay-websocket.cpp | Tests : fonctions websocket pour l'extension Relay.
|             irc/ | Racine des tests unitaires pour le protocole relay "irc".
|                test-relay-irc.cpp | Tests : Protocole relay "irc".
|          xfer/ | Racine des tests unitaires pour l'extension Xfer.
|             test-xfer-file.cpp | Tests : fonctions sur les fichiers.
|             test-xfer-network.cpp | Tests : fonctions réseau.
|===
[[documentation_translations]]
+86 -84
View File
@@ -428,143 +428,145 @@ WeeChat "core" は以下のディレクトリに配置されています:
[width="100%",cols="2m,3",options="header"]
|===
| パス/ファイル名 | 説明
| tests/ | テスト用のルートディレクトリ
|    tests.cpp | 全テストの実行時に使われるプログラム
| パス/ファイル名 | 説明
| tests/ | テスト用のルートディレクトリ
|    tests.cpp | 全テストの実行時に使われるプログラム
// TRANSLATION MISSING
|    tests-record.cpp | Record and search in messages displayed.
|    scripts/ | スクリプト API テスト用のルートディレクトリ
|       test-scripts.cpp | スクリプト API テストの実行時に使われるプログラム
|       python/ | スクリプト API テストを生成、実行する Python スクリプト
|          testapigen.py | スクリプト API のテスト時にすべての言語に関するスクリプトを生成する Python スクリプト
|          testapi.py | スクリプト API テスト時に使われる Python スクリプト (スクリプト testapigen.py から使われます)
|          unparse.py | Python コードを別の言語に変換 (スクリプト testapigen.py から使われます)
|    unit/ | 単体テスト用のルートディレクトリ
|       test-plugins.cpp | テスト: プラグイン
|    tests-record.cpp | Record and search in messages displayed.
|    scripts/ | スクリプト API テスト用のルートディレクトリ
|       test-scripts.cpp | スクリプト API テストの実行時に使われるプログラム
|       python/ | スクリプト API テストを生成、実行する Python スクリプト
|          testapigen.py | スクリプト API のテスト時にすべての言語に関するスクリプトを生成する Python スクリプト
|          testapi.py | スクリプト API テスト時に使われる Python スクリプト (スクリプト testapigen.py から使われます)
|          unparse.py | Python コードを別の言語に変換 (スクリプト testapigen.py から使われます)
|    unit/ | 単体テスト用のルートディレクトリ
|       test-plugins.cpp | テスト: プラグイン
// TRANSLATION MISSING
|       test-plugin-api-info.cpp | Tests: plugin API info functions.
|       test-plugin-api-info.cpp | Tests: plugin API info functions.
// TRANSLATION MISSING
|       test-plugin-config.cpp | Tests: plugin config functions.
|       core/ | core 向け単体テスト用のルートディレクトリ
|          test-core-arraylist.cpp | テスト: 配列リスト
|       test-plugin-config.cpp | Tests: plugin config functions.
|       core/ | core 向け単体テスト用のルートディレクトリ
|          test-core-arraylist.cpp | テスト: 配列リスト
// TRANSLATION MISSING
|          test-core-calc.cpp | Tests: calculation of expressions.
|          test-core-calc.cpp | Tests: calculation of expressions.
// TRANSLATION MISSING
|          test-core-command.cpp | Tests: commands.
|          test-core-command.cpp | Tests: commands.
// TRANSLATION MISSING
|          test-core-config-file.cpp | Tests: configuration files.
|          test-core-config-file.cpp | Tests: configuration files.
// TRANSLATION MISSING
|          test-core-crypto.cpp | Tests: cryptographic functions.
|          test-core-crypto.cpp | Tests: cryptographic functions.
// TRANSLATION MISSING
|          test-core-dir.cpp | Tests: directory/file functions.
|          test-core-eval.cpp | テスト: 式の評価
|          test-core-hashtble.cpp | テスト: ハッシュテーブル
|          test-core-hdata.cpp | テスト: hdata
|          test-core-hook.cpp | テスト: フック
|          test-core-infolist.cpp | テスト: インフォリスト
|          test-core-list.cpp | テスト: リスト
|          test-core-dir.cpp | Tests: directory/file functions.
|          test-core-eval.cpp | テスト: 式の評価
|          test-core-hashtble.cpp | テスト: ハッシュテーブル
|          test-core-hdata.cpp | テスト: hdata
|          test-core-hook.cpp | テスト: フック
|          test-core-infolist.cpp | テスト: インフォリスト
|          test-core-list.cpp | テスト: リスト
// TRANSLATION MISSING
|          test-core-network.cpp | Tests: network functions.
|          test-core-secure.cpp | テスト: データ保護
|          test-core-network.cpp | Tests: network functions.
|          test-core-secure.cpp | テスト: データ保護
// TRANSLATION MISSING
|          test-core-signal.cpp | テスト: signals.
|          test-core-string.cpp | テスト: 文字列
|          test-core-url.cpp | テスト: URL
|          test-core-utf8.cpp | テスト: UTF-8
|          test-core-util.cpp | テスト: ユーティリティ関数
|          test-core-signal.cpp | テスト: signals.
|          test-core-string.cpp | テスト: 文字列
|          test-core-url.cpp | テスト: URL
|          test-core-utf8.cpp | テスト: UTF-8
|          test-core-util.cpp | テスト: ユーティリティ関数
// TRANSLATION MISSING
|          test-core-sys.cpp | Tests: system functions.
|          test-core-sys.cpp | Tests: system functions.
// TRANSLATION MISSING
|          hook/ | Root of unit tests for hooks.
|          hook/ | Root of unit tests for hooks.
// TRANSLATION MISSING
|             test-hook-command.cpp | Tests: hooks "command".
|       gui/ | インターフェースの単体テストを収める最上位ディレクトリ
|             test-hook-command.cpp | Tests: hooks "command".
|       gui/ | インターフェースの単体テストを収める最上位ディレクトリ
// TRANSLATION MISSING
|          test-gui-bar-window.cpp | Tests: bar window functions.
|          test-gui-bar-window.cpp | Tests: bar window functions.
// TRANSLATION MISSING
|          test-gui-buffer.cpp | Tests: buffer functions.
|          test-gui-buffer.cpp | Tests: buffer functions.
// TRANSLATION MISSING
|          test-gui-chat.cpp | Tests: chat functions.
|          test-gui-chat.cpp | Tests: chat functions.
// TRANSLATION MISSING
|          test-gui-color.cpp | Tests: colors.
|          test-gui-color.cpp | Tests: colors.
// TRANSLATION MISSING
|          test-gui-filter.cpp | Tests: filters.
|          test-gui-filter.cpp | Tests: filters.
// TRANSLATION MISSING
|          test-gui-input.cpp | Tests: input functions.
|          test-gui-input.cpp | Tests: input functions.
// TRANSLATION MISSING
|          test-gui-key.cpp | Tests: keys.
|          test-gui-line.cpp | テスト: 行
|          test-gui-key.cpp | Tests: keys.
|          test-gui-line.cpp | テスト: 行
// TRANSLATION MISSING
|          test-gui-nick.cpp | テスト: nicks
|       plugins/ | プラグインの単体テストを収める最上位ディレクトリ
|          irc/ | IRC プラグインの単体テストを収める最上位ディレクトリ
|          test-gui-nick.cpp | テスト: nicks
|       plugins/ | プラグインの単体テストを収める最上位ディレクトリ
|          irc/ | IRC プラグインの単体テストを収める最上位ディレクトリ
// TRANSLATION MISSING
|             test-irc-batch.cpp | Tests: IRC batched events.
|             test-irc-batch.cpp | Tests: IRC batched events.
// TRANSLATION MISSING
|             test-irc-buffer.cpp | Tests: IRC buffers.
|             test-irc-buffer.cpp | Tests: IRC buffers.
// TRANSLATION MISSING
|             test-irc-channel.cpp | Tests: IRC channels.
|             test-irc-color.cpp | Tests: IRC colors.
|             test-irc-config.cpp | テスト: IRC 設定
|             test-irc-channel.cpp | Tests: IRC channels.
|             test-irc-color.cpp | Tests: IRC colors.
|             test-irc-config.cpp | テスト: IRC 設定
// TRANSLATION MISSING
|             test-irc-ctcp.cpp | Tests: IRC CTCP.
|             test-irc-ctcp.cpp | Tests: IRC CTCP.
// TRANSLATION MISSING
|             test-irc-ignore.cpp | Tests: IRC ignores.
|             test-irc-ignore.cpp | Tests: IRC ignores.
// TRANSLATION MISSING
|             test-irc-info.cpp | Tests: IRC info.
|             test-irc-info.cpp | Tests: IRC info.
// TRANSLATION MISSING
|             test-irc-join.cpp | Tests: IRC join functions.
|             test-irc-join.cpp | Tests: IRC join functions.
// TRANSLATION MISSING
|             test-irc-list.cpp | Tests: IRC buffer for reply to /list command.
|             test-irc-list.cpp | Tests: IRC buffer for reply to /list command.
// TRANSLATION MISSING
|             test-irc-message.cpp | Tests: IRC messages.
|             test-irc-message.cpp | Tests: IRC messages.
// TRANSLATION MISSING
|             test-irc-mode.cpp | Tests: IRC modes.
|             test-irc-mode.cpp | Tests: IRC modes.
// TRANSLATION MISSING
|             test-irc-nick.cpp | Tests: IRC nicks.
|             test-irc-protocol.cpp | テスト: IRC プロトコル
|             test-irc-nick.cpp | Tests: IRC nicks.
|             test-irc-protocol.cpp | テスト: IRC プロトコル
// TRANSLATION MISSING
|             test-irc-sasl.cpp | Tests: SASL authentication with IRC protocol.
|             test-irc-sasl.cpp | Tests: SASL authentication with IRC protocol.
// TRANSLATION MISSING
|             test-irc-server.cpp | Tests: IRC server.
|             test-irc-server.cpp | Tests: IRC server.
// TRANSLATION MISSING
|             test-irc-tag.cpp | Tests: IRC message tags.
|             test-irc-tag.cpp | Tests: IRC message tags.
// TRANSLATION MISSING
|          logger/ | Root of unit tests for logger plugin.
|          logger/ | Root of unit tests for logger plugin.
// TRANSLATION MISSING
|             test-logger.cpp | Tests: logger.
|             test-logger.cpp | Tests: logger.
// TRANSLATION MISSING
|             test-logger-backlog.cpp | Tests: logger backlog.
|             test-logger-backlog.cpp | Tests: logger backlog.
// TRANSLATION MISSING
|             test-logger-tail.cpp | Tests: logger tail functions.
|             test-logger-tail.cpp | Tests: logger tail functions.
// TRANSLATION MISSING
|          trigger/ | Root of unit tests for trigger plugin.
|          trigger/ | Root of unit tests for trigger plugin.
// TRANSLATION MISSING
|             test-trigger.cpp | Tests: triggers.
|             test-trigger.cpp | Tests: triggers.
// TRANSLATION MISSING
|             test-trigger-config.cpp | Tests: trigger configuration.
|             test-trigger-config.cpp | Tests: trigger configuration.
// TRANSLATION MISSING
|          typing/ | Root of unit tests for typing plugin.
|          typing/ | Root of unit tests for typing plugin.
// TRANSLATION MISSING
|             test-typing.cpp | Tests: typing.
|             test-typing.cpp | Tests: typing.
// TRANSLATION MISSING
|             test-typing-status.cpp | Tests: typing status.
|             test-typing-status.cpp | Tests: typing status.
// TRANSLATION MISSING
|          relay/ | Root of unit tests for Relay plugin.
|          relay/ | Root of unit tests for Relay plugin.
// TRANSLATION MISSING
|             test-relay-auth.cpp | Tests: clients authentication.
|             test-relay-auth.cpp | Tests: clients authentication.
// TRANSLATION MISSING
|             test-relay-http.cpp | Tests: HTTP functions for Relay plugin.
|             test-relay-http.cpp | Tests: HTTP functions for Relay plugin.
// TRANSLATION MISSING
|             irc/ | Root of unit tests for Relay "irc" protocol.
|             test-relay-websocket.cpp | Tests: websocket functions for Relay plugin.
// TRANSLATION MISSING
|                test-relay-irc.cpp | Tests: Relay "irc" protocol.
|             irc/ | Root of unit tests for Relay "irc" protocol.
// TRANSLATION MISSING
|          xfer/ | Root of unit tests for Xfer plugin.
|                test-relay-irc.cpp | Tests: Relay "irc" protocol.
// TRANSLATION MISSING
|             test-xfer-file.cpp | Tests: file functions.
|          xfer/ | Root of unit tests for Xfer plugin.
// TRANSLATION MISSING
|             test-xfer-network.cpp | Tests: network functions.
|             test-xfer-file.cpp | Tests: file functions.
// TRANSLATION MISSING
|             test-xfer-network.cpp | Tests: network functions.
|===
[[documentation_translations]]
+86 -84
View File
@@ -404,91 +404,93 @@ WeeChat „језгро” се налази у следећим директо
[width="100%", cols="2m,3", options="header"]
|===
| Путања/фајл | Опис
| tests/ | Корен тестова.
|    tests.cpp | Програм који се користи за извршавање свих тестова.
|    tests-record.cpp | Бележење и претрага у приказаним порукама.
|    scripts/ | Корен тестова за API скриптовања.
|       test-scripts.cpp | Програм који се користи за извршавање тестова API скриптовања.
|       python/ | Python скрипте које генеришу и покрећу тестове API скриптовања.
|          testapigen.py | Python скрипта која генерише скрипте на свим језицима за тестирање API скриптовања.
|          testapi.py | Python скрипта са тестовима API скриптовања, користи је скрипта testapigen.py.
|          unparse.py | Конверзија Python кода у остале језике, користи је скрипта testapigen.py.
|    unit/ | Корен unit тестова.
|       test-plugins.cpp | Тестови: plugins.
|       test-plugin-api-info.cpp | Тестови: инфо функције API додатака.
|       test-plugin-config.cpp | Тестови: функције конфигурације додатка.
|       core/ | Корен unit тестова језгра.
|          test-core-arraylist.cpp | Тестови: arraylists.
|          test-core-calc.cpp | Тестови: калкулација израза.
|          test-core-command.cpp | Тестови: команде.
|          test-core-config-file.cpp | Тестови: конфигурациони фајлови.
|          test-core-crypto.cpp | Тестови: криптографске функције.
|          test-core-dir.cpp | Тестови: функције директоријума/фајла.
|          test-core-eval.cpp | Тестови: израчунавање израза.
|          test-core-hashtble.cpp | Тестови: hashtables.
|          test-core-hdata.cpp | Тестови: hdata.
|          test-core-hook.cpp | Тестови: куке.
|          test-core-infolist.cpp | Тестови: infolists.
|          test-core-list.cpp | Тестови: листе.
|          test-core-network.cpp | Тестови: мрежне функције.
|          test-core-secure.cpp | Тестови: обезбеђени подаци.
|          test-core-signal.cpp | Тестови: сигнали.
|          test-core-string.cpp | Тестови: стрингови.
|          test-core-url.cpp | Тестови: URL адресе.
|          test-core-utf8.cpp | Тестови: UTF-8.
|          test-core-util.cpp | Тестови: помоћне функције.
|          test-core-sys.cpp | Тестови: системске функције.
|          hook/ | Корен unit тестова за куке.
|             test-hook-command.cpp | Тестови: куке „command”.
|       gui/ | Корен unit тестова интерфејса.
|          test-gui-bar-window.cpp | Тестови: функције прозора траке.
|          test-gui-buffer.cpp | Тестови: бафер функције.
|          test-gui-chat.cpp | Тестови: чет функције.
|          test-gui-color.cpp | Тестови: боје.
|          test-gui-filter.cpp | Тестови: филтери.
|          test-gui-input.cpp | Тестови: улазне функкције.
|          test-gui-key.cpp | Тестови: тастери.
|          test-gui-line.cpp | Тестови: линије.
|          test-gui-nick.cpp | Тестови: надимци.
|       plugins/ | Корен unit тестова додатака.
|          irc/ | Корен unit тестова IRC додатка.
|             test-irc-batch.cpp | Тестови: IRC пакетни догађаји.
|             test-irc-buffer.cpp | Тестови: IRC бафери.
|             test-irc-channel.cpp | Тестови: IRC канали.
|             test-irc-color.cpp | Тестови: IRC боје.
|             test-irc-config.cpp | Тестови: IRC конфигурација.
|             test-irc-ctcp.cpp | Тестови: IRC CTCP.
|             test-irc-ignore.cpp | Тестови: IRC игнорисања.
|             test-irc-info.cpp | Тестови: IRC информације.
|             test-irc-join.cpp | Тестови: IRC функције приступања.
|             test-irc-list.cpp | Тестови: IRC бафер за одговор на /list команду.
|             test-irc-message.cpp | Тестови: IRC поруке.
|             test-irc-mode.cpp | Тестови: IRC режими.
|             test-irc-nick.cpp | Тестови: IRC надимци.
|             test-irc-protocol.cpp | Тестови: IRC протокол.
|             test-irc-sasl.cpp | Тестови: SASL аутентификација са IRC протоколом.
|             test-irc-server.cpp | Тестови: IRC сервер.
|             test-irc-tag.cpp | Тестови: IRC ознаке порука.
|          logger/ | Корен unit тестива за logger додатак.
|             test-logger.cpp | Тестови: logger.
|             test-logger-backlog.cpp | Тестови: logger заостатак.
|             test-logger-tail.cpp | Тестови: logger tail фунцкије.
|          trigger/ | Корен unit тестова за окидач додатак.
|             test-trigger.cpp | Тестови: окидачи.
|             test-trigger-config.cpp | Тестови: конфигурација окидача.
|          typing/ | Корен unit тестова за typing додатак.
|             test-typing.cpp | Тестови: typing.
|             test-typing-status.cpp | Тестови: typing статус.
|          relay/ | Корен unit тестова за Релеј додатак.
|             test-relay-auth.cpp | Тестови: аутентификација клијената.
| Путања/фајл | Опис
| tests/ | Корен тестова.
|    tests.cpp | Програм који се користи за извршавање свих тестова.
|    tests-record.cpp | Бележење и претрага у приказаним порукама.
|    scripts/ | Корен тестова за API скриптовања.
|       test-scripts.cpp | Програм који се користи за извршавање тестова API скриптовања.
|       python/ | Python скрипте које генеришу и покрећу тестове API скриптовања.
|          testapigen.py | Python скрипта која генерише скрипте на свим језицима за тестирање API скриптовања.
|          testapi.py | Python скрипта са тестовима API скриптовања, користи је скрипта testapigen.py.
|          unparse.py | Конверзија Python кода у остале језике, користи је скрипта testapigen.py.
|    unit/ | Корен unit тестова.
|       test-plugins.cpp | Тестови: plugins.
|       test-plugin-api-info.cpp | Тестови: инфо функције API додатака.
|       test-plugin-config.cpp | Тестови: функције конфигурације додатка.
|       core/ | Корен unit тестова језгра.
|          test-core-arraylist.cpp | Тестови: arraylists.
|          test-core-calc.cpp | Тестови: калкулација израза.
|          test-core-command.cpp | Тестови: команде.
|          test-core-config-file.cpp | Тестови: конфигурациони фајлови.
|          test-core-crypto.cpp | Тестови: криптографске функције.
|          test-core-dir.cpp | Тестови: функције директоријума/фајла.
|          test-core-eval.cpp | Тестови: израчунавање израза.
|          test-core-hashtble.cpp | Тестови: hashtables.
|          test-core-hdata.cpp | Тестови: hdata.
|          test-core-hook.cpp | Тестови: куке.
|          test-core-infolist.cpp | Тестови: infolists.
|          test-core-list.cpp | Тестови: листе.
|          test-core-network.cpp | Тестови: мрежне функције.
|          test-core-secure.cpp | Тестови: обезбеђени подаци.
|          test-core-signal.cpp | Тестови: сигнали.
|          test-core-string.cpp | Тестови: стрингови.
|          test-core-url.cpp | Тестови: URL адресе.
|          test-core-utf8.cpp | Тестови: UTF-8.
|          test-core-util.cpp | Тестови: помоћне функције.
|          test-core-sys.cpp | Тестови: системске функције.
|          hook/ | Корен unit тестова за куке.
|             test-hook-command.cpp | Тестови: куке „command”.
|       gui/ | Корен unit тестова интерфејса.
|          test-gui-bar-window.cpp | Тестови: функције прозора траке.
|          test-gui-buffer.cpp | Тестови: бафер функције.
|          test-gui-chat.cpp | Тестови: чет функције.
|          test-gui-color.cpp | Тестови: боје.
|          test-gui-filter.cpp | Тестови: филтери.
|          test-gui-input.cpp | Тестови: улазне функкције.
|          test-gui-key.cpp | Тестови: тастери.
|          test-gui-line.cpp | Тестови: линије.
|          test-gui-nick.cpp | Тестови: надимци.
|       plugins/ | Корен unit тестова додатака.
|          irc/ | Корен unit тестова IRC додатка.
|             test-irc-batch.cpp | Тестови: IRC пакетни догађаји.
|             test-irc-buffer.cpp | Тестови: IRC бафери.
|             test-irc-channel.cpp | Тестови: IRC канали.
|             test-irc-color.cpp | Тестови: IRC боје.
|             test-irc-config.cpp | Тестови: IRC конфигурација.
|             test-irc-ctcp.cpp | Тестови: IRC CTCP.
|             test-irc-ignore.cpp | Тестови: IRC игнорисања.
|             test-irc-info.cpp | Тестови: IRC информације.
|             test-irc-join.cpp | Тестови: IRC функције приступања.
|             test-irc-list.cpp | Тестови: IRC бафер за одговор на /list команду.
|             test-irc-message.cpp | Тестови: IRC поруке.
|             test-irc-mode.cpp | Тестови: IRC режими.
|             test-irc-nick.cpp | Тестови: IRC надимци.
|             test-irc-protocol.cpp | Тестови: IRC протокол.
|             test-irc-sasl.cpp | Тестови: SASL аутентификација са IRC протоколом.
|             test-irc-server.cpp | Тестови: IRC сервер.
|             test-irc-tag.cpp | Тестови: IRC ознаке порука.
|          logger/ | Корен unit тестива за logger додатак.
|             test-logger.cpp | Тестови: logger.
|             test-logger-backlog.cpp | Тестови: logger заостатак.
|             test-logger-tail.cpp | Тестови: logger tail фунцкије.
|          trigger/ | Корен unit тестова за окидач додатак.
|             test-trigger.cpp | Тестови: окидачи.
|             test-trigger-config.cpp | Тестови: конфигурација окидача.
|          typing/ | Корен unit тестова за typing додатак.
|             test-typing.cpp | Тестови: typing.
|             test-typing-status.cpp | Тестови: typing статус.
|          relay/ | Корен unit тестова за Релеј додатак.
|             test-relay-auth.cpp | Тестови: аутентификација клијената.
// TRANSLATION MISSING
|             test-relay-http.cpp | Tests: HTTP functions for Relay plugin.
|             irc/ | Корен unit тестова за Релеј „irc” протокол.
|                test-relay-irc.cpp | Тестови: РЕлеј „irc” протокол.
|          xfer/ | Корен unit тестова за Xfer додатак.
|             test-xfer-file.cpp | Тестови: фајл функције.
|             test-xfer-network.cpp | Тестови: мрежне функције.
|             test-relay-http.cpp | Tests: HTTP functions for Relay plugin.
// TRANSLATION MISSING
|             test-relay-websocket.cpp | Tests: websocket functions for Relay plugin.
|             irc/ | Корен unit тестова за Релеј „irc” протокол.
|                test-relay-irc.cpp | Тестови: РЕлеј „irc” протокол.
|          xfer/ | Корен unit тестова за Xfer додатак.
|             test-xfer-file.cpp | Тестови: фајл функције.
|             test-xfer-network.cpp | Тестови: мрежне функције.
|===
[[documentation_translations]]
+1
View File
@@ -32,6 +32,7 @@
#include "../relay.h"
#include "../relay-client.h"
#include "../relay-http.h"
#include "../relay-websocket.h"
#include "relay-api.h"
#include "relay-api-msg.h"
#include "relay-api-protocol.h"
@@ -32,6 +32,7 @@
#include "../relay-client.h"
#include "../relay-config.h"
#include "../relay-http.h"
#include "../relay-websocket.h"
#include "relay-api.h"
#include "relay-api-msg.h"
#include "relay-api-protocol.h"
+197 -41
View File
@@ -28,8 +28,8 @@
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <gnutls/gnutls.h>
#include <zlib.h>
#include "../weechat-plugin.h"
#include "relay.h"
@@ -549,6 +549,85 @@ relay_client_recv_text_buffer (struct t_relay_client *client,
}
}
/*
* Reads websocket frames.
*/
void
relay_client_read_websocket_frames (struct t_relay_client *client,
struct t_relay_websocket_frame *frames,
int num_frames)
{
int i;
if (!frames || (num_frames <= 0))
return;
for (i = 0; i < num_frames; i++)
{
if (frames[i].payload_size == 0)
{
/*
* When decoded length is 0, assume client sent a PONG frame.
*
* RFC 6455 Section 5.5.3:
*
* "A Pong frame MAY be sent unsolicited. This serves as a
* unidirectional heartbeat. A response to an unsolicited
* Pong frame is not expected."
*/
continue;
}
switch (frames[i].opcode)
{
case RELAY_CLIENT_MSG_PING:
/* print message in raw buffer */
relay_raw_print (client, RELAY_CLIENT_MSG_PING,
RELAY_RAW_FLAG_RECV | RELAY_RAW_FLAG_BINARY,
frames[i].payload,
frames[i].payload_size);
/* answer with a PONG */
relay_client_send (client,
RELAY_CLIENT_MSG_PONG,
frames[i].payload,
frames[i].payload_size,
NULL);
break;
case RELAY_CLIENT_MSG_CLOSE:
/* print message in raw buffer */
relay_raw_print (client, RELAY_CLIENT_MSG_CLOSE,
RELAY_RAW_FLAG_RECV | RELAY_RAW_FLAG_BINARY,
frames[i].payload,
frames[i].payload_size);
/* answer with a CLOSE */
relay_client_send (client,
RELAY_CLIENT_MSG_CLOSE,
frames[i].payload,
frames[i].payload_size,
NULL);
/* close the connection */
relay_client_set_status (client, RELAY_STATUS_DISCONNECTED);
/* ignore any other message after the close */
return;
default:
if (frames[i].payload)
{
if ((client->websocket == RELAY_CLIENT_WEBSOCKET_INITIALIZING)
|| (client->recv_data_type == RELAY_CLIENT_DATA_HTTP))
{
relay_http_recv (client, frames[i].payload);
}
else if ((client->recv_data_type == RELAY_CLIENT_DATA_TEXT_LINE)
|| (client->recv_data_type == RELAY_CLIENT_DATA_TEXT_MULTILINE))
{
relay_client_recv_text (client, frames[i].payload);
}
}
break;
}
}
}
/*
* Reads data from a client.
*/
@@ -557,10 +636,9 @@ int
relay_client_recv_cb (const void *pointer, void *data, int fd)
{
struct t_relay_client *client;
static char buffer[4096], decoded[8192 + 1];
const char *ptr_buffer;
int num_read, rc;
unsigned long long decoded_length, length_buffer;
static char buffer[4096];
int i, num_read, rc, num_frames;
struct t_relay_websocket_frame *frames;
/* make C compiler happy */
(void) data;
@@ -590,8 +668,6 @@ relay_client_recv_cb (const void *pointer, void *data, int fd)
if (num_read > 0)
{
buffer[num_read] = '\0';
ptr_buffer = buffer;
length_buffer = num_read;
/*
* if we are receiving the first message from client, check if it looks
@@ -599,7 +675,7 @@ relay_client_recv_cb (const void *pointer, void *data, int fd)
*/
if (client->bytes_recv == 0)
{
if (relay_websocket_is_valid_http_get (client, buffer))
if (relay_websocket_is_valid_http_get (client->protocol, buffer))
{
/*
* web socket is just initializing for now, it's not accepted
@@ -615,23 +691,12 @@ relay_client_recv_cb (const void *pointer, void *data, int fd)
if (client->websocket == RELAY_CLIENT_WEBSOCKET_READY)
{
/* websocket used, decode message */
rc = relay_websocket_decode_frame ((unsigned char *)buffer,
frames = NULL;
num_frames = 0;
rc = relay_websocket_decode_frame (client,
(unsigned char *)buffer,
(unsigned long long)num_read,
(unsigned char *)decoded,
&decoded_length);
if (decoded_length == 0)
{
/*
* When decoded length is 0, assume client sent a PONG frame.
*
* RFC 6455 Section 5.5.3:
*
* "A Pong frame MAY be sent unsolicited. This serves as a
* unidirectional heartbeat. A response to an unsolicited
* Pong frame is not expected."
*/
return WEECHAT_RC_OK;
}
&frames, &num_frames);
if (!rc)
{
/* error when decoding frame: close connection */
@@ -646,22 +711,26 @@ relay_client_recv_cb (const void *pointer, void *data, int fd)
relay_client_set_status (client, RELAY_STATUS_DISCONNECTED);
return WEECHAT_RC_OK;
}
ptr_buffer = decoded;
length_buffer = decoded_length;
}
if ((client->websocket == RELAY_CLIENT_WEBSOCKET_INITIALIZING)
|| (client->recv_data_type == RELAY_CLIENT_DATA_TEXT_LINE)
|| (client->recv_data_type == RELAY_CLIENT_DATA_TEXT_MULTILINE)
|| (client->recv_data_type == RELAY_CLIENT_DATA_HTTP))
{
/* websocket initializing or text/HTTP data for this client */
relay_client_recv_text_buffer (client, ptr_buffer, length_buffer);
relay_client_read_websocket_frames (client, frames, num_frames);
for (i = 0; i < num_frames; i++)
{
if (frames[i].payload)
free (frames[i].payload);
}
free (frames);
}
else
{
/* receive buffer as-is (binary data) */
/* currently, all supported protocols receive only text, no binary */
if ((client->websocket == RELAY_CLIENT_WEBSOCKET_INITIALIZING)
|| (client->recv_data_type == RELAY_CLIENT_DATA_HTTP))
{
relay_http_recv (client, buffer);
}
else if ((client->recv_data_type == RELAY_CLIENT_DATA_TEXT_LINE)
|| (client->recv_data_type == RELAY_CLIENT_DATA_TEXT_MULTILINE))
{
relay_client_recv_text (client, buffer);
}
}
relay_buffer_refresh (NULL);
}
@@ -1103,9 +1172,8 @@ relay_client_send (struct t_relay_client *client,
WEBSOCKET_FRAME_OPCODE_TEXT : WEBSOCKET_FRAME_OPCODE_BINARY;
break;
}
websocket_frame = relay_websocket_encode_frame (opcode, data,
data_size,
&length_frame);
websocket_frame = relay_websocket_encode_frame (
client, opcode, data, data_size, &length_frame);
if (websocket_frame)
{
ptr_data = websocket_frame;
@@ -1307,6 +1375,7 @@ relay_client_new (int sock, const char *address, struct t_relay_server *server)
new_client->hook_timer_handshake = NULL;
new_client->gnutls_handshake_ok = 0;
new_client->websocket = RELAY_CLIENT_WEBSOCKET_NOT_USED;
new_client->ws_deflate = relay_websocket_deflate_alloc ();
new_client->http_req = relay_http_request_alloc ();
new_client->address = strdup ((address && address[0]) ?
address : "local");
@@ -1516,6 +1585,8 @@ relay_client_new_with_infolist (struct t_infolist *infolist)
{
struct t_relay_client *new_client;
const char *str;
Bytef *ptr_dict;
int dict_size;
new_client = malloc (sizeof (*new_client));
if (new_client)
@@ -1533,6 +1604,46 @@ relay_client_new_with_infolist (struct t_infolist *infolist)
new_client->hook_timer_handshake = NULL;
new_client->gnutls_handshake_ok = 0;
new_client->websocket = weechat_infolist_integer (infolist, "websocket");
new_client->ws_deflate = relay_websocket_deflate_alloc ();
new_client->ws_deflate->enabled = weechat_infolist_integer (infolist, "ws_deflate_enabled");
new_client->ws_deflate->server_context_takeover = weechat_infolist_integer (infolist, "ws_deflate_server_context_takeover");
new_client->ws_deflate->client_context_takeover = weechat_infolist_integer (infolist, "ws_deflate_client_context_takeover");
new_client->ws_deflate->window_bits_deflate = weechat_infolist_integer (infolist, "ws_deflate_window_bits_deflate");
new_client->ws_deflate->window_bits_inflate = weechat_infolist_integer (infolist, "ws_deflate_window_bits_inflate");
new_client->ws_deflate->strm_deflate = NULL;
new_client->ws_deflate->strm_inflate = NULL;
if (weechat_infolist_search_var (infolist, "ws_deflate_strm_deflate_dict"))
{
ptr_dict = weechat_infolist_buffer (infolist, "ws_deflate_strm_deflate_dict", &dict_size);
if (ptr_dict)
{
new_client->ws_deflate->strm_deflate = calloc (1, sizeof (*new_client->ws_deflate->strm_deflate));
if (new_client->ws_deflate->strm_deflate)
{
if (relay_websocket_deflate_init_stream_deflate (new_client->ws_deflate))
{
deflateSetDictionary (new_client->ws_deflate->strm_deflate,
ptr_dict, dict_size);
}
}
}
}
if (weechat_infolist_search_var (infolist, "ws_deflate_strm_inflate_dict"))
{
ptr_dict = weechat_infolist_buffer (infolist, "ws_deflate_strm_inflate_dict", &dict_size);
if (ptr_dict)
{
new_client->ws_deflate->strm_inflate = calloc (1, sizeof (*new_client->ws_deflate->strm_inflate));
if (new_client->ws_deflate->strm_inflate)
{
if (relay_websocket_deflate_init_stream_inflate (new_client->ws_deflate))
{
inflateSetDictionary (new_client->ws_deflate->strm_inflate,
ptr_dict, dict_size);
}
}
}
}
new_client->http_req = relay_http_request_alloc ();
new_client->address = strdup (weechat_infolist_string (infolist, "address"));
str = weechat_infolist_string (infolist, "real_ip");
@@ -1780,6 +1891,7 @@ relay_client_free (struct t_relay_client *client)
free (client->nonce);
if (client->hook_timer_handshake)
weechat_unhook (client->hook_timer_handshake);
relay_websocket_deflate_free (client->ws_deflate);
relay_http_request_free (client->http_req);
if (client->hook_fd)
weechat_unhook (client->hook_fd);
@@ -1881,6 +1993,8 @@ relay_client_add_to_infolist (struct t_infolist *infolist,
{
struct t_infolist_item *ptr_item;
char value[128];
Bytef *dict;
uInt dict_size;
if (!infolist || !client)
return 0;
@@ -1929,6 +2043,47 @@ relay_client_add_to_infolist (struct t_infolist *infolist,
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "websocket", client->websocket))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "ws_deflate_enabled", client->ws_deflate->enabled))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "ws_deflate_server_context_takeover", client->ws_deflate->server_context_takeover))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "ws_deflate_client_context_takeover", client->ws_deflate->client_context_takeover))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "ws_deflate_window_bits_deflate", client->ws_deflate->window_bits_deflate))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "ws_deflate_window_bits_inflate", client->ws_deflate->window_bits_inflate))
return 0;
if (!weechat_infolist_new_var_pointer (ptr_item, "ws_deflate_strm_deflate", client->ws_deflate->strm_deflate))
return 0;
if (!weechat_infolist_new_var_pointer (ptr_item, "ws_deflate_strm_inflate", client->ws_deflate->strm_inflate))
return 0;
if (client->ws_deflate->strm_deflate || client->ws_deflate->strm_inflate)
{
/* save the deflate/inflate dictionary, as it's required after /upgrade */
dict = malloc (32768);
if (dict)
{
if (client->ws_deflate->strm_deflate)
{
if (deflateGetDictionary (client->ws_deflate->strm_deflate, dict, &dict_size) == Z_OK)
{
weechat_infolist_new_var_buffer (ptr_item,
"ws_deflate_strm_deflate_dict",
dict, dict_size);
}
}
if (client->ws_deflate->strm_inflate)
{
if (inflateGetDictionary (client->ws_deflate->strm_inflate, dict, &dict_size) == Z_OK)
{
weechat_infolist_new_var_buffer (ptr_item,
"ws_deflate_strm_inflate_dict",
dict, dict_size);
}
}
free (dict);
}
}
if (!weechat_infolist_new_var_string (ptr_item, "address", client->address))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "real_ip", client->real_ip))
@@ -2015,7 +2170,8 @@ relay_client_print_log ()
weechat_log_printf (" gnutls_sess . . . . . . . : 0x%lx", ptr_client->gnutls_sess);
weechat_log_printf (" hook_timer_handshake. . . : 0x%lx", ptr_client->hook_timer_handshake);
weechat_log_printf (" gnutls_handshake_ok . . . : 0x%lx", ptr_client->gnutls_handshake_ok);
weechat_log_printf (" websocket . . . . . . . . : %d", ptr_client->websocket);
weechat_log_printf (" websocket . . . . . . . . ; %d", ptr_client->websocket);
relay_websocket_deflate_print_log (ptr_client->ws_deflate, "");
relay_http_print_log (ptr_client->http_req);
weechat_log_printf (" address . . . . . . . . . : '%s'", ptr_client->address);
weechat_log_printf (" real_ip . . . . . . . . . : '%s'", ptr_client->real_ip);
+1
View File
@@ -108,6 +108,7 @@ struct t_relay_client
struct t_hook *hook_timer_handshake; /* timer for doing gnutls handshake*/
int gnutls_handshake_ok; /* 1 if handshake was done and OK */
enum t_relay_client_websocket_status websocket; /* websocket status */
struct t_relay_websocket_deflate *ws_deflate; /* websocket deflate data */
struct t_relay_http_request *http_req; /* HTTP request */
char *address; /* string with IP address */
char *real_ip; /* real IP (X-Real-IP HTTP header) */
+22 -11
View File
@@ -78,6 +78,11 @@ relay_http_request_reinit (struct t_relay_http_request *request)
}
weechat_hashtable_remove_all (request->headers);
weechat_hashtable_remove_all (request->accept_encoding);
if (request->ws_deflate)
{
relay_websocket_deflate_free (request->ws_deflate);
request->ws_deflate = relay_websocket_deflate_alloc ();
}
request->content_length = 0;
request->body_size = 0;
if (request->body)
@@ -122,6 +127,7 @@ relay_http_request_alloc ()
WEECHAT_HASHTABLE_STRING,
WEECHAT_HASHTABLE_STRING,
NULL, NULL);
new_request->ws_deflate = relay_websocket_deflate_alloc ();
new_request->content_length = 0;
new_request->body_size = 0;
new_request->body = NULL;
@@ -400,18 +406,12 @@ relay_http_parse_header (struct t_relay_http_request *request,
/* if header is "Accept-Encoding", save the allowed encoding */
if (strcmp (name_lower, "accept-encoding") == 0)
{
items = weechat_string_split (ptr_value, ",", NULL, 0, 0, &num_items);
items = weechat_string_split (ptr_value, ",", " ", 0, 0, &num_items);
if (items)
{
for (i = 0; i < num_items; i++)
{
pos = items[i];
while (pos[0] == ' ')
{
pos++;
}
weechat_hashtable_set (request->accept_encoding,
pos, NULL);
weechat_hashtable_set (request->accept_encoding, items[i], NULL);
}
weechat_string_free_split (items);
}
@@ -426,6 +426,13 @@ relay_http_parse_header (struct t_relay_http_request *request,
request->content_length = (int)number;
}
/*
* if header is "Sec-WebSocket-Extensions", save supported websocket
* extensions
*/
if (strcmp (name_lower, "sec-websocket-extensions") == 0)
relay_websocket_parse_extensions (ptr_value, request->ws_deflate);
free (name);
free (name_lower);
@@ -635,7 +642,7 @@ relay_http_process_websocket (struct t_relay_client *client)
char *handshake;
int rc;
rc = relay_websocket_client_handshake_valid (client);
rc = relay_websocket_client_handshake_valid (client->http_req);
if (rc == -1)
{
@@ -712,7 +719,7 @@ relay_http_process_websocket (struct t_relay_client *client)
}
}
handshake = relay_websocket_build_handshake (client);
handshake = relay_websocket_build_handshake (client->http_req);
if (handshake)
{
relay_client_send (client,
@@ -721,6 +728,8 @@ relay_http_process_websocket (struct t_relay_client *client)
strlen (handshake), NULL);
free (handshake);
client->websocket = RELAY_CLIENT_WEBSOCKET_READY;
memcpy (client->ws_deflate, client->http_req->ws_deflate,
sizeof (*(client->ws_deflate)));
if (client->protocol == RELAY_PROTOCOL_API)
{
/* "api" protocol uses JSON in input/output (multi-line text) */
@@ -977,6 +986,7 @@ relay_http_compress (struct t_relay_http_request *request,
dest = malloc (dest_size);
if (dest)
{
memset (&strm, 0, sizeof (strm));
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
@@ -1252,7 +1262,7 @@ relay_http_print_log (struct t_relay_http_request *request)
{
int i;
weechat_log_printf (" [http_request]");
weechat_log_printf (" http_request:");
weechat_log_printf (" raw . . . . . . . . . . : '%s'",
(request->raw) ? *(request->raw) : NULL);
weechat_log_printf (" status. . . . . . . . . : %d", request->status);
@@ -1278,6 +1288,7 @@ relay_http_print_log (struct t_relay_http_request *request)
request->accept_encoding,
weechat_hashtable_get_string (request->accept_encoding,
"keys_values"));
relay_websocket_deflate_print_log (request->ws_deflate, " ");
weechat_log_printf (" content_length. . . . . : %d", request->content_length);
weechat_log_printf (" body_size . . . . . . . : %d", request->body_size);
weechat_log_printf (" body. . . . . . . . . . : '%s'", request->body);
+1
View File
@@ -61,6 +61,7 @@ struct t_relay_http_request
struct t_hashtable *headers; /* HTTP headers for websocket */
/* and API protocol */
struct t_hashtable *accept_encoding; /* allowed encoding for response */
struct t_relay_websocket_deflate *ws_deflate; /* websocket deflate data */
int content_length; /* value of header "Content-Length" */
int body_size; /* size of HTTP body read so far */
char *body; /* HTTP body (can be NULL) */
+544 -44
View File
@@ -23,6 +23,7 @@
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <zlib.h>
#include "../weechat-plugin.h"
#include "relay.h"
@@ -39,6 +40,120 @@
#define WEBSOCKET_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
/*
* Allocates a t_relay_websocket_deflate structure.
*/
struct t_relay_websocket_deflate *
relay_websocket_deflate_alloc ()
{
struct t_relay_websocket_deflate *new_ws_deflate;
new_ws_deflate = (struct t_relay_websocket_deflate *)malloc (sizeof (*new_ws_deflate));
if (!new_ws_deflate)
return NULL;
new_ws_deflate->enabled = 0;
new_ws_deflate->server_context_takeover = 0;
new_ws_deflate->server_context_takeover = 0;
new_ws_deflate->window_bits_deflate = 0;
new_ws_deflate->window_bits_inflate = 0;
new_ws_deflate->strm_deflate = NULL;
new_ws_deflate->strm_inflate = NULL;
return new_ws_deflate;
}
/*
* Initializes stream for deflate (compression).
*
* Returns:
* 1: OK
* 0: error
*/
int
relay_websocket_deflate_init_stream_deflate (struct t_relay_websocket_deflate *ws_deflate)
{
int rc, compression, compression_level;
compression = weechat_config_integer (relay_config_network_compression);
/* convert % to zlib compression level (1-9) */
compression_level = (((compression - 1) * 9) / 100) + 1;
rc = deflateInit2 (
ws_deflate->strm_deflate,
compression_level,
Z_DEFLATED, /* method */
-1 * ws_deflate->window_bits_deflate,
8, /* memLevel */
Z_DEFAULT_STRATEGY); /* strategy */
return (rc == Z_OK) ? 1 : 0;
}
/*
* Frees a deflate stream in a deflate structure.
*/
void
relay_websocket_deflate_free_stream_deflate (struct t_relay_websocket_deflate *ws_deflate)
{
if (ws_deflate->strm_deflate)
{
deflateEnd (ws_deflate->strm_deflate);
free (ws_deflate->strm_deflate);
ws_deflate->strm_deflate = NULL;
}
}
/*
* Initializes stream for inflate (decompression).
*
* Returns:
* 1: OK
* 0: error
*/
int
relay_websocket_deflate_init_stream_inflate (struct t_relay_websocket_deflate *ws_deflate)
{
int rc;
rc = inflateInit2 (ws_deflate->strm_inflate,
-1 * ws_deflate->window_bits_inflate);
return (rc == Z_OK) ? 1 : 0;
}
/*
* Frees an inflate stream in a deflate structure.
*/
void
relay_websocket_deflate_free_stream_inflate (struct t_relay_websocket_deflate *ws_deflate)
{
if (ws_deflate->strm_inflate)
{
inflateEnd (ws_deflate->strm_inflate);
free (ws_deflate->strm_inflate);
ws_deflate->strm_inflate = NULL;
}
}
/*
* Frees a websocket deflate structure.
*/
void
relay_websocket_deflate_free (struct t_relay_websocket_deflate *ws_deflate)
{
relay_websocket_deflate_free_stream_deflate (ws_deflate);
relay_websocket_deflate_free_stream_inflate (ws_deflate);
free (ws_deflate);
}
/*
* Checks if a message is a HTTP GET with resource "/weechat" (for weechat
* protocol) or "/api" (for api protocol).
@@ -49,15 +164,18 @@
*/
int
relay_websocket_is_valid_http_get (struct t_relay_client *client,
relay_websocket_is_valid_http_get (enum t_relay_protocol protocol,
const char *message)
{
char string[128];
int length;
if (!message)
return 0;
/* the message must start with "GET /weechat" or "GET /api" */
snprintf (string, sizeof (string),
"GET /%s", relay_protocol_string[client->protocol]);
"GET /%s", relay_protocol_string[protocol]);
length = strlen (string);
if (strncmp (message, string, length) != 0)
@@ -122,25 +240,28 @@ relay_websocket_is_valid_http_get (struct t_relay_client *client,
*/
int
relay_websocket_client_handshake_valid (struct t_relay_client *client)
relay_websocket_client_handshake_valid (struct t_relay_http_request *request)
{
const char *value;
if (!request || !request->headers)
return -1;
/* check if we have header "Upgrade" with value "websocket" */
value = weechat_hashtable_get (client->http_req->headers, "upgrade");
value = weechat_hashtable_get (request->headers, "upgrade");
if (!value)
return -1;
if (weechat_strcasecmp (value, "websocket") != 0)
return -1;
/* check if we have header "Sec-WebSocket-Key" with non-empty value */
value = weechat_hashtable_get (client->http_req->headers, "sec-websocket-key");
value = weechat_hashtable_get (request->headers, "sec-websocket-key");
if (!value || !value[0])
return -1;
if (relay_config_regex_websocket_allowed_origins)
{
value = weechat_hashtable_get (client->http_req->headers, "origin");
value = weechat_hashtable_get (request->headers, "origin");
if (!value || !value[0])
return -2;
if (regexec (relay_config_regex_websocket_allowed_origins, value, 0,
@@ -154,6 +275,89 @@ relay_websocket_client_handshake_valid (struct t_relay_client *client)
return 0;
}
/*
* Parses websocket extensions (header "Sec-WebSocket-Extensions").
*
* Header is for example:
* Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
*/
void
relay_websocket_parse_extensions (const char *extensions,
struct t_relay_websocket_deflate *ws_deflate)
{
char **exts, **params, **items, *error;
int i, j, num_exts, num_params, num_items;
long number;
if (!extensions || !ws_deflate)
return;
exts = weechat_string_split (extensions, ",", " ", 0, 0, &num_exts);
if (!exts)
return;
for (i = 0; i < num_exts; i++)
{
params = weechat_string_split (exts[i], ";", " ", 0, 0, &num_params);
if (params && (num_params >= 1)
&& (strcmp (params[0], "permessage-deflate") == 0))
{
ws_deflate->enabled = 1;
ws_deflate->server_context_takeover = 1;
ws_deflate->client_context_takeover = 1;
ws_deflate->window_bits_deflate = 15;
ws_deflate->window_bits_inflate = 15;
for (j = 1; j < num_params; j++)
{
items = weechat_string_split (params[j], "=", " ", 0, 0, &num_items);
if (items && (num_items >= 1))
{
if (strcmp (items[0], "server_no_context_takeover") == 0)
{
ws_deflate->server_context_takeover = 0;
}
else if (strcmp (items[0], "client_no_context_takeover") == 0)
{
ws_deflate->client_context_takeover = 0;
}
else if ((strcmp (items[0], "server_max_window_bits") == 0)
|| (strcmp (items[0], "client_max_window_bits") == 0))
{
number = 15;
if (num_items >= 2)
{
error = NULL;
number = strtol (items[1], &error, 10);
if (error && !error[0])
{
if (number < 8)
number = 8;
else if (number > 15)
number = 15;
}
else
{
number = 15;
}
}
if (strcmp (items[0], "server_max_window_bits") == 0)
ws_deflate->window_bits_deflate = (int)number;
else
ws_deflate->window_bits_inflate = (int)number;
}
}
if (items)
weechat_string_free_split (items);
}
}
if (params)
weechat_string_free_split (params);
}
weechat_string_free_split (exts);
}
/*
* Builds the handshake that will be returned to client, to initialize and use
* the websocket.
@@ -168,13 +372,17 @@ relay_websocket_client_handshake_valid (struct t_relay_client *client)
*/
char *
relay_websocket_build_handshake (struct t_relay_client *client)
relay_websocket_build_handshake (struct t_relay_http_request *request)
{
const char *sec_websocket_key;
char *key, sec_websocket_accept[128], handshake[1024], hash[160 / 8];
char *key, sec_websocket_accept[128], handshake[4096], hash[160 / 8];
char sec_websocket_extensions[512];
int length, hash_size;
sec_websocket_key = weechat_hashtable_get (client->http_req->headers,
if (!request)
return NULL;
sec_websocket_key = weechat_hashtable_get (request->headers,
"sec-websocket-key");
if (!sec_websocket_key || !sec_websocket_key[0])
return NULL;
@@ -204,20 +412,135 @@ relay_websocket_build_handshake (struct t_relay_client *client)
free (key);
if (request->ws_deflate->enabled)
{
snprintf (
sec_websocket_extensions, sizeof (sec_websocket_extensions),
"Sec-WebSocket-Extensions: permessage-deflate; "
"%s"
"%s"
"server_max_window_bits=%d; "
"client_max_window_bits=%d\r\n",
(!request->ws_deflate->server_context_takeover) ? "server_no_context_takeover; " : "",
(!request->ws_deflate->client_context_takeover) ? "client_no_context_takeover; " : "",
request->ws_deflate->window_bits_deflate,
request->ws_deflate->window_bits_inflate);
}
else
{
sec_websocket_extensions[0] = '\0';
}
/* build the handshake (it will be sent as-is to client) */
snprintf (handshake, sizeof (handshake),
"HTTP/1.1 101 Switching Protocols\r\n"
"Upgrade: websocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Accept: %s\r\n"
"%s"
"\r\n",
sec_websocket_accept);
sec_websocket_accept,
sec_websocket_extensions);
return strdup (handshake);
}
/*
* Decodes a websocket frame.
* Decompresses a decoded and compressed websocket frame compressed with
* "deflate" (when websocket extension "permessage-deflate" is enabled).
*
* A final '\0' is added after the decompressed data (the size_decompressed
* does not count this final '\0').
*
* Returns pointer to decompressed data, NULL if error.
*/
char *
relay_websocket_inflate (const void *data, size_t size, z_stream *strm,
size_t *size_decompressed)
{
int rc;
unsigned char append_bytes[4] = { 0x00, 0x00, 0xFF, 0xFF };
Bytef *data2, *dest, *dest2;
uLongf size2, dest_size;
if (!data || (size == 0) || !strm || !size_decompressed)
return NULL;
dest = NULL;
*size_decompressed = 0;
/* append "0x00 0x00 0xFF 0xFF" to data */
size2 = size + sizeof (append_bytes);
data2 = malloc (size2);
if (!data2)
goto error;
memcpy (data2, data, size);
memcpy (data2 + size, append_bytes, sizeof (append_bytes));
/* estimate the decompressed size, by default 10 * size */
dest_size = 10 * size2;
dest = malloc (dest_size);
if (!dest)
goto error;
strm->avail_in = (uInt)size2;
strm->next_in = (Bytef *)data2;
strm->total_in = 0;
strm->avail_out = (uInt)dest_size;
strm->next_out = (Bytef *)dest;
strm->total_out = 0;
/* loop until we manage to decompress whole data in dest */
while (1)
{
rc = inflate (strm, Z_SYNC_FLUSH);
if ((rc == Z_STREAM_END) || (rc == Z_OK))
{
/* data successfully decompressed */
*size_decompressed = strm->total_out;
break;
}
else if (rc == Z_BUF_ERROR)
{
strm->avail_out += dest_size;
dest_size *= 2;
dest2 = realloc (dest, dest_size);
if (!dest2)
goto error;
dest = dest2;
strm->next_out = dest + strm->total_out;
}
else
{
/* any other error is fatal */
goto error;
}
}
dest2 = realloc (dest, *size_decompressed + 1);
if (!dest2)
goto error;
dest = dest2;
dest[*size_decompressed] = '\0';
if (data2)
free (data2);
return (char *)dest;
error:
if (data2)
free (data2);
if (dest)
free (dest);
return NULL;
}
/*
* Decodes a websocket frame and return a list of frames in "*frames" (each
* frame is first decompressed if "permessage-deflate" websocket extension
* is used).
*
* Returns:
* 1: frame decoded successfully
@@ -225,20 +548,42 @@ relay_websocket_build_handshake (struct t_relay_client *client)
*/
int
relay_websocket_decode_frame (const unsigned char *buffer,
relay_websocket_decode_frame (struct t_relay_client *client,
const unsigned char *buffer,
unsigned long long buffer_length,
unsigned char *decoded,
unsigned long long *decoded_length)
struct t_relay_websocket_frame **frames,
int *num_frames)
{
unsigned long long i, index_buffer, length_frame_size, length_frame;
unsigned char opcode;
size_t size_decompressed;
char *payload_decompressed;
struct t_relay_websocket_frame *frames2, *ptr_frame;
if (!buffer || !frames || !num_frames)
return 0;
*frames = NULL;
*num_frames = 0;
*decoded_length = 0;
index_buffer = 0;
/* loop to decode all frames in message */
while (index_buffer + 1 < buffer_length)
{
(*num_frames)++;
frames2 = realloc (*frames, sizeof (**frames) * (*num_frames));
if (!frames2)
return 0;
*frames = frames2;
ptr_frame = &((*frames)[*num_frames - 1]);
ptr_frame->opcode = 0;
ptr_frame->payload_size = 0;
ptr_frame->payload = NULL;
opcode = buffer[index_buffer] & 15;
/*
@@ -276,20 +621,19 @@ relay_websocket_decode_frame (const unsigned char *buffer,
}
index_buffer += 4;
/* copy opcode in decoded data */
/* save opcode */
switch (opcode)
{
case WEBSOCKET_FRAME_OPCODE_PING:
decoded[*decoded_length] = RELAY_CLIENT_MSG_PING;
ptr_frame->opcode = RELAY_CLIENT_MSG_PING;
break;
case WEBSOCKET_FRAME_OPCODE_CLOSE:
decoded[*decoded_length] = RELAY_CLIENT_MSG_CLOSE;
ptr_frame->opcode = RELAY_CLIENT_MSG_CLOSE;
break;
default:
decoded[*decoded_length] = RELAY_CLIENT_MSG_STANDARD;
ptr_frame->opcode = RELAY_CLIENT_MSG_STANDARD;
break;
}
*decoded_length += 1;
/* decode data using masks */
if ((length_frame > buffer_length)
@@ -297,18 +641,98 @@ relay_websocket_decode_frame (const unsigned char *buffer,
{
return 0;
}
ptr_frame->payload = malloc (length_frame + 1);
if (!ptr_frame->payload)
return 0;
ptr_frame->payload_size = length_frame;
/* fill payload */
for (i = 0; i < length_frame; i++)
{
decoded[*decoded_length + i] = (int)((unsigned char)buffer[index_buffer + i]) ^ masks[i % 4];
ptr_frame->payload[i] = (int)((unsigned char)buffer[index_buffer + i]) ^ masks[i % 4];
}
decoded[*decoded_length + length_frame] = '\0';
*decoded_length += length_frame + 1;
ptr_frame->payload[length_frame] = '\0';
/*
* decompress data if frame is not empty and if "permessage-deflate"
* is enabled
*/
if ((length_frame > 0) && client->ws_deflate->enabled)
{
if (!client->ws_deflate->strm_inflate)
{
client->ws_deflate->strm_inflate = calloc (
1, sizeof (*client->ws_deflate->strm_inflate));
if (!client->ws_deflate->strm_inflate)
return 0;
if (!relay_websocket_deflate_init_stream_inflate (client->ws_deflate))
return 0;
}
payload_decompressed = relay_websocket_inflate (
ptr_frame->payload,
ptr_frame->payload_size,
client->ws_deflate->strm_inflate,
&size_decompressed);
if (!payload_decompressed)
return 0;
free (ptr_frame->payload);
ptr_frame->payload = payload_decompressed;
ptr_frame->payload_size = size_decompressed;
if (!client->ws_deflate->client_context_takeover)
relay_websocket_deflate_free_stream_inflate (client->ws_deflate);
}
index_buffer += length_frame;
}
return 1;
}
/*
* Compresses data to send in a websocket frame (when websocket extension
* "permessage-deflate" is enabled).
*
* Returns pointer to compressed data, NULL if error.
*/
char *
relay_websocket_deflate (const void *data, size_t size, z_stream *strm,
size_t *size_compressed)
{
int rc;
uLongf dest_size;
Bytef *dest;
if (!data || (size == 0) || !strm || !size_compressed)
return NULL;
*size_compressed = 0;
dest_size = compressBound (size);
dest = malloc (dest_size);
if (!dest)
return NULL;
strm->avail_in = (uInt)size;
strm->next_in = (Bytef *)data;
strm->total_in = 0;
strm->avail_out = (uInt)dest_size;
strm->next_out = (Bytef *)dest;
strm->total_out = 0;
rc = deflate (strm, Z_SYNC_FLUSH);
if ((rc == Z_STREAM_END) || (rc == Z_OK))
{
*size_compressed = strm->total_out;
return (char *)dest;
}
free (dest);
return NULL;
}
/*
* Encodes data in a websocket frame.
*
@@ -319,56 +743,132 @@ relay_websocket_decode_frame (const unsigned char *buffer,
*/
char *
relay_websocket_encode_frame (int opcode,
const char *buffer,
unsigned long long length,
relay_websocket_encode_frame (struct t_relay_client *client,
int opcode,
const char *payload,
unsigned long long payload_size,
unsigned long long *length_frame)
{
const char *ptr_data;
char *payload_compressed;
size_t size_compressed;
unsigned char *frame;
unsigned long long index;
unsigned long long index, data_size;
*length_frame = 0;
frame = malloc (length + 10);
ptr_data = payload;
data_size = payload_size;
payload_compressed = NULL;
size_compressed = 0;
/*
* compress data if payload is not empty and if "permessage-deflate"
* is enabled
*/
if (((opcode == WEBSOCKET_FRAME_OPCODE_TEXT)
|| (opcode == WEBSOCKET_FRAME_OPCODE_BINARY))
&& (payload_size > 0)
&& client->ws_deflate->enabled)
{
if (!client->ws_deflate->strm_deflate)
{
client->ws_deflate->strm_deflate = calloc (
1, sizeof (*client->ws_deflate->strm_deflate));
if (!client->ws_deflate->strm_deflate)
return NULL;
if (!relay_websocket_deflate_init_stream_deflate (client->ws_deflate))
return NULL;
}
payload_compressed = relay_websocket_deflate (
payload,
payload_size,
client->ws_deflate->strm_deflate,
&size_compressed);
if (!payload_compressed)
return NULL;
ptr_data = payload_compressed;
data_size = size_compressed;
if ((data_size > 4)
&& ((unsigned char)ptr_data[data_size - 4] == 0x00)
&& ((unsigned char)ptr_data[data_size - 3] == 0x00)
&& ((unsigned char)ptr_data[data_size - 2] == 0xFF)
&& ((unsigned char)ptr_data[data_size - 1] == 0xFF))
{
data_size -= 4;
}
if (!client->ws_deflate->server_context_takeover)
relay_websocket_deflate_free_stream_deflate (client->ws_deflate);
/* set bit RSV1: indicate permessage-deflate compressed data */
opcode |= 0x40;
}
frame = malloc (data_size + 10);
if (!frame)
{
if (payload_compressed)
free (payload_compressed);
return NULL;
}
frame[0] = 0x80;
frame[0] |= opcode;
if (length <= 125)
if (data_size <= 125)
{
/* length on one byte */
frame[1] = length;
frame[1] = data_size;
index = 2;
}
else if (length <= 65535)
else if (data_size <= 65535)
{
/* length on 2 bytes */
frame[1] = 126;
frame[2] = (length >> 8) & 0xFF;
frame[3] = length & 0xFF;
frame[2] = (data_size >> 8) & 0xFF;
frame[3] = data_size & 0xFF;
index = 4;
}
else
{
/* length on 8 bytes */
frame[1] = 127;
frame[2] = (length >> 56) & 0xFF;
frame[3] = (length >> 48) & 0xFF;
frame[4] = (length >> 40) & 0xFF;
frame[5] = (length >> 32) & 0xFF;
frame[6] = (length >> 24) & 0xFF;
frame[7] = (length >> 16) & 0xFF;
frame[8] = (length >> 8) & 0xFF;
frame[9] = length & 0xFF;
frame[2] = (data_size >> 56) & 0xFF;
frame[3] = (data_size >> 48) & 0xFF;
frame[4] = (data_size >> 40) & 0xFF;
frame[5] = (data_size >> 32) & 0xFF;
frame[6] = (data_size >> 24) & 0xFF;
frame[7] = (data_size >> 16) & 0xFF;
frame[8] = (data_size >> 8) & 0xFF;
frame[9] = data_size & 0xFF;
index = 10;
}
/* copy buffer after length */
memcpy (frame + index, buffer, length);
/* copy buffer after data_size */
memcpy (frame + index, ptr_data, data_size);
*length_frame = index + length;
*length_frame = index + data_size;
if (payload_compressed)
free (payload_compressed);
return (char *)frame;
}
/*
* Prints websocket deflate data in WeeChat log file (usually for crash dump).
*/
void
relay_websocket_deflate_print_log (struct t_relay_websocket_deflate *ws_deflate,
const char *prefix)
{
weechat_log_printf ("%s ws_deflate:", prefix);
weechat_log_printf ("%s enabled . . . . . . . . : %d", prefix, ws_deflate->enabled);
weechat_log_printf ("%s server_context_takeover : %d", prefix, ws_deflate->server_context_takeover);
weechat_log_printf ("%s client_context_takeover : %d", prefix, ws_deflate->client_context_takeover);
weechat_log_printf ("%s window_bits_deflate . . : %d", prefix, ws_deflate->window_bits_deflate);
weechat_log_printf ("%s window_bits_inflate . . : %d", prefix, ws_deflate->window_bits_inflate);
weechat_log_printf ("%s strm_deflate. . . . . . : 0x%lx", prefix, ws_deflate->strm_deflate);
weechat_log_printf ("%s strm_inflate. . . . . . : 0x%lx", prefix, ws_deflate->strm_inflate);
}
+46 -9
View File
@@ -20,6 +20,10 @@
#ifndef WEECHAT_PLUGIN_RELAY_WEBSOCKET_H
#define WEECHAT_PLUGIN_RELAY_WEBSOCKET_H
#include "relay.h"
#include <zlib.h>
#define WEBSOCKET_FRAME_OPCODE_CONTINUATION 0x00
#define WEBSOCKET_FRAME_OPCODE_TEXT 0x01
#define WEBSOCKET_FRAME_OPCODE_BINARY 0x02
@@ -27,17 +31,50 @@
#define WEBSOCKET_FRAME_OPCODE_PING 0x09
#define WEBSOCKET_FRAME_OPCODE_PONG 0x0A
extern int relay_websocket_is_valid_http_get (struct t_relay_client *client,
struct t_relay_client;
struct t_relay_http_request;
struct t_relay_websocket_deflate
{
int enabled; /* 1 if permessage-deflate is enabled*/
int server_context_takeover; /* context takeover for server */
int client_context_takeover; /* context takeover for client */
int window_bits_deflate; /* window bits for server (comp.) */
/* ("server_max_window_bits") */
int window_bits_inflate; /* window bits for client (decomp.) */
/* ("client_max_window_bits") */
z_stream *strm_deflate; /* stream for deflate (compression) */
z_stream *strm_inflate; /* stream for inflate (decompression)*/
};
struct t_relay_websocket_frame
{
int opcode; /* frame opcode */
int payload_size; /* size of payload */
char *payload; /* payload */
};
extern struct t_relay_websocket_deflate *relay_websocket_deflate_alloc ();
extern int relay_websocket_deflate_init_stream_deflate (struct t_relay_websocket_deflate *ws_deflate);
extern int relay_websocket_deflate_init_stream_inflate (struct t_relay_websocket_deflate *ws_deflate);
extern void relay_websocket_deflate_free (struct t_relay_websocket_deflate *ws_deflate);
extern int relay_websocket_is_valid_http_get (enum t_relay_protocol protocol,
const char *message);
extern int relay_websocket_client_handshake_valid (struct t_relay_client *client);
extern char *relay_websocket_build_handshake (struct t_relay_client *client);
extern int relay_websocket_decode_frame (const unsigned char *buffer,
extern int relay_websocket_client_handshake_valid (struct t_relay_http_request *request);
extern void relay_websocket_parse_extensions (const char *extensions,
struct t_relay_websocket_deflate *ws_deflate);
extern char *relay_websocket_build_handshake (struct t_relay_http_request *request);
extern int relay_websocket_decode_frame (struct t_relay_client *client,
const unsigned char *buffer,
unsigned long long length,
unsigned char *decoded,
unsigned long long *decoded_length);
extern char *relay_websocket_encode_frame (int opcode,
const char *buffer,
unsigned long long length,
struct t_relay_websocket_frame **frames,
int *num_frames);
extern char *relay_websocket_encode_frame (struct t_relay_client *client,
int opcode,
const char *payload,
unsigned long long payload_size,
unsigned long long *length_frame);
extern void relay_websocket_deflate_print_log (struct t_relay_websocket_deflate *ws_deflate,
const char *prefix);
#endif /* WEECHAT_PLUGIN_RELAY_WEBSOCKET_H */
+1
View File
@@ -108,6 +108,7 @@ if (ENABLE_RELAY)
list(APPEND LIB_WEECHAT_UNIT_TESTS_PLUGINS_SRC
unit/plugins/relay/test-relay-auth.cpp
unit/plugins/relay/test-relay-http.cpp
unit/plugins/relay/test-relay-websocket.cpp
unit/plugins/relay/irc/test-relay-irc.cpp
)
endif()
@@ -34,6 +34,7 @@ extern "C"
#include "src/core/wee-string.h"
#include "src/plugins/relay/relay-config.h"
#include "src/plugins/relay/relay-http.h"
#include "src/plugins/relay/relay-websocket.h"
#include "src/plugins/weechat-plugin.h"
extern char *relay_http_url_decode (const char *url);
@@ -89,6 +90,14 @@ TEST(RelayHttp, AllocReinitFree)
LONGS_EQUAL(0, request->headers->items_count);
CHECK(request->accept_encoding);
LONGS_EQUAL(0, request->accept_encoding->items_count);
CHECK(request->ws_deflate);
LONGS_EQUAL(0, request->ws_deflate->enabled);
LONGS_EQUAL(0, request->ws_deflate->server_context_takeover);
LONGS_EQUAL(0, request->ws_deflate->server_context_takeover);
LONGS_EQUAL(0, request->ws_deflate->window_bits_deflate);
LONGS_EQUAL(0, request->ws_deflate->window_bits_inflate);
POINTERS_EQUAL(NULL, request->ws_deflate->strm_deflate);
POINTERS_EQUAL(NULL, request->ws_deflate->strm_inflate);
LONGS_EQUAL(0, request->content_length);
LONGS_EQUAL(0, request->body_size);
POINTERS_EQUAL(NULL, request->body);
@@ -102,6 +111,7 @@ TEST(RelayHttp, AllocReinitFree)
request->http_version = strdup ("HTTP/1.1");
hashtable_set (request->headers, "x-test", "value");
hashtable_set (request->accept_encoding, "gzip", "");
request->ws_deflate->enabled = 1;
request->content_length = 100;
request->body_size = 16;
request->body = (char *)malloc (16);
@@ -123,6 +133,14 @@ TEST(RelayHttp, AllocReinitFree)
LONGS_EQUAL(0, request->headers->items_count);
CHECK(request->accept_encoding);
LONGS_EQUAL(0, request->accept_encoding->items_count);
CHECK(request->ws_deflate);
LONGS_EQUAL(0, request->ws_deflate->enabled);
LONGS_EQUAL(0, request->ws_deflate->server_context_takeover);
LONGS_EQUAL(0, request->ws_deflate->server_context_takeover);
LONGS_EQUAL(0, request->ws_deflate->window_bits_deflate);
LONGS_EQUAL(0, request->ws_deflate->window_bits_inflate);
POINTERS_EQUAL(NULL, request->ws_deflate->strm_deflate);
POINTERS_EQUAL(NULL, request->ws_deflate->strm_inflate);
LONGS_EQUAL(0, request->content_length);
LONGS_EQUAL(0, request->body_size);
POINTERS_EQUAL(NULL, request->body);
@@ -477,6 +495,29 @@ TEST(RelayHttp, ParseHeader)
LONGS_EQUAL(1, request->headers->items_count);
LONGS_EQUAL(123, request->content_length);
free (request);
/* websocket request */
request = relay_http_request_alloc ();
CHECK(request);
relay_http_parse_method_path (request, "GET /api HTTP/1.1");
relay_http_parse_header (
request,
"Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits");
LONGS_EQUAL(RELAY_HTTP_HEADERS, request->status);
STRCMP_EQUAL(
"GET /api HTTP/1.1\n"
"Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits\n",
*(request->raw));
LONGS_EQUAL(1, request->headers->items_count);
CHECK(request->ws_deflate);
LONGS_EQUAL(1, request->ws_deflate->enabled);
LONGS_EQUAL(1, request->ws_deflate->server_context_takeover);
LONGS_EQUAL(1, request->ws_deflate->server_context_takeover);
LONGS_EQUAL(15, request->ws_deflate->window_bits_deflate);
LONGS_EQUAL(15, request->ws_deflate->window_bits_inflate);
POINTERS_EQUAL(NULL, request->ws_deflate->strm_deflate);
POINTERS_EQUAL(NULL, request->ws_deflate->strm_inflate);
free (request);
}
/*
@@ -0,0 +1,415 @@
/*
* test-relay-websocket.cpp - test websocket functions
*
* Copyright (C) 2024 Sébastien Helleu <flashcode@flashtux.org>
*
* This file is part of WeeChat, the extensible chat client.
*
* WeeChat is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* WeeChat is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with WeeChat. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CppUTest/TestHarness.h"
#include "tests/tests.h"
extern "C"
{
#include <string.h>
#include <zlib.h>
#include "src/core/wee-config-file.h"
#include "src/core/wee-hashtable.h"
#include "src/plugins/relay/relay-config.h"
#include "src/plugins/relay/relay-http.h"
#include "src/plugins/relay/relay-websocket.h"
extern void relay_websocket_deflate_free_stream_deflate (struct t_relay_websocket_deflate *ws_deflate);
extern void relay_websocket_deflate_free_stream_inflate (struct t_relay_websocket_deflate *ws_deflate);
extern char *relay_websocket_deflate (const void *data, size_t size, z_stream *strm,
size_t *size_compressed);
extern char *relay_websocket_inflate (const void *data, size_t size, z_stream *strm,
size_t *size_decompressed);
}
TEST_GROUP(RelayWebsocket)
{
};
/*
* Tests functions:
* relay_websocket_deflate_alloc
* relay_websocket_deflate_init_stream_deflate
* relay_websocket_deflate_free_stream_deflate
* relay_websocket_deflate_init_stream_inflate
* relay_websocket_deflate_free_stream_inflate
* relay_websocket_deflate_free
*/
TEST(RelayWebsocket, DeflateAllocFree)
{
struct t_relay_websocket_deflate *ws_deflate;
ws_deflate = relay_websocket_deflate_alloc ();
LONGS_EQUAL(0, ws_deflate->enabled);
LONGS_EQUAL(0, ws_deflate->server_context_takeover);
LONGS_EQUAL(0, ws_deflate->server_context_takeover);
LONGS_EQUAL(0, ws_deflate->window_bits_deflate);
LONGS_EQUAL(0, ws_deflate->window_bits_inflate);
POINTERS_EQUAL(NULL, ws_deflate->strm_deflate);
POINTERS_EQUAL(NULL, ws_deflate->strm_inflate);
ws_deflate->window_bits_deflate = 15;
ws_deflate->window_bits_inflate = 15;
ws_deflate->strm_deflate = (z_stream *)calloc (1, sizeof (*ws_deflate->strm_deflate));
CHECK(ws_deflate->strm_deflate);
relay_websocket_deflate_init_stream_deflate (ws_deflate);
relay_websocket_deflate_free_stream_deflate (ws_deflate);
POINTERS_EQUAL(NULL, ws_deflate->strm_deflate);
ws_deflate->strm_inflate = (z_stream *)calloc (1, sizeof (*ws_deflate->strm_inflate));
CHECK(ws_deflate->strm_inflate);
relay_websocket_deflate_init_stream_inflate (ws_deflate);
relay_websocket_deflate_free_stream_inflate (ws_deflate);
POINTERS_EQUAL(NULL, ws_deflate->strm_inflate);
relay_websocket_deflate_free (ws_deflate);
}
/*
* Tests functions:
* relay_websocket_is_valid_http_get
*/
TEST(RelayWebsocket, IsValidHttpGet)
{
LONGS_EQUAL(0, relay_websocket_is_valid_http_get (RELAY_PROTOCOL_WEECHAT, NULL));
LONGS_EQUAL(0, relay_websocket_is_valid_http_get (RELAY_PROTOCOL_WEECHAT, ""));
LONGS_EQUAL(0, relay_websocket_is_valid_http_get (RELAY_PROTOCOL_WEECHAT, "xxx"));
LONGS_EQUAL(0, relay_websocket_is_valid_http_get (RELAY_PROTOCOL_WEECHAT, "GET /api\r\n"));
LONGS_EQUAL(0, relay_websocket_is_valid_http_get (RELAY_PROTOCOL_WEECHAT, "GET /api test\r\n"));
LONGS_EQUAL(0, relay_websocket_is_valid_http_get (RELAY_PROTOCOL_WEECHAT, "GET /api HTTP/1.1\r\n"));
LONGS_EQUAL(0, relay_websocket_is_valid_http_get (RELAY_PROTOCOL_API, "GET /weechat\r\n"));
LONGS_EQUAL(0, relay_websocket_is_valid_http_get (RELAY_PROTOCOL_API, "GET /weechat test\r\n"));
LONGS_EQUAL(0, relay_websocket_is_valid_http_get (RELAY_PROTOCOL_API, "GET /weechat HTTP/1.1\r\n"));
LONGS_EQUAL(0, relay_websocket_is_valid_http_get (RELAY_PROTOCOL_WEECHAT, "GET /weechat test\r\n"));
LONGS_EQUAL(0, relay_websocket_is_valid_http_get (RELAY_PROTOCOL_API, "GET /api test\r\n"));
LONGS_EQUAL(1, relay_websocket_is_valid_http_get (RELAY_PROTOCOL_WEECHAT, "GET /weechat\r\n"));
LONGS_EQUAL(1, relay_websocket_is_valid_http_get (RELAY_PROTOCOL_WEECHAT, "GET /weechat HTTP/1.1\r\n"));
LONGS_EQUAL(1, relay_websocket_is_valid_http_get (RELAY_PROTOCOL_API, "GET /api\r\n"));
LONGS_EQUAL(1, relay_websocket_is_valid_http_get (RELAY_PROTOCOL_API, "GET /api HTTP/1.1\r\n"));
}
/*
* Tests functions:
* relay_websocket_client_handshake_valid
* relay_websocket_build_handshake
*/
TEST(RelayWebsocket, ClientHandshakeValid)
{
struct t_relay_http_request *request;
char *str;
LONGS_EQUAL(-1, relay_websocket_client_handshake_valid (NULL));
request = relay_http_request_alloc ();
CHECK(request);
LONGS_EQUAL(-1, relay_websocket_client_handshake_valid (NULL));
LONGS_EQUAL(-1, relay_websocket_client_handshake_valid (request));
hashtable_set (request->headers, "upgrade", NULL);
LONGS_EQUAL(-1, relay_websocket_client_handshake_valid (request));
hashtable_set (request->headers, "upgrade", "test");
LONGS_EQUAL(-1, relay_websocket_client_handshake_valid (request));
hashtable_set (request->headers, "upgrade", "websocket");
LONGS_EQUAL(-1, relay_websocket_client_handshake_valid (request));
hashtable_set (request->headers, "sec-websocket-key", NULL);
LONGS_EQUAL(-1, relay_websocket_client_handshake_valid (request));
hashtable_set (request->headers, "sec-websocket-key", "CI1sXhf/u2o34BfWK7NeIg==");
LONGS_EQUAL(0, relay_websocket_client_handshake_valid (request));
POINTERS_EQUAL(NULL, relay_websocket_build_handshake (NULL));
WEE_TEST_STR(
"HTTP/1.1 101 Switching Protocols\r\n"
"Upgrade: websocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Accept: fhLJYtv//ugX2vQXpifQgByRZ5Y=\r\n"
"\r\n",
relay_websocket_build_handshake (request));
config_file_option_set (relay_config_network_websocket_allowed_origins, "example.com", 1);
LONGS_EQUAL(-2, relay_websocket_client_handshake_valid (request));
hashtable_set (request->headers, "origin", NULL);
LONGS_EQUAL(-2, relay_websocket_client_handshake_valid (request));
hashtable_set (request->headers, "origin", "weechat.org");
LONGS_EQUAL(-2, relay_websocket_client_handshake_valid (request));
hashtable_set (request->headers, "origin", "example.com");
LONGS_EQUAL(0, relay_websocket_client_handshake_valid (request));
relay_websocket_parse_extensions (
"permessage-deflate; client_max_window_bits",
request->ws_deflate);
WEE_TEST_STR(
"HTTP/1.1 101 Switching Protocols\r\n"
"Upgrade: websocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Accept: fhLJYtv//ugX2vQXpifQgByRZ5Y=\r\n"
"Sec-WebSocket-Extensions: permessage-deflate; server_max_window_bits=15; client_max_window_bits=15\r\n"
"\r\n",
relay_websocket_build_handshake (request));
relay_websocket_parse_extensions (
"permessage-deflate; client_max_window_bits = 12; server_no_context_takeover",
request->ws_deflate);
WEE_TEST_STR(
"HTTP/1.1 101 Switching Protocols\r\n"
"Upgrade: websocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Accept: fhLJYtv//ugX2vQXpifQgByRZ5Y=\r\n"
"Sec-WebSocket-Extensions: permessage-deflate; server_no_context_takeover; server_max_window_bits=15; client_max_window_bits=12\r\n"
"\r\n",
relay_websocket_build_handshake (request));
relay_websocket_parse_extensions (
"permessage-deflate; client_max_window_bits = 12; server_max_window_bits=8; client_no_context_takeover; server_no_context_takeover",
request->ws_deflate);
WEE_TEST_STR(
"HTTP/1.1 101 Switching Protocols\r\n"
"Upgrade: websocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Accept: fhLJYtv//ugX2vQXpifQgByRZ5Y=\r\n"
"Sec-WebSocket-Extensions: permessage-deflate; server_no_context_takeover; client_no_context_takeover; server_max_window_bits=8; client_max_window_bits=12\r\n"
"\r\n",
relay_websocket_build_handshake (request));
relay_http_request_free (request);
}
/*
* Tests functions:
* relay_websocket_parse_extensions
*/
TEST(RelayWebsocket, ParseExtensions)
{
struct t_relay_websocket_deflate ws_deflate;
relay_websocket_parse_extensions (NULL, NULL);
relay_websocket_parse_extensions ("test", NULL);
relay_websocket_parse_extensions (NULL, &ws_deflate);
memset (&ws_deflate, 0, sizeof (ws_deflate));
relay_websocket_parse_extensions ("test", &ws_deflate);
LONGS_EQUAL(0, ws_deflate.enabled);
memset (&ws_deflate, 0, sizeof (ws_deflate));
relay_websocket_parse_extensions ("permessage-deflate", &ws_deflate);
LONGS_EQUAL(1, ws_deflate.enabled);
LONGS_EQUAL(1, ws_deflate.server_context_takeover);
LONGS_EQUAL(1, ws_deflate.client_context_takeover);
LONGS_EQUAL(15, ws_deflate.window_bits_deflate);
LONGS_EQUAL(15, ws_deflate.window_bits_inflate);
POINTERS_EQUAL(NULL, ws_deflate.strm_deflate);
POINTERS_EQUAL(NULL, ws_deflate.strm_inflate);
memset (&ws_deflate, 0, sizeof (ws_deflate));
relay_websocket_parse_extensions ("permessage-deflate; client_max_window_bits",
&ws_deflate);
LONGS_EQUAL(1, ws_deflate.enabled);
LONGS_EQUAL(1, ws_deflate.server_context_takeover);
LONGS_EQUAL(1, ws_deflate.client_context_takeover);
LONGS_EQUAL(15, ws_deflate.window_bits_deflate);
LONGS_EQUAL(15, ws_deflate.window_bits_inflate);
POINTERS_EQUAL(NULL, ws_deflate.strm_deflate);
POINTERS_EQUAL(NULL, ws_deflate.strm_inflate);
/* client_max_window_bits < 8 (min value) */
memset (&ws_deflate, 0, sizeof (ws_deflate));
relay_websocket_parse_extensions (
"permessage-deflate; client_max_window_bits=4",
&ws_deflate);
LONGS_EQUAL(1, ws_deflate.enabled);
LONGS_EQUAL(1, ws_deflate.server_context_takeover);
LONGS_EQUAL(1, ws_deflate.client_context_takeover);
LONGS_EQUAL(15, ws_deflate.window_bits_deflate);
LONGS_EQUAL(8, ws_deflate.window_bits_inflate);
POINTERS_EQUAL(NULL, ws_deflate.strm_deflate);
POINTERS_EQUAL(NULL, ws_deflate.strm_inflate);
/* client_max_window_bits > 15 (max value) */
memset (&ws_deflate, 0, sizeof (ws_deflate));
relay_websocket_parse_extensions (
"permessage-deflate; client_max_window_bits=30",
&ws_deflate);
LONGS_EQUAL(1, ws_deflate.enabled);
LONGS_EQUAL(1, ws_deflate.server_context_takeover);
LONGS_EQUAL(1, ws_deflate.client_context_takeover);
LONGS_EQUAL(15, ws_deflate.window_bits_deflate);
LONGS_EQUAL(15, ws_deflate.window_bits_inflate);
POINTERS_EQUAL(NULL, ws_deflate.strm_deflate);
POINTERS_EQUAL(NULL, ws_deflate.strm_inflate);
/* invalid value for client_max_window_bits */
memset (&ws_deflate, 0, sizeof (ws_deflate));
relay_websocket_parse_extensions (
"permessage-deflate; client_max_window_bits=test",
&ws_deflate);
LONGS_EQUAL(1, ws_deflate.enabled);
LONGS_EQUAL(1, ws_deflate.server_context_takeover);
LONGS_EQUAL(1, ws_deflate.client_context_takeover);
LONGS_EQUAL(15, ws_deflate.window_bits_deflate);
LONGS_EQUAL(15, ws_deflate.window_bits_inflate);
POINTERS_EQUAL(NULL, ws_deflate.strm_deflate);
POINTERS_EQUAL(NULL, ws_deflate.strm_inflate);
memset (&ws_deflate, 0, sizeof (ws_deflate));
relay_websocket_parse_extensions (
"permessage-deflate; client_max_window_bits=9",
&ws_deflate);
LONGS_EQUAL(1, ws_deflate.enabled);
LONGS_EQUAL(1, ws_deflate.server_context_takeover);
LONGS_EQUAL(1, ws_deflate.client_context_takeover);
LONGS_EQUAL(15, ws_deflate.window_bits_deflate);
LONGS_EQUAL(9, ws_deflate.window_bits_inflate);
POINTERS_EQUAL(NULL, ws_deflate.strm_deflate);
POINTERS_EQUAL(NULL, ws_deflate.strm_inflate);
memset (&ws_deflate, 0, sizeof (ws_deflate));
relay_websocket_parse_extensions (
"permessage-deflate; client_max_window_bits=9; server_max_window_bits=10",
&ws_deflate);
LONGS_EQUAL(1, ws_deflate.enabled);
LONGS_EQUAL(1, ws_deflate.server_context_takeover);
LONGS_EQUAL(1, ws_deflate.client_context_takeover);
LONGS_EQUAL(10, ws_deflate.window_bits_deflate);
LONGS_EQUAL(9, ws_deflate.window_bits_inflate);
POINTERS_EQUAL(NULL, ws_deflate.strm_deflate);
POINTERS_EQUAL(NULL, ws_deflate.strm_inflate);
memset (&ws_deflate, 0, sizeof (ws_deflate));
relay_websocket_parse_extensions (
"permessage-deflate; client_max_window_bits=9; server_max_window_bits=10; "
"server_no_context_takeover",
&ws_deflate);
LONGS_EQUAL(1, ws_deflate.enabled);
LONGS_EQUAL(0, ws_deflate.server_context_takeover);
LONGS_EQUAL(1, ws_deflate.client_context_takeover);
LONGS_EQUAL(10, ws_deflate.window_bits_deflate);
LONGS_EQUAL(9, ws_deflate.window_bits_inflate);
POINTERS_EQUAL(NULL, ws_deflate.strm_deflate);
POINTERS_EQUAL(NULL, ws_deflate.strm_inflate);
memset (&ws_deflate, 0, sizeof (ws_deflate));
relay_websocket_parse_extensions (
"permessage-deflate; client_max_window_bits=9; server_max_window_bits=10; "
"server_no_context_takeover; client_no_context_takeover",
&ws_deflate);
LONGS_EQUAL(1, ws_deflate.enabled);
LONGS_EQUAL(0, ws_deflate.server_context_takeover);
LONGS_EQUAL(0, ws_deflate.client_context_takeover);
LONGS_EQUAL(10, ws_deflate.window_bits_deflate);
LONGS_EQUAL(9, ws_deflate.window_bits_inflate);
POINTERS_EQUAL(NULL, ws_deflate.strm_deflate);
POINTERS_EQUAL(NULL, ws_deflate.strm_inflate);
}
/*
* Tests functions:
* relay_websocket_deflate
* relay_websocket_inflate
*/
TEST(RelayWebsocket, Inflate)
{
struct t_relay_websocket_deflate *ws_deflate;
char payload[256], *payload_comp, *payload_decomp;
size_t size_comp, size_decomp;
int i;
ws_deflate = relay_websocket_deflate_alloc ();
CHECK(ws_deflate);
ws_deflate->window_bits_deflate = 15;
ws_deflate->window_bits_inflate = 15;
ws_deflate->strm_deflate = (z_stream *)calloc (1, sizeof (*ws_deflate->strm_deflate));
CHECK(ws_deflate->strm_deflate);
LONGS_EQUAL(1, relay_websocket_deflate_init_stream_deflate (ws_deflate));
ws_deflate->strm_inflate = (z_stream *)calloc (1, sizeof (*ws_deflate->strm_inflate));
CHECK(ws_deflate->strm_inflate);
LONGS_EQUAL(1, relay_websocket_deflate_init_stream_inflate (ws_deflate));
for (i = 0; i < (int)sizeof (payload); i++)
{
payload[i] = i % 64;
}
POINTERS_EQUAL(NULL, relay_websocket_deflate (NULL, 0, NULL, NULL));
POINTERS_EQUAL(NULL, relay_websocket_deflate (payload, 0, NULL, NULL));
POINTERS_EQUAL(NULL, relay_websocket_deflate (payload, sizeof (payload),
NULL, NULL));
POINTERS_EQUAL(NULL, relay_websocket_deflate (payload, sizeof (payload),
ws_deflate->strm_deflate, NULL));
payload_comp = (char *)relay_websocket_deflate (payload, sizeof (payload),
ws_deflate->strm_deflate, &size_comp);
CHECK(payload_comp);
CHECK((size_comp > 0) && (size_comp < (int)sizeof (payload)));
payload_decomp = (char *)relay_websocket_inflate (payload_comp, size_comp,
ws_deflate->strm_inflate, &size_decomp);
CHECK(payload_decomp);
LONGS_EQUAL(sizeof (payload), size_decomp);
MEMCMP_EQUAL(payload, payload_decomp, sizeof (payload));
free (payload_decomp);
free (payload_comp);
relay_websocket_deflate_free (ws_deflate);
}
/*
* Tests functions:
* relay_websocket_decode_frame
*/
TEST(RelayWebsocket, DecodeFrame)
{
/* TODO: write tests */
}
/*
* Tests functions:
* relay_websocket_encode_frame
*/
TEST(RelayWebsocket, EncodeFrame)
{
/* TODO: write tests */
}
/*
* Tests functions:
* relay_websocket_deflate_print_log
*/
TEST(RelayWebsocket, DeflatePrintLog)
{
/* TODO: write tests */
}