]> arthur.barton.de Git - netdata.git/blobdiff - src/web_client.c
Merge pull request #1801 from l2isbad/freeradius_plugin_p26_fix
[netdata.git] / src / web_client.c
index 2765767f6738ce98db67642085d3115da1246937..9a9e2376608a964f19448dea8c3c757603fe3099 100644 (file)
@@ -5,7 +5,8 @@
 #define TOO_BIG_REQUEST 16384
 
 int web_client_timeout = DEFAULT_DISCONNECT_IDLE_WEB_CLIENTS_AFTER_SECONDS;
-int web_donotrack_comply = 0;
+int respect_web_browser_do_not_track_policy = 0;
+char *web_x_frame_options = NULL;
 
 #ifdef NETDATA_WITH_ZLIB
 int web_enable_gzip = 1, web_gzip_level = 3, web_gzip_strategy = Z_DEFAULT_STRATEGY;
@@ -290,12 +291,7 @@ gid_t web_files_gid(void) {
 
 int mysendfile(struct web_client *w, char *filename)
 {
-    static char *web_dir = NULL;
-
-    // initialize our static data
-    if(unlikely(!web_dir)) web_dir = config_get("global", "web files directory", WEB_DIR);
-
-    debug(D_WEB_CLIENT, "%llu: Looking for file '%s/%s'", w->id, web_dir, filename);
+    debug(D_WEB_CLIENT, "%llu: Looking for file '%s/%s'", w->id, netdata_configured_web_dir, filename);
 
     // skip leading slashes
     while (*filename == '/') filename++;
@@ -324,7 +320,7 @@ int mysendfile(struct web_client *w, char *filename)
 
     // access the file
     char webfilename[FILENAME_MAX + 1];
-    snprintfz(webfilename, FILENAME_MAX, "%s/%s", web_dir, filename);
+    snprintfz(webfilename, FILENAME_MAX, "%s/%s", netdata_configured_web_dir, filename);
 
     // check if the file exists
     struct stat stat;
@@ -896,7 +892,7 @@ int web_client_api_request_v1_badge(struct web_client *w, char *url) {
     if(!st) st = rrdset_find_byname(chart);
     if(!st) {
         buffer_no_cacheable(w->response.data);
-        buffer_svg(w->response.data, "chart not found", 0, "", NULL, NULL, 1, -1);
+        buffer_svg(w->response.data, "chart not found", NAN, "", NULL, NULL, -1);
         ret = 200;
         goto cleanup;
     }
@@ -906,18 +902,18 @@ int web_client_api_request_v1_badge(struct web_client *w, char *url) {
         rc = rrdcalc_find(st, alarm);
         if (!rc) {
             buffer_no_cacheable(w->response.data);
-            buffer_svg(w->response.data, "alarm not found", 0, "", NULL, NULL, 1, -1);
+            buffer_svg(w->response.data, "alarm not found", NAN, "", NULL, NULL, -1);
             ret = 200;
             goto cleanup;
         }
     }
 
-    long long multiply  = (multiply_str  && *multiply_str )?atol(multiply_str):1;
-    long long divide    = (divide_str    && *divide_str   )?atol(divide_str):1;
-    long long before    = (before_str    && *before_str   )?atol(before_str):0;
-    long long after     = (after_str     && *after_str    )?atol(after_str):-st->update_every;
-    int       points    = (points_str    && *points_str   )?atoi(points_str):1;
-    int       precision = (precision_str && *precision_str)?atoi(precision_str):-1;
+    long long multiply  = (multiply_str  && *multiply_str )?str2l(multiply_str):1;
+    long long divide    = (divide_str    && *divide_str   )?str2l(divide_str):1;
+    long long before    = (before_str    && *before_str   )?str2l(before_str):0;
+    long long after     = (after_str     && *after_str    )?str2l(after_str):-st->update_every;
+    int       points    = (points_str    && *points_str   )?str2i(points_str):1;
+    int       precision = (precision_str && *precision_str)?str2i(precision_str):-1;
 
     if(!multiply) multiply = 1;
     if(!divide) divide = 1;
@@ -934,7 +930,7 @@ int web_client_api_request_v1_badge(struct web_client *w, char *url) {
             }
         }
         else {
-            refresh = atoi(refresh_str);
+            refresh = str2i(refresh_str);
             if(refresh < 0) refresh = -refresh;
         }
     }
@@ -982,9 +978,6 @@ int web_client_api_request_v1_badge(struct web_client *w, char *url) {
             );
 
     if(rc) {
-        calculated_number n = rc->value;
-        if(isnan(n) || isinf(n)) n = 0;
-
         if (refresh > 0) {
             buffer_sprintf(w->response.header, "Refresh: %d\r\n", refresh);
             w->response.data->expires = now_realtime_sec() + refresh;
@@ -1020,19 +1013,18 @@ int web_client_api_request_v1_badge(struct web_client *w, char *url) {
         }
 
         buffer_svg(w->response.data,
-                   label,
-                   rc->value * multiply / divide,
-                   units,
-                   label_color,
-                   value_color,
-                   0,
-                   precision);
+                label,
+                (isnan(rc->value)||isinf(rc->value)) ? rc->value : rc->value * multiply / divide,
+                units,
+                label_color,
+                value_color,
+                precision);
         ret = 200;
     }
     else {
         time_t latest_timestamp = 0;
         int value_is_null = 1;
-        calculated_number n = 0;
+        calculated_number n = NAN;
         ret = 500;
 
         // if the collected value is too old, don't calculate its value
@@ -1065,13 +1057,12 @@ int web_client_api_request_v1_badge(struct web_client *w, char *url) {
 
         // render the badge
         buffer_svg(w->response.data,
-                   label,
-                   n * multiply / divide,
-                   units,
-                   label_color,
-                   value_color,
-                   value_is_null,
-                   precision);
+                label,
+                (value_is_null)?NAN:(n * multiply / divide),
+                units,
+                label_color,
+                value_color,
+                precision);
     }
 
 cleanup:
@@ -1192,9 +1183,9 @@ int web_client_api_request_v1_data(struct web_client *w, char *url)
         goto cleanup;
     }
 
