@cryptotaxi247 / netdata-1 / commits / e0f6bc886

registry: fixes netdata to respond to CORS with the requested origin, so that withCredentials: true will work

Costa Tsaousis committed May 11, 2016 at 04:36 UTC e0f6bc886de978c84a49711af16512735b41c4c0
4 files changed +178 -97
src/registry.c
+5 -3
@@ -22,6 +22,7 @@
22
23 #include "web_client.h"
24 #include "rrd.h"
25 +#include "rrd2json.h"
26 #include "registry.h"
27
28
@@ -78,6 +79,7 @@ struct registry {
79 // configuration
80 unsigned long long save_registry_every_entries;
81 char *registry_domain;
82 + char *hostname;
83 char *registry_to_announce;
84 time_t persons_expiration; // seconds to expire idle persons
85
@@ -1005,9 +1007,8 @@ static inline void registry_set_person_cookie(struct web_client *w, PERSON *p) {
1007 static inline void registry_json_header(struct web_client *w, int status) {
1008 w->response.data->contenttype = CT_APPLICATION_JSON;
1009 buffer_flush(w->response.data);
1008 - buffer_sprintf(w->response.data, "{\n\t\"success\": %s,\n\t\"machine_guid\": \"%s\"",
1009 - status?"true":"false",
1010 - registry.machine_guid);
1010 + buffer_sprintf(w->response.data, "{\n\t\"success\": %s,\n\t\"hostname\": \"%s\",\n\t\"machine_guid\": \"%s\"",
1011 + status?"true":"false", registry.hostname, registry.machine_guid);
1012 }
1013
1014 static inline void registry_json_footer(struct web_client *w) {
@@ -1551,6 +1552,7 @@ int registry_init(void) {
1552 registry.persons_expiration = config_get_number("registry", "registry expire idle persons days", 365) * 86400;
1553 registry.registry_domain = config_get("registry", "registry domain", "");
1554 registry.registry_to_announce = config_get("registry", "registry to announce", "https://registry.netdata.online");
1555 + registry.hostname = config_get("registry", "registry hostname", config_get("global", "hostname", hostname));
1556
1557 // initialize entries counters
1558 registry.persons_count = 0;
src/web_client.c
+164 -91
@@ -129,6 +129,7 @@ struct web_client *web_client_create(int listener)
129 return NULL;
130 }
131
132 + w->origin[0] = '*';
133 w->wait_receive = 1;
134
135 if(web_clients) web_clients->prev = w;
@@ -175,8 +176,16 @@ void web_client_reset(struct web_client *w)
176
177 w->last_url[0] = '\0';
178 w->cookie[0] = '\0';
179 + w->origin[0] = '*';
180 + w->origin[1] = '\0';
181
182 w->mode = WEB_CLIENT_MODE_NORMAL;
183 + w->enable_gzip = 0;
184 + w->keepalive = 0;
185 + if(w->decoded_url) {
186 + free(w->decoded_url);
187 + w->decoded_url = NULL;
188 + }
189
190 buffer_reset(w->response.header_output);
191 buffer_reset(w->response.header);
@@ -1168,99 +1177,191 @@ cleanup:
1177 }
1178 */
1179
1171 -// get the request buffer, just after the GET or OPTIONS
1172 -// and find the url to be decoded, decode it and return
1173 -// a newly allocated buffer with it
1174 -static inline char *find_url_and_decode_it(char *request) {
1175 - char *e = request, *url = NULL;
1180 +
1181 +static inline char *http_header_parse(struct web_client *w, char *s) {
1182 + static uint32_t connection_hash = 0, accept_encoding_hash = 0, origin_hash = 0;
1183 +
1184 + if(unlikely(connection_hash == 0)) {
1185 + connection_hash = simple_hash("Connection");
1186 + accept_encoding_hash = simple_hash("Accept-Encoding");
1187 + origin_hash = simple_hash("Origin");
1188 + }
1189 +
1190 + char *e = s;
1191 +
1192 + // find the :
1193 + while(*e && *e != ':') e++;
1194 + if(!*e || e[1] != ' ') return e;
1195 +
1196 + // get the name
1197 + *e = '\0';
1198 + uint32_t hash = simple_hash(s);
1199 +
1200 + // find the value
1201 + char *v, *ve;
1202 + v = ve = e + 2;
1203 +
1204 + // find the \r
1205 + while(*ve && *ve != '\r') ve++;
1206 + if(!*ve || ve[1] != '\n') {
1207 + *e = ':';
1208 + return ve;
1209 + }
1210 +
1211 + // terminate the value
1212 + *ve = '\0';
1213 +
1214 + if(hash == origin_hash && !strcmp(s, "Origin")) {
1215 + strncpy(w->origin, v, ORIGIN_MAX);
1216 + }
1217 + else if(hash == connection_hash && !strcmp(s, "Connection")) {
1218 + if(!strcasestr(v, "keep-alive"))
1219 + w->keepalive = 1;
1220 + }
1221 +#ifdef NETDATA_WITH_ZLIB
1222 + else if(hash == accept_encoding_hash && !strcmp(s, "Accept-Encoding")) {
1223 + if(web_enable_gzip && !strcasestr(v, "gzip"))
1224 + w->enable_gzip = 1;
1225 + }
1226 +#endif // NETDATA_WITH_ZLIB
1227 +
1228 + *e = ':';
1229 + *ve = '\r';
1230 + return ve;
1231 +}
1232 +
1233 +// http_request_validate()
1234 +// returns:
1235 +// = 0 : all good, process the request
1236 +// > 0 : request is complete, but is not supported
1237 +// < 0 : request is incomplete - wait for more data
1238 +
1239 +static inline int http_request_validate(struct web_client *w) {
1240 + char *s = w->response.data->buffer, *encoded_url = NULL;
1241 +
1242 + // is is a valid request?
1243 + if(!strncmp(s, "GET ", 4)) {
1244 + encoded_url = s = &s[4];
1245 + w->mode = WEB_CLIENT_MODE_NORMAL;
1246 + }
1247 + else if(!strncmp(s, "OPTIONS ", 8)) {
1248 + encoded_url = s = &s[8];
1249 + w->mode = WEB_CLIENT_MODE_OPTIONS;
1250 + }
1251 + else {
1252 + w->wait_receive = 0;
1253 + return 1;
1254 + }
1255
1256 // find the SPACE + "HTTP/"
1178 - while(*e) {
1257 + while(*s) {
1258 // find the space
1180 - while (*e && *e != ' ') e++;
1259 + while (*s && *s != ' ') s++;
1260
1261 // is it SPACE + "HTTP/" ?
1183 - if(*e && !strncmp(e, " HTTP/", 6))
1184 - break;
1262 + if(*s && !strncmp(s, " HTTP/", 6)) break;
1263 + else s++;
1264 }
1265
1187 - if(*e) {
1188 - // we have the end
1189 - *e = '\0';
1190 - url = url_decode(request);
1191 - *e = ' ';
1266 + // incomplete requests
1267 + if(!*s) {
1268 + w->wait_receive = 1;
1269 + return -2;
1270 }
1271
1194 - return url;
1195 -}
1272 + // we have the end of encoded_url - remember it
1273 + char *ue = s;
1274
1197 -void web_client_process(struct web_client *w) {
1198 - int code = 500;
1199 - ssize_t bytes;
1200 - int enable_gzip = 0;
1275 + while(*s) {
1276 + // find a line feed
1277 + while (*s && *s != '\r') s++;
1278
1202 - w->wait_receive = 0;
1279 + // did we reach the end?
1280 + if(unlikely(!*s)) break;
1281
1204 - // check if we have an empty line (end of HTTP header)
1205 - if(strstr(w->response.data->buffer, "\r\n\r\n")) {
1206 - global_statistics_lock();
1207 - global_statistics.web_requests++;
1208 - global_statistics_unlock();
1282 + // is it \r\n ?
1283 + if (likely(s[1] == '\n')) {
1284
1210 - gettimeofday(&w->tv_in, NULL);
1211 - debug(D_WEB_DATA, "%llu: Processing data buffer of %d bytes: '%s'.", w->id, w->response.data->len, w->response.data->buffer);
1285 + // is it again \r\n ? (header end)
1286 + if(unlikely(s[2] == '\r' && s[3] == '\n')) {
1287 + // a valid complete HTTP request found
1288
1213 - // check if the client requested keep-alive HTTP
1214 - if(strcasestr(w->response.data->buffer, "Connection: keep-alive")) w->keepalive = 1;
1215 - else w->keepalive = 0;
1289 + *ue = '\0';
1290 + w->decoded_url = url_decode(encoded_url);
1291 + *ue = ' ';
1292
1217 -#ifdef NETDATA_WITH_ZLIB
1218 - // check if the client accepts deflate
1219 - if(web_enable_gzip && strstr(w->response.data->buffer, "gzip"))
1220 - enable_gzip = 1;
1221 -#endif // NETDATA_WITH_ZLIB
1293 + w->wait_receive = 0;
1294 + return 0;
1295 + }
1296
1223 - w->mode = WEB_CLIENT_MODE_NORMAL;
1297 + // another header line
1298 + s = http_header_parse(w, &s[2]);
1299 + }
1300 + else s++;
1301 + }
1302
1225 - char *tok = (char *)buffer_tostring(w->response.data);
1226 - char *url = NULL, *encoded_url = NULL;
1227 - char *pointer_to_free = NULL; // keep url_decode() allocated buffer
1303 + // incomplete request
1304 + w->wait_receive = 1;
1305 + return -3;
1306 +}
1307
1229 - if(!strncmp(tok, "GET ", 4))
1230 - encoded_url = &tok[4];
1231 - else if(!strncmp(tok, "OPTIONS ", 8)) {
1232 - encoded_url = &tok[8];
1233 - w->mode = WEB_CLIENT_MODE_OPTIONS;
1234 - }
1308 +void web_client_process(struct web_client *w) {
1309 + int code = 500;
1310 + ssize_t bytes;
1311 +
1312 + int what_to_do = http_request_validate(w);
1313
1236 - if(encoded_url) {
1237 - pointer_to_free = url = find_url_and_decode_it(encoded_url);
1314 + // wait for more data
1315 + if(what_to_do < 0) {
1316 + if(w->response.data->len > TOO_BIG_REQUEST) {
1317 + strcpy(w->last_url, "too big request");
1318
1239 - if(url) debug(D_WEB_CLIENT, "%llu: Processing url '%s'.", w->id, url);
1240 - else debug(D_WEB_CLIENT, "%llu: Cannot find a valid URL in '%s'", w->id, encoded_url);
1319 + debug(D_WEB_CLIENT_ACCESS, "%llu: Received request is too big (%zd bytes).", w->id, w->response.data->len);
1320 +
1321 + code = 400;
1322 + buffer_flush(w->response.data);
1323 + buffer_sprintf(w->response.data, "Received request is too big (%zd bytes).\r\n", w->response.data->len);
1324 }
1325 + else {
1326 + // wait for more data
1327 + return;
1328 + }
1329 + }
1330 + else if(what_to_do > 0) {
1331 + strcpy(w->last_url, "not a valid response");
1332
1243 - w->last_url[0] = '\0';
1333 + debug(D_WEB_CLIENT_ACCESS, "%llu: Cannot understand '%s'.", w->id, w->response.data->buffer);
1334
1245 - if(w->mode == WEB_CLIENT_MODE_OPTIONS) {
1246 - strncpy(w->last_url, url, URL_MAX);
1247 - w->last_url[URL_MAX] = '\0';
1335 + code = 500;
1336 + buffer_flush(w->response.data);
1337 + buffer_strcat(w->response.data, "I don't understand you...\r\n");
1338 + }
1339 + else { // what_to_do == 0
1340 + gettimeofday(&w->tv_in, NULL);
1341
1342 + global_statistics_lock();
1343 + global_statistics.web_requests++;
1344 + global_statistics_unlock();
1345 +
1346 + // copy the URL - we are going to overwrite parts of it
1347 + // FIXME -- we should avoid it
1348 + strncpy(w->last_url, w->decoded_url, URL_MAX);
1349 + w->last_url[URL_MAX] = '\0';
1350 +
1351 + if(w->mode == WEB_CLIENT_MODE_OPTIONS) {
1352 code = 200;
1353 w->response.data->contenttype = CT_TEXT_PLAIN;
1354 buffer_flush(w->response.data);
1355 buffer_strcat(w->response.data, "OK");
1356 }
1254 - else if(url) {
1357 + else {
1358 #ifdef NETDATA_WITH_ZLIB
1256 - if(enable_gzip)
1359 + if(w->enable_gzip)
1360 web_client_enable_deflate(w);
1361 #endif
1362
1260 - strncpy(w->last_url, url, URL_MAX);
1261 - w->last_url[URL_MAX] = '\0';
1262 -
1263 - tok = mystrsep(&url, "/?");
1363 + char *url = w->decoded_url;
1364 + char *tok = mystrsep(&url, "/?");
1365 if(tok && *tok) {
1366 debug(D_WEB_CLIENT, "%llu: Processing command '%s'.", w->id, tok);
1367
@@ -1411,35 +1512,6 @@ void web_client_process(struct web_client *w) {
1512 code = mysendfile(w, (tok && *tok)?tok:"/");
1513 }
1514 }
1414 - else {
1415 - strcpy(w->last_url, "not a valid response");
1416 -
1417 - debug(D_WEB_CLIENT_ACCESS, "%llu: Cannot understand '%s'.", w->id, w->response.data->buffer);
1418 -
1419 - code = 500;
1420 - buffer_flush(w->response.data);
1421 - buffer_strcat(w->response.data, "I don't understand you...\r\n");
1422 - }
1423 -
1424 - // free url_decode() buffer
1425 - if(pointer_to_free) {
1426 - free(pointer_to_free);
1427 - pointer_to_free = NULL;
1428 - }
1429 - }
1430 - else if(w->response.data->len > TOO_BIG_REQUEST) {
1431 - strcpy(w->last_url, "too big request");
1432 -
1433 - debug(D_WEB_CLIENT_ACCESS, "%llu: Received request is too big (%zd bytes).", w->id, w->response.data->len);
1434 -
1435 - code = 400;
1436 - buffer_flush(w->response.data);
1437 - buffer_sprintf(w->response.data, "Received request is too big (%zd bytes).\r\n", w->response.data->len);
1438 - }
1439 - else {
1440 - // wait for more data
1441 - w->wait_receive = 1;
1442 - return;
1515 }
1516
1517 gettimeofday(&w->tv_ready, NULL);
@@ -1573,11 +1645,13 @@ void web_client_process(struct web_client *w) {
1645 "HTTP/1.1 %d %s\r\n"
1646 "Connection: %s\r\n"
1647 "Server: NetData Embedded HTTP Server\r\n"
1576 - "Access-Control-Allow-Origin: *\r\n"
1648 + "Access-Control-Allow-Origin: %s\r\n"
1649 + "Access-Control-Allow-Credentials: true\r\n"
1650 "Content-Type: %s\r\n"
1651 "Date: %s\r\n"
1652 , code, code_msg
1653 , w->keepalive?"keep-alive":"close"
1654 + , w->origin
1655 , content_type_string
1656 , date
1657 );
@@ -1591,7 +1665,6 @@ void web_client_process(struct web_client *w) {
1665 if(w->mode == WEB_CLIENT_MODE_OPTIONS) {
1666 buffer_strcat(w->response.header_output,
1667 "Access-Control-Allow-Methods: GET, OPTIONS\r\n"
1594 - "Access-Control-Allow-Credentials: true\r\n"
1668 "Access-Control-Allow-Headers: Accept, X-Requested-With, Content-Type, Cookie\r\n"
1669 "Access-Control-Max-Age: 1209600\r\n" // 86400 * 14
1670 );
src/web_client.h
+4
@@ -28,6 +28,7 @@ extern int web_enable_gzip;
28 #define ZLIB_CHUNK 16384
29 #define HTTP_RESPONSE_HEADER_SIZE 4096
30 #define COOKIE_MAX 1024
31 +#define ORIGIN_MAX 1024
32
33 struct response {
34 BUFFER *header; // our response header
@@ -61,9 +62,12 @@ struct web_client {
62 struct timeval tv_in, tv_ready;
63
64 char cookie[COOKIE_MAX+1];
65 + char origin[ORIGIN_MAX+1];
66
67 int mode;
68 int keepalive;
69 + int enable_gzip;
70 + char *decoded_url;
71
72 struct sockaddr_storage clientaddr;
73
web/dashboard.js
+5 -3
@@ -5473,8 +5473,9 @@
5473 };
5474
5475 NETDATA.registry = {
5476 - host: null,
5476 + server: null,
5477 machine_guid: null,
5478 + hostname: null,
5479 urls: null,
5480
5481 init: function() {
@@ -5495,8 +5496,9 @@
5496 xhrFields: { withCredentials: true }
5497 })
5498 .done(function(data) {
5498 - NETDATA.registry.host = data.registry;
5499 + NETDATA.registry.server = data.registry;
5500 NETDATA.registry.machine_guid = data.machine_guid;
5501 + NETDATA.registry.hostname = data.hostname;
5502
5503 if(typeof callback === 'function')
5504 callback();
@@ -5508,7 +5510,7 @@
5510
5511 access: function(callback) {
5512 $.ajax({
5511 - url: NETDATA.registry.host + '/api/v1/registry?action=access&machine=' + NETDATA.registry.machine_guid + '&name=test&url=' + document.location,
5513 + url: NETDATA.registry.server + '/api/v1/registry?action=access&machine=' + NETDATA.registry.machine_guid + '&name=' + encodeURIComponent(NETDATA.registry.hostname) + '&url=' + encodeURIComponent(document.location),
5514 async: true,
5515 cache: false,
5516 xhrFields: { withCredentials: true }