| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | #include "../libnetdata.h" |
| 4 | |
| 5 | ENUM_STR_MAP_DEFINE(HTTP_REQUEST_MODE) = |
| 6 | { |
| 7 | { .name = "OPTIONS", .id = HTTP_REQUEST_MODE_OPTIONS }, |
| 8 | { .name = "GET", .id = HTTP_REQUEST_MODE_GET }, |
| 9 | { .name = "POST", .id = HTTP_REQUEST_MODE_POST }, |
| 10 | { .name = "PUT", .id = HTTP_REQUEST_MODE_PUT }, |
| 11 | { .name = "DELETE", .id = HTTP_REQUEST_MODE_DELETE }, |
| 12 | { .name = "STREAM", .id = HTTP_REQUEST_MODE_STREAM }, |
| 13 | |
| 14 | // terminator |
| 15 | { .name = NULL, .id = 0 } |
| 16 | }; |
| 17 | |
| 18 | ENUM_STR_DEFINE_FUNCTIONS(HTTP_REQUEST_MODE, 0, "UNKNOWN"); |
| 19 | |
| 20 | const char *http_response_code2string(int code) { |
| 21 | switch(code) { |
| 22 | case 100: |
| 23 | return "Continue"; |
| 24 | case 101: |
| 25 | return "Switching Protocols"; |
| 26 | case 102: |
| 27 | return "Processing"; |
| 28 | case 103: |
| 29 | return "Early Hints"; |
| 30 | |
| 31 | case 200: |
| 32 | return "OK"; |
| 33 | case 201: |
| 34 | return "Created"; |
| 35 | case 202: |
| 36 | return "Accepted"; |
| 37 | case 203: |
| 38 | return "Non-Authoritative Information"; |
| 39 | case 204: |
| 40 | return "No Content"; |
| 41 | case 205: |
| 42 | return "Reset Content"; |
| 43 | case 206: |
| 44 | return "Partial Content"; |
| 45 | case 207: |
| 46 | return "Multi-Status"; |
| 47 | case 208: |
| 48 | return "Already Reported"; |
| 49 | case 226: |
| 50 | return "IM Used"; |
| 51 | |
| 52 | case 300: |
| 53 | return "Multiple Choices"; |
| 54 | case 301: |
| 55 | return "Moved Permanently"; |
| 56 | case 302: |
| 57 | return "Found"; |
| 58 | case 303: |
| 59 | return "See Other"; |
| 60 | case 304: |
| 61 | return "Not Modified"; |
| 62 | case 305: |
| 63 | return "Use Proxy"; |
| 64 | case 306: |
| 65 | return "Switch Proxy"; |
| 66 | case 307: |
| 67 | return "Temporary Redirect"; |
| 68 | case 308: |
| 69 | return "Permanent Redirect"; |
| 70 | |
| 71 | case 400: |
| 72 | return "Bad Request"; |
| 73 | case 401: |
| 74 | return "Unauthorized"; |
| 75 | case 402: |
| 76 | return "Payment Required"; |
| 77 | case 403: |
| 78 | return "Forbidden"; |
| 79 | case 404: |
| 80 | return "Not Found"; |
| 81 | case 405: |
| 82 | return "Method Not Allowed"; |
| 83 | case 406: |
| 84 | return "Not Acceptable"; |
| 85 | case 407: |
| 86 | return "Proxy Authentication Required"; |
| 87 | case 408: |
| 88 | return "Request Timeout"; |
| 89 | case 409: |
| 90 | return "Conflict"; |
| 91 | case 410: |
| 92 | return "Gone"; |
| 93 | case 411: |
| 94 | return "Length Required"; |
| 95 | case 412: |
| 96 | return "Precondition Failed"; |
| 97 | case 413: |
| 98 | return "Payload Too Large"; |
| 99 | case 414: |
| 100 | return "URI Too Long"; |
| 101 | case 415: |
| 102 | return "Unsupported Media Type"; |
| 103 | case 416: |
| 104 | return "Range Not Satisfiable"; |
| 105 | case 417: |
| 106 | return "Expectation Failed"; |
| 107 | case 418: |
| 108 | return "I'm a teapot"; |
| 109 | case 421: |
| 110 | return "Misdirected Request"; |
| 111 | case 422: |
| 112 | return "Unprocessable Entity"; |
| 113 | case 423: |
| 114 | return "Locked"; |
| 115 | case 424: |
| 116 | return "Failed Dependency"; |
| 117 | case 425: |
| 118 | return "Too Early"; |
| 119 | case 426: |
| 120 | return "Upgrade Required"; |
| 121 | case 428: |
| 122 | return "Precondition Required"; |
| 123 | case 429: |
| 124 | return "Too Many Requests"; |
| 125 | case 431: |
| 126 | return "Request Header Fields Too Large"; |
| 127 | case 451: |
| 128 | return "Unavailable For Legal Reasons"; |
| 129 | case 499: // nginx's extension to the standard |
| 130 | return "Client Closed Request"; |
| 131 | |
| 132 | case 500: |
| 133 | return "Internal Server Error"; |
| 134 | case 501: |
| 135 | return "Not Implemented"; |
| 136 | case 502: |
| 137 | return "Bad Gateway"; |
| 138 | case 503: |
| 139 | return "Service Unavailable"; |
| 140 | case 504: |
| 141 | return "Gateway Timeout"; |
| 142 | case 505: |
| 143 | return "HTTP Version Not Supported"; |
| 144 | case 506: |
| 145 | return "Variant Also Negotiates"; |
| 146 | case 507: |
| 147 | return "Insufficient Storage"; |
| 148 | case 508: |
| 149 | return "Loop Detected"; |
| 150 | case 510: |
| 151 | return "Not Extended"; |
| 152 | case 511: |
| 153 | return "Network Authentication Required"; |
| 154 | |
| 155 | default: |
| 156 | if(code >= 100 && code < 200) |
| 157 | return "Informational"; |
| 158 | |
| 159 | if(code >= 200 && code < 300) |
| 160 | return "Successful"; |
| 161 | |
| 162 | if(code >= 300 && code < 400) |
| 163 | return "Redirection"; |
| 164 | |
| 165 | if(code >= 400 && code < 500) |
| 166 | return "Client Error"; |
| 167 | |
| 168 | if(code >= 500 && code < 600) |
| 169 | return "Server Error"; |
| 170 | |
| 171 | return "Undefined Error"; |
| 172 | } |
| 173 | } |
| 174 | |
| 175 | |
| 176 | static struct { |
| 177 | const char *extension; |
| 178 | uint32_t hash; |
| 179 | HTTP_CONTENT_TYPE contenttype; |
| 180 | } mime_types[] = { |
| 181 | { "html" , 0 , CT_TEXT_HTML } |
| 182 | , { "js" , 0 , CT_APPLICATION_X_JAVASCRIPT } |
| 183 | , { "css" , 0 , CT_TEXT_CSS } |
| 184 | , { "xml" , 0 , CT_TEXT_XML } |
| 185 | , { "xsl" , 0 , CT_TEXT_XSL } |
| 186 | , { "txt" , 0 , CT_TEXT_PLAIN } |
| 187 | , { "svg" , 0 , CT_IMAGE_SVG_XML } |
| 188 | , { "ttf" , 0 , CT_APPLICATION_X_FONT_TRUETYPE } |
| 189 | , { "otf" , 0 , CT_APPLICATION_X_FONT_OPENTYPE } |
| 190 | , { "woff2", 0 , CT_APPLICATION_FONT_WOFF2 } |
| 191 | , { "woff" , 0 , CT_APPLICATION_FONT_WOFF } |
| 192 | , { "eot" , 0 , CT_APPLICATION_VND_MS_FONTOBJ } |
| 193 | , { "png" , 0 , CT_IMAGE_PNG } |
| 194 | , { "jpg" , 0 , CT_IMAGE_JPG } |
| 195 | , { "jpeg" , 0 , CT_IMAGE_JPG } |
| 196 | , { "gif" , 0 , CT_IMAGE_GIF } |
| 197 | , { "bmp" , 0 , CT_IMAGE_BMP } |
| 198 | , { "ico" , 0 , CT_IMAGE_XICON } |
| 199 | , { "icns" , 0 , CT_IMAGE_ICNS } |
| 200 | , { "wasm" , 0 , CT_APPLICATION_WASM } |
| 201 | |
| 202 | // terminator |
| 203 | , { NULL , 0 , 0 } |
| 204 | }; |
| 205 | |
| 206 | HTTP_CONTENT_TYPE contenttype_for_filename(const char *filename) { |
| 207 | // netdata_log_info("checking filename '%s'", filename); |
| 208 | |
| 209 | static int initialized = 0; |
| 210 | int i; |
| 211 | |
| 212 | if(unlikely(!initialized)) { |
| 213 | for (i = 0; mime_types[i].extension; i++) |
| 214 | mime_types[i].hash = simple_hash(mime_types[i].extension); |
| 215 | |
| 216 | initialized = 1; |
| 217 | } |
| 218 | |
| 219 | const char *s = filename, *last_dot = NULL; |
| 220 | |
| 221 | // find the last dot |
| 222 | while(*s) { |
| 223 | if(unlikely(*s == '.')) last_dot = s; |
| 224 | s++; |
| 225 | } |
| 226 | |
| 227 | if(unlikely(!last_dot || !*last_dot || !last_dot[1])) { |
| 228 | // netdata_log_info("no extension for filename '%s'", filename); |
| 229 | return CT_APPLICATION_OCTET_STREAM; |
| 230 | } |
| 231 | last_dot++; |
| 232 | |
| 233 | // netdata_log_info("extension for filename '%s' is '%s'", filename, last_dot); |
| 234 | |
| 235 | uint32_t hash = simple_hash(last_dot); |
| 236 | for(i = 0; mime_types[i].extension ; i++) { |
| 237 | if(unlikely(hash == mime_types[i].hash && !strcmp(last_dot, mime_types[i].extension))) { |
| 238 | // netdata_log_info("matched extension for filename '%s': '%s'", filename, last_dot); |
| 239 | return mime_types[i].contenttype; |
| 240 | } |
| 241 | } |
| 242 | |
| 243 | // netdata_log_info("not matched extension for filename '%s': '%s'", filename, last_dot); |
| 244 | return CT_APPLICATION_OCTET_STREAM; |
| 245 | } |