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 }