summaryrefslogtreecommitdiffstats
path: root/src/libnetdata/http
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 12:08:03 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 12:08:18 +0000
commit5da14042f70711ea5cf66e034699730335462f66 (patch)
tree0f6354ccac934ed87a2d555f45be4c831cf92f4a /src/libnetdata/http
parentReleasing debian version 1.44.3-2. (diff)
downloadnetdata-5da14042f70711ea5cf66e034699730335462f66.tar.xz
netdata-5da14042f70711ea5cf66e034699730335462f66.zip
Merging upstream version 1.45.3+dfsg.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/libnetdata/http')
-rw-r--r--src/libnetdata/http/content_type.c96
-rw-r--r--src/libnetdata/http/content_type.h45
-rw-r--r--src/libnetdata/http/http_access.c186
-rw-r--r--src/libnetdata/http/http_access.h148
-rw-r--r--src/libnetdata/http/http_defs.c245
-rw-r--r--src/libnetdata/http/http_defs.h61
6 files changed, 781 insertions, 0 deletions
diff --git a/src/libnetdata/http/content_type.c b/src/libnetdata/http/content_type.c
new file mode 100644
index 000000000..05bede17f
--- /dev/null
+++ b/src/libnetdata/http/content_type.c
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "content_type.h"
+
+
+static struct {
+ const char *format;
+ HTTP_CONTENT_TYPE content_type;
+ bool needs_charset;
+ const char *options;
+} content_types[] = {
+ // primary - preferred during id-to-string conversions
+ { .format = "text/html", CT_TEXT_HTML, true },
+ { .format = "text/plain", CT_TEXT_PLAIN, true },
+ { .format = "text/css", CT_TEXT_CSS, true },
+ { .format = "text/yaml", CT_TEXT_YAML, true },
+ { .format = "text/xml", CT_TEXT_XML, true },
+ { .format = "text/xsl", CT_TEXT_XSL, true },
+ { .format = "application/json", CT_APPLICATION_JSON, true },
+ { .format = "application/xml", CT_APPLICATION_XML, true },
+ { .format = "application/javascript", CT_APPLICATION_X_JAVASCRIPT, true },
+ { .format = "application/octet-stream", CT_APPLICATION_OCTET_STREAM, false },
+ { .format = "image/svg+xml", CT_IMAGE_SVG_XML, false },
+ { .format = "application/x-font-truetype", CT_APPLICATION_X_FONT_TRUETYPE, false },
+ { .format = "application/x-font-opentype", CT_APPLICATION_X_FONT_OPENTYPE, false },
+ { .format = "application/font-woff", CT_APPLICATION_FONT_WOFF, false },
+ { .format = "application/font-woff2", CT_APPLICATION_FONT_WOFF2, false },
+ { .format = "application/vnd.ms-fontobject",CT_APPLICATION_VND_MS_FONTOBJ, false },
+ { .format = "image/png", CT_IMAGE_PNG, false },
+ { .format = "image/jpeg", CT_IMAGE_JPG, false },
+ { .format = "image/gif", CT_IMAGE_GIF, false },
+ { .format = "image/x-icon", CT_IMAGE_XICON, false },
+ { .format = "image/bmp", CT_IMAGE_BMP, false },
+ { .format = "image/icns", CT_IMAGE_ICNS, false },
+ { .format = "audio/mpeg", CT_AUDIO_MPEG, false },
+ { .format = "audio/ogg", CT_AUDIO_OGG, false },
+ { .format = "video/mp4", CT_VIDEO_MP4, false },
+ { .format = "application/pdf", CT_APPLICATION_PDF, false },
+ { .format = "application/zip", CT_APPLICATION_ZIP, false },
+ { .format = "image/png", CT_IMAGE_PNG, false },
+
+ // secondary - overlapping with primary
+
+ { .format = "text/plain", CT_PROMETHEUS, false, "version=0.0.4" },
+ { .format = "prometheus", CT_PROMETHEUS },
+ { .format = "text", CT_TEXT_PLAIN },
+ { .format = "txt", CT_TEXT_PLAIN },
+ { .format = "json", CT_APPLICATION_JSON },
+ { .format = "html", CT_TEXT_HTML },
+ { .format = "xml", CT_APPLICATION_XML },
+
+ // terminator
+ { .format = NULL, CT_TEXT_PLAIN },
+};
+
+HTTP_CONTENT_TYPE content_type_string2id(const char *format) {
+ if(format && *format) {
+ for (int i = 0; content_types[i].format; i++)
+ if (strcmp(content_types[i].format, format) == 0)
+ return content_types[i].content_type;
+ }
+
+ return CT_TEXT_PLAIN;
+}
+
+const char *content_type_id2string(HTTP_CONTENT_TYPE content_type) {
+ for (int i = 0; content_types[i].format; i++)
+ if (content_types[i].content_type == content_type)
+ return content_types[i].format;
+
+ return "text/plain";
+}
+
+void http_header_content_type(BUFFER *wb, HTTP_CONTENT_TYPE content_type) {
+ buffer_strcat(wb, "Content-Type: ");
+
+ for (int i = 0; content_types[i].format; i++) {
+ if (content_types[i].content_type == content_type) {
+ buffer_strcat(wb, content_types[i].format);
+
+ if(content_types[i].needs_charset) {
+ buffer_strcat(wb, "; charset=utf-8");
+ }
+ if(content_types[i].options) {
+ buffer_strcat(wb, "; ");
+ buffer_strcat(wb, content_types[i].options);
+ }
+
+ buffer_strcat(wb, "\r\n");
+
+ return;
+ }
+ }
+
+ buffer_strcat(wb, "text/plain; charset=utf-8\r\n");
+}
diff --git a/src/libnetdata/http/content_type.h b/src/libnetdata/http/content_type.h
new file mode 100644
index 000000000..66fba95a8
--- /dev/null
+++ b/src/libnetdata/http/content_type.h
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#ifndef NETDATA_CONTENT_TYPE_H
+#define NETDATA_CONTENT_TYPE_H
+
+typedef enum __attribute__ ((__packed__)) {
+ CT_NONE = 0,
+ CT_APPLICATION_JSON,
+ CT_TEXT_PLAIN,
+ CT_TEXT_HTML,
+ CT_APPLICATION_X_JAVASCRIPT,
+ CT_TEXT_CSS,
+ CT_TEXT_XML,
+ CT_APPLICATION_XML,
+ CT_TEXT_XSL,
+ CT_APPLICATION_OCTET_STREAM,
+ CT_APPLICATION_X_FONT_TRUETYPE,
+ CT_APPLICATION_X_FONT_OPENTYPE,
+ CT_APPLICATION_FONT_WOFF,
+ CT_APPLICATION_FONT_WOFF2,
+ CT_APPLICATION_VND_MS_FONTOBJ,
+ CT_IMAGE_SVG_XML,
+ CT_IMAGE_PNG,
+ CT_IMAGE_JPG,
+ CT_IMAGE_GIF,
+ CT_IMAGE_XICON,
+ CT_IMAGE_ICNS,
+ CT_IMAGE_BMP,
+ CT_PROMETHEUS,
+ CT_AUDIO_MPEG,
+ CT_AUDIO_OGG,
+ CT_VIDEO_MP4,
+ CT_APPLICATION_PDF,
+ CT_APPLICATION_ZIP,
+ CT_TEXT_YAML,
+} HTTP_CONTENT_TYPE;
+
+HTTP_CONTENT_TYPE content_type_string2id(const char *format);
+const char *content_type_id2string(HTTP_CONTENT_TYPE content_type);
+
+#include "../libnetdata.h"
+
+void http_header_content_type(struct web_buffer *wb, HTTP_CONTENT_TYPE type);
+
+#endif //NETDATA_CONTENT_TYPE_H
diff --git a/src/libnetdata/http/http_access.c b/src/libnetdata/http/http_access.c
new file mode 100644
index 000000000..5be63bb19
--- /dev/null
+++ b/src/libnetdata/http/http_access.c
@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "../libnetdata.h"
+
+static struct {
+ HTTP_USER_ROLE access;
+ const char *name;
+} user_roles[] = {
+ { .access = HTTP_USER_ROLE_NONE, .name = "none" },
+ { .access = HTTP_USER_ROLE_ADMIN, .name = "admin" },
+ { .access = HTTP_USER_ROLE_MANAGER, .name = "manager" },
+ { .access = HTTP_USER_ROLE_TROUBLESHOOTER, .name = "troubleshooter" },
+ { .access = HTTP_USER_ROLE_OBSERVER, .name = "observer" },
+ { .access = HTTP_USER_ROLE_MEMBER, .name = "member" },
+ { .access = HTTP_USER_ROLE_BILLING, .name = "billing" },
+ { .access = HTTP_USER_ROLE_ANY, .name = "any" },
+
+ { .access = HTTP_USER_ROLE_MEMBER, .name = "members" },
+ { .access = HTTP_USER_ROLE_ADMIN, .name = "admins" },
+ { .access = HTTP_USER_ROLE_ANY, .name = "all" },
+
+ // terminator
+ { .access = 0, .name = NULL },
+};
+
+HTTP_USER_ROLE http_user_role2id(const char *role) {
+ if(!role || !*role)
+ return HTTP_USER_ROLE_MEMBER;
+
+ for(size_t i = 0; user_roles[i].name ;i++) {
+ if(strcmp(user_roles[i].name, role) == 0)
+ return user_roles[i].access;
+ }
+
+ nd_log(NDLS_DAEMON, NDLP_WARNING, "HTTP user role '%s' is not valid", role);
+ return HTTP_USER_ROLE_NONE;
+}
+
+const char *http_id2user_role(HTTP_USER_ROLE role) {
+ for(size_t i = 0; user_roles[i].name ;i++) {
+ if(role == user_roles[i].access)
+ return user_roles[i].name;
+ }
+
+ nd_log(NDLS_DAEMON, NDLP_WARNING, "HTTP user role %d is not valid", role);
+ return "none";
+}
+
+// --------------------------------------------------------------------------------------------------------------------
+
+static struct {
+ const char *name;
+ uint32_t hash;
+ HTTP_ACCESS value;
+} http_accesses[] = {
+ {"none" , 0 , HTTP_ACCESS_NONE}
+ , {"signed-in" , 0 , HTTP_ACCESS_SIGNED_ID}
+ , {"same-space" , 0 , HTTP_ACCESS_SAME_SPACE}
+ , {"commercial" , 0 , HTTP_ACCESS_COMMERCIAL_SPACE}
+ , {"anonymous-data" , 0 , HTTP_ACCESS_ANONYMOUS_DATA}
+ , {"sensitive-data" , 0 , HTTP_ACCESS_SENSITIVE_DATA}
+ , {"view-config" , 0 , HTTP_ACCESS_VIEW_AGENT_CONFIG}
+ , {"edit-config" , 0 , HTTP_ACCESS_EDIT_AGENT_CONFIG}
+ , {"view-notifications-config" , 0 , HTTP_ACCESS_VIEW_NOTIFICATIONS_CONFIG}
+ , {"edit-notifications-config" , 0 , HTTP_ACCESS_EDIT_NOTIFICATIONS_CONFIG}
+ , {"view-alerts-silencing" , 0 , HTTP_ACCESS_VIEW_ALERTS_SILENCING}
+ , {"edit-alerts-silencing" , 0 , HTTP_ACCESS_EDIT_ALERTS_SILENCING}
+
+ , {NULL , 0 , 0}
+};
+
+inline HTTP_ACCESS http_access2id_one(const char *str) {
+ HTTP_ACCESS ret = 0;
+
+ if(!str || !*str) return ret;
+
+ uint32_t hash = simple_hash(str);
+ int i;
+ for(i = 0; http_accesses[i].name ; i++) {
+ if(unlikely(!http_accesses[i].hash))
+ http_accesses[i].hash = simple_hash(http_accesses[i].name);
+
+ if (unlikely(hash == http_accesses[i].hash && !strcmp(str, http_accesses[i].name))) {
+ ret |= http_accesses[i].value;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+inline HTTP_ACCESS http_access2id(char *str) {
+ HTTP_ACCESS ret = 0;
+ char *tok;
+
+ while(str && *str && (tok = strsep_skip_consecutive_separators(&str, ", |"))) {
+ if(!*tok) continue;
+ ret |= http_access2id_one(tok);
+ }
+
+ return ret;
+}
+
+void http_access2buffer_json_array(BUFFER *wb, const char *key, HTTP_ACCESS access) {
+ buffer_json_member_add_array(wb, key);
+
+ HTTP_ACCESS used = 0; // to prevent adding duplicates
+ for(int i = 0; http_accesses[i].name ; i++) {
+ if (unlikely((http_accesses[i].value & access) && !(http_accesses[i].value & used))) {
+ const char *name = http_accesses[i].name;
+ used |= http_accesses[i].value;
+
+ buffer_json_add_array_item_string(wb, name);
+ }
+ }
+
+ buffer_json_array_close(wb);
+}
+
+void http_access2txt(char *buf, size_t size, const char *separator, HTTP_ACCESS access) {
+ char *write = buf;
+ char *end = &buf[size - 1];
+
+ HTTP_ACCESS used = 0; // to prevent adding duplicates
+ int added = 0;
+ for(int i = 0; http_accesses[i].name ; i++) {
+ if (unlikely((http_accesses[i].value & access) && !(http_accesses[i].value & used))) {
+ const char *name = http_accesses[i].name;
+ used |= http_accesses[i].value;
+
+ if(added && write < end) {
+ const char *s = separator;
+ while(*s && write < end)
+ *write++ = *s++;
+ }
+
+ while(*name && write < end)
+ *write++ = *name++;
+
+ added++;
+ }
+ }
+ *write = *end = '\0';
+}
+
+HTTP_ACCESS http_access_from_hex_mapping_old_roles(const char *str) {
+ if(!str || !*str)
+ return HTTP_ACCESS_NONE;
+
+ if(strcmp(str, "any") == 0 || strcmp(str, "all") == 0)
+ return HTTP_ACCESS_MAP_OLD_ANY;
+
+ if(strcmp(str, "member") == 0 || strcmp(str, "members") == 0)
+ return HTTP_ACCESS_MAP_OLD_MEMBER;
+
+ else if(strcmp(str, "admin") == 0 || strcmp(str, "admins") == 0)
+ return HTTP_ACCESS_MAP_OLD_ADMIN;
+
+ return (HTTP_ACCESS)strtoull(str, NULL, 16) & HTTP_ACCESS_ALL;
+}
+
+HTTP_ACCESS http_access_from_hex(const char *str) {
+ if(!str || !*str)
+ return HTTP_ACCESS_NONE;
+
+ return (HTTP_ACCESS)strtoull(str, NULL, 16) & HTTP_ACCESS_ALL;
+}
+
+HTTP_ACCESS http_access_from_source(const char *str) {
+ if(!str || !*str)
+ return HTTP_ACCESS_NONE;
+
+ HTTP_ACCESS access = HTTP_ACCESS_NONE;
+
+ const char *permissions = strstr(str, "permissions=");
+ if(permissions)
+ access = (HTTP_ACCESS)strtoull(permissions + 12, NULL, 16) & HTTP_ACCESS_ALL;
+
+ return access;
+}
+
+bool log_cb_http_access_to_hex(BUFFER *wb, void *data) {
+ HTTP_ACCESS access = *((HTTP_ACCESS *)data);
+ buffer_sprintf(wb, HTTP_ACCESS_FORMAT, (HTTP_ACCESS_FORMAT_CAST)access);
+ return true;
+}
diff --git a/src/libnetdata/http/http_access.h b/src/libnetdata/http/http_access.h
new file mode 100644
index 000000000..afc2e1dc7
--- /dev/null
+++ b/src/libnetdata/http/http_access.h
@@ -0,0 +1,148 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#ifndef NETDATA_HTTP_ACCESS_H
+#define NETDATA_HTTP_ACCESS_H
+
+typedef enum __attribute__((packed)) {
+ HTTP_USER_ROLE_NONE = 0,
+ HTTP_USER_ROLE_ADMIN = 1,
+ HTTP_USER_ROLE_MANAGER = 2,
+ HTTP_USER_ROLE_TROUBLESHOOTER = 3,
+ HTTP_USER_ROLE_OBSERVER = 4,
+ HTTP_USER_ROLE_MEMBER = 5,
+ HTTP_USER_ROLE_BILLING = 6,
+ HTTP_USER_ROLE_ANY = 7,
+
+ // keep this list so that lower numbers are more strict access levels
+} HTTP_USER_ROLE;
+const char *http_id2user_role(HTTP_USER_ROLE role);
+HTTP_USER_ROLE http_user_role2id(const char *role);
+
+typedef enum __attribute__((packed)) {
+ HTTP_ACCESS_NONE = 0, // adm man trb obs mem bil
+ HTTP_ACCESS_SIGNED_ID = (1 << 0), // User is authenticated A A A A A A
+ HTTP_ACCESS_SAME_SPACE = (1 << 1), // NC user+agent = same space A A A A A A
+ HTTP_ACCESS_COMMERCIAL_SPACE = (1 << 2), // NC P P P P P P
+ HTTP_ACCESS_ANONYMOUS_DATA = (1 << 3), // NC room:Read A A A SR SR -
+ HTTP_ACCESS_SENSITIVE_DATA = (1 << 4), // NC agent:ViewSensitiveData A A A - SR -
+ HTTP_ACCESS_VIEW_AGENT_CONFIG = (1 << 5), // NC agent:ReadDynCfg P P - - - -
+ HTTP_ACCESS_EDIT_AGENT_CONFIG = (1 << 6), // NC agent:EditDynCfg P P - - - -
+ HTTP_ACCESS_VIEW_NOTIFICATIONS_CONFIG = (1 << 7), // NC agent:ViewNotificationsConfig P - - - - -
+ HTTP_ACCESS_EDIT_NOTIFICATIONS_CONFIG = (1 << 8), // NC agent:EditNotificationsConfig P - - - - -
+ HTTP_ACCESS_VIEW_ALERTS_SILENCING = (1 << 9), // NC space:GetSystemSilencingRules A A A - A -
+ HTTP_ACCESS_EDIT_ALERTS_SILENCING = (1 << 10), // NC space:CreateSystemSilencingRule P P - - P -
+} HTTP_ACCESS; // ---------------------
+ // A = always
+ // P = commercial plan
+ // SR = same room (Us+Ag)
+
+#define HTTP_ACCESS_FORMAT "0x%" PRIx32
+#define HTTP_ACCESS_FORMAT_CAST uint32_t
+
+#define HTTP_ACCESS_ALL (HTTP_ACCESS)( \
+ HTTP_ACCESS_SIGNED_ID \
+ | HTTP_ACCESS_SAME_SPACE \
+ | HTTP_ACCESS_COMMERCIAL_SPACE \
+ | HTTP_ACCESS_ANONYMOUS_DATA \
+ | HTTP_ACCESS_SENSITIVE_DATA \
+ | HTTP_ACCESS_VIEW_AGENT_CONFIG \
+ | HTTP_ACCESS_EDIT_AGENT_CONFIG \
+ | HTTP_ACCESS_VIEW_NOTIFICATIONS_CONFIG \
+ | HTTP_ACCESS_EDIT_NOTIFICATIONS_CONFIG \
+ | HTTP_ACCESS_VIEW_ALERTS_SILENCING \
+ | HTTP_ACCESS_EDIT_ALERTS_SILENCING \
+)
+
+#define HTTP_ACCESS_MAP_OLD_ANY (HTTP_ACCESS)(HTTP_ACCESS_ANONYMOUS_DATA)
+
+#define HTTP_ACCESS_MAP_OLD_MEMBER (HTTP_ACCESS)( \
+ HTTP_ACCESS_SIGNED_ID \
+ | HTTP_ACCESS_SAME_SPACE \
+ | HTTP_ACCESS_ANONYMOUS_DATA | HTTP_ACCESS_SENSITIVE_DATA)
+
+#define HTTP_ACCESS_MAP_OLD_ADMIN (HTTP_ACCESS)( \
+ HTTP_ACCESS_SIGNED_ID \
+ | HTTP_ACCESS_SAME_SPACE \
+ | HTTP_ACCESS_ANONYMOUS_DATA | HTTP_ACCESS_SENSITIVE_DATA | HTTP_ACCESS_VIEW_AGENT_CONFIG \
+ | HTTP_ACCESS_EDIT_AGENT_CONFIG \
+)
+
+HTTP_ACCESS http_access2id_one(const char *str);
+HTTP_ACCESS http_access2id(char *str);
+struct web_buffer;
+void http_access2buffer_json_array(struct web_buffer *wb, const char *key, HTTP_ACCESS access);
+void http_access2txt(char *buf, size_t size, const char *separator, HTTP_ACCESS access);
+HTTP_ACCESS http_access_from_hex(const char *str);
+HTTP_ACCESS http_access_from_hex_mapping_old_roles(const char *str);
+HTTP_ACCESS http_access_from_source(const char *str);
+bool log_cb_http_access_to_hex(struct web_buffer *wb, void *data);
+
+#define HTTP_ACCESS_PERMISSION_DENIED_HTTP_CODE(access) ((access & HTTP_ACCESS_SIGNED_ID) ? HTTP_RESP_FORBIDDEN : HTTP_RESP_PRECOND_FAIL)
+
+typedef enum __attribute__((packed)) {
+ HTTP_ACL_NONE = (0),
+
+ HTTP_ACL_NOCHECK = (1 << 0), // Don't check anything - adding this to an endpoint, disables ACL checking
+
+ // transports
+ HTTP_ACL_API = (1 << 1), // from the internal web server (TCP port)
+ HTTP_ACL_API_UDP = (1 << 2), // from the internal web server (UDP port)
+ HTTP_ACL_API_UNIX = (1 << 3), // from the internal web server (UNIX socket)
+ HTTP_ACL_H2O = (1 << 4), // from the h2o web server
+ HTTP_ACL_ACLK = (1 << 5), // from ACLK
+ HTTP_ACL_WEBRTC = (1 << 6), // from WebRTC
+
+ // HTTP_ACL_API takes the following additional ACLs, based on pattern matching of the client IP
+ HTTP_ACL_DASHBOARD = (1 << 10),
+ HTTP_ACL_REGISTRY = (1 << 11),
+ HTTP_ACL_BADGES = (1 << 12),
+ HTTP_ACL_MANAGEMENT = (1 << 13),
+ HTTP_ACL_STREAMING = (1 << 14),
+ HTTP_ACL_NETDATACONF = (1 << 15),
+
+ // SSL related
+ HTTP_ACL_SSL_OPTIONAL = (1 << 28),
+ HTTP_ACL_SSL_FORCE = (1 << 29),
+ HTTP_ACL_SSL_DEFAULT = (1 << 30),
+} HTTP_ACL;
+
+#define HTTP_ACL_TRANSPORTS (HTTP_ACL)( \
+ HTTP_ACL_API \
+ | HTTP_ACL_API_UDP \
+ | HTTP_ACL_API_UNIX \
+ | HTTP_ACL_H2O \
+ | HTTP_ACL_ACLK \
+ | HTTP_ACL_WEBRTC \
+)
+
+#define HTTP_ACL_TRANSPORTS_WITHOUT_CLIENT_IP_VALIDATION (HTTP_ACL)( \
+ HTTP_ACL_ACLK \
+ | HTTP_ACL_WEBRTC \
+)
+
+#define HTTP_ACL_ALL_FEATURES (HTTP_ACL)( \
+ HTTP_ACL_DASHBOARD \
+ | HTTP_ACL_REGISTRY \
+ | HTTP_ACL_BADGES \
+ | HTTP_ACL_MANAGEMENT \
+ | HTTP_ACL_STREAMING \
+ | HTTP_ACL_NETDATACONF \
+)
+
+#ifdef NETDATA_DEV_MODE
+#define ACL_DEV_OPEN_ACCESS HTTP_ACL_NOCHECK
+#else
+#define ACL_DEV_OPEN_ACCESS 0
+#endif
+
+#define http_can_access_dashboard(w) ((w)->acl & HTTP_ACL_DASHBOARD)
+#define http_can_access_registry(w) ((w)->acl & HTTP_ACL_REGISTRY)
+#define http_can_access_badges(w) ((w)->acl & HTTP_ACL_BADGES)
+#define http_can_access_mgmt(w) ((w)->acl & HTTP_ACL_MANAGEMENT)
+#define http_can_access_stream(w) ((w)->acl & HTTP_ACL_STREAMING)
+#define http_can_access_netdataconf(w) ((w)->acl & HTTP_ACL_NETDATACONF)
+#define http_is_using_ssl_optional(w) ((w)->port_acl & HTTP_ACL_SSL_OPTIONAL)
+#define http_is_using_ssl_force(w) ((w)->port_acl & HTTP_ACL_SSL_FORCE)
+#define http_is_using_ssl_default(w) ((w)->port_acl & HTTP_ACL_SSL_DEFAULT)
+
+#endif //NETDATA_HTTP_ACCESS_H
diff --git a/src/libnetdata/http/http_defs.c b/src/libnetdata/http/http_defs.c
new file mode 100644
index 000000000..ef7621a65
--- /dev/null
+++ b/src/libnetdata/http/http_defs.c
@@ -0,0 +1,245 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "../libnetdata.h"
+
+ENUM_STR_MAP_DEFINE(HTTP_REQUEST_MODE) =
+{
+ { .name = "OPTIONS", .id = HTTP_REQUEST_MODE_OPTIONS },
+ { .name = "GET", .id = HTTP_REQUEST_MODE_GET },
+ { .name = "FILECOPY", .id = HTTP_REQUEST_MODE_FILECOPY },
+ { .name = "POST", .id = HTTP_REQUEST_MODE_POST },
+ { .name = "PUT", .id = HTTP_REQUEST_MODE_PUT },
+ { .name = "DELETE", .id = HTTP_REQUEST_MODE_DELETE },
+ { .name = "STREAM", .id = HTTP_REQUEST_MODE_STREAM },
+
+ // terminator
+ { .name = NULL, .id = 0 }
+};
+
+ENUM_STR_DEFINE_FUNCTIONS(HTTP_REQUEST_MODE, 0, "UNKNOWN");
+
+const char *http_response_code2string(int code) {
+ switch(code) {
+ case 100:
+ return "Continue";
+ case 101:
+ return "Switching Protocols";
+ case 102:
+ return "Processing";
+ case 103:
+ return "Early Hints";
+
+ case 200:
+ return "OK";
+ case 201:
+ return "Created";
+ case 202:
+ return "Accepted";
+ case 203:
+ return "Non-Authoritative Information";
+ case 204:
+ return "No Content";
+ case 205:
+ return "Reset Content";
+ case 206:
+ return "Partial Content";
+ case 207:
+ return "Multi-Status";
+ case 208:
+ return "Already Reported";
+ case 226:
+ return "IM Used";
+
+ case 300:
+ return "Multiple Choices";
+ case 301:
+ return "Moved Permanently";
+ case 302:
+ return "Found";
+ case 303:
+ return "See Other";
+ case 304:
+ return "Not Modified";
+ case 305:
+ return "Use Proxy";
+ case 306:
+ return "Switch Proxy";
+ case 307:
+ return "Temporary Redirect";
+ case 308:
+ return "Permanent Redirect";
+
+ case 400:
+ return "Bad Request";
+ case 401:
+ return "Unauthorized";
+ case 402:
+ return "Payment Required";
+ case 403:
+ return "Forbidden";
+ case 404:
+ return "Not Found";
+ case 405:
+ return "Method Not Allowed";
+ case 406:
+ return "Not Acceptable";
+ case 407:
+ return "Proxy Authentication Required";
+ case 408:
+ return "Request Timeout";
+ case 409:
+ return "Conflict";
+ case 410:
+ return "Gone";
+ case 411:
+ return "Length Required";
+ case 412:
+ return "Precondition Failed";
+ case 413:
+ return "Payload Too Large";
+ case 414:
+ return "URI Too Long";
+ case 415:
+ return "Unsupported Media Type";
+ case 416:
+ return "Range Not Satisfiable";
+ case 417:
+ return "Expectation Failed";
+ case 418:
+ return "I'm a teapot";
+ case 421:
+ return "Misdirected Request";
+ case 422:
+ return "Unprocessable Entity";
+ case 423:
+ return "Locked";
+ case 424:
+ return "Failed Dependency";
+ case 425:
+ return "Too Early";
+ case 426:
+ return "Upgrade Required";
+ case 428:
+ return "Precondition Required";
+ case 429:
+ return "Too Many Requests";
+ case 431:
+ return "Request Header Fields Too Large";
+ case 451:
+ return "Unavailable For Legal Reasons";
+ case 499: // nginx's extension to the standard
+ return "Client Closed Request";
+
+ case 500:
+ return "Internal Server Error";
+ case 501:
+ return "Not Implemented";
+ case 502:
+ return "Bad Gateway";
+ case 503:
+ return "Service Unavailable";
+ case 504:
+ return "Gateway Timeout";
+ case 505:
+ return "HTTP Version Not Supported";
+ case 506:
+ return "Variant Also Negotiates";
+ case 507:
+ return "Insufficient Storage";
+ case 508:
+ return "Loop Detected";
+ case 510:
+ return "Not Extended";
+ case 511:
+ return "Network Authentication Required";
+
+ default:
+ if(code >= 100 && code < 200)
+ return "Informational";
+
+ if(code >= 200 && code < 300)
+ return "Successful";
+
+ if(code >= 300 && code < 400)
+ return "Redirection";
+
+ if(code >= 400 && code < 500)
+ return "Client Error";
+
+ if(code >= 500 && code < 600)
+ return "Server Error";
+
+ return "Undefined Error";
+ }
+}
+
+
+static struct {
+ const char *extension;
+ uint32_t hash;
+ HTTP_CONTENT_TYPE contenttype;
+} mime_types[] = {
+ { "html" , 0 , CT_TEXT_HTML }
+ , { "js" , 0 , CT_APPLICATION_X_JAVASCRIPT }
+ , { "css" , 0 , CT_TEXT_CSS }
+ , { "xml" , 0 , CT_TEXT_XML }
+ , { "xsl" , 0 , CT_TEXT_XSL }
+ , { "txt" , 0 , CT_TEXT_PLAIN }
+ , { "svg" , 0 , CT_IMAGE_SVG_XML }
+ , { "ttf" , 0 , CT_APPLICATION_X_FONT_TRUETYPE }
+ , { "otf" , 0 , CT_APPLICATION_X_FONT_OPENTYPE }
+ , { "woff2", 0 , CT_APPLICATION_FONT_WOFF2 }
+ , { "woff" , 0 , CT_APPLICATION_FONT_WOFF }
+ , { "eot" , 0 , CT_APPLICATION_VND_MS_FONTOBJ }
+ , { "png" , 0 , CT_IMAGE_PNG }
+ , { "jpg" , 0 , CT_IMAGE_JPG }
+ , { "jpeg" , 0 , CT_IMAGE_JPG }
+ , { "gif" , 0 , CT_IMAGE_GIF }
+ , { "bmp" , 0 , CT_IMAGE_BMP }
+ , { "ico" , 0 , CT_IMAGE_XICON }
+ , { "icns" , 0 , CT_IMAGE_ICNS }
+
+ // terminator
+ , { NULL , 0 , 0 }
+};
+
+HTTP_CONTENT_TYPE contenttype_for_filename(const char *filename) {
+ // netdata_log_info("checking filename '%s'", filename);
+
+ static int initialized = 0;
+ int i;
+
+ if(unlikely(!initialized)) {
+ for (i = 0; mime_types[i].extension; i++)
+ mime_types[i].hash = simple_hash(mime_types[i].extension);
+
+ initialized = 1;
+ }
+
+ const char *s = filename, *last_dot = NULL;
+
+ // find the last dot
+ while(*s) {
+ if(unlikely(*s == '.')) last_dot = s;
+ s++;
+ }
+
+ if(unlikely(!last_dot || !*last_dot || !last_dot[1])) {
+ // netdata_log_info("no extension for filename '%s'", filename);
+ return CT_APPLICATION_OCTET_STREAM;
+ }
+ last_dot++;
+
+ // netdata_log_info("extension for filename '%s' is '%s'", filename, last_dot);
+
+ uint32_t hash = simple_hash(last_dot);
+ for(i = 0; mime_types[i].extension ; i++) {
+ if(unlikely(hash == mime_types[i].hash && !strcmp(last_dot, mime_types[i].extension))) {
+ // netdata_log_info("matched extension for filename '%s': '%s'", filename, last_dot);
+ return mime_types[i].contenttype;
+ }
+ }
+
+ // netdata_log_info("not matched extension for filename '%s': '%s'", filename, last_dot);
+ return CT_APPLICATION_OCTET_STREAM;
+}
diff --git a/src/libnetdata/http/http_defs.h b/src/libnetdata/http/http_defs.h
new file mode 100644
index 000000000..e1e26863e
--- /dev/null
+++ b/src/libnetdata/http/http_defs.h
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#ifndef NETDATA_HTTP_DEFS_H
+#define NETDATA_HTTP_DEFS_H
+
+#define HTTP_1_1 " HTTP/1.1"
+#define HTTP_HDR_END "\r\n\r\n"
+#define HTTP_ENDL "\r\n"
+
+// HTTP_CODES 1XX
+#define HTTP_RESP_SWITCH_PROTO 101
+
+// HTTP_CODES 2XX Success
+#define HTTP_RESP_OK 200
+#define HTTP_RESP_ACCEPTED 202
+
+// HTTP_CODES 3XX Redirections
+#define HTTP_RESP_MOVED_PERM 301
+#define HTTP_RESP_NOT_MODIFIED 304
+#define HTTP_RESP_REDIR_TEMP 307
+#define HTTP_RESP_REDIR_PERM 308
+#define HTTP_RESP_HTTPS_UPGRADE 399
+
+// HTTP_CODES 4XX Client Errors
+#define HTTP_RESP_BAD_REQUEST 400
+#define HTTP_RESP_NOT_FOUND 404
+#define HTTP_RESP_METHOD_NOT_ALLOWED 405
+#define HTTP_RESP_CONFLICT 409
+#define HTTP_RESP_CONTENT_TOO_LONG 413
+
+#define HTTP_RESP_UNAUTHORIZED 401 // do not use 401 when responding to users - it is used by authenticating proxies
+#define HTTP_RESP_FORBIDDEN 403 // not enough permissions to access this resource
+#define HTTP_RESP_PRECOND_FAIL 412 // An authorization bearer is required by it was not found in the request
+#define HTTP_RESP_UNAVAILABLE_FOR_LEGAL_REASONS 451 // Unavailable For Legal Reasons, we use it instead of 403 when access is forbidden due to an ACL.
+
+#define HTTP_RESP_CLIENT_CLOSED_REQUEST 499 // nginx's enxtension to the standard
+
+// HTTP_CODES 5XX Server Errors
+#define HTTP_RESP_INTERNAL_SERVER_ERROR 500
+#define HTTP_RESP_NOT_IMPLEMENTED 501
+#define HTTP_RESP_SERVICE_UNAVAILABLE 503
+#define HTTP_RESP_GATEWAY_TIMEOUT 504
+#define HTTP_RESP_BACKEND_RESPONSE_INVALID 591
+
+typedef enum __attribute__((__packed__)) {
+ HTTP_REQUEST_MODE_NONE = 0,
+ HTTP_REQUEST_MODE_GET = 1,
+ HTTP_REQUEST_MODE_POST = 2,
+ HTTP_REQUEST_MODE_PUT = 3,
+ HTTP_REQUEST_MODE_DELETE = 4,
+ HTTP_REQUEST_MODE_FILECOPY = 5,
+ HTTP_REQUEST_MODE_OPTIONS = 6,
+ HTTP_REQUEST_MODE_STREAM = 7,
+} HTTP_REQUEST_MODE;
+
+ENUM_STR_DEFINE_FUNCTIONS_EXTERN(HTTP_REQUEST_MODE);
+
+const char *http_response_code2string(int code);
+HTTP_CONTENT_TYPE contenttype_for_filename(const char *filename);
+
+#endif /* NETDATA_HTTP_DEFS_H */