-    long long before = (before_str && *before_str)?atol(before_str):0;
-    long long after  = (after_str  && *after_str) ?atol(after_str):0;
-    int       points = (points_str && *points_str)?atoi(points_str):0;
+    long long before = (before_str && *before_str)?str2l(before_str):0;
+    long long after  = (after_str  && *after_str) ?str2l(after_str):0;
+    int       points = (points_str && *points_str)?str2i(points_str):0;
 
     debug(D_WEB_CLIENT, "%llu: API command 'data' for chart '%s', dimensions '%s', after '%lld', before '%lld', points '%d', group '%d', format '%u', options '0x%08x'"
             , w->id
@@ -1357,7 +1348,7 @@ int web_client_api_request_v1_registry(struct web_client *w, char *url)
 #endif /* NETDATA_INTERNAL_CHECKS */
     }
 
-    if(web_donotrack_comply && w->donottrack) {
+    if(respect_web_browser_do_not_track_policy && w->donottrack) {
         buffer_flush(w->response.data);
         buffer_sprintf(w->response.data, "Your web browser is sending 'DNT: 1' (Do Not Track). The registry requires persistent cookies on your browser to work.");
         return 400;
@@ -1550,13 +1541,13 @@ int web_client_api_old_data_request(struct web_client *w, char *url, int datasou
     if(url) {
         // parse the lines required
         tok = mystrsep(&url, "/");
-        if(tok) lines = atoi(tok);
+        if(tok) lines = str2i(tok);
         if(lines < 1) lines = 1;
     }
     if(url) {
         // parse the group count required
         tok = mystrsep(&url, "/");
-        if(tok && *tok) group_count = atoi(tok);
+        if(tok && *tok) group_count = str2i(tok);
         if(group_count < 1) group_count = 1;
         //if(group_count > save_history / 20) group_count = save_history / 20;
     }
@@ -1573,13 +1564,13 @@ int web_client_api_old_data_request(struct web_client *w, char *url, int datasou
     if(url) {
         // parse after time
         tok = mystrsep(&url, "/");
-        if(tok && *tok) after = strtoul(tok, NULL, 10);
+        if(tok && *tok) after = str2ul(tok);
         if(after < 0) after = 0;
     }
     if(url) {
         // parse before time
         tok = mystrsep(&url, "/");
-        if(tok && *tok) before = strtoul(tok, NULL, 10);
+        if(tok && *tok) before = str2ul(tok);
         if(before < 0) before = 0;
     }
     if(url) {
@@ -1834,7 +1825,7 @@ static inline char *http_header_parse(struct web_client *w, char *s) {
         if(strcasestr(v, "keep-alive"))
             w->keepalive = 1;
     }
-    else if(web_donotrack_comply && hash == hash_donottrack && !strcasecmp(s, "DNT")) {
+    else if(respect_web_browser_do_not_track_policy && hash == hash_donottrack && !strcasecmp(s, "DNT")) {
         if(*v == '0') w->donottrack = 0;
         else if(*v == '1') w->donottrack = 1;
     }
@@ -1936,6 +1927,148 @@ static inline int http_request_validate(struct web_client *w) {
     return -3;
 }
 
+static inline void web_client_send_http_header(struct web_client *w) {
+    if(unlikely(w->response.code != 200))
+        buffer_no_cacheable(w->response.data);
+
+    // set a proper expiration date, if not already set
+    if(unlikely(!w->response.data->expires)) {
+        if(w->response.data->options & WB_CONTENT_NO_CACHEABLE)
+            w->response.data->expires = w->tv_ready.tv_sec + rrd_update_every;
+        else
+            w->response.data->expires = w->tv_ready.tv_sec + 86400;
+    }
+
+    // prepare the HTTP response header
+    debug(D_WEB_CLIENT, "%llu: Generating HTTP header with response %d.", w->id, w->response.code);
+
+    const char *content_type_string = web_content_type_to_string(w->response.data->contenttype);
+    const char *code_msg = web_response_code_to_string(w->response.code);
+
+    // prepare the last modified and expiration dates
+    char date[32], edate[32];
+    {
+        struct tm tmbuf, *tm;
+
+        tm = gmtime_r(&w->response.data->date, &tmbuf);
+        strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %Z", tm);
+
+        tm = gmtime_r(&w->response.data->expires, &tmbuf);
+        strftime(edate, sizeof(edate), "%a, %d %b %Y %H:%M:%S %Z", tm);
+    }
+
+    buffer_sprintf(w->response.header_output,
+            "HTTP/1.1 %d %s\r\n"
+                    "Connection: %s\r\n"
+                    "Server: NetData Embedded HTTP Server\r\n"
+                    "Access-Control-Allow-Origin: %s\r\n"
+                    "Access-Control-Allow-Credentials: true\r\n"
+                    "Content-Type: %s\r\n"
+                    "Date: %s\r\n"
+                   , w->response.code, code_msg
+                   , w->keepalive?"keep-alive":"close"
+                   , w->origin
+                   , content_type_string
+                   , date
+    );
+
+    if(unlikely(web_x_frame_options))
+        buffer_sprintf(w->response.header_output, "X-Frame-Options: %s\r\n", web_x_frame_options);
+
+    if(w->cookie1[0] || w->cookie2[0]) {
+        if(w->cookie1[0]) {
+            buffer_sprintf(w->response.header_output,
+                    "Set-Cookie: %s\r\n",
+                    w->cookie1);
+        }
+
+        if(w->cookie2[0]) {
+            buffer_sprintf(w->response.header_output,
+                    "Set-Cookie: %s\r\n",
+                    w->cookie2);
+        }
+
+        if(respect_web_browser_do_not_track_policy)
+            buffer_sprintf(w->response.header_output,
+                    "Tk: T;cookies\r\n");
+    }
+    else {
+        if(respect_web_browser_do_not_track_policy) {
+            if(w->tracking_required)
+                buffer_sprintf(w->response.header_output,
+                        "Tk: T;cookies\r\n");
+            else
+                buffer_sprintf(w->response.header_output,
+                        "Tk: N\r\n");
+        }
+    }
+
+    if(w->mode == WEB_CLIENT_MODE_OPTIONS) {
+        buffer_strcat(w->response.header_output,
+                "Access-Control-Allow-Methods: GET, OPTIONS\r\n"
+                        "Access-Control-Allow-Headers: accept, x-requested-with, origin, content-type, cookie, pragma, cache-control\r\n"
+                        "Access-Control-Max-Age: 1209600\r\n" // 86400 * 14
+        );
+    }
+    else {
+        buffer_sprintf(w->response.header_output,
+                "Cache-Control: %s\r\n"
+                        "Expires: %s\r\n",
+                (w->response.data->options & WB_CONTENT_NO_CACHEABLE)?"no-cache":"public",
+                edate);
+    }
+
+    // copy a possibly available custom header
+    if(unlikely(buffer_strlen(w->response.header)))
+        buffer_strcat(w->response.header_output, buffer_tostring(w->response.header));
+
+    // headers related to the transfer method
+    if(likely(w->response.zoutput)) {
+        buffer_strcat(w->response.header_output,
+                "Content-Encoding: gzip\r\n"
+                        "Transfer-Encoding: chunked\r\n"
+        );
+    }
+    else {
+        if(likely((w->response.data->len || w->response.rlen))) {
+            // we know the content length, put it
+            buffer_sprintf(w->response.header_output, "Content-Length: %zu\r\n", w->response.data->len? w->response.data->len: w->response.rlen);
+        }
+        else {
+            // we don't know the content length, disable keep-alive
+            w->keepalive = 0;
+        }
+    }
+
+    // end of HTTP header
+    buffer_strcat(w->response.header_output, "\r\n");
+
+    // sent the HTTP header
+    debug(D_WEB_DATA, "%llu: Sending response HTTP header of size %zu: '%s'"
+          , w->id
+          , buffer_strlen(w->response.header_output)
+          , buffer_tostring(w->response.header_output)
+    );
+
+    web_client_crock_socket(w);
+
+    ssize_t bytes = send(w->ofd, buffer_tostring(w->response.header_output), buffer_strlen(w->response.header_output), 0);
+    if(bytes != (ssize_t) buffer_strlen(w->response.header_output)) {
+        if(bytes > 0)
+            w->stats_sent_bytes += bytes;
+
+        debug(D_WEB_CLIENT, "%llu: HTTP Header failed to be sent (I sent %zu bytes but the system sent %zd bytes). Closing web client."
+              , w->id
+              , buffer_strlen(w->response.header_output)
+              , bytes);
+
+        WEB_CLIENT_IS_DEAD(w);
+        return;
+    }
+    else
+        w->stats_sent_bytes += bytes;
+}
+
 void web_client_process(struct web_client *w) {
     static uint32_t
             hash_api = 0,
@@ -1969,7 +2102,6 @@ void web_client_process(struct web_client *w) {
     }
 
     int code = 500;
-    ssize_t bytes;
 
     int what_to_do = http_request_validate(w);
 
@@ -2170,142 +2302,7 @@ void web_client_process(struct web_client *w) {
     if(unlikely(!w->response.data->date))
         w->response.data->date = w->tv_ready.tv_sec;
 
-    if(unlikely(code != 200))
-        buffer_no_cacheable(w->response.data);
-
-    // set a proper expiration date, if not already set
-    if(unlikely(!w->response.data->expires)) {
-        if(w->response.data->options & WB_CONTENT_NO_CACHEABLE)
-            w->response.data->expires = w->tv_ready.tv_sec + rrd_update_every;
-        else
-            w->response.data->expires = w->tv_ready.tv_sec + 86400;
-    }
-
-    // prepare the HTTP response header
-    debug(D_WEB_CLIENT, "%llu: Generating HTTP header with response %d.", w->id, code);
-
-    const char *content_type_string = web_content_type_to_string(w->response.data->contenttype);
-    const char *code_msg = web_response_code_to_string(code);
-
-    // prepare the last modified and expiration dates
-    char date[32], edate[32];
-    {
-        struct tm tmbuf, *tm;
-
-        tm = gmtime_r(&w->response.data->date, &tmbuf);
-        strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %Z", tm);
-
-        tm = gmtime_r(&w->response.data->expires, &tmbuf);
-        strftime(edate, sizeof(edate), "%a, %d %b %Y %H:%M:%S %Z", tm);
-    }
-
-    buffer_sprintf(w->response.header_output,
-        "HTTP/1.1 %d %s\r\n"
-        "Connection: %s\r\n"
-        "Server: NetData Embedded HTTP Server\r\n"
-        "Access-Control-Allow-Origin: %s\r\n"
-        "Access-Control-Allow-Credentials: true\r\n"
-        "Content-Type: %s\r\n"
-        "Date: %s\r\n"
-        , code, code_msg
-        , w->keepalive?"keep-alive":"close"
-        , w->origin
-        , content_type_string
-        , date
-        );
-
-    if(w->cookie1[0] || w->cookie2[0]) {
-        if(w->cookie1[0]) {
-            buffer_sprintf(w->response.header_output,
-               "Set-Cookie: %s\r\n",
-               w->cookie1);
-        }
-
-        if(w->cookie2[0]) {
-            buffer_sprintf(w->response.header_output,
-               "Set-Cookie: %s\r\n",
-               w->cookie2);
-        }
-
-        if(web_donotrack_comply)
-            buffer_sprintf(w->response.header_output,
-               "Tk: T;cookies\r\n");
-    }
-    else {
-        if(web_donotrack_comply) {
-            if(w->tracking_required)
-                buffer_sprintf(w->response.header_output,
-                   "Tk: T;cookies\r\n");
-            else
-                buffer_sprintf(w->response.header_output,
-                   "Tk: N\r\n");
-        }
-    }
-
-    if(w->mode == WEB_CLIENT_MODE_OPTIONS) {
-        buffer_strcat(w->response.header_output,
-            "Access-Control-Allow-Methods: GET, OPTIONS\r\n"
-            "Access-Control-Allow-Headers: accept, x-requested-with, origin, content-type, cookie, pragma, cache-control\r\n"
-            "Access-Control-Max-Age: 1209600\r\n" // 86400 * 14
-            );
-    }
-    else {
-        buffer_sprintf(w->response.header_output,
-            "Cache-Control: %s\r\n"
-            "Expires: %s\r\n",
-            (w->response.data->options & WB_CONTENT_NO_CACHEABLE)?"no-cache":"public",
-            edate);
-    }
-
-    // copy a possibly available custom header
-    if(unlikely(buffer_strlen(w->response.header)))
-        buffer_strcat(w->response.header_output, buffer_tostring(w->response.header));
-
-    // headers related to the transfer method
-    if(likely(w->response.zoutput)) {
-        buffer_strcat(w->response.header_output,
-            "Content-Encoding: gzip\r\n"
-            "Transfer-Encoding: chunked\r\n"
-            );
-    }
-    else {
-        if(likely((w->response.data->len || w->response.rlen))) {
-            // we know the content length, put it
-            buffer_sprintf(w->response.header_output, "Content-Length: %zu\r\n", w->response.data->len? w->response.data->len: w->response.rlen);
-        }
-        else {
-            // we don't know the content length, disable keep-alive
-            w->keepalive = 0;
-        }
-    }
-
-    // end of HTTP header
-    buffer_strcat(w->response.header_output, "\r\n");
-
-    // sent the HTTP header
-    debug(D_WEB_DATA, "%llu: Sending response HTTP header of size %zu: '%s'"
-            , w->id
-            , buffer_strlen(w->response.header_output)
-            , buffer_tostring(w->response.header_output)
-            );
-
-    web_client_crock_socket(w);
-
-    bytes = send(w->ofd, buffer_tostring(w->response.header_output), buffer_strlen(w->response.header_output), 0);
-    if(bytes != (ssize_t) buffer_strlen(w->response.header_output)) {
-        if(bytes > 0)
-            w->stats_sent_bytes += bytes;
-
-        debug(D_WEB_CLIENT, "%llu: HTTP Header failed to be sent (I sent %zu bytes but the system sent %zd bytes). Closing web client."
-            , w->id
-            , buffer_strlen(w->response.header_output)
-            , bytes);
-
-        WEB_CLIENT_IS_DEAD(w);
-        return;
-    }
-    else 
-        w->stats_sent_bytes += bytes;
+    web_client_send_http_header(w);
 
     // enable sending immediately if we have data
     if(w->response.data->len) w->wait_send = 1;