123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241 |
- // SPDX-License-Identifier: GPL-3.0-or-later
- #include "http_header.h"
- static void web_client_enable_deflate(struct web_client *w, bool gzip) {
- if(gzip)
- web_client_flag_set(w, WEB_CLIENT_ENCODING_GZIP);
- else
- web_client_flag_set(w, WEB_CLIENT_ENCODING_DEFLATE);
- if(!web_client_check_conn_unix(w) && !web_client_check_conn_tcp(w) && !web_client_check_conn_cloud(w))
- return;
- if(unlikely(w->response.zinitialized)) {
- // compression has already been initialized for this client.
- return;
- }
- if(unlikely(w->response.sent)) {
- netdata_log_error("%llu: Cannot enable compression in the middle of a conversation.", w->id);
- return;
- }
- w->response.zstream.zalloc = Z_NULL;
- w->response.zstream.zfree = Z_NULL;
- w->response.zstream.opaque = Z_NULL;
- w->response.zstream.next_in = (Bytef *)w->response.data->buffer;
- w->response.zstream.avail_in = 0;
- w->response.zstream.total_in = 0;
- w->response.zstream.next_out = w->response.zbuffer;
- w->response.zstream.avail_out = 0;
- w->response.zstream.total_out = 0;
- w->response.zstream.zalloc = Z_NULL;
- w->response.zstream.zfree = Z_NULL;
- w->response.zstream.opaque = Z_NULL;
- // Select GZIP compression: windowbits = 15 + 16 = 31
- if(deflateInit2(&w->response.zstream, web_gzip_level, Z_DEFLATED, 15 + ((gzip)?16:0), 8, web_gzip_strategy) != Z_OK) {
- netdata_log_error("%llu: Failed to initialize zlib. Proceeding without compression.", w->id);
- return;
- }
- w->response.zsent = 0;
- w->response.zoutput = true;
- w->response.zinitialized = true;
- if(!web_client_check_conn_cloud(w))
- // cloud sends the entire response at once, not in chunks
- web_client_flag_set(w, WEB_CLIENT_CHUNKED_TRANSFER);
- netdata_log_debug(D_DEFLATE, "%llu: Initialized compression.", w->id);
- }
- static void http_header_origin(struct web_client *w, const char *v, size_t len __maybe_unused) {
- freez(w->origin);
- w->origin = strdupz(v);
- }
- static void http_header_connection(struct web_client *w, const char *v, size_t len __maybe_unused) {
- if(strcasestr(v, "keep-alive"))
- web_client_enable_keepalive(w);
- }
- static void http_header_dnt(struct web_client *w, const char *v, size_t len __maybe_unused) {
- if(respect_web_browser_do_not_track_policy) {
- if (*v == '0') web_client_disable_donottrack(w);
- else if (*v == '1') web_client_enable_donottrack(w);
- }
- }
- static void http_header_user_agent(struct web_client *w, const char *v, size_t len __maybe_unused) {
- if(w->mode == HTTP_REQUEST_MODE_STREAM) {
- freez(w->user_agent);
- w->user_agent = strdupz(v);
- }
- }
- static void http_header_x_auth_token(struct web_client *w, const char *v, size_t len __maybe_unused) {
- freez(w->auth_bearer_token);
- w->auth_bearer_token = strdupz(v);
- }
- static void http_header_host(struct web_client *w, const char *v, size_t len) {
- char buffer[NI_MAXHOST];
- strncpyz(buffer, v, (len < sizeof(buffer) - 1 ? len : sizeof(buffer) - 1));
- freez(w->server_host);
- w->server_host = strdupz(buffer);
- }
- static void http_header_accept_encoding(struct web_client *w, const char *v, size_t len __maybe_unused) {
- if(web_enable_gzip) {
- if(strcasestr(v, "gzip"))
- web_client_enable_deflate(w, true);
- // does not seem to work
- // else if(strcasestr(v, "deflate"))
- // web_client_enable_deflate(w, 0);
- }
- }
- static void http_header_x_forwarded_host(struct web_client *w, const char *v, size_t len) {
- char buffer[NI_MAXHOST];
- strncpyz(buffer, v, (len < sizeof(buffer) - 1 ? len : sizeof(buffer) - 1));
- freez(w->forwarded_host);
- w->forwarded_host = strdupz(buffer);
- }
- static void http_header_x_forwarded_for(struct web_client *w, const char *v, size_t len) {
- char buffer[NI_MAXHOST];
- strncpyz(buffer, v, (len < sizeof(buffer) - 1 ? len : sizeof(buffer) - 1));
- freez(w->forwarded_for);
- w->forwarded_for = strdupz(buffer);
- }
- static void http_header_x_transaction_id(struct web_client *w, const char *v, size_t len) {
- char buffer[UUID_STR_LEN * 2];
- strncpyz(buffer, v, (len < sizeof(buffer) - 1 ? len : sizeof(buffer) - 1));
- uuid_parse_flexi(buffer, w->transaction); // will not alter w->transaction if it fails
- }
- static void http_header_x_netdata_account_id(struct web_client *w, const char *v, size_t len) {
- if(web_client_flag_check(w, WEB_CLIENT_FLAG_CONN_CLOUD) && w->acl == HTTP_ACL_ACLK) {
- char buffer[UUID_STR_LEN * 2];
- strncpyz(buffer, v, (len < sizeof(buffer) - 1 ? len : sizeof(buffer) - 1));
- uuid_parse_flexi(buffer, w->auth.cloud_account_id); // will not alter w->cloud_account_id if it fails
- }
- }
- static void http_header_x_netdata_role(struct web_client *w, const char *v, size_t len) {
- if(web_client_flag_check(w, WEB_CLIENT_FLAG_CONN_CLOUD) && w->acl == HTTP_ACL_ACLK) {
- char buffer[100];
- strncpyz(buffer, v, (len < sizeof(buffer) - 1 ? len : sizeof(buffer) - 1));
- if (strcasecmp(buffer, "admin") == 0)
- w->access = HTTP_ACCESS_ADMIN;
- else if(strcasecmp(buffer, "member") == 0)
- w->access = HTTP_ACCESS_MEMBER;
- else
- w->access = HTTP_ACCESS_ANY;
- web_client_flags_clear_auth(w);
- web_client_flag_set(w, WEB_CLIENT_FLAG_AUTH_CLOUD);
- }
- }
- static void http_header_x_netdata_user_name(struct web_client *w, const char *v, size_t len) {
- if(web_client_flag_check(w, WEB_CLIENT_FLAG_CONN_CLOUD) && w->acl == HTTP_ACL_ACLK) {
- strncpyz(w->auth.client_name, v, (len < sizeof(w->auth.client_name) - 1 ? len : sizeof(w->auth.client_name) - 1));
- }
- }
- static void http_header_x_netdata_auth(struct web_client *w, const char *v, size_t len __maybe_unused) {
- if(web_client_flag_check(w, WEB_CLIENT_FLAG_CONN_CLOUD) && w->acl == HTTP_ACL_ACLK)
- // we don't need authorization bearer when the request comes from netdata cloud
- return;
- if(strncasecmp(v, "Bearer ", 7) == 0) {
- v = &v[7];
- while(*v && isspace(*v)) v++;
- web_client_bearer_token_auth(w, v);
- }
- }
- struct {
- uint32_t hash;
- const char *key;
- void (*cb)(struct web_client *w, const char *value, size_t value_len);
- } supported_headers[] = {
- { .hash = 0, .key = "Origin", .cb = http_header_origin },
- { .hash = 0, .key = "Connection", .cb = http_header_connection },
- { .hash = 0, .key = "DNT", .cb = http_header_dnt },
- { .hash = 0, .key = "User-Agent", .cb = http_header_user_agent},
- { .hash = 0, .key = "X-Auth-Token", .cb = http_header_x_auth_token },
- { .hash = 0, .key = "Host", .cb = http_header_host },
- { .hash = 0, .key = "Accept-Encoding", .cb = http_header_accept_encoding },
- { .hash = 0, .key = "X-Forwarded-Host", .cb = http_header_x_forwarded_host },
- { .hash = 0, .key = "X-Forwarded-For", .cb = http_header_x_forwarded_for },
- { .hash = 0, .key = "X-Transaction-Id", .cb = http_header_x_transaction_id },
- { .hash = 0, .key = "X-Netdata-Account-Id", .cb = http_header_x_netdata_account_id },
- { .hash = 0, .key = "X-Netdata-Role", .cb = http_header_x_netdata_role },
- { .hash = 0, .key = "X-Netdata-User-Name", .cb = http_header_x_netdata_user_name },
- { .hash = 0, .key = "X-Netdata-Auth", .cb = http_header_x_netdata_auth },
- // for historical reasons.
- // there are a few nightly versions of netdata UI that incorrectly use this instead of X-Netdata-Auth
- { .hash = 0, .key = "Authorization", .cb = http_header_x_netdata_auth },
- // terminator
- { .hash = 0, .key = NULL, .cb = NULL }
- };
- char *http_header_parse_line(struct web_client *w, char *s) {
- if(unlikely(!supported_headers[0].hash)) {
- // initialize the hashes, the first time it runs
- for(size_t i = 0; supported_headers[i].key ;i++)
- supported_headers[i].hash = simple_uhash(supported_headers[i].key);
- }
- char *e = s;
- // find the colon
- while(*e && *e != ':') e++;
- if(!*e) return e;
- // get the name
- *e = '\0';
- // find the value
- char *v = e + 1, *ve;
- // skip leading spaces from value
- while(*v == ' ') v++;
- ve = v;
- // find the \r
- while(*ve && *ve != '\r') ve++;
- if(!*ve || ve[1] != '\n') {
- *e = ':';
- return ve;
- }
- // terminate the value
- *ve = '\0';
- uint32_t hash = simple_uhash(s);
- for(size_t i = 0; supported_headers[i].key ;i++) {
- if(likely(hash != supported_headers[i].hash || strcasecmp(s, supported_headers[i].key) != 0))
- continue;
- supported_headers[i].cb(w, v, ve - v);
- break;
- }
- *e = ':';
- *ve = '\r';
- return ve;
- }
|