| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | #ifndef NETDATA_WEB_SERVER_WEBSOCKET_H |
| 4 | #define NETDATA_WEB_SERVER_WEBSOCKET_H 1 |
| 5 | |
| 6 | #include "libnetdata/libnetdata.h" |
| 7 | |
| 8 | // WebSocket subprotocols supported by Netdata |
| 9 | typedef enum __attribute__((packed)) { |
| 10 | WS_PROTOCOL_DEFAULT = 0, // the protocol is selected from the url |
| 11 | WS_PROTOCOL_UNKNOWN, // Unknown or unsupported protocol |
| 12 | WS_PROTOCOL_JSONRPC, // JSON-RPC protocol |
| 13 | WS_PROTOCOL_ECHO, // Echo protocol |
| 14 | WS_PROTOCOL_MCP, // Model Context Protocol |
| 15 | } WEBSOCKET_PROTOCOL; |
| 16 | ENUM_STR_DEFINE_FUNCTIONS_EXTERN(WEBSOCKET_PROTOCOL); |
| 17 | |
| 18 | // WebSocket extensions supported by Netdata |
| 19 | typedef enum __attribute__((packed)) { |
| 20 | // RFC 7692 |
| 21 | WS_EXTENSION_NONE = 0, // No extensions |
| 22 | WS_EXTENSION_PERMESSAGE_DEFLATE = (1 << 0), // permessage-deflate |
| 23 | WS_EXTENSION_CLIENT_NO_CONTEXT_TAKEOVER = (1 << 1), // client_no_context_takeover |
| 24 | WS_EXTENSION_SERVER_NO_CONTEXT_TAKEOVER = (1 << 2), // server_no_context_takeover |
| 25 | WS_EXTENSION_SERVER_MAX_WINDOW_BITS = (1 << 3), // server_max_window_bits |
| 26 | WS_EXTENSION_CLIENT_MAX_WINDOW_BITS = (1 << 4) // client_max_window_bits |
| 27 | } WEBSOCKET_EXTENSION; |
| 28 | |
| 29 | // Forward declarations |
| 30 | struct web_client; |
| 31 | struct websocket_server_client; |
| 32 | |
| 33 | // WebSocket connection state |
| 34 | typedef enum __attribute__((packed)) { |
| 35 | WS_STATE_HANDSHAKE = 0, // Initial handshake in progress |
| 36 | WS_STATE_OPEN = 1, // Connection established |
| 37 | WS_STATE_CLOSING_SERVER = 2, // Server initiated closing handshake |
| 38 | WS_STATE_CLOSING_CLIENT = 3, // Client initiated closing handshake |
| 39 | WS_STATE_CLOSED = 4 // Connection closed |
| 40 | } WEBSOCKET_STATE; |
| 41 | ENUM_STR_DEFINE_FUNCTIONS_EXTERN(WEBSOCKET_STATE); |
| 42 | |
| 43 | // WebSocket message types (opcodes) as per RFC 6455 |
| 44 | typedef enum __attribute__((packed)) { |
| 45 | WS_OPCODE_CONTINUATION = 0x0, |
| 46 | WS_OPCODE_TEXT = 0x1, |
| 47 | WS_OPCODE_BINARY = 0x2, |
| 48 | WS_OPCODE_CLOSE = 0x8, |
| 49 | WS_OPCODE_PING = 0x9, |
| 50 | WS_OPCODE_PONG = 0xA |
| 51 | } WEBSOCKET_OPCODE; |
| 52 | ENUM_STR_DEFINE_FUNCTIONS_EXTERN(WEBSOCKET_OPCODE); |
| 53 | |
| 54 | // WebSocket close codes as per RFC 6455 |
| 55 | typedef enum __attribute__((packed)) { |
| 56 | // Standard WebSocket close codes |
| 57 | WS_CLOSE_NORMAL = 1000, // Normal closure, meaning the purpose for which the connection was established has been fulfilled |
| 58 | WS_CLOSE_GOING_AWAY = 1001, // Server/client going away (such as server shutdown or browser navigating away) |
| 59 | WS_CLOSE_PROTOCOL_ERROR = 1002, // Protocol error |
| 60 | WS_CLOSE_UNSUPPORTED_DATA = 1003, // Client received data it couldn't accept (e.g., server sent binary data when client only supports text) |
| 61 | WS_CLOSE_RESERVED = 1004, // Reserved. Specific meaning might be defined in the future. |
| 62 | WS_CLOSE_NO_STATUS = 1005, // No status code was provided even though one was expected |
| 63 | WS_CLOSE_ABNORMAL = 1006, // Connection closed abnormally (no close frame received) |
| 64 | WS_CLOSE_INVALID_PAYLOAD = 1007, // Frame payload data is invalid (e.g., non-UTF-8 data in a text frame) |
| 65 | WS_CLOSE_POLICY_VIOLATION = 1008, // Generic message received that violates policy |
| 66 | WS_CLOSE_MESSAGE_TOO_BIG = 1009, // Message too big to process |
| 67 | WS_CLOSE_EXTENSION_MISSING = 1010, // Client expected the server to negotiate one or more extensions, but server didn't |
| 68 | WS_CLOSE_INTERNAL_ERROR = 1011, // Server encountered an unexpected condition preventing it from fulfilling the request |
| 69 | WS_CLOSE_TLS_HANDSHAKE = 1015, // Transport Layer Security (TLS) handshake failure |
| 70 | |
| 71 | // Netdata-specific close codes (4000-4999 range is available for private use) |
| 72 | WS_CLOSE_NETDATA_TIMEOUT = 4000, // Client timed out due to inactivity |
| 73 | WS_CLOSE_NETDATA_SHUTDOWN = 4001, // Server is shutting down |
| 74 | WS_CLOSE_NETDATA_REJECTED = 4002, // Connection rejected by server |
| 75 | WS_CLOSE_NETDATA_RATE_LIMIT= 4003 // Client exceeded rate limit |
| 76 | } WEBSOCKET_CLOSE_CODE; |
| 77 | ENUM_STR_DEFINE_FUNCTIONS_EXTERN(WEBSOCKET_CLOSE_CODE); |
| 78 | |
| 79 | /** |
| 80 | * WebSocket Protocol Handler Callbacks |
| 81 | * |
| 82 | * These callbacks are invoked when specific events occur during the WebSocket lifecycle: |
| 83 | * |
| 84 | * - on_connect: Called when a client successfully connects and is ready to exchange messages. |
| 85 | * This happens after the WebSocket handshake is complete and the client is added to a thread. |
| 86 | * Use this callback to welcome the client or initialize any protocol-specific state. |
| 87 | * |
| 88 | * - on_message: Called when a complete message is received from the client. |
| 89 | * This is where the protocol processes incoming messages from clients. |
| 90 | * |
| 91 | * - on_close: Called BEFORE sending a close frame to the client. |
| 92 | * This gives the protocol a chance to inject a final message before the connection closes. |
| 93 | * |
| 94 | * - on_disconnect: Called when a client is about to be disconnected. |
| 95 | * Use this callback to clean up any protocol-specific state for the client. |
| 96 | */ |
| 97 | |
| 98 | // Public WebSocket API functions |
| 99 | |
| 100 | // WebSocket detection and handshake |
| 101 | short int websocket_handle_handshake(struct web_client *w); |
| 102 | |
| 103 | // Initialize the WebSocket subsystem |
| 104 | void websocket_initialize(void); |
| 105 | |
| 106 | #endif // NETDATA_WEB_SERVER_WEBSOCKET_H |