| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | #ifndef NETDATA_WEB_CLIENT_H |
| 4 | #define NETDATA_WEB_CLIENT_H 1 |
| 5 | |
| 6 | #include "libnetdata/libnetdata.h" |
| 7 | #include "../websocket/websocket.h" |
| 8 | |
| 9 | struct web_client; |
| 10 | |
| 11 | extern int web_enable_gzip, web_gzip_level, web_gzip_strategy; |
| 12 | |
| 13 | #define HTTP_REQ_MAX_HEADER_FETCH_TRIES 100 |
| 14 | |
| 15 | extern int respect_web_browser_do_not_track_policy; |
| 16 | extern const char *web_x_frame_options; |
| 17 | |
| 18 | typedef enum __attribute__((packed)) { |
| 19 | HTTP_VALIDATION_OK, |
| 20 | HTTP_VALIDATION_NOT_SUPPORTED, |
| 21 | HTTP_VALIDATION_TOO_MANY_READ_RETRIES, |
| 22 | HTTP_VALIDATION_MALFORMED_URL, |
| 23 | HTTP_VALIDATION_INCOMPLETE, |
| 24 | HTTP_VALIDATION_REDIRECT |
| 25 | } HTTP_VALIDATION; |
| 26 | |
| 27 | typedef enum __attribute__((packed)) { |
| 28 | WEB_CLIENT_FLAG_DEAD = (1 << 0), // this client is dead |
| 29 | |
| 30 | WEB_CLIENT_FLAG_KEEPALIVE = (1 << 1), // the web client will be re-used |
| 31 | |
| 32 | // compression |
| 33 | WEB_CLIENT_ENCODING_GZIP = (1 << 2), |
| 34 | WEB_CLIENT_ENCODING_DEFLATE = (1 << 3), |
| 35 | WEB_CLIENT_CHUNKED_TRANSFER = (1 << 4), // chunked transfer (used with zlib compression) |
| 36 | |
| 37 | WEB_CLIENT_FLAG_WAIT_RECEIVE = (1 << 5), // we are waiting more input data |
| 38 | WEB_CLIENT_FLAG_WAIT_SEND = (1 << 6), // we have data to send to the client |
| 39 | WEB_CLIENT_FLAG_SSL_WAIT_RECEIVE = (1 << 7), // we are waiting more input data from ssl connection |
| 40 | WEB_CLIENT_FLAG_SSL_WAIT_SEND = (1 << 8), // we have data to send to the client from ssl connection |
| 41 | |
| 42 | // DNT |
| 43 | WEB_CLIENT_FLAG_DO_NOT_TRACK = (1 << 9), // we should not set cookies on this client |
| 44 | WEB_CLIENT_FLAG_TRACKING_REQUIRED = (1 << 10), // we need to send cookies |
| 45 | |
| 46 | // connection type |
| 47 | WEB_CLIENT_FLAG_CONN_TCP = (1 << 11), // the client is using a TCP socket |
| 48 | WEB_CLIENT_FLAG_CONN_UNIX = (1 << 12), // the client is using a UNIX socket |
| 49 | WEB_CLIENT_FLAG_CONN_CLOUD = (1 << 13), // the client is using Netdata Cloud |
| 50 | WEB_CLIENT_FLAG_CONN_WEBRTC = (1 << 14), // the client is using WebRTC |
| 51 | |
| 52 | // streaming and websocket |
| 53 | WEB_CLIENT_FLAG_DONT_CLOSE_SOCKET = (1 << 15), // don't close the socket when cleaning up |
| 54 | |
| 55 | // dashboard version |
| 56 | WEB_CLIENT_FLAG_PATH_IS_V0 = (1 << 16), // v0 dashboard found on the path |
| 57 | WEB_CLIENT_FLAG_PATH_IS_V1 = (1 << 17), // v1 dashboard found on the path |
| 58 | WEB_CLIENT_FLAG_PATH_IS_V2 = (1 << 18), // v2 dashboard found on the path |
| 59 | WEB_CLIENT_FLAG_PATH_IS_V3 = (1 << 19), // v3 dashboard found on the path |
| 60 | WEB_CLIENT_FLAG_PATH_HAS_TRAILING_SLASH = (1 << 20), // the path has a trailing hash |
| 61 | WEB_CLIENT_FLAG_PATH_HAS_FILE_EXTENSION = (1 << 21), // the path ends with a filename extension |
| 62 | |
| 63 | // transient settings |
| 64 | WEB_CLIENT_FLAG_PROGRESS_TRACKING = (1 << 22), // flag to avoid redoing progress work |
| 65 | |
| 66 | // websocket flags |
| 67 | WEB_CLIENT_FLAG_WEBSOCKET_CLIENT = (1 << 23), // this is a websocket client |
| 68 | WEB_CLIENT_FLAG_WEBSOCKET_HANDSHAKE = (1 << 24), // websocket handshake detected |
| 69 | WEB_CLIENT_FLAG_ACCEPT_JSON = (1 << 25), |
| 70 | WEB_CLIENT_FLAG_ACCEPT_SSE = (1 << 26), |
| 71 | WEB_CLIENT_FLAG_ACCEPT_TEXT = (1 << 27), |
| 72 | WEB_CLIENT_FLAG_MCP_PREVIEW_KEY = (1 << 28), // Authorization header matched MCP preview key |
| 73 | WEB_CLIENT_FLAG_PATH_IS_MCP = (1 << 29), // URL path is /mcp[/...] or /sse[/...] — set during URL decoding so it's also available for OPTIONS preflights (which skip the URL dispatcher) |
| 74 | } WEB_CLIENT_FLAGS; |
| 75 | |
| 76 | #define WEB_CLIENT_FLAG_PATH_WITH_VERSION (WEB_CLIENT_FLAG_PATH_IS_V0|WEB_CLIENT_FLAG_PATH_IS_V1|WEB_CLIENT_FLAG_PATH_IS_V2|WEB_CLIENT_FLAG_PATH_IS_V3) |
| 77 | // PATH_IS_MCP is intentionally *not* in the reset mask: it is set during |
| 78 | // URL decoding, not during URL dispatch, so resetting it here (which runs |
| 79 | // after decoding but before dispatch on POST/GET/etc.) would wipe it |
| 80 | // before the response builder could read it. |
| 81 | #define web_client_reset_path_flags(w) (w)->flags &= ~(WEB_CLIENT_FLAG_PATH_WITH_VERSION|WEB_CLIENT_FLAG_PATH_HAS_TRAILING_SLASH|WEB_CLIENT_FLAG_PATH_HAS_FILE_EXTENSION) |
| 82 | |
| 83 | #define web_client_flag_check(w, flag) ((w)->flags & (flag)) |
| 84 | #define web_client_flag_set(w, flag) (w)->flags |= (flag) |
| 85 | #define web_client_flag_clear(w, flag) (w)->flags &= ~(flag) |
| 86 | |
| 87 | #define WEB_CLIENT_IS_DEAD(w) web_client_flag_set(w, WEB_CLIENT_FLAG_DEAD) |
| 88 | #define web_client_check_dead(w) web_client_flag_check(w, WEB_CLIENT_FLAG_DEAD) |
| 89 | |
| 90 | #define web_client_has_keepalive(w) web_client_flag_check(w, WEB_CLIENT_FLAG_KEEPALIVE) |
| 91 | #define web_client_enable_keepalive(w) web_client_flag_set(w, WEB_CLIENT_FLAG_KEEPALIVE) |
| 92 | #define web_client_disable_keepalive(w) web_client_flag_clear(w, WEB_CLIENT_FLAG_KEEPALIVE) |
| 93 | |
| 94 | #define web_client_has_donottrack(w) web_client_flag_check(w, WEB_CLIENT_FLAG_DO_NOT_TRACK) |
| 95 | #define web_client_enable_donottrack(w) web_client_flag_set(w, WEB_CLIENT_FLAG_DO_NOT_TRACK) |
| 96 | #define web_client_disable_donottrack(w) web_client_flag_clear(w, WEB_CLIENT_FLAG_DO_NOT_TRACK) |
| 97 | |
| 98 | #define web_client_has_tracking_required(w) web_client_flag_check(w, WEB_CLIENT_FLAG_TRACKING_REQUIRED) |
| 99 | #define web_client_enable_tracking_required(w) web_client_flag_set(w, WEB_CLIENT_FLAG_TRACKING_REQUIRED) |
| 100 | #define web_client_disable_tracking_required(w) web_client_flag_clear(w, WEB_CLIENT_FLAG_TRACKING_REQUIRED) |
| 101 | |
| 102 | #define web_client_has_wait_receive(w) web_client_flag_check(w, WEB_CLIENT_FLAG_WAIT_RECEIVE) |
| 103 | #define web_client_enable_wait_receive(w) web_client_flag_set(w, WEB_CLIENT_FLAG_WAIT_RECEIVE) |
| 104 | #define web_client_disable_wait_receive(w) web_client_flag_clear(w, WEB_CLIENT_FLAG_WAIT_RECEIVE) |
| 105 | |
| 106 | #define web_client_has_wait_send(w) web_client_flag_check(w, WEB_CLIENT_FLAG_WAIT_SEND) |
| 107 | #define web_client_enable_wait_send(w) web_client_flag_set(w, WEB_CLIENT_FLAG_WAIT_SEND) |
| 108 | #define web_client_disable_wait_send(w) web_client_flag_clear(w, WEB_CLIENT_FLAG_WAIT_SEND) |
| 109 | |
| 110 | #define web_client_has_ssl_wait_receive(w) web_client_flag_check(w, WEB_CLIENT_FLAG_SSL_WAIT_RECEIVE) |
| 111 | #define web_client_enable_ssl_wait_receive(w) web_client_flag_set(w, WEB_CLIENT_FLAG_SSL_WAIT_RECEIVE) |
| 112 | #define web_client_disable_ssl_wait_receive(w) web_client_flag_clear(w, WEB_CLIENT_FLAG_SSL_WAIT_RECEIVE) |
| 113 | |
| 114 | #define web_client_has_ssl_wait_send(w) web_client_flag_check(w, WEB_CLIENT_FLAG_SSL_WAIT_SEND) |
| 115 | #define web_client_enable_ssl_wait_send(w) web_client_flag_set(w, WEB_CLIENT_FLAG_SSL_WAIT_SEND) |
| 116 | #define web_client_disable_ssl_wait_send(w) web_client_flag_clear(w, WEB_CLIENT_FLAG_SSL_WAIT_SEND) |
| 117 | |
| 118 | #define web_client_has_mcp_preview_key(w) web_client_flag_check(w, WEB_CLIENT_FLAG_MCP_PREVIEW_KEY) |
| 119 | #define web_client_set_mcp_preview_key(w) web_client_flag_set(w, WEB_CLIENT_FLAG_MCP_PREVIEW_KEY) |
| 120 | #define web_client_clear_mcp_preview_key(w) web_client_flag_clear(w, WEB_CLIENT_FLAG_MCP_PREVIEW_KEY) |
| 121 | |
| 122 | #define web_client_check_conn_unix(w) web_client_flag_check(w, WEB_CLIENT_FLAG_CONN_UNIX) |
| 123 | #define web_client_check_conn_tcp(w) web_client_flag_check(w, WEB_CLIENT_FLAG_CONN_TCP) |
| 124 | #define web_client_check_conn_cloud(w) web_client_flag_check(w, WEB_CLIENT_FLAG_CONN_CLOUD) |
| 125 | #define web_client_check_conn_webrtc(w) web_client_flag_check(w, WEB_CLIENT_FLAG_CONN_WEBRTC) |
| 126 | #define web_client_flags_clear_conn(w) web_client_flag_clear(w, WEB_CLIENT_FLAG_CONN_TCP | WEB_CLIENT_FLAG_CONN_UNIX | WEB_CLIENT_FLAG_CONN_CLOUD | WEB_CLIENT_FLAG_CONN_WEBRTC) |
| 127 | |
| 128 | #define web_client_is_websocket(w) web_client_flag_check(w, WEB_CLIENT_FLAG_WEBSOCKET_CLIENT) |
| 129 | #define web_client_set_websocket(w) web_client_flag_set(w, WEB_CLIENT_FLAG_WEBSOCKET_CLIENT) |
| 130 | #define web_client_clear_websocket(w) web_client_flag_clear(w, WEB_CLIENT_FLAG_WEBSOCKET_CLIENT) |
| 131 | |
| 132 | #define web_client_has_websocket_handshake(w) web_client_flag_check(w, WEB_CLIENT_FLAG_WEBSOCKET_HANDSHAKE) |
| 133 | #define web_client_set_websocket_handshake(w) web_client_flag_set(w, WEB_CLIENT_FLAG_WEBSOCKET_HANDSHAKE) |
| 134 | #define web_client_clear_websocket_handshake(w) web_client_flag_clear(w, WEB_CLIENT_FLAG_WEBSOCKET_HANDSHAKE) |
| 135 | |
| 136 | void web_client_reset_permissions(struct web_client *w); |
| 137 | void web_client_set_permissions(struct web_client *w, HTTP_ACCESS access, HTTP_USER_ROLE role, USER_AUTH_METHOD type); |
| 138 | |
| 139 | void web_client_set_conn_tcp(struct web_client *w); |
| 140 | void web_client_set_conn_unix(struct web_client *w); |
| 141 | void web_client_set_conn_cloud(struct web_client *w); |
| 142 | void web_client_set_conn_webrtc(struct web_client *w); |
| 143 | |
| 144 | #define NETDATA_WEB_REQUEST_URL_SIZE 65536 // static allocation |
| 145 | |
| 146 | #define NETDATA_WEB_RESPONSE_ZLIB_CHUNK_SIZE 16384 |
| 147 | |
| 148 | #define NETDATA_WEB_RESPONSE_HEADER_INITIAL_SIZE 4096 |
| 149 | #define NETDATA_WEB_RESPONSE_INITIAL_SIZE 8192 |
| 150 | #define NETDATA_WEB_REQUEST_INITIAL_SIZE 8192 |
| 151 | #define NETDATA_WEB_REQUEST_MAX_SIZE (128 * 1024) |
| 152 | #define NETDATA_WEB_DECODED_URL_INITIAL_SIZE 512 |
| 153 | |
| 154 | struct response { |
| 155 | BUFFER *header; // our response header |
| 156 | BUFFER *header_output; // internal use |
| 157 | BUFFER *data; // our response data buffer |
| 158 | size_t sent; // current data length sent to output |
| 159 | short int code; // the HTTP response code |
| 160 | bool has_cookies; |
| 161 | bool zoutput; // if set to 1, web_client_send() will send compressed data |
| 162 | bool zinitialized; |
| 163 | z_stream zstream; // zlib stream for sending compressed output to client |
| 164 | size_t zsent; // the compressed bytes we have sent to the client |
| 165 | size_t zhave; // the compressed bytes that we have received from zlib |
| 166 | Bytef zbuffer[NETDATA_WEB_RESPONSE_ZLIB_CHUNK_SIZE]; // temporary buffer for storing compressed output |
| 167 | }; |
| 168 | |
| 169 | struct web_client; |
| 170 | typedef bool (*web_client_interrupt_t)(struct web_client *, void *data); |
| 171 | |
| 172 | struct web_client { |
| 173 | unsigned long long id; |
| 174 | size_t use_count; |
| 175 | |
| 176 | nd_uuid_t transaction; |
| 177 | |
| 178 | WEB_CLIENT_FLAGS flags; // status flags for the client |
| 179 | HTTP_REQUEST_MODE mode; // the operational mode of the client |
| 180 | HTTP_ACL acl; // the access list of the client |
| 181 | HTTP_ACL port_acl; // the operations permitted on the port the client connected to |
| 182 | HTTP_ACCESS access; // the access permissions of the client |
| 183 | size_t header_parse_tries; |
| 184 | size_t header_parse_last_size; |
| 185 | |
| 186 | int fd; |
| 187 | |
| 188 | USER_AUTH user_auth; // the user authentication data |
| 189 | |
| 190 | char client_port[NI_MAXSERV]; |
| 191 | char client_host[NI_MAXHOST]; |
| 192 | |
| 193 | BUFFER *url_as_received; // the entire URL as received, used for logging - DO NOT MODIFY |
| 194 | BUFFER *url_path_decoded; // the path, decoded - it is incrementally parsed and altered |
| 195 | BUFFER *url_query_string_decoded; // the query string, decoded - it is incrementally parsed and altered |
| 196 | |
| 197 | // THESE NEED TO BE FREED |
| 198 | char *auth_bearer_token; // the Bearer auth token (if sent) |
| 199 | char *server_host; // the Host: header |
| 200 | char *forwarded_host; // the X-Forwarded-Host: header |
| 201 | char *origin; // the Origin: header |
| 202 | char *user_agent; // the User-Agent: header |
| 203 | nd_uuid_t mcp_session_id; // the Mcp-Session-Id: header (MCP HTTP transport) |
| 204 | |
| 205 | // WebSocket related data - NEED TO BE FREED |
| 206 | struct { |
| 207 | char *key; // the Sec-WebSocket-Key header |
| 208 | WEBSOCKET_PROTOCOL protocol; // the selected subprotocol |
| 209 | WEBSOCKET_EXTENSION ext_flags; // bit flags for supported extensions |
| 210 | uint8_t client_max_window_bits; // client_max_window_bits parameter (8-15) |
| 211 | uint8_t server_max_window_bits; // server_max_window_bits parameter (8-15) |
| 212 | } websocket; |
| 213 | |
| 214 | BUFFER *payload; // when this request is a POST, this has the payload |
| 215 | |
| 216 | NETDATA_SSL ssl; |
| 217 | |
| 218 | struct { |
| 219 | nd_uuid_t bearer_token; |
| 220 | } auth; |
| 221 | |
| 222 | struct { // A callback to check if the query should be interrupted / stopped |
| 223 | web_client_interrupt_t callback; |
| 224 | void *callback_data; |
| 225 | } interrupt; |
| 226 | |
| 227 | struct { |
| 228 | size_t received_bytes; |
| 229 | size_t sent_bytes; |
| 230 | size_t *memory_accounting; // temporary pointer for constructor to use |
| 231 | } statistics; |
| 232 | |
| 233 | struct { |
| 234 | usec_t timeout_ut; // timeout if set, or zero |
| 235 | struct timeval tv_in; // request received |
| 236 | struct timeval tv_ready; // request processed - response ready |
| 237 | struct timeval tv_timeout_last_checkpoint; // last checkpoint |
| 238 | } timings; |
| 239 | |
| 240 | struct { |
| 241 | struct web_client *prev; |
| 242 | struct web_client *next; |
| 243 | } cache; |
| 244 | |
| 245 | struct response response; |
| 246 | }; |
| 247 | |
| 248 | int web_client_permission_denied(struct web_client *w); |
| 249 | int web_client_permission_denied_acl(struct web_client *w); |
| 250 | |
| 251 | int web_client_service_unavailable(struct web_client *w); |
| 252 | |
| 253 | ssize_t web_client_send(struct web_client *w); |
| 254 | ssize_t web_client_receive(struct web_client *w); |
| 255 | |
| 256 | void web_client_process_request_from_web_server(struct web_client *w); |
| 257 | void web_client_request_done(struct web_client *w); |
| 258 | |
| 259 | void web_client_build_http_header(struct web_client *w); |
| 260 | |
| 261 | void web_client_reuse_from_cache(struct web_client *w); |
| 262 | struct web_client *web_client_create(size_t *statistics_memory_accounting); |
| 263 | void web_client_free(struct web_client *w); |
| 264 | |
| 265 | #include "web/api/web_api_v1.h" |
| 266 | #include "web/api/web_api_v2.h" |
| 267 | #include "database/rrd.h" |
| 268 | |
| 269 | void web_client_decode_path_and_query_string(struct web_client *w, const char *path_and_query_string); |
| 270 | int web_client_api_request(RRDHOST *host, struct web_client *w, char *url_path_fragment); |
| 271 | int web_client_api_request_with_node_selection(RRDHOST *host, struct web_client *w, char *decoded_url_path); |
| 272 | |
| 273 | void web_client_timeout_checkpoint_init(struct web_client *w); |
| 274 | void web_client_timeout_checkpoint_set(struct web_client *w, int timeout_ms); |
| 275 | usec_t web_client_timeout_checkpoint(struct web_client *w); |
| 276 | bool web_client_timeout_checkpoint_and_check(struct web_client *w, usec_t *usec_since_last_checkpoint); |
| 277 | usec_t web_client_timeout_checkpoint_response_ready(struct web_client *w, usec_t *usec_since_last_checkpoint); |
| 278 | void web_client_log_completed_request(struct web_client *w, bool update_web_stats); |
| 279 | |
| 280 | HTTP_VALIDATION http_request_validate(struct web_client *w); |
| 281 | |
| 282 | #endif |