diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 12:08:03 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 12:08:18 +0000 |
commit | 5da14042f70711ea5cf66e034699730335462f66 (patch) | |
tree | 0f6354ccac934ed87a2d555f45be4c831cf92f4a /src/libnetdata/http | |
parent | Releasing debian version 1.44.3-2. (diff) | |
download | netdata-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.c | 96 | ||||
-rw-r--r-- | src/libnetdata/http/content_type.h | 45 | ||||
-rw-r--r-- | src/libnetdata/http/http_access.c | 186 | ||||
-rw-r--r-- | src/libnetdata/http/http_access.h | 148 | ||||
-rw-r--r-- | src/libnetdata/http/http_defs.c | 245 | ||||
-rw-r--r-- | src/libnetdata/http/http_defs.h | 61 |
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 */ |