summaryrefslogtreecommitdiffstats
path: root/web/server/web_client.c
diff options
context:
space:
mode:
Diffstat (limited to 'web/server/web_client.c')
-rw-r--r--web/server/web_client.c702
1 files changed, 489 insertions, 213 deletions
diff --git a/web/server/web_client.c b/web/server/web_client.c
index 5dcff0b0f..1a1d63155 100644
--- a/web/server/web_client.c
+++ b/web/server/web_client.c
@@ -18,12 +18,28 @@ inline int web_client_permission_denied(struct web_client *w) {
return HTTP_RESP_FORBIDDEN;
}
-static inline int web_client_crock_socket(struct web_client *w __maybe_unused) {
+inline int web_client_bearer_required(struct web_client *w) {
+ w->response.data->content_type = CT_TEXT_PLAIN;
+ buffer_flush(w->response.data);
+ buffer_strcat(w->response.data, "An authorization bearer is required to access the resource.");
+ w->response.code = HTTP_RESP_UNAUTHORIZED;
+ return HTTP_RESP_UNAUTHORIZED;
+}
+
+static inline int bad_request_multiple_dashboard_versions(struct web_client *w) {
+ w->response.data->content_type = CT_TEXT_PLAIN;
+ buffer_flush(w->response.data);
+ buffer_strcat(w->response.data, "Multiple dashboard versions given at the URL.");
+ w->response.code = HTTP_RESP_BAD_REQUEST;
+ return HTTP_RESP_BAD_REQUEST;
+}
+
+static inline int web_client_cork_socket(struct web_client *w __maybe_unused) {
#ifdef TCP_CORK
if(likely(web_client_is_corkable(w) && !w->tcp_cork && w->ofd != -1)) {
w->tcp_cork = true;
if(unlikely(setsockopt(w->ofd, IPPROTO_TCP, TCP_CORK, (char *) &w->tcp_cork, sizeof(int)) != 0)) {
- error("%llu: failed to enable TCP_CORK on socket.", w->id);
+ netdata_log_error("%llu: failed to enable TCP_CORK on socket.", w->id);
w->tcp_cork = false;
return -1;
@@ -45,11 +61,12 @@ static inline void web_client_enable_wait_from_ssl(struct web_client *w) {
}
}
-static inline int web_client_uncrock_socket(struct web_client *w __maybe_unused) {
+static inline int web_client_uncork_socket(struct web_client *w __maybe_unused) {
#ifdef TCP_CORK
if(likely(w->tcp_cork && w->ofd != -1)) {
+ w->tcp_cork = false;
if(unlikely(setsockopt(w->ofd, IPPROTO_TCP, TCP_CORK, (char *) &w->tcp_cork, sizeof(int)) != 0)) {
- error("%llu: failed to disable TCP_CORK on socket.", w->id);
+ netdata_log_error("%llu: failed to disable TCP_CORK on socket.", w->id);
w->tcp_cork = true;
return -1;
}
@@ -140,12 +157,14 @@ static void web_client_reset_allocations(struct web_client *w, bool free_all) {
w->response.zinitialized = false;
w->flags &= ~WEB_CLIENT_CHUNKED_TRANSFER;
}
+
+ web_client_reset_path_flags(w);
}
void web_client_request_done(struct web_client *w) {
- web_client_uncrock_socket(w);
+ web_client_uncork_socket(w);
- debug(D_WEB_CLIENT, "%llu: Resetting client.", w->id);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Resetting client.", w->id);
if(likely(buffer_strlen(w->url_as_received))) {
struct timeval tv;
@@ -195,7 +214,7 @@ void web_client_request_done(struct web_client *w) {
}
// access log
- log_access("%llu: %d '[%s]:%s' '%s' (sent/all = %zu/%zu bytes %0.0f%%, prep/sent/total = %0.2f/%0.2f/%0.2f ms) %d '%s'",
+ netdata_log_access("%llu: %d '[%s]:%s' '%s' (sent/all = %zu/%zu bytes %0.0f%%, prep/sent/total = %0.2f/%0.2f/%0.2f ms) %d '%s'",
w->id
, gettid()
, w->client_ip
@@ -214,7 +233,7 @@ void web_client_request_done(struct web_client *w) {
if(unlikely(w->mode == WEB_CLIENT_MODE_FILECOPY)) {
if(w->ifd != w->ofd) {
- debug(D_WEB_CLIENT, "%llu: Closing filecopy input file descriptor %d.", w->id, w->ifd);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Closing filecopy input file descriptor %d.", w->id, w->ifd);
if(web_server_mode != WEB_SERVER_MODE_STATIC_THREADED) {
if (w->ifd != -1){
@@ -275,7 +294,7 @@ static struct {
};
static inline uint8_t contenttype_for_filename(const char *filename) {
- // info("checking filename '%s'", filename);
+ // netdata_log_info("checking filename '%s'", filename);
static int initialized = 0;
int i;
@@ -296,36 +315,172 @@ static inline uint8_t contenttype_for_filename(const char *filename) {
}
if(unlikely(!last_dot || !*last_dot || !last_dot[1])) {
- // info("no extension for filename '%s'", filename);
+ // netdata_log_info("no extension for filename '%s'", filename);
return CT_APPLICATION_OCTET_STREAM;
}
last_dot++;
- // info("extension for filename '%s' is '%s'", filename, last_dot);
+ // netdata_log_info("extension for filename '%s' is '%s'", filename, last_dot);
uint32_t hash = simple_hash(last_dot);
for(i = 0; mime_types[i].extension ; i++) {
if(unlikely(hash == mime_types[i].hash && !strcmp(last_dot, mime_types[i].extension))) {
- // info("matched extension for filename '%s': '%s'", filename, last_dot);
+ // netdata_log_info("matched extension for filename '%s': '%s'", filename, last_dot);
return mime_types[i].contenttype;
}
}
- // info("not matched extension for filename '%s': '%s'", filename, last_dot);
+ // netdata_log_info("not matched extension for filename '%s': '%s'", filename, last_dot);
return CT_APPLICATION_OCTET_STREAM;
}
-static inline int access_to_file_is_not_permitted(struct web_client *w, const char *filename) {
+static int append_slash_to_url_and_redirect(struct web_client *w) {
+ // this function returns a relative redirect
+ // it finds the last path component on the URL and just appends / to it
+ //
+ // So, if the URL is:
+ //
+ // /path/to/file?query_string
+ //
+ // It adds a Location header like this:
+ //
+ // Location: file/?query_string\r\n
+ //
+ // The web browser already knows that it is inside /path/to/
+ // so it converts the path to /path/to/file/ and executes the
+ // request again.
+
+ buffer_strcat(w->response.header, "Location: ");
+ const char *b = buffer_tostring(w->url_as_received);
+ const char *q = strchr(b, '?');
+ if(q && q > b) {
+ const char *e = q - 1;
+ while(e > b && *e != '/') e--;
+ if(*e == '/') e++;
+
+ size_t len = q - e;
+ buffer_strncat(w->response.header, e, len);
+ buffer_strncat(w->response.header, "/", 1);
+ buffer_strcat(w->response.header, q);
+ }
+ else {
+ const char *e = &b[buffer_strlen(w->url_as_received) - 1];
+ while(e > b && *e != '/') e--;
+ if(*e == '/') e++;
+
+ buffer_strcat(w->response.header, e);
+ buffer_strncat(w->response.header, "/", 1);
+ }
+
+ buffer_strncat(w->response.header, "\r\n", 2);
+
w->response.data->content_type = CT_TEXT_HTML;
- buffer_strcat(w->response.data, "Access to file is not permitted: ");
- buffer_strcat_htmlescape(w->response.data, filename);
- return HTTP_RESP_FORBIDDEN;
+ buffer_flush(w->response.data);
+ buffer_strcat(w->response.data,
+ "<!DOCTYPE html><html>"
+ "<body onload=\"window.location.href = window.location.origin + window.location.pathname + '/' + window.location.search + window.location.hash\">"
+ "Redirecting. In case your browser does not support redirection, please click "
+ "<a onclick=\"window.location.href = window.location.origin + window.location.pathname + '/' + window.location.search + window.location.hash\">here</a>."
+ "</body></html>");
+ return HTTP_RESP_MOVED_PERM;
}
// Work around a bug in the CMocka library by removing this function during testing.
#ifndef REMOVE_MYSENDFILE
-int mysendfile(struct web_client *w, char *filename) {
- debug(D_WEB_CLIENT, "%llu: Looking for file '%s/%s'", w->id, netdata_configured_web_dir, filename);
+
+static inline int dashboard_version(struct web_client *w) {
+ if(!web_client_flag_check(w, WEB_CLIENT_FLAG_PATH_WITH_VERSION))
+ return -1;
+
+ if(web_client_flag_check(w, WEB_CLIENT_FLAG_PATH_IS_V0))
+ return 0;
+ if(web_client_flag_check(w, WEB_CLIENT_FLAG_PATH_IS_V1))
+ return 1;
+ if(web_client_flag_check(w, WEB_CLIENT_FLAG_PATH_IS_V2))
+ return 2;
+
+ return -1;
+}
+
+static bool find_filename_to_serve(const char *filename, char *dst, size_t dst_len, struct stat *statbuf, struct web_client *w, bool *is_dir) {
+ int d_version = dashboard_version(w);
+ bool has_extension = web_client_flag_check(w, WEB_CLIENT_FLAG_PATH_HAS_FILE_EXTENSION);
+
+ int fallback = 0;
+
+ if(has_extension) {
+ if(d_version == -1)
+ snprintfz(dst, dst_len, "%s/%s", netdata_configured_web_dir, filename);
+ else {
+ // check if the filename or directory exists
+ // fallback to the same path without the dashboard version otherwise
+ snprintfz(dst, dst_len, "%s/v%d/%s", netdata_configured_web_dir, d_version, filename);
+ fallback = 1;
+ }
+ }
+ else if(d_version != -1) {
+ if(filename && *filename) {
+ // check if the filename exists
+ // fallback to /vN/index.html otherwise
+ snprintfz(dst, dst_len, "%s/%s", netdata_configured_web_dir, filename);
+ fallback = 2;
+ }
+ else {
+ if(filename && *filename)
+ web_client_flag_set(w, WEB_CLIENT_FLAG_PATH_HAS_TRAILING_SLASH);
+ snprintfz(dst, dst_len, "%s/v%d", netdata_configured_web_dir, d_version);
+ }
+ }
+ else {
+ // check if filename exists
+ // this is needed to serve {filename}/index.html, in case a user puts a html file into a directory
+ // fallback to /index.html otherwise
+ snprintfz(dst, dst_len, "%s/%s", netdata_configured_web_dir, filename);
+ fallback = 3;
+ }
+
+ if (stat(dst, statbuf) != 0) {
+ if(fallback == 1) {
+ snprintfz(dst, dst_len, "%s/%s", netdata_configured_web_dir, filename);
+ if (stat(dst, statbuf) != 0)
+ return false;
+ }
+ else if(fallback == 2) {
+ if(filename && *filename)
+ web_client_flag_set(w, WEB_CLIENT_FLAG_PATH_HAS_TRAILING_SLASH);
+ snprintfz(dst, dst_len, "%s/v%d", netdata_configured_web_dir, d_version);
+ if (stat(dst, statbuf) != 0)
+ return false;
+ }
+ else if(fallback == 3) {
+ if(filename && *filename)
+ web_client_flag_set(w, WEB_CLIENT_FLAG_PATH_HAS_TRAILING_SLASH);
+ snprintfz(dst, dst_len, "%s", netdata_configured_web_dir);
+ if (stat(dst, statbuf) != 0)
+ return false;
+ }
+ else
+ return false;
+ }
+
+ if((statbuf->st_mode & S_IFMT) == S_IFDIR) {
+ size_t len = strlen(dst);
+ if(len > dst_len - 11)
+ return false;
+
+ strncpyz(&dst[len], "/index.html", dst_len - len);
+
+ if (stat(dst, statbuf) != 0)
+ return false;
+
+ *is_dir = true;
+ }
+
+ return true;
+}
+
+static int mysendfile(struct web_client *w, char *filename) {
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Looking for file '%s/%s'", w->id, netdata_configured_web_dir, filename);
if(!web_client_can_access_dashboard(w))
return web_client_permission_denied(w);
@@ -337,7 +492,7 @@ int mysendfile(struct web_client *w, char *filename) {
char *s;
for(s = filename; *s ;s++) {
if( !isalnum(*s) && *s != '/' && *s != '.' && *s != '-' && *s != '_') {
- debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not acceptable.", w->id, filename);
+ netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not acceptable.", w->id, filename);
w->response.data->content_type = CT_TEXT_HTML;
buffer_sprintf(w->response.data, "Filename contains invalid characters: ");
buffer_strcat_htmlescape(w->response.data, filename);
@@ -347,7 +502,7 @@ int mysendfile(struct web_client *w, char *filename) {
// if the filename contains a double dot refuse to serve it
if(strstr(filename, "..") != 0) {
- debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not acceptable.", w->id, filename);
+ netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not acceptable.", w->id, filename);
w->response.data->content_type = CT_TEXT_HTML;
buffer_strcat(w->response.data, "Relative filenames are not supported: ");
buffer_strcat_htmlescape(w->response.data, filename);
@@ -355,60 +510,45 @@ int mysendfile(struct web_client *w, char *filename) {
}
// find the physical file on disk
- char webfilename[FILENAME_MAX + 1];
- snprintfz(webfilename, FILENAME_MAX, "%s/%s", netdata_configured_web_dir, filename);
-
+ bool is_dir = false;
+ char web_filename[FILENAME_MAX + 1];
struct stat statbuf;
- int done = 0;
- while(!done) {
- // check if the file exists
- if (lstat(webfilename, &statbuf) != 0) {
- debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not found.", w->id, webfilename);
- w->response.data->content_type = CT_TEXT_HTML;
- buffer_strcat(w->response.data, "File does not exist, or is not accessible: ");
- buffer_strcat_htmlescape(w->response.data, webfilename);
- return HTTP_RESP_NOT_FOUND;
- }
-
- if ((statbuf.st_mode & S_IFMT) == S_IFDIR) {
- snprintfz(webfilename, FILENAME_MAX, "%s/%s/index.html", netdata_configured_web_dir, filename);
- continue;
- }
-
- if ((statbuf.st_mode & S_IFMT) != S_IFREG) {
- error("%llu: File '%s' is not a regular file. Access Denied.", w->id, webfilename);
- return access_to_file_is_not_permitted(w, webfilename);
- }
-
- done = 1;
+ if(!find_filename_to_serve(filename, web_filename, FILENAME_MAX, &statbuf, w, &is_dir)) {
+ w->response.data->content_type = CT_TEXT_HTML;
+ buffer_strcat(w->response.data, "File does not exist, or is not accessible: ");
+ buffer_strcat_htmlescape(w->response.data, web_filename);
+ return HTTP_RESP_NOT_FOUND;
}
+ if(is_dir && !web_client_flag_check(w, WEB_CLIENT_FLAG_PATH_HAS_TRAILING_SLASH))
+ return append_slash_to_url_and_redirect(w);
+
// open the file
- w->ifd = open(webfilename, O_NONBLOCK, O_RDONLY);
+ w->ifd = open(web_filename, O_NONBLOCK, O_RDONLY);
if(w->ifd == -1) {
w->ifd = w->ofd;
if(errno == EBUSY || errno == EAGAIN) {
- error("%llu: File '%s' is busy, sending 307 Moved Temporarily to force retry.", w->id, webfilename);
+ netdata_log_error("%llu: File '%s' is busy, sending 307 Moved Temporarily to force retry.", w->id, web_filename);
w->response.data->content_type = CT_TEXT_HTML;
buffer_sprintf(w->response.header, "Location: /%s\r\n", filename);
buffer_strcat(w->response.data, "File is currently busy, please try again later: ");
- buffer_strcat_htmlescape(w->response.data, webfilename);
+ buffer_strcat_htmlescape(w->response.data, web_filename);
return HTTP_RESP_REDIR_TEMP;
}
else {
- error("%llu: Cannot open file '%s'.", w->id, webfilename);
+ netdata_log_error("%llu: Cannot open file '%s'.", w->id, web_filename);
w->response.data->content_type = CT_TEXT_HTML;
buffer_strcat(w->response.data, "Cannot open file: ");
- buffer_strcat_htmlescape(w->response.data, webfilename);
+ buffer_strcat_htmlescape(w->response.data, web_filename);
return HTTP_RESP_NOT_FOUND;
}
}
sock_setnonblock(w->ifd);
- w->response.data->content_type = contenttype_for_filename(webfilename);
- debug(D_WEB_CLIENT_ACCESS, "%llu: Sending file '%s' (%"PRId64" bytes, ifd %d, ofd %d).", w->id, webfilename, (int64_t)statbuf.st_size, w->ifd, w->ofd);
+ w->response.data->content_type = contenttype_for_filename(web_filename);
+ netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: Sending file '%s' (%"PRId64" bytes, ifd %d, ofd %d).", w->id, web_filename, (int64_t)statbuf.st_size, w->ifd, w->ofd);
w->mode = WEB_CLIENT_MODE_FILECOPY;
web_client_enable_wait_receive(w);
@@ -429,12 +569,12 @@ int mysendfile(struct web_client *w, char *filename) {
void web_client_enable_deflate(struct web_client *w, int gzip) {
if(unlikely(w->response.zinitialized)) {
- debug(D_DEFLATE, "%llu: Compression has already be initialized for this client.", w->id);
+ netdata_log_debug(D_DEFLATE, "%llu: Compression has already be initialized for this client.", w->id);
return;
}
if(unlikely(w->response.sent)) {
- error("%llu: Cannot enable compression in the middle of a conversation.", w->id);
+ netdata_log_error("%llu: Cannot enable compression in the middle of a conversation.", w->id);
return;
}
@@ -455,13 +595,13 @@ void web_client_enable_deflate(struct web_client *w, int gzip) {
w->response.zstream.opaque = Z_NULL;
// if(deflateInit(&w->response.zstream, Z_DEFAULT_COMPRESSION) != Z_OK) {
-// error("%llu: Failed to initialize zlib. Proceeding without compression.", w->id);
+// netdata_log_error("%llu: Failed to initialize zlib. Proceeding without compression.", w->id);
// return;
// }
// Select GZIP compression: windowbits = 15 + 16 = 31
if(deflateInit2(&w->response.zstream, web_gzip_level, Z_DEFLATED, 15 + ((gzip)?16:0), 8, web_gzip_strategy) != Z_OK) {
- error("%llu: Failed to initialize zlib. Proceeding without compression.", w->id);
+ netdata_log_error("%llu: Failed to initialize zlib. Proceeding without compression.", w->id);
return;
}
@@ -470,7 +610,7 @@ void web_client_enable_deflate(struct web_client *w, int gzip) {
w->response.zinitialized = true;
w->flags |= WEB_CLIENT_CHUNKED_TRANSFER;
- debug(D_DEFLATE, "%llu: Initialized compression.", w->id);
+ netdata_log_debug(D_DEFLATE, "%llu: Initialized compression.", w->id);
}
void buffer_data_options2string(BUFFER *wb, uint32_t options) {
@@ -571,7 +711,7 @@ int web_client_api_request(RRDHOST *host, struct web_client *w, char *url_path_f
// get the api version
char *tok = strsep_skip_consecutive_separators(&url_path_fragment, "/");
if(tok && *tok) {
- debug(D_WEB_CLIENT, "%llu: Searching for API version '%s'.", w->id, tok);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Searching for API version '%s'.", w->id, tok);
if(strcmp(tok, "v2") == 0)
return web_client_api_request_v2(host, w, url_path_fragment);
else if(strcmp(tok, "v1") == 0)
@@ -603,7 +743,7 @@ const char *web_content_type_to_string(HTTP_CONTENT_TYPE content_type) {
return "application/json; charset=utf-8";
case CT_APPLICATION_X_JAVASCRIPT:
- return "application/x-javascript; charset=utf-8";
+ return "application/javascript; charset=utf-8";
case CT_TEXT_CSS:
return "text/css; charset=utf-8";
@@ -656,35 +796,159 @@ const char *web_content_type_to_string(HTTP_CONTENT_TYPE content_type) {
case CT_PROMETHEUS:
return "text/plain; version=0.0.4";
+ case CT_AUDIO_MPEG:
+ return "audio/mpeg";
+
+ case CT_AUDIO_OGG:
+ return "audio/ogg";
+
+ case CT_VIDEO_MP4:
+ return "video/mp4";
+
+ case CT_APPLICATION_PDF:
+ return "application/pdf";
+
+ case CT_APPLICATION_ZIP:
+ return "application/zip";
+
default:
case CT_TEXT_PLAIN:
return "text/plain; charset=utf-8";
}
}
-
const char *web_response_code_to_string(int code) {
switch(code) {
- case HTTP_RESP_OK:
+ case 100:
+ return "Continue";
+ case 101:
+ return "Switching Protocols";
+ case 102:
+ return "Processing";
+ case 103:
+ return "Early Hints";
+
+ case 200:
return "OK";
-
- case HTTP_RESP_MOVED_PERM:
+ case 201:
+ return "Created";
+ case 202:
+ return "Accepted";
+ case 203:
+ return "Non-Authoritative Information";
+ case 204:
+ return "No Content";
+ case 205:
+ return "Reset Content";
+ case 206:
+ return "Partial Content";
+ case 207:
+ return "Multi-Status";
+ case 208:
+ return "Already Reported";
+ case 226:
+ return "IM Used";
+
+ case 300:
+ return "Multiple Choices";
+ case 301:
return "Moved Permanently";
-
- case HTTP_RESP_REDIR_TEMP:
+ case 302:
+ return "Found";
+ case 303:
+ return "See Other";
+ case 304:
+ return "Not Modified";
+ case 305:
+ return "Use Proxy";
+ case 306:
+ return "Switch Proxy";
+ case 307:
return "Temporary Redirect";
+ case 308:
+ return "Permanent Redirect";
- case HTTP_RESP_BAD_REQUEST:
+ case 400:
return "Bad Request";
-
- case HTTP_RESP_FORBIDDEN:
+ case 401:
+ return "Unauthorized";
+ case 402:
+ return "Payment Required";
+ case 403:
return "Forbidden";
-
- case HTTP_RESP_NOT_FOUND:
+ case 404:
return "Not Found";
-
- case HTTP_RESP_PRECOND_FAIL:
- return "Preconditions Failed";
+ case 405:
+ return "Method Not Allowed";
+ case 406:
+ return "Not Acceptable";
+ case 407:
+ return "Proxy Authentication Required";
+ case 408:
+ return "Request Timeout";
+ case 409:
+ return "Conflict";
+ case 410:
+ return "Gone";
+ case 411:
+ return "Length Required";
+ case 412:
+ return "Precondition Failed";
+ case 413:
+ return "Payload Too Large";
+ case 414:
+ return "URI Too Long";
+ case 415:
+ return "Unsupported Media Type";
+ case 416:
+ return "Range Not Satisfiable";
+ case 417:
+ return "Expectation Failed";
+ case 418:
+ return "I'm a teapot";
+ case 421:
+ return "Misdirected Request";
+ case 422:
+ return "Unprocessable Entity";
+ case 423:
+ return "Locked";
+ case 424:
+ return "Failed Dependency";
+ case 425:
+ return "Too Early";
+ case 426:
+ return "Upgrade Required";
+ case 428:
+ return "Precondition Required";
+ case 429:
+ return "Too Many Requests";
+ case 431:
+ return "Request Header Fields Too Large";
+ case 451:
+ return "Unavailable For Legal Reasons";
+
+ case 500:
+ return "Internal Server Error";
+ case 501:
+ return "Not Implemented";
+ case 502:
+ return "Bad Gateway";
+ case 503:
+ return "Service Unavailable";
+ case 504:
+ return "Gateway Timeout";
+ case 505:
+ return "HTTP Version Not Supported";
+ case 506:
+ return "Variant Also Negotiates";
+ case 507:
+ return "Insufficient Storage";
+ case 508:
+ return "Loop Detected";
+ case 510:
+ return "Not Extended";
+ case 511:
+ return "Network Authentication Required";
default:
if(code >= 100 && code < 200)
@@ -697,7 +961,7 @@ const char *web_response_code_to_string(int code) {
return "Redirection";
if(code >= 400 && code < 500)
- return "Bad Request";
+ return "Client Error";
if(code >= 500 && code < 600)
return "Server Error";
@@ -708,7 +972,7 @@ const char *web_response_code_to_string(int code) {
static inline char *http_header_parse(struct web_client *w, char *s, int parse_useragent) {
static uint32_t hash_origin = 0, hash_connection = 0, hash_donottrack = 0, hash_useragent = 0,
- hash_authorization = 0, hash_host = 0, hash_forwarded_proto = 0, hash_forwarded_host = 0;
+ hash_authorization = 0, hash_host = 0, hash_forwarded_host = 0;
static uint32_t hash_accept_encoding = 0;
if(unlikely(!hash_origin)) {
@@ -719,7 +983,6 @@ static inline char *http_header_parse(struct web_client *w, char *s, int parse_u
hash_useragent = simple_uhash("User-Agent");
hash_authorization = simple_uhash("X-Auth-Token");
hash_host = simple_uhash("Host");
- hash_forwarded_proto = simple_uhash("X-Forwarded-Proto");
hash_forwarded_host = simple_uhash("X-Forwarded-Host");
}
@@ -783,10 +1046,6 @@ static inline char *http_header_parse(struct web_client *w, char *s, int parse_u
// web_client_enable_deflate(w, 0);
}
}
- else if(hash == hash_forwarded_proto && !strcasecmp(s, "X-Forwarded-Proto")) {
- if(strcasestr(v, "https"))
- w->flags |= WEB_CLIENT_FLAG_PROXY_HTTPS;
- }
else if(hash == hash_forwarded_host && !strcasecmp(s, "X-Forwarded-Host")) {
char buffer[NI_MAXHOST];
strncpyz(buffer, v, ((size_t)(ve - v) < sizeof(buffer) - 1 ? (size_t)(ve - v) : sizeof(buffer) - 1));
@@ -850,7 +1109,7 @@ static inline char *web_client_valid_method(struct web_client *w, char *s) {
memcpy(hostname,"not available",13);
hostname[13] = 0x00;
}
- error("The server is configured to always use encrypted connections, please enable the SSL on child with hostname '%s'.",hostname);
+ netdata_log_error("The server is configured to always use encrypted connections, please enable the SSL on child with hostname '%s'.",hostname);
s = NULL;
}
#endif
@@ -891,7 +1150,7 @@ static inline HTTP_VALIDATION http_request_validate(struct web_client *w) {
is_it_valid = url_is_request_complete(s, &s[last_pos], w->header_parse_last_size, &w->post_payload, &w->post_payload_size);
if(!is_it_valid) {
if(w->header_parse_tries > HTTP_REQ_MAX_HEADER_FETCH_TRIES) {
- info("Disabling slow client after %zu attempts to read the request (%zu bytes received)", w->header_parse_tries, buffer_strlen(w->response.data));
+ netdata_log_info("Disabling slow client after %zu attempts to read the request (%zu bytes received)", w->header_parse_tries, buffer_strlen(w->response.data));
w->header_parse_tries = 0;
w->header_parse_last_size = 0;
web_client_disable_wait_receive(w);
@@ -1017,16 +1276,19 @@ void web_client_build_http_header(struct web_client *w) {
if(unlikely(w->response.code != HTTP_RESP_OK))
buffer_no_cacheable(w->response.data);
+ if(unlikely(!w->response.data->date))
+ w->response.data->date = now_realtime_sec();
+
// 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->timings.tv_ready.tv_sec + localhost->rrd_update_every;
+ w->response.data->expires = w->response.data->date + localhost->rrd_update_every;
else
- w->response.data->expires = w->timings.tv_ready.tv_sec + 86400;
+ w->response.data->expires = w->response.data->date + 86400;
}
// prepare the HTTP response header
- debug(D_WEB_CLIENT, "%llu: Generating HTTP header with response %d.", w->id, w->response.code);
+ netdata_log_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->content_type);
const char *code_msg = web_response_code_to_string(w->response.code);
@@ -1043,14 +1305,16 @@ void web_client_build_http_header(struct web_client *w) {
strftime(edate, sizeof(edate), "%a, %d %b %Y %H:%M:%S %Z", tm);
}
- if (w->response.code == HTTP_RESP_MOVED_PERM) {
+ if (w->response.code == HTTP_RESP_HTTPS_UPGRADE) {
buffer_sprintf(w->response.header_output,
"HTTP/1.1 %d %s\r\n"
"Location: https://%s%s\r\n",
w->response.code, code_msg,
w->server_host ? w->server_host : "",
buffer_tostring(w->url_as_received));
- }else {
+ w->response.code = HTTP_RESP_MOVED_PERM;
+ }
+ else {
buffer_sprintf(w->response.header_output,
"HTTP/1.1 %d %s\r\n"
"Connection: %s\r\n"
@@ -1131,13 +1395,13 @@ static inline void web_client_send_http_header(struct web_client *w) {
web_client_build_http_header(w);
// sent the HTTP header
- debug(D_WEB_DATA, "%llu: Sending response HTTP header of size %zu: '%s'"
+ netdata_log_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);
+ web_client_cork_socket(w);
size_t count = 0;
ssize_t bytes;
@@ -1152,7 +1416,7 @@ static inline void web_client_send_http_header(struct web_client *w) {
count++;
if(count > 100 || (errno != EAGAIN && errno != EWOULDBLOCK)) {
- error("Cannot send HTTP headers to web client.");
+ netdata_log_error("Cannot send HTTP headers to web client.");
break;
}
}
@@ -1163,7 +1427,7 @@ static inline void web_client_send_http_header(struct web_client *w) {
count++;
if(count > 100 || (errno != EAGAIN && errno != EWOULDBLOCK)) {
- error("Cannot send HTTP headers to web client.");
+ netdata_log_error("Cannot send HTTP headers to web client.");
break;
}
}
@@ -1173,7 +1437,7 @@ static inline void web_client_send_http_header(struct web_client *w) {
count++;
if(count > 100 || (errno != EAGAIN && errno != EWOULDBLOCK)) {
- error("Cannot send HTTP headers to web client.");
+ netdata_log_error("Cannot send HTTP headers to web client.");
break;
}
}
@@ -1184,8 +1448,7 @@ static inline void web_client_send_http_header(struct web_client *w) {
w->statistics.sent_bytes += bytes;
if (bytes < 0) {
-
- error("HTTP headers failed to be sent (I sent %zu bytes but the system sent %zd bytes). Closing web client."
+ netdata_log_error("HTTP headers failed to be sent (I sent %zu bytes but the system sent %zd bytes). Closing web client."
, buffer_strlen(w->response.header_output)
, bytes);
@@ -1212,7 +1475,7 @@ static inline int web_client_switch_host(RRDHOST *host, struct web_client *w, ch
char *tok = strsep_skip_consecutive_separators(&url, "/");
if(tok && *tok) {
- debug(D_WEB_CLIENT, "%llu: Searching for host with name '%s'.", w->id, tok);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Searching for host with name '%s'.", w->id, tok);
if(nodeid) {
host = find_host_by_node_id(tok);
@@ -1243,36 +1506,9 @@ static inline int web_client_switch_host(RRDHOST *host, struct web_client *w, ch
}
if (host) {
- if(!url) { //no delim found
- debug(D_WEB_CLIENT, "%llu: URL doesn't end with / generating redirect.", w->id);
- char *protocol, *url_host;
- protocol = (
-#ifdef ENABLE_HTTPS
- SSL_connection(&w->ssl) ||
-#endif
- (w->flags & WEB_CLIENT_FLAG_PROXY_HTTPS)) ? "https" : "http";
-
- url_host = w->forwarded_host;
- if(!url_host) {
- url_host = w->server_host;
- if(!url_host) url_host = "";
- }
-
- buffer_sprintf(w->response.header, "Location: %s://%s/%s/%s/%s",
- protocol, url_host, nodeid?"node":"host", tok, buffer_tostring(w->url_path_decoded));
-
- if(buffer_strlen(w->url_query_string_decoded)) {
- const char *query_string = buffer_tostring(w->url_query_string_decoded);
- if(*query_string) {
- if(*query_string != '?')
- buffer_fast_strcat(w->response.header, "?", 1);
- buffer_strcat(w->response.header, query_string);
- }
- }
- buffer_fast_strcat(w->response.header, "\r\n", 2);
- buffer_strcat(w->response.data, "Permanent redirect");
- return HTTP_RESP_REDIR_PERM;
- }
+ if(!url)
+ //no delim found
+ return append_slash_to_url_and_redirect(w);
size_t len = strlen(url) + 2;
char buf[len];
@@ -1311,12 +1547,12 @@ int web_client_api_request_with_node_selection(RRDHOST *host, struct web_client
if(unlikely(hash == hash_api && strcmp(tok, "api") == 0)) {
// current API
- debug(D_WEB_CLIENT_ACCESS, "%llu: API request ...", w->id);
+ netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: API request ...", w->id);
return check_host_and_call(host, w, decoded_url_path, web_client_api_request);
}
else if(unlikely((hash == hash_host && strcmp(tok, "host") == 0) || (hash == hash_node && strcmp(tok, "node") == 0))) {
// host switching
- debug(D_WEB_CLIENT_ACCESS, "%llu: host switch request ...", w->id);
+ netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: host switch request ...", w->id);
return web_client_switch_host(host, w, decoded_url_path, hash == hash_node, web_client_api_request_with_node_selection);
}
}
@@ -1335,7 +1571,10 @@ static inline int web_client_process_url(RRDHOST *host, struct web_client *w, ch
hash_api = 0,
hash_netdata_conf = 0,
hash_host = 0,
- hash_node = 0;
+ hash_node = 0,
+ hash_v0 = 0,
+ hash_v1 = 0,
+ hash_v2 = 0;
#ifdef NETDATA_INTERNAL_CHECKS
static uint32_t hash_exit = 0, hash_debug = 0, hash_mirror = 0;
@@ -1346,6 +1585,9 @@ static inline int web_client_process_url(RRDHOST *host, struct web_client *w, ch
hash_netdata_conf = simple_hash("netdata.conf");
hash_host = simple_hash("host");
hash_node = simple_hash("node");
+ hash_v0 = simple_hash("v0");
+ hash_v1 = simple_hash("v1");
+ hash_v2 = simple_hash("v2");
#ifdef NETDATA_INTERNAL_CHECKS
hash_exit = simple_hash("exit");
hash_debug = simple_hash("debug");
@@ -1355,26 +1597,44 @@ static inline int web_client_process_url(RRDHOST *host, struct web_client *w, ch
// keep a copy of the decoded path, in case we need to serve it as a filename
char filename[FILENAME_MAX + 1];
- strncpyz(filename, buffer_tostring(w->url_path_decoded), FILENAME_MAX);
+ strncpyz(filename, decoded_url_path ? decoded_url_path : "", FILENAME_MAX);
char *tok = strsep_skip_consecutive_separators(&decoded_url_path, "/?");
if(likely(tok && *tok)) {
uint32_t hash = simple_hash(tok);
- debug(D_WEB_CLIENT, "%llu: Processing command '%s'.", w->id, tok);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Processing command '%s'.", w->id, tok);
- if(unlikely(hash == hash_api && strcmp(tok, "api") == 0)) { // current API
- debug(D_WEB_CLIENT_ACCESS, "%llu: API request ...", w->id);
+ if(likely(hash == hash_api && strcmp(tok, "api") == 0)) { // current API
+ netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: API request ...", w->id);
return check_host_and_call(host, w, decoded_url_path, web_client_api_request);
}
else if(unlikely((hash == hash_host && strcmp(tok, "host") == 0) || (hash == hash_node && strcmp(tok, "node") == 0))) { // host switching
- debug(D_WEB_CLIENT_ACCESS, "%llu: host switch request ...", w->id);
+ netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: host switch request ...", w->id);
return web_client_switch_host(host, w, decoded_url_path, hash == hash_node, web_client_process_url);
}
+ else if(unlikely(hash == hash_v2 && strcmp(tok, "v2") == 0)) {
+ if(web_client_flag_check(w, WEB_CLIENT_FLAG_PATH_WITH_VERSION))
+ return bad_request_multiple_dashboard_versions(w);
+ web_client_flag_set(w, WEB_CLIENT_FLAG_PATH_IS_V2);
+ return web_client_process_url(host, w, decoded_url_path);
+ }
+ else if(unlikely(hash == hash_v1 && strcmp(tok, "v1") == 0)) {
+ if(web_client_flag_check(w, WEB_CLIENT_FLAG_PATH_WITH_VERSION))
+ return bad_request_multiple_dashboard_versions(w);
+ web_client_flag_set(w, WEB_CLIENT_FLAG_PATH_IS_V1);
+ return web_client_process_url(host, w, decoded_url_path);
+ }
+ else if(unlikely(hash == hash_v0 && strcmp(tok, "v0") == 0)) {
+ if(web_client_flag_check(w, WEB_CLIENT_FLAG_PATH_WITH_VERSION))
+ return bad_request_multiple_dashboard_versions(w);
+ web_client_flag_set(w, WEB_CLIENT_FLAG_PATH_IS_V0);
+ return web_client_process_url(host, w, decoded_url_path);
+ }
else if(unlikely(hash == hash_netdata_conf && strcmp(tok, "netdata.conf") == 0)) { // netdata.conf
if(unlikely(!web_client_can_access_netdataconf(w)))
return web_client_permission_denied(w);
- debug(D_WEB_CLIENT_ACCESS, "%llu: generating netdata.conf ...", w->id);
+ netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: generating netdata.conf ...", w->id);
w->response.data->content_type = CT_TEXT_PLAIN;
buffer_flush(w->response.data);
config_generate(w->response.data, 0);
@@ -1393,7 +1653,7 @@ static inline int web_client_process_url(RRDHOST *host, struct web_client *w, ch
else
buffer_strcat(w->response.data, "I am doing it already");
- error("web request to exit received.");
+ netdata_log_error("web request to exit received.");
netdata_cleanup_and_exit(0);
return HTTP_RESP_OK;
}
@@ -1406,7 +1666,7 @@ static inline int web_client_process_url(RRDHOST *host, struct web_client *w, ch
// get the name of the data to show
tok = strsep_skip_consecutive_separators(&decoded_url_path, "&");
if(tok && *tok) {
- debug(D_WEB_CLIENT, "%llu: Searching for RRD data with name '%s'.", w->id, tok);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Searching for RRD data with name '%s'.", w->id, tok);
// do we have such a data set?
RRDSET *st = rrdset_find_byname(host, tok);
@@ -1415,7 +1675,7 @@ static inline int web_client_process_url(RRDHOST *host, struct web_client *w, ch
w->response.data->content_type = CT_TEXT_HTML;
buffer_strcat(w->response.data, "Chart is not found: ");
buffer_strcat_htmlescape(w->response.data, tok);
- debug(D_WEB_CLIENT_ACCESS, "%llu: %s is not found.", w->id, tok);
+ netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: %s is not found.", w->id, tok);
return HTTP_RESP_NOT_FOUND;
}
@@ -1429,7 +1689,7 @@ static inline int web_client_process_url(RRDHOST *host, struct web_client *w, ch
w->response.data->content_type = CT_TEXT_HTML;
buffer_sprintf(w->response.data, "Chart has now debug %s: ", rrdset_flag_check(st, RRDSET_FLAG_DEBUG)?"enabled":"disabled");
buffer_strcat_htmlescape(w->response.data, tok);
- debug(D_WEB_CLIENT_ACCESS, "%llu: debug for %s is %s.", w->id, tok, rrdset_flag_check(st, RRDSET_FLAG_DEBUG)?"enabled":"disabled");
+ netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: debug for %s is %s.", w->id, tok, rrdset_flag_check(st, RRDSET_FLAG_DEBUG)?"enabled":"disabled");
return HTTP_RESP_OK;
}
@@ -1441,7 +1701,7 @@ static inline int web_client_process_url(RRDHOST *host, struct web_client *w, ch
if(unlikely(!web_client_can_access_netdataconf(w)))
return web_client_permission_denied(w);
- debug(D_WEB_CLIENT_ACCESS, "%llu: Mirroring...", w->id);
+ netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: Mirroring...", w->id);
// replace the zero bytes with spaces
buffer_char_replace(w->response.data, '\0', ' ');
@@ -1507,7 +1767,33 @@ void web_client_process_request(struct web_client *w) {
break;
}
- w->response.code = web_client_process_url(localhost, w, (char *)buffer_tostring(w->url_path_decoded));
+ web_client_reset_path_flags(w);
+
+ // find if the URL path has a filename extension
+ char path[FILENAME_MAX + 1];
+ strncpyz(path, buffer_tostring(w->url_path_decoded), FILENAME_MAX);
+ char *s = path, *e = path;
+
+ // remove the query string and find the last char
+ for (; *e ; e++) {
+ if (*e == '?')
+ break;
+ }
+
+ if(e == s || (*(e - 1) == '/'))
+ web_client_flag_set(w, WEB_CLIENT_FLAG_PATH_HAS_TRAILING_SLASH);
+
+ // check if there is a filename extension
+ while (--e > s) {
+ if (*e == '/')
+ break;
+ if(*e == '.') {
+ web_client_flag_set(w, WEB_CLIENT_FLAG_PATH_HAS_FILE_EXTENSION);
+ break;
+ }
+ }
+
+ w->response.code = (short)web_client_process_url(localhost, w, path);
break;
}
break;
@@ -1517,7 +1803,7 @@ void web_client_process_request(struct web_client *w) {
buffer_flush(w->url_as_received);
buffer_strcat(w->url_as_received, "too big request");
- debug(D_WEB_CLIENT_ACCESS, "%llu: Received request is too big (%zu bytes).", w->id, w->response.data->len);
+ netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: Received request is too big (%zu bytes).", w->id, w->response.data->len);
size_t len = w->response.data->len;
buffer_flush(w->response.data);
@@ -1546,33 +1832,33 @@ void web_client_process_request(struct web_client *w) {
" click <a onclick=\"window.location.href ='https://'+ window.location.hostname + ':' "
" + window.location.port + window.location.pathname + window.location.search\">here</a>."
"</body></html>");
- w->response.code = HTTP_RESP_MOVED_PERM;
+ w->response.code = HTTP_RESP_HTTPS_UPGRADE;
break;
}
#endif
case HTTP_VALIDATION_MALFORMED_URL:
- debug(D_WEB_CLIENT_ACCESS, "%llu: Malformed URL '%s'.", w->id, w->response.data->buffer);
+ netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: Malformed URL '%s'.", w->id, w->response.data->buffer);
buffer_flush(w->response.data);
buffer_strcat(w->response.data, "Malformed URL...\r\n");
w->response.code = HTTP_RESP_BAD_REQUEST;
break;
case HTTP_VALIDATION_EXCESS_REQUEST_DATA:
- debug(D_WEB_CLIENT_ACCESS, "%llu: Excess data in request '%s'.", w->id, w->response.data->buffer);
+ netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: Excess data in request '%s'.", w->id, w->response.data->buffer);
buffer_flush(w->response.data);
buffer_strcat(w->response.data, "Excess data in request.\r\n");
w->response.code = HTTP_RESP_BAD_REQUEST;
break;
case HTTP_VALIDATION_TOO_MANY_READ_RETRIES:
- debug(D_WEB_CLIENT_ACCESS, "%llu: Too many retries to read request '%s'.", w->id, w->response.data->buffer);
+ netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: Too many retries to read request '%s'.", w->id, w->response.data->buffer);
buffer_flush(w->response.data);
buffer_strcat(w->response.data, "Too many retries to read request.\r\n");
w->response.code = HTTP_RESP_BAD_REQUEST;
break;
case HTTP_VALIDATION_NOT_SUPPORTED:
- debug(D_WEB_CLIENT_ACCESS, "%llu: HTTP method requested is not supported '%s'.", w->id, w->response.data->buffer);
+ netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: HTTP method requested is not supported '%s'.", w->id, w->response.data->buffer);
buffer_flush(w->response.data);
buffer_strcat(w->response.data, "HTTP method requested is not supported...\r\n");
@@ -1585,10 +1871,6 @@ void web_client_process_request(struct web_client *w) {
w->response.sent = 0;
- // set a proper last modified date
- if(unlikely(!w->response.data->date))
- w->response.data->date = w->timings.tv_ready.tv_sec;
-
web_client_send_http_header(w);
// enable sending immediately if we have data
@@ -1597,21 +1879,21 @@ void web_client_process_request(struct web_client *w) {
switch(w->mode) {
case WEB_CLIENT_MODE_STREAM:
- debug(D_WEB_CLIENT, "%llu: STREAM done.", w->id);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: STREAM done.", w->id);
break;
case WEB_CLIENT_MODE_OPTIONS:
- debug(D_WEB_CLIENT, "%llu: Done preparing the OPTIONS response. Sending data (%zu bytes) to client.", w->id, w->response.data->len);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Done preparing the OPTIONS response. Sending data (%zu bytes) to client.", w->id, w->response.data->len);
break;
case WEB_CLIENT_MODE_POST:
case WEB_CLIENT_MODE_GET:
- debug(D_WEB_CLIENT, "%llu: Done preparing the response. Sending data (%zu bytes) to client.", w->id, w->response.data->len);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Done preparing the response. Sending data (%zu bytes) to client.", w->id, w->response.data->len);
break;
case WEB_CLIENT_MODE_FILECOPY:
if(w->response.rlen) {
- debug(D_WEB_CLIENT, "%llu: Done preparing the response. Will be sending data file of %zu bytes to client.", w->id, w->response.rlen);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Done preparing the response. Will be sending data file of %zu bytes to client.", w->id, w->response.rlen);
web_client_enable_wait_receive(w);
/*
@@ -1621,14 +1903,14 @@ void web_client_process_request(struct web_client *w) {
{
long len = sendfile(w->ofd, w->ifd, NULL, w->response.data->rbytes);
if(len != w->response.data->rbytes)
- error("%llu: sendfile() should copy %ld bytes, but copied %ld. Falling back to manual copy.", w->id, w->response.data->rbytes, len);
+ netdata_log_error("%llu: sendfile() should copy %ld bytes, but copied %ld. Falling back to manual copy.", w->id, w->response.data->rbytes, len);
else
web_client_request_done(w);
}
*/
}
else
- debug(D_WEB_CLIENT, "%llu: Done preparing the response. Will be sending an unknown amount of bytes to client.", w->id);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Done preparing the response. Will be sending an unknown amount of bytes to client.", w->id);
break;
default:
@@ -1639,7 +1921,7 @@ void web_client_process_request(struct web_client *w) {
ssize_t web_client_send_chunk_header(struct web_client *w, size_t len)
{
- debug(D_DEFLATE, "%llu: OPEN CHUNK of %zu bytes (hex: %zx).", w->id, len, len);
+ netdata_log_debug(D_DEFLATE, "%llu: OPEN CHUNK of %zu bytes (hex: %zx).", w->id, len, len);
char buf[24];
ssize_t bytes;
bytes = (ssize_t)sprintf(buf, "%zX\r\n", len);
@@ -1647,15 +1929,15 @@ ssize_t web_client_send_chunk_header(struct web_client *w, size_t len)
bytes = web_client_send_data(w,buf,strlen(buf),0);
if(bytes > 0) {
- debug(D_DEFLATE, "%llu: Sent chunk header %zd bytes.", w->id, bytes);
+ netdata_log_debug(D_DEFLATE, "%llu: Sent chunk header %zd bytes.", w->id, bytes);
w->statistics.sent_bytes += bytes;
}
else if(bytes == 0) {
- debug(D_WEB_CLIENT, "%llu: Did not send chunk header to the client.", w->id);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Did not send chunk header to the client.", w->id);
}
else {
- debug(D_WEB_CLIENT, "%llu: Failed to send chunk header to client.", w->id);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Failed to send chunk header to client.", w->id);
WEB_CLIENT_IS_DEAD(w);
}
@@ -1669,15 +1951,15 @@ ssize_t web_client_send_chunk_close(struct web_client *w)
ssize_t bytes;
bytes = web_client_send_data(w,"\r\n",2,0);
if(bytes > 0) {
- debug(D_DEFLATE, "%llu: Sent chunk suffix %zd bytes.", w->id, bytes);
+ netdata_log_debug(D_DEFLATE, "%llu: Sent chunk suffix %zd bytes.", w->id, bytes);
w->statistics.sent_bytes += bytes;
}
else if(bytes == 0) {
- debug(D_WEB_CLIENT, "%llu: Did not send chunk suffix to the client.", w->id);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Did not send chunk suffix to the client.", w->id);
}
else {
- debug(D_WEB_CLIENT, "%llu: Failed to send chunk suffix to client.", w->id);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Failed to send chunk suffix to client.", w->id);
WEB_CLIENT_IS_DEAD(w);
}
@@ -1691,15 +1973,15 @@ ssize_t web_client_send_chunk_finalize(struct web_client *w)
ssize_t bytes;
bytes = web_client_send_data(w,"\r\n0\r\n\r\n",7,0);
if(bytes > 0) {
- debug(D_DEFLATE, "%llu: Sent chunk suffix %zd bytes.", w->id, bytes);
+ netdata_log_debug(D_DEFLATE, "%llu: Sent chunk suffix %zd bytes.", w->id, bytes);
w->statistics.sent_bytes += bytes;
}
else if(bytes == 0) {
- debug(D_WEB_CLIENT, "%llu: Did not send chunk finalize suffix to the client.", w->id);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Did not send chunk finalize suffix to the client.", w->id);
}
else {
- debug(D_WEB_CLIENT, "%llu: Failed to send chunk finalize suffix to client.", w->id);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Failed to send chunk finalize suffix to client.", w->id);
WEB_CLIENT_IS_DEAD(w);
}
@@ -1713,13 +1995,13 @@ ssize_t web_client_send_deflate(struct web_client *w)
// when using compression,
// w->response.sent is the amount of bytes passed through compression
- debug(D_DEFLATE, "%llu: web_client_send_deflate(): w->response.data->len = %zu, w->response.sent = %zu, w->response.zhave = %zu, w->response.zsent = %zu, w->response.zstream.avail_in = %u, w->response.zstream.avail_out = %u, w->response.zstream.total_in = %lu, w->response.zstream.total_out = %lu.",
+ netdata_log_debug(D_DEFLATE, "%llu: web_client_send_deflate(): w->response.data->len = %zu, w->response.sent = %zu, w->response.zhave = %zu, w->response.zsent = %zu, w->response.zstream.avail_in = %u, w->response.zstream.avail_out = %u, w->response.zstream.total_in = %lu, w->response.zstream.total_out = %lu.",
w->id, w->response.data->len, w->response.sent, w->response.zhave, w->response.zsent, w->response.zstream.avail_in, w->response.zstream.avail_out, w->response.zstream.total_in, w->response.zstream.total_out);
if(w->response.data->len - w->response.sent == 0 && w->response.zstream.avail_in == 0 && w->response.zhave == w->response.zsent && w->response.zstream.avail_out != 0) {
// there is nothing to send
- debug(D_WEB_CLIENT, "%llu: Out of output data.", w->id);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Out of output data.", w->id);
// finalize the chunk
if(w->response.sent != 0) {
@@ -1729,20 +2011,20 @@ ssize_t web_client_send_deflate(struct web_client *w)
if(w->mode == WEB_CLIENT_MODE_FILECOPY && web_client_has_wait_receive(w) && w->response.rlen && w->response.rlen > w->response.data->len) {
// we have to wait, more data will come
- debug(D_WEB_CLIENT, "%llu: Waiting for more data to become available.", w->id);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Waiting for more data to become available.", w->id);
web_client_disable_wait_send(w);
return t;
}
if(unlikely(!web_client_has_keepalive(w))) {
- debug(D_WEB_CLIENT, "%llu: Closing (keep-alive is not enabled). %zu bytes sent.", w->id, w->response.sent);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Closing (keep-alive is not enabled). %zu bytes sent.", w->id, w->response.sent);
WEB_CLIENT_IS_DEAD(w);
return t;
}
// reset the client
web_client_request_done(w);
- debug(D_WEB_CLIENT, "%llu: Done sending all data on socket.", w->id);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Done sending all data on socket.", w->id);
return t;
}
@@ -1755,7 +2037,7 @@ ssize_t web_client_send_deflate(struct web_client *w)
if(t < 0) return t;
}
- debug(D_DEFLATE, "%llu: Compressing %zu new bytes starting from %zu (and %u left behind).", w->id, (w->response.data->len - w->response.sent), w->response.sent, w->response.zstream.avail_in);
+ netdata_log_debug(D_DEFLATE, "%llu: Compressing %zu new bytes starting from %zu (and %u left behind).", w->id, (w->response.data->len - w->response.sent), w->response.sent, w->response.zstream.avail_in);
// give the compressor all the data not passed through the compressor yet
if(w->response.data->len > w->response.sent) {
@@ -1772,15 +2054,15 @@ ssize_t web_client_send_deflate(struct web_client *w)
if((w->mode == WEB_CLIENT_MODE_GET || w->mode == WEB_CLIENT_MODE_POST)
|| (w->mode == WEB_CLIENT_MODE_FILECOPY && !web_client_has_wait_receive(w) && w->response.data->len == w->response.rlen)) {
flush = Z_FINISH;
- debug(D_DEFLATE, "%llu: Requesting Z_FINISH, if possible.", w->id);
+ netdata_log_debug(D_DEFLATE, "%llu: Requesting Z_FINISH, if possible.", w->id);
}
else {
- debug(D_DEFLATE, "%llu: Requesting Z_SYNC_FLUSH.", w->id);
+ netdata_log_debug(D_DEFLATE, "%llu: Requesting Z_SYNC_FLUSH.", w->id);
}
// compress
if(deflate(&w->response.zstream, flush) == Z_STREAM_ERROR) {
- error("%llu: Compression failed. Closing down client.", w->id);
+ netdata_log_error("%llu: Compression failed. Closing down client.", w->id);
web_client_request_done(w);
return(-1);
}
@@ -1791,30 +2073,30 @@ ssize_t web_client_send_deflate(struct web_client *w)
// keep track of the bytes passed through the compressor
w->response.sent = w->response.data->len;
- debug(D_DEFLATE, "%llu: Compression produced %zu bytes.", w->id, w->response.zhave);
+ netdata_log_debug(D_DEFLATE, "%llu: Compression produced %zu bytes.", w->id, w->response.zhave);
// open a new chunk
ssize_t t2 = web_client_send_chunk_header(w, w->response.zhave);
if(t2 < 0) return t2;
t += t2;
}
-
- debug(D_WEB_CLIENT, "%llu: Sending %zu bytes of data (+%zd of chunk header).", w->id, w->response.zhave - w->response.zsent, t);
+
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Sending %zu bytes of data (+%zd of chunk header).", w->id, w->response.zhave - w->response.zsent, t);
len = web_client_send_data(w,&w->response.zbuffer[w->response.zsent], (size_t) (w->response.zhave - w->response.zsent), MSG_DONTWAIT);
if(len > 0) {
w->statistics.sent_bytes += len;
w->response.zsent += len;
len += t;
- debug(D_WEB_CLIENT, "%llu: Sent %zd bytes.", w->id, len);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Sent %zd bytes.", w->id, len);
}
else if(len == 0) {
- debug(D_WEB_CLIENT, "%llu: Did not send any bytes to the client (zhave = %zu, zsent = %zu, need to send = %zu).",
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Did not send any bytes to the client (zhave = %zu, zsent = %zu, need to send = %zu).",
w->id, w->response.zhave, w->response.zsent, w->response.zhave - w->response.zsent);
}
else {
- debug(D_WEB_CLIENT, "%llu: Failed to send data to client.", w->id);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Failed to send data to client.", w->id);
WEB_CLIENT_IS_DEAD(w);
}
@@ -1829,7 +2111,7 @@ ssize_t web_client_send(struct web_client *w) {
if(unlikely(w->response.data->len - w->response.sent == 0)) {
// there is nothing to send
- debug(D_WEB_CLIENT, "%llu: Out of output data.", w->id);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Out of output data.", w->id);
// there can be two cases for this
// A. we have done everything
@@ -1837,19 +2119,19 @@ ssize_t web_client_send(struct web_client *w) {
if(w->mode == WEB_CLIENT_MODE_FILECOPY && web_client_has_wait_receive(w) && w->response.rlen && w->response.rlen > w->response.data->len) {
// we have to wait, more data will come
- debug(D_WEB_CLIENT, "%llu: Waiting for more data to become available.", w->id);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Waiting for more data to become available.", w->id);
web_client_disable_wait_send(w);
return 0;
}
if(unlikely(!web_client_has_keepalive(w))) {
- debug(D_WEB_CLIENT, "%llu: Closing (keep-alive is not enabled). %zu bytes sent.", w->id, w->response.sent);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Closing (keep-alive is not enabled). %zu bytes sent.", w->id, w->response.sent);
WEB_CLIENT_IS_DEAD(w);
return 0;
}
web_client_request_done(w);
- debug(D_WEB_CLIENT, "%llu: Done sending all data on socket. Waiting for next request on the same socket.", w->id);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Done sending all data on socket. Waiting for next request on the same socket.", w->id);
return 0;
}
@@ -1857,13 +2139,13 @@ ssize_t web_client_send(struct web_client *w) {
if(likely(bytes > 0)) {
w->statistics.sent_bytes += bytes;
w->response.sent += bytes;
- debug(D_WEB_CLIENT, "%llu: Sent %zd bytes.", w->id, bytes);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Sent %zd bytes.", w->id, bytes);
}
else if(likely(bytes == 0)) {
- debug(D_WEB_CLIENT, "%llu: Did not send any bytes to the client.", w->id);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Did not send any bytes to the client.", w->id);
}
else {
- debug(D_WEB_CLIENT, "%llu: Failed to send data to client.", w->id);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Failed to send data to client.", w->id);
WEB_CLIENT_IS_DEAD(w);
}
@@ -1887,8 +2169,8 @@ ssize_t web_client_read_file(struct web_client *w)
w->response.data->len += bytes;
w->response.data->buffer[w->response.data->len] = '\0';
- debug(D_WEB_CLIENT, "%llu: Read %zd bytes.", w->id, bytes);
- debug(D_WEB_DATA, "%llu: Read data: '%s'.", w->id, &w->response.data->buffer[old]);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Read %zd bytes.", w->id, bytes);
+ netdata_log_debug(D_WEB_DATA, "%llu: Read data: '%s'.", w->id, &w->response.data->buffer[old]);
web_client_enable_wait_send(w);
@@ -1896,7 +2178,7 @@ ssize_t web_client_read_file(struct web_client *w)
web_client_disable_wait_receive(w);
}
else if(likely(bytes == 0)) {
- debug(D_WEB_CLIENT, "%llu: Out of input file data.", w->id);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Out of input file data.", w->id);
// if we cannot read, it means we have an error on input.
// if however, we are copying a file from ifd to ofd, we should not return an error.
@@ -1906,7 +2188,7 @@ ssize_t web_client_read_file(struct web_client *w)
// let it finish copying...
web_client_disable_wait_receive(w);
- debug(D_WEB_CLIENT, "%llu: Read the whole file.", w->id);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Read the whole file.", w->id);
if(web_server_mode != WEB_SERVER_MODE_STATIC_THREADED) {
if (w->ifd != w->ofd) close(w->ifd);
@@ -1915,7 +2197,7 @@ ssize_t web_client_read_file(struct web_client *w)
w->ifd = w->ofd;
}
else {
- debug(D_WEB_CLIENT, "%llu: read data failed.", w->id);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: read data failed.", w->id);
WEB_CLIENT_IS_DEAD(w);
}
@@ -1961,23 +2243,22 @@ ssize_t web_client_receive(struct web_client *w)
w->response.data->len += bytes;
w->response.data->buffer[w->response.data->len] = '\0';
- debug(D_WEB_CLIENT, "%llu: Received %zd bytes.", w->id, bytes);
- debug(D_WEB_DATA, "%llu: Received data: '%s'.", w->id, &w->response.data->buffer[old]);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Received %zd bytes.", w->id, bytes);
+ netdata_log_debug(D_WEB_DATA, "%llu: Received data: '%s'.", w->id, &w->response.data->buffer[old]);
}
else if(unlikely(bytes < 0 && (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR))) {
web_client_enable_wait_receive(w);
return 0;
}
else if (bytes < 0) {
- debug(D_WEB_CLIENT, "%llu: receive data failed.", w->id);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: receive data failed.", w->id);
WEB_CLIENT_IS_DEAD(w);
} else
- debug(D_WEB_CLIENT, "%llu: Received %zd bytes.", w->id, bytes);
+ netdata_log_debug(D_WEB_CLIENT, "%llu: Received %zd bytes.", w->id, bytes);
return(bytes);
}
-
void web_client_decode_path_and_query_string(struct web_client *w, const char *path_and_query_string) {
char buffer[NETDATA_WEB_REQUEST_URL_SIZE + 2];
buffer[0] = '\0';
@@ -1999,33 +2280,28 @@ void web_client_decode_path_and_query_string(struct web_client *w, const char *p
}
else {
// in non-stream mode, there is a path
-
// FIXME - the way this is implemented, query string params never accept the symbol &, not even encoded as %26
// To support the symbol & in query string params, we need to turn the url_query_string_decoded into a
// dictionary and decode each of the parameters individually.
// OR: in url_query_string_decoded use as separator a control character that cannot appear in the URL.
- char *question_mark_start = strchr(path_and_query_string, '?');
- if (question_mark_start)
- url_decode_r(buffer, question_mark_start, NETDATA_WEB_REQUEST_URL_SIZE + 1);
-
- buffer[NETDATA_WEB_REQUEST_URL_SIZE + 1] = '\0';
- buffer_strcat(w->url_query_string_decoded, buffer);
+ url_decode_r(buffer, path_and_query_string, NETDATA_WEB_REQUEST_URL_SIZE + 1);
+ char *question_mark_start = strchr(buffer, '?');
if (question_mark_start) {
+ buffer_strcat(w->url_query_string_decoded, question_mark_start);
char c = *question_mark_start;
*question_mark_start = '\0';
- url_decode_r(buffer, path_and_query_string, NETDATA_WEB_REQUEST_URL_SIZE + 1);
+ buffer_strcat(w->url_path_decoded, buffer);
*question_mark_start = c;
- } else
- url_decode_r(buffer, path_and_query_string, NETDATA_WEB_REQUEST_URL_SIZE + 1);
-
- buffer[NETDATA_WEB_REQUEST_URL_SIZE + 1] = '\0';
- buffer_strcat(w->url_path_decoded, buffer);
+ } else {
+ buffer_strcat(w->url_query_string_decoded, "");
+ buffer_strcat(w->url_path_decoded, buffer);
+ }
}
}
-void web_client_zero(struct web_client *w) {
+void web_client_reuse_from_cache(struct web_client *w) {
// zero everything about it - but keep the buffers
web_client_reset_allocations(w, false);