@cryptotaxi247 / netdata-1 / commits / 19f1bd14d

Utf8 Badge Fix And URL Parser International Support (initial) (#6426)

#### Summary Fixes #3117 Additionally it adds support for UTF-8 in URL parser (as it should). Label sizes now are updated by browser with JavaScript (although guess is still calculated by verdana11_widths with minor improvements) #### Component Name API/Badges, LibNetData/URL #### Additional Information It was found that not only verdana11_widths need to be updated but the url parser replaces international characters with spaces (one space per each byte of multibyte character). Therefore I update both to support international chars.

Timo committed Jul 24, 2019 at 14:32 UTC 19f1bd14debbc3654f156d05e44b792efc45d3d3
8 files changed +226 -164
CMakeLists.txt
+1
@@ -320,6 +320,7 @@ set(LIBNETDATA_FILES
320 libnetdata/json/jsmn.h
321 libnetdata/health/health.c
322 libnetdata/health/health.h
323 + libnetdata/string/utf8.h
324 libnetdata/socket/security.c
325 libnetdata/socket/security.h)
326
Makefile.am
+1
@@ -167,6 +167,7 @@ LIBNETDATA_FILES = \
167 libnetdata/json/jsmn.h \
168 libnetdata/health/health.c \
169 libnetdata/health/health.h \
170 + libnetdata/string/utf8.h \
171 $(NULL)
172
173 APPS_PLUGIN_FILES = \
libnetdata/libnetdata.h
+1
@@ -313,5 +313,6 @@ extern char *netdata_configured_host_prefix;
313 #include "url/url.h"
314 #include "json/json.h"
315 #include "health/health.h"
316 +#include "string/utf8.h"
317
318 #endif // NETDATA_LIB_H
libnetdata/string/utf8.h new
+9
@@ -0,0 +1,9 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_STRING_UTF8_H
4 +#define NETDATA_STRING_UTF8_H 1
5 +
6 +#define IS_UTF8_BYTE(x) (x & 0x80)
7 +#define IS_UTF8_STARTBYTE(x) (IS_UTF8_BYTE(x)&&(x & 0x40))
8 +
9 +#endif /* NETDATA_STRING_UTF8_H */
libnetdata/url/url.c
+141 -3
@@ -52,6 +52,125 @@ char *url_decode(char *str) {
52 return url_decode_r(buf, str, size);
53 }
54
55 +//decode %XX character or return 0 if cannot
56 +char url_percent_escape_decode(char *s) {
57 + if(likely(s[1] && s[2]))
58 + return from_hex(s[1]) << 4 | from_hex(s[2]);
59 + return 0;
60 +}
61 +
62 +//this (utf8 string related) should be moved in separate file in future
63 +char url_utf8_get_byte_length(char c) {
64 + if(!IS_UTF8_BYTE(c))
65 + return 1;
66 +
67 + char length = 0;
68 + while(likely(c & 0x80)) {
69 + length++;
70 + c <<= 1;
71 + }
72 + //4 byte is max size for UTF-8 char
73 + //10XX XXXX is not valid character -> check length == 1
74 + if(length > 4 || length == 1)
75 + return -1;
76 +
77 + return length;
78 +}
79 +
80 +//decode % encoded UTF-8 characters and copy them to *d
81 +//return count of bytes written to *d
82 +char url_decode_multibyte_utf8(char *s, char *d, char *d_end) {
83 + char first_byte = url_percent_escape_decode(s);
84 +
85 + if(unlikely(!first_byte || !IS_UTF8_STARTBYTE(first_byte)))
86 + return 0;
87 +
88 + char byte_length = url_utf8_get_byte_length(first_byte);
89 +
90 + if(unlikely(byte_length <= 0 || d+byte_length >= d_end))
91 + return 0;
92 +
93 + char to_read = byte_length;
94 + while(to_read > 0) {
95 + char c = url_percent_escape_decode(s);
96 +
97 + if(unlikely( !IS_UTF8_BYTE(c) ))
98 + return 0;
99 + if((to_read != byte_length) && IS_UTF8_STARTBYTE(c))
100 + return 0;
101 +
102 + *d++ = c;
103 + s+=3;
104 + to_read--;
105 + }
106 +
107 + return byte_length;
108 +}
109 +
110 +/*
111 + * The utf8_check() function scans the '\0'-terminated string starting
112 + * at s. It returns a pointer to the first byte of the first malformed
113 + * or overlong UTF-8 sequence found, or NULL if the string contains
114 + * only correct UTF-8. It also spots UTF-8 sequences that could cause
115 + * trouble if converted to UTF-16, namely surrogate characters
116 + * (U+D800..U+DFFF) and non-Unicode positions (U+FFFE..U+FFFF). This
117 + * routine is very likely to find a malformed sequence if the input
118 + * uses any other encoding than UTF-8. It therefore can be used as a
119 + * very effective heuristic for distinguishing between UTF-8 and other
120 + * encodings.
121 + *
122 + * Markus Kuhn <http://www.cl.cam.ac.uk/~mgk25/> -- 2005-03-30
123 + * License: http://www.cl.cam.ac.uk/~mgk25/short-license.html
124 + */
125 +
126 +unsigned char *utf8_check(unsigned char *s)
127 +{
128 + while (*s)
129 + {
130 + if (*s < 0x80)
131 + /* 0xxxxxxx */
132 + s++;
133 + else if ((s[0] & 0xe0) == 0xc0)
134 + {
135 + /* 110XXXXx 10xxxxxx */
136 + if ((s[1] & 0xc0) != 0x80 ||
137 + (s[0] & 0xfe) == 0xc0) /* overlong? */
138 + return s;
139 + else
140 + s += 2;
141 + }
142 + else if ((s[0] & 0xf0) == 0xe0)
143 + {
144 + /* 1110XXXX 10Xxxxxx 10xxxxxx */
145 + if ((s[1] & 0xc0) != 0x80 ||
146 + (s[2] & 0xc0) != 0x80 ||
147 + (s[0] == 0xe0 && (s[1] & 0xe0) == 0x80) || /* overlong? */
148 + (s[0] == 0xed && (s[1] & 0xe0) == 0xa0) || /* surrogate? */
149 + (s[0] == 0xef && s[1] == 0xbf &&
150 + (s[2] & 0xfe) == 0xbe)) /* U+FFFE or U+FFFF? */
151 + return s;
152 + else
153 + s += 3;
154 + }
155 + else if ((s[0] & 0xf8) == 0xf0)
156 + {
157 + /* 11110XXX 10XXxxxx 10xxxxxx 10xxxxxx */
158 + if ((s[1] & 0xc0) != 0x80 ||
159 + (s[2] & 0xc0) != 0x80 ||
160 + (s[3] & 0xc0) != 0x80 ||
161 + (s[0] == 0xf0 && (s[1] & 0xf0) == 0x80) || /* overlong? */
162 + (s[0] == 0xf4 && s[1] > 0x8f) || s[0] > 0xf4) /* > U+10FFFF? */
163 + return s;
164 + else
165 + s += 4;
166 + }
167 + else
168 + return s;
169 + }
170 +
171 + return NULL;
172 +}
173 +
174 char *url_decode_r(char *to, char *url, size_t size) {
175 char *s = url, // source
176 *d = to, // destination
@@ -59,12 +178,24 @@ char *url_decode_r(char *to, char *url, size_t size) {
178
179 while(*s && d < e) {
180 if(unlikely(*s == '%')) {
62 - if(likely(s[1] && s[2])) {
63 - char t = from_hex(s[1]) << 4 | from_hex(s[2]);
181 + char t = url_percent_escape_decode(s);
182 + if(IS_UTF8_BYTE(t)) {
183 + char bytes_written = url_decode_multibyte_utf8(s, d, e);
184 + if(likely(bytes_written)){
185 + d += bytes_written;
186 + s += (bytes_written * 3)-1;
187 + }
188 + else {
189 + goto fail_cleanup;
190 + }
191 + }
192 + else if(likely(t) && isprint(t)) {
193 // avoid HTTP header injection
65 - *d++ = (char)((isprint(t))? t : ' ');
194 + *d++ = t;
195 s += 2;
196 }
197 + else
198 + goto fail_cleanup;
199 }
200 else if(unlikely(*s == '+'))
201 *d++ = ' ';
@@ -77,5 +208,12 @@ char *url_decode_r(char *to, char *url, size_t size) {
208
209 *d = '\0';
210
211 + if(unlikely( utf8_check(to) )) //NULL means sucess here
212 + return NULL;
213 +
214 return to;
215 +
216 +fail_cleanup:
217 + *d = '\0';
218 + return NULL;
219 }
web/api/badges/web_buffer_svg.c
+58 -158
@@ -11,7 +11,7 @@
11 * https://github.com/badges/shields/blob/master/measure-text.js
12 */
13
14 -static double verdana11_widths[256] = {
14 +static double verdana11_widths[128] = {
15 [0] = 0.0,
16 [1] = 0.0,
17 [2] = 0.0,
@@ -139,157 +139,36 @@ static double verdana11_widths[256] = {
139 [124] = 4.9951171875, // |
140 [125] = 6.982421875, // }
141 [126] = 9.001953125, // ~
142 - [127] = 0.0,
143 - [128] = 0.0,
144 - [129] = 0.0,
145 - [130] = 0.0,
146 - [131] = 0.0,
147 - [132] = 0.0,
148 - [133] = 0.0,
149 - [134] = 0.0,
150 - [135] = 0.0,
151 - [136] = 0.0,
152 - [137] = 0.0,
153 - [138] = 0.0,
154 - [139] = 0.0,
155 - [140] = 0.0,
156 - [141] = 0.0,
157 - [142] = 0.0,
158 - [143] = 0.0,
159 - [144] = 0.0,
160 - [145] = 0.0,
161 - [146] = 0.0,
162 - [147] = 0.0,
163 - [148] = 0.0,
164 - [149] = 0.0,
165 - [150] = 0.0,
166 - [151] = 0.0,
167 - [152] = 0.0,
168 - [153] = 0.0,
169 - [154] = 0.0,
170 - [155] = 0.0,
171 - [156] = 0.0,
172 - [157] = 0.0,
173 - [158] = 0.0,
174 - [159] = 0.0,
175 - [160] = 0.0,
176 - [161] = 0.0,
177 - [162] = 0.0,
178 - [163] = 0.0,
179 - [164] = 0.0,
180 - [165] = 0.0,
181 - [166] = 0.0,
182 - [167] = 0.0,
183 - [168] = 0.0,
184 - [169] = 0.0,
185 - [170] = 0.0,
186 - [171] = 0.0,
187 - [172] = 0.0,
188 - [173] = 0.0,
189 - [174] = 0.0,
190 - [175] = 0.0,
191 - [176] = 0.0,
192 - [177] = 0.0,
193 - [178] = 0.0,
194 - [179] = 0.0,
195 - [180] = 0.0,
196 - [181] = 0.0,
197 - [182] = 0.0,
198 - [183] = 0.0,
199 - [184] = 0.0,
200 - [185] = 0.0,
201 - [186] = 0.0,
202 - [187] = 0.0,
203 - [188] = 0.0,
204 - [189] = 0.0,
205 - [190] = 0.0,
206 - [191] = 0.0,
207 - [192] = 0.0,
208 - [193] = 0.0,
209 - [194] = 0.0,
210 - [195] = 0.0,
211 - [196] = 0.0,
212 - [197] = 0.0,
213 - [198] = 0.0,
214 - [199] = 0.0,
215 - [200] = 0.0,
216 - [201] = 0.0,
217 - [202] = 0.0,
218 - [203] = 0.0,
219 - [204] = 0.0,
220 - [205] = 0.0,
221 - [206] = 0.0,
222 - [207] = 0.0,
223 - [208] = 0.0,
224 - [209] = 0.0,
225 - [210] = 0.0,
226 - [211] = 0.0,
227 - [212] = 0.0,
228 - [213] = 0.0,
229 - [214] = 0.0,
230 - [215] = 0.0,
231 - [216] = 0.0,
232 - [217] = 0.0,
233 - [218] = 0.0,
234 - [219] = 0.0,
235 - [220] = 0.0,
236 - [221] = 0.0,
237 - [222] = 0.0,
238 - [223] = 0.0,
239 - [224] = 0.0,
240 - [225] = 0.0,
241 - [226] = 0.0,
242 - [227] = 0.0,
243 - [228] = 0.0,
244 - [229] = 0.0,
245 - [230] = 0.0,
246 - [231] = 0.0,
247 - [232] = 0.0,
248 - [233] = 0.0,
249 - [234] = 0.0,
250 - [235] = 0.0,
251 - [236] = 0.0,
252 - [237] = 0.0,
253 - [238] = 0.0,
254 - [239] = 0.0,
255 - [240] = 0.0,
256 - [241] = 0.0,
257 - [242] = 0.0,
258 - [243] = 0.0,
259 - [244] = 0.0,
260 - [245] = 0.0,
261 - [246] = 0.0,
262 - [247] = 0.0,
263 - [248] = 0.0,
264 - [249] = 0.0,
265 - [250] = 0.0,
266 - [251] = 0.0,
267 - [252] = 0.0,
268 - [253] = 0.0,
269 - [254] = 0.0,
270 - [255] = 0.0
142 + [127] = 0.0
143 };
144
145 // find the width of the string using the verdana 11points font
274 -// re-write the string in place, skiping zero-length characters
275 -static inline double verdana11_width(char *s) {
146 +static inline double verdana11_width(const char *s, float em_size) {
147 double w = 0.0;
277 - char *d = s;
148
149 while(*s) {
280 - double t = verdana11_widths[(unsigned char)*s];
281 - if(t == 0.0)
150 + // if UTF8 multibyte char found and guess it's width equal 1em
151 + // as label width will be updated with JavaScript this is not so important
152 +
153 + // TODO: maybe move UTF8 functions from url.c to separate util in libnetdata
154 + // then use url_utf8_get_byte_length etc.
155 + if(IS_UTF8_STARTBYTE(*s)) {
156 s++;
157 + while(IS_UTF8_BYTE(*s) && !IS_UTF8_STARTBYTE(*s)){
158 + s++;
159 + }
160 + w += em_size;
161 + }
162 else {
284 - w += t + VERDANA_KERNING;
285 - if(d != s)
286 - *d++ = *s++;
287 - else
288 - d = ++s;
163 + if(likely(!(*s & 0x80))){ // Byte 1XXX XXXX is not valid in UTF8
164 + double t = verdana11_widths[(unsigned char)*s];
165 + if(t != 0.0)
166 + w += t + VERDANA_KERNING;
167 + }
168 + s++;
169 }
170 }
171
292 - *d = '\0';
172 w -= VERDANA_KERNING;
173 w += VERDANA_PADDING;
174 return w;
@@ -810,8 +689,7 @@ static inline void calc_colorz(const char *color, char *final, size_t len, calcu
689 #define COLOR_STRING_SIZE 100
690
691 void buffer_svg(BUFFER *wb, const char *label, calculated_number value, const char *units, const char *label_color, const char *value_color, int precision, int scale, uint32_t options) {
813 - char label_buffer[LABEL_STRING_SIZE + 1]
814 - , value_color_buffer[COLOR_STRING_SIZE + 1]
692 + char value_color_buffer[COLOR_STRING_SIZE + 1]
693 , value_string[VALUE_STRING_SIZE + 1]
694 , label_escaped[LABEL_STRING_SIZE + 1]
695 , value_escaped[VALUE_STRING_SIZE + 1]
@@ -831,14 +709,11 @@ void buffer_svg(BUFFER *wb, const char *label, calculated_number value, const ch
709 calc_colorz(value_color, value_color_buffer, COLOR_STRING_SIZE, value);
710 format_value_and_unit(value_string, VALUE_STRING_SIZE, (options & RRDR_OPTION_DISPLAY_ABS)?calculated_number_fabs(value):value, units, precision);
711
834 - // we need to copy the label, since verdana11_width may write to it
835 - strncpyz(label_buffer, label, LABEL_STRING_SIZE);
836 -
837 - label_width = verdana11_width(label_buffer) + (BADGE_HORIZONTAL_PADDING * 2);
838 - value_width = verdana11_width(value_string) + (BADGE_HORIZONTAL_PADDING * 2);
712 + label_width = verdana11_width(label, font_size) + (BADGE_HORIZONTAL_PADDING * 2);
713 + value_width = verdana11_width(value_string, font_size) + (BADGE_HORIZONTAL_PADDING * 2);
714 total_width = label_width + value_width;
715
841 - escape_xmlz(label_escaped, label_buffer, LABEL_STRING_SIZE);
716 + escape_xmlz(label_escaped, label, LABEL_STRING_SIZE);
717 escape_xmlz(value_escaped, value_string, VALUE_STRING_SIZE);
718 escape_xmlz(label_color_escaped, color_map(label_color), COLOR_STRING_SIZE);
719 escape_xmlz(value_color_escaped, color_map(value_color_buffer), COLOR_STRING_SIZE);
@@ -862,19 +737,43 @@ void buffer_svg(BUFFER *wb, const char *label, calculated_number value, const ch
737 "<stop offset=\"1\" stop-opacity=\".1\"/>"
738 "</linearGradient>"
739 "<mask id=\"round\">"
865 - "<rect width=\"%0.2f\" height=\"%0.2f\" rx=\"%0.2f\" fill=\"#fff\"/>"
740 + "<rect class=\"bdge-ttl-width\" width=\"%0.2f\" height=\"%0.2f\" rx=\"%0.2f\" fill=\"#fff\"/>"
741 "</mask>"
742 "<g mask=\"url(#round)\">"
868 - "<rect width=\"%0.2f\" height=\"%0.2f\" fill=\"%s\"/>"
869 - "<rect x=\"%0.2f\" width=\"%0.2f\" height=\"%0.2f\" fill=\"%s\"/>"
870 - "<rect width=\"%0.2f\" height=\"%0.2f\" fill=\"url(#smooth)\"/>"
743 + "<rect class=\"bdge-rect-lbl\" width=\"%0.2f\" height=\"%0.2f\" fill=\"%s\"/>"
744 + "<rect class=\"bdge-rect-val\" x=\"%0.2f\" width=\"%0.2f\" height=\"%0.2f\" fill=\"%s\"/>"
745 + "<rect class=\"bdge-ttl-width\" width=\"%0.2f\" height=\"%0.2f\" fill=\"url(#smooth)\"/>"
746 "</g>"
747 "<g fill=\"#fff\" text-anchor=\"middle\" font-family=\"DejaVu Sans,Verdana,Geneva,sans-serif\" font-size=\"%0.2f\">"
873 - "<text x=\"%0.2f\" y=\"%0.0f\" fill=\"#010101\" fill-opacity=\".3\">%s</text>"
874 - "<text x=\"%0.2f\" y=\"%0.0f\">%s</text>"
875 - "<text x=\"%0.2f\" y=\"%0.0f\" fill=\"#010101\" fill-opacity=\".3\">%s</text>"
876 - "<text x=\"%0.2f\" y=\"%0.0f\">%s</text>"
748 + "<text class=\"bdge-lbl-lbl\" x=\"%0.2f\" y=\"%0.0f\" fill=\"#010101\" fill-opacity=\".3\">%s</text>"
749 + "<text class=\"bdge-lbl-lbl\" x=\"%0.2f\" y=\"%0.0f\">%s</text>"
750 + "<text class=\"bdge-lbl-val\" x=\"%0.2f\" y=\"%0.0f\" fill=\"#010101\" fill-opacity=\".3\">%s</text>"
751 + "<text class=\"bdge-lbl-val\" x=\"%0.2f\" y=\"%0.0f\">%s</text>"
752 "</g>"
753 + "<script type=\"text/javascript\">"
754 + "var bdg_horiz_padding = %d;"
755 + "function netdata_bdge_each(list, attr, value){"
756 + "Array.prototype.forEach.call(list, function(el){"
757 + "el.setAttribute(attr, value);"
758 + "});"
759 + "};"
760 + "var this_svg = document.currentScript.closest(\"svg\");"
761 + "var elem_lbl = this_svg.getElementsByClassName(\"bdge-lbl-lbl\");"
762 + "var elem_val = this_svg.getElementsByClassName(\"bdge-lbl-val\");"
763 + "var lbl_size = elem_lbl[0].getBBox();"
764 + "var val_size = elem_val[0].getBBox();"
765 + "var width_total = lbl_size.width + bdg_horiz_padding*2;"
766 + "this_svg.getElementsByClassName(\"bdge-rect-lbl\")[0].setAttribute(\"width\", width_total);"
767 + "netdata_bdge_each(elem_lbl, \"x\", (lbl_size.width / 2) + bdg_horiz_padding);"
768 + "netdata_bdge_each(elem_val, \"x\", width_total + (val_size.width / 2) + bdg_horiz_padding);"
769 + "var val_rect = this_svg.getElementsByClassName(\"bdge-rect-val\")[0];"
770 + "val_rect.setAttribute(\"width\", val_size.width + bdg_horiz_padding*2);"
771 + "val_rect.setAttribute(\"x\", width_total);"
772 + "width_total += val_size.width + bdg_horiz_padding*2;"
773 + "var width_update_elems = this_svg.getElementsByClassName(\"bdge-ttl-width\");"
774 + "netdata_bdge_each(width_update_elems, \"width\", width_total);"
775 + "this_svg.setAttribute(\"width\", width_total);"
776 + "</script>"
777 "</svg>",
778 total_width, height,
779 total_width, height, round_corner,
@@ -885,7 +784,8 @@ void buffer_svg(BUFFER *wb, const char *label, calculated_number value, const ch
784 label_width / 2, ceil(height - text_offset), label_escaped,
785 label_width / 2, ceil(height - text_offset - 1.0), label_escaped,
786 label_width + value_width / 2 -1, ceil(height - text_offset), value_escaped,
888 - label_width + value_width / 2 -1, ceil(height - text_offset - 1.0), value_escaped);
787 + label_width + value_width / 2 -1, ceil(height - text_offset - 1.0), value_escaped,
788 + BADGE_HORIZONTAL_PADDING );
789 }
790
791 int web_client_api_request_v1_badge(RRDHOST *host, struct web_client *w, char *url) {
web/server/web_client.c
+12 -3
@@ -821,6 +821,7 @@ static inline char *http_header_parse(struct web_client *w, char *s, int parse_u
821 typedef enum {
822 HTTP_VALIDATION_OK,
823 HTTP_VALIDATION_NOT_SUPPORTED,
824 + HTTP_VALIDATION_MALFORMED_URL,
825 #ifdef ENABLE_HTTPS
826 HTTP_VALIDATION_INCOMPLETE,
827 HTTP_VALIDATION_REDIRECT
@@ -941,7 +942,8 @@ static inline HTTP_VALIDATION http_request_validate(struct web_client *w) {
942 // a valid complete HTTP request found
943
944 *ue = '\0';
944 - url_decode_r(w->decoded_url, encoded_url, NETDATA_WEB_REQUEST_URL_SIZE + 1);
945 + if(!url_decode_r(w->decoded_url, encoded_url, NETDATA_WEB_REQUEST_URL_SIZE + 1))
946 + return HTTP_VALIDATION_MALFORMED_URL;
947 *ue = ' ';
948
949 // copy the URL - we are going to overwrite parts of it
@@ -1424,7 +1426,7 @@ void web_client_process_request(struct web_client *w) {
1426
1427 buffer_flush(w->response.data);
1428 buffer_sprintf(w->response.data, "Received request is too big (%zu bytes).\r\n", w->response.data->len);
1427 - w->response.code = 400;
1429 + w->response.code = HTTP_RESPONSE_BAD_REQUEST;
1430 }
1431 else {
1432 // wait for more data
@@ -1441,12 +1443,19 @@ void web_client_process_request(struct web_client *w) {
1443 break;
1444 }
1445 #endif
1446 + case HTTP_VALIDATION_MALFORMED_URL:
1447 + debug(D_WEB_CLIENT_ACCESS, "%llu: URL parsing failed (malformed URL). Cannot understand '%s'.", w->id, w->response.data->buffer);
1448 +
1449 + buffer_flush(w->response.data);
1450 + buffer_strcat(w->response.data, "URL not valid. I don't understand you...\r\n");
1451 + w->response.code = HTTP_RESPONSE_BAD_REQUEST;
1452 + break;
1453 case HTTP_VALIDATION_NOT_SUPPORTED:
1454 debug(D_WEB_CLIENT_ACCESS, "%llu: Cannot understand '%s'.", w->id, w->response.data->buffer);
1455
1456 buffer_flush(w->response.data);
1457 buffer_strcat(w->response.data, "I don't understand you...\r\n");
1449 - w->response.code = 400;
1458 + w->response.code = HTTP_RESPONSE_BAD_REQUEST;
1459 break;
1460 }
1461
web/server/web_client.h
+3
@@ -11,6 +11,9 @@ extern int web_enable_gzip,
11 web_gzip_strategy;
12 #endif /* NETDATA_WITH_ZLIB */
13
14 +//HTTP_CODES 4XX
15 +#define HTTP_RESPONSE_BAD_REQUEST 400
16 +
17 extern int respect_web_browser_do_not_track_policy;
18 extern char *web_x_frame_options;
19