From 17b945890c89b33a08d0d36d2c60e9193d55831d Mon Sep 17 00:00:00 2001 From: "Costa Tsaousis (ktsaou)" Date: Sat, 28 Nov 2015 06:08:17 +0200 Subject: [PATCH] code cleanup and re-arrangements for better scalability --- src/appconfig.c | 16 +- src/appconfig.h | 2 +- src/daemon.c | 24 ++- src/rrd2json.c | 98 ++++----- src/rrd2json.h | 12 +- src/web_buffer.c | 176 +++++++++++++--- src/web_buffer.h | 35 ++-- src/web_client.c | 519 +++++++++++++++++++++++++---------------------- src/web_client.h | 52 ++--- 9 files changed, 533 insertions(+), 401 deletions(-) diff --git a/src/appconfig.c b/src/appconfig.c index 0f957e40..1735dbdf 100755 --- a/src/appconfig.c +++ b/src/appconfig.c @@ -353,7 +353,7 @@ int config_set_boolean(const char *section, const char *name, int value) return value; } -void generate_config(struct web_buffer *wb, int only_changed) +void generate_config(BUFFER *wb, int only_changed) { int i, pri; struct config *co; @@ -362,7 +362,7 @@ void generate_config(struct web_buffer *wb, int only_changed) for(i = 0; i < 3 ;i++) { switch(i) { case 0: - web_buffer_strcat(wb, + buffer_strcat(wb, "# NetData Configuration\n" "# You can uncomment and change any of the options below.\n" "# The value shown in the commented settings, is the default value.\n" @@ -370,11 +370,11 @@ void generate_config(struct web_buffer *wb, int only_changed) break; case 1: - web_buffer_strcat(wb, "\n\n# per plugin configuration\n"); + buffer_strcat(wb, "\n\n# per plugin configuration\n"); break; case 2: - web_buffer_strcat(wb, "\n\n# per chart configuration\n"); + buffer_strcat(wb, "\n\n# per chart configuration\n"); break; } @@ -397,17 +397,17 @@ void generate_config(struct web_buffer *wb, int only_changed) if(only_changed && !changed) continue; if(!used) { - web_buffer_snprintf(wb, CONFIG_FILE_LINE_MAX+1, "\n# node '%s' is not used.", co->name); + buffer_sprintf(wb, "\n# node '%s' is not used.", co->name); } - web_buffer_snprintf(wb, CONFIG_FILE_LINE_MAX+1, "\n[%s]\n", co->name); + buffer_sprintf(wb, "\n[%s]\n", co->name); for(cv = co->values; cv ; cv = cv->next) { if(used && !(cv->flags & CONFIG_VALUE_USED)) { - web_buffer_snprintf(wb, CONFIG_FILE_LINE_MAX + 1, "\n\t# option '%s' is not used.\n", cv->name); + buffer_sprintf(wb, "\n\t# option '%s' is not used.\n", cv->name); } - web_buffer_snprintf(wb, CONFIG_FILE_LINE_MAX + 1, "\t%s%s = %s\n", ((!(cv->flags & CONFIG_VALUE_CHANGED)) && (cv->flags & CONFIG_VALUE_USED))?"# ":"", cv->name, cv->value); + buffer_sprintf(wb, "\t%s%s = %s\n", ((!(cv->flags & CONFIG_VALUE_CHANGED)) && (cv->flags & CONFIG_VALUE_USED))?"# ":"", cv->name, cv->value); } } } diff --git a/src/appconfig.h b/src/appconfig.h index 79c22388..365d1e2f 100755 --- a/src/appconfig.h +++ b/src/appconfig.h @@ -20,6 +20,6 @@ extern const char *config_set(const char *section, const char *name, const char extern long long config_set_number(const char *section, const char *name, long long value); extern int config_set_boolean(const char *section, const char *name, int value); -extern void generate_config(struct web_buffer *wb, int only_changed); +extern void generate_config(BUFFER *wb, int only_changed); #endif /* NETDATA_CONFIG_H */ diff --git a/src/daemon.c b/src/daemon.c index e2315f0b..652cc861 100755 --- a/src/daemon.c +++ b/src/daemon.c @@ -112,29 +112,39 @@ int become_user(const char *username) { struct passwd *pw = getpwnam(username); if(!pw) { - fprintf(stderr, "User %s is not present. Error: %s\n", username, strerror(errno)); + error("User %s is not present. Error: %s\n", username, strerror(errno)); return -1; } if(chown(rundir, pw->pw_uid, pw->pw_gid) != 0) { - fprintf(stderr, "Cannot chown directory '%s' to user %s. Error: %s\n", rundir, username, strerror(errno)); + error("Cannot chown directory '%s' to user %s. Error: %s\n", rundir, username, strerror(errno)); + return -1; + } + + if(setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) { + error("Cannot switch to user's %s group (gid: %d). Error: %s\n", username, pw->pw_gid, strerror(errno)); + return -1; + } + + if(setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0) { + error("Cannot switch to user %s (uid: %d). Error: %s\n", username, pw->pw_uid, strerror(errno)); return -1; } if(setgid(pw->pw_gid) != 0) { - fprintf(stderr, "Cannot switch to user's %s group (gid: %d). Error: %s\n", username, pw->pw_gid, strerror(errno)); + error("Cannot switch to user's %s group (gid: %d). Error: %s\n", username, pw->pw_gid, strerror(errno)); return -1; } if(setegid(pw->pw_gid) != 0) { - fprintf(stderr, "Cannot effectively switch to user's %s group (gid: %d). Error: %s\n", username, pw->pw_gid, strerror(errno)); + error("Cannot effectively switch to user's %s group (gid: %d). Error: %s\n", username, pw->pw_gid, strerror(errno)); return -1; } if(setuid(pw->pw_uid) != 0) { - fprintf(stderr, "Cannot switch to user %s (uid: %d). Error: %s\n", username, pw->pw_uid, strerror(errno)); + error("Cannot switch to user %s (uid: %d). Error: %s\n", username, pw->pw_uid, strerror(errno)); return -1; } if(seteuid(pw->pw_uid) != 0) { - fprintf(stderr, "Cannot effectively switch to user %s (uid: %d). Error: %s\n", username, pw->pw_uid, strerror(errno)); + error("Cannot effectively switch to user %s (uid: %d). Error: %s\n", username, pw->pw_uid, strerror(errno)); return -1; } @@ -319,7 +329,7 @@ int become_daemon(int dont_fork, int close_all_files, const char *user, const ch if(become_user(user) != 0) { error("Cannot become user '%s'. Continuing as we are.", user); } - else debug(D_OPTIONS, "Successfully became user '%s'.", user); + else info("Successfully became user '%s'.", user); } return(0); diff --git a/src/rrd2json.c b/src/rrd2json.c index 0583e01b..a12369ff 100755 --- a/src/rrd2json.c +++ b/src/rrd2json.c @@ -12,13 +12,13 @@ #define HOSTNAME_MAX 1024 char *hostname = "unknown"; -void rrd_stats_api_v1_chart(RRDSET *st, struct web_buffer *wb) +void rrd_stats_api_v1_chart(RRDSET *st, BUFFER *wb) { time_t now = time(NULL); pthread_rwlock_rdlock(&st->rwlock); - web_buffer_snprintf(wb, 65536, + buffer_sprintf(wb, "\t\t{\n" "\t\t\t\"id\": \"%s\",\n" "\t\t\t\"name\": \"%s\",\n" @@ -60,7 +60,7 @@ void rrd_stats_api_v1_chart(RRDSET *st, struct web_buffer *wb) memory += rd->memsize; - web_buffer_snprintf(wb, 4096, + buffer_sprintf(wb, "%s" "\t\t\t\t\"%s\": { \"name\": \"%s\" }" , c?",\n":"" @@ -71,7 +71,7 @@ void rrd_stats_api_v1_chart(RRDSET *st, struct web_buffer *wb) c++; } - web_buffer_snprintf(wb, 200, + buffer_sprintf(wb, "\n\t\t\t}\n" "\t\t}" ); @@ -79,12 +79,12 @@ void rrd_stats_api_v1_chart(RRDSET *st, struct web_buffer *wb) pthread_rwlock_unlock(&st->rwlock); } -void rrd_stats_api_v1_charts(struct web_buffer *wb) +void rrd_stats_api_v1_charts(BUFFER *wb) { long c; RRDSET *st; - web_buffer_snprintf(wb, 4096, "{\n" + buffer_sprintf(wb, "{\n" "\t\"hostname\": \"%s\"" ",\n\t\"update_every\": %d" ",\n\t\"history\": %d" @@ -97,27 +97,27 @@ void rrd_stats_api_v1_charts(struct web_buffer *wb) pthread_rwlock_rdlock(&rrdset_root_rwlock); for(st = rrdset_root, c = 0; st ; st = st->next) { if(st->enabled) { - if(c) web_buffer_strcat(wb, ","); - web_buffer_strcat(wb, "\n\t\t\""); - web_buffer_strcat(wb, st->id); - web_buffer_strcat(wb, "\": "); + if(c) buffer_strcat(wb, ","); + buffer_strcat(wb, "\n\t\t\""); + buffer_strcat(wb, st->id); + buffer_strcat(wb, "\": "); rrd_stats_api_v1_chart(st, wb); c++; } } pthread_rwlock_unlock(&rrdset_root_rwlock); - web_buffer_strcat(wb, "\n\t}\n}\n"); + buffer_strcat(wb, "\n\t}\n}\n"); } -unsigned long rrd_stats_one_json(RRDSET *st, char *options, struct web_buffer *wb) +unsigned long rrd_stats_one_json(RRDSET *st, char *options, BUFFER *wb) { time_t now = time(NULL); pthread_rwlock_rdlock(&st->rwlock); - web_buffer_snprintf(wb, 65536, + buffer_sprintf(wb, "\t\t{\n" "\t\t\t\"id\": \"%s\",\n" "\t\t\t\"name\": \"%s\",\n" @@ -171,7 +171,7 @@ unsigned long rrd_stats_one_json(RRDSET *st, char *options, struct web_buffer *w memory += rd->memsize; - web_buffer_snprintf(wb, 4096, + buffer_sprintf(wb, "\t\t\t\t{\n" "\t\t\t\t\t\"id\": \"%s\",\n" "\t\t\t\t\t\"name\": \"%s\",\n" @@ -204,7 +204,7 @@ unsigned long rrd_stats_one_json(RRDSET *st, char *options, struct web_buffer *w ); } - web_buffer_snprintf(wb, 200, + buffer_sprintf(wb, "\t\t\t],\n" "\t\t\t\"memory\" : %lu\n" "\t\t}" @@ -218,32 +218,32 @@ unsigned long rrd_stats_one_json(RRDSET *st, char *options, struct web_buffer *w #define RRD_GRAPH_JSON_HEADER "{\n\t\"charts\": [\n" #define RRD_GRAPH_JSON_FOOTER "\n\t]\n}\n" -void rrd_stats_graph_json(RRDSET *st, char *options, struct web_buffer *wb) +void rrd_stats_graph_json(RRDSET *st, char *options, BUFFER *wb) { - web_buffer_snprintf(wb, sizeof(RRD_GRAPH_JSON_HEADER), RRD_GRAPH_JSON_HEADER); + buffer_strcat(wb, RRD_GRAPH_JSON_HEADER); rrd_stats_one_json(st, options, wb); - web_buffer_snprintf(wb, sizeof(RRD_GRAPH_JSON_FOOTER), RRD_GRAPH_JSON_FOOTER); + buffer_strcat(wb, RRD_GRAPH_JSON_FOOTER); } -void rrd_stats_all_json(struct web_buffer *wb) +void rrd_stats_all_json(BUFFER *wb) { unsigned long memory = 0; long c; RRDSET *st; - web_buffer_snprintf(wb, sizeof(RRD_GRAPH_JSON_HEADER), RRD_GRAPH_JSON_HEADER); + buffer_strcat(wb, RRD_GRAPH_JSON_HEADER); pthread_rwlock_rdlock(&rrdset_root_rwlock); for(st = rrdset_root, c = 0; st ; st = st->next) { if(st->enabled) { - if(c) web_buffer_strcat(wb, ",\n"); + if(c) buffer_strcat(wb, ",\n"); memory += rrd_stats_one_json(st, NULL, wb); c++; } } pthread_rwlock_unlock(&rrdset_root_rwlock); - web_buffer_snprintf(wb, 4096, "\n\t],\n" + buffer_sprintf(wb, "\n\t],\n" "\t\"hostname\": \"%s\",\n" "\t\"update_every\": %d,\n" "\t\"history\": %d,\n" @@ -668,7 +668,7 @@ RRDR *rrd2rrdr(RRDSET *st, long points, time_t after, time_t before, int group_m return NULL; } -unsigned long rrd_stats_json(int type, RRDSET *st, struct web_buffer *wb, int points, int group, int group_method, time_t after, time_t before, int only_non_zero) +unsigned long rrd_stats_json(int type, RRDSET *st, BUFFER *wb, int points, int group, int group_method, time_t after, time_t before, int only_non_zero) { int c; pthread_rwlock_rdlock(&st->rwlock); @@ -716,7 +716,7 @@ unsigned long rrd_stats_json(int type, RRDSET *st, struct web_buffer *wb, int po for( rd = st->dimensions ; rd ; rd = rd->next) dimensions++; if(!dimensions) { pthread_rwlock_unlock(&st->rwlock); - web_buffer_strcat(wb, "No dimensions yet."); + buffer_strcat(wb, "No dimensions yet."); return 0; } @@ -788,10 +788,10 @@ unsigned long rrd_stats_json(int type, RRDSET *st, struct web_buffer *wb, int po // ------------------------------------------------------------------------- // print the JSON header - web_buffer_snprintf(wb, 1024, "{\n %scols%s:\n [\n", kq, kq); - web_buffer_snprintf(wb, 1024, " {%sid%s:%s%s,%slabel%s:%stime%s,%spattern%s:%s%s,%stype%s:%sdatetime%s},\n", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq); - web_buffer_snprintf(wb, 1024, " {%sid%s:%s%s,%slabel%s:%s%s,%spattern%s:%s%s,%stype%s:%sstring%s,%sp%s:{%srole%s:%sannotation%s}},\n", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, kq, kq, sq, sq); - web_buffer_snprintf(wb, 1024, " {%sid%s:%s%s,%slabel%s:%s%s,%spattern%s:%s%s,%stype%s:%sstring%s,%sp%s:{%srole%s:%sannotationText%s}}", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, kq, kq, sq, sq); + buffer_sprintf(wb, "{\n %scols%s:\n [\n", kq, kq); + buffer_sprintf(wb, " {%sid%s:%s%s,%slabel%s:%stime%s,%spattern%s:%s%s,%stype%s:%sdatetime%s},\n", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq); + buffer_sprintf(wb, " {%sid%s:%s%s,%slabel%s:%s%s,%spattern%s:%s%s,%stype%s:%sstring%s,%sp%s:{%srole%s:%sannotation%s}},\n", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, kq, kq, sq, sq); + buffer_sprintf(wb, " {%sid%s:%s%s,%slabel%s:%s%s,%spattern%s:%s%s,%stype%s:%sstring%s,%sp%s:{%srole%s:%sannotationText%s}}", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, kq, kq, sq, sq); // print the header for each dimension // and update the print_hidden array for the dimensions that should be hidden @@ -799,15 +799,15 @@ unsigned long rrd_stats_json(int type, RRDSET *st, struct web_buffer *wb, int po for( rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) { if(!print_hidden[c]) { pc++; - web_buffer_snprintf(wb, 1024, ",\n {%sid%s:%s%s,%slabel%s:%s%s%s,%spattern%s:%s%s,%stype%s:%snumber%s}", kq, kq, sq, sq, kq, kq, sq, rd->name, sq, kq, kq, sq, sq, kq, kq, sq, sq); + buffer_sprintf(wb, ",\n {%sid%s:%s%s,%slabel%s:%s%s%s,%spattern%s:%s%s,%stype%s:%snumber%s}", kq, kq, sq, sq, kq, kq, sq, rd->name, sq, kq, kq, sq, sq, kq, kq, sq, sq); } } if(!pc) { - web_buffer_snprintf(wb, 1024, ",\n {%sid%s:%s%s,%slabel%s:%s%s%s,%spattern%s:%s%s,%stype%s:%snumber%s}", kq, kq, sq, sq, kq, kq, sq, "no data", sq, kq, kq, sq, sq, kq, kq, sq, sq); + buffer_sprintf(wb, ",\n {%sid%s:%s%s,%slabel%s:%s%s%s,%spattern%s:%s%s,%stype%s:%snumber%s}", kq, kq, sq, sq, kq, kq, sq, "no data", sq, kq, kq, sq, sq, kq, kq, sq, sq); } // print the begin of row data - web_buffer_snprintf(wb, 1024, "\n ],\n %srows%s:\n [\n", kq, kq); + buffer_sprintf(wb, "\n ],\n %srows%s:\n [\n", kq, kq); // ------------------------------------------------------------------------- @@ -884,10 +884,10 @@ unsigned long rrd_stats_json(int type, RRDSET *st, struct web_buffer *wb, int po if(!tm) { error("localtime() failed."); continue; } if(now > last_timestamp) last_timestamp = now; - if(printed) web_buffer_strcat(wb, "]},\n"); - web_buffer_strcat(wb, pre_date); - web_buffer_jsdate(wb, tm->tm_year + 1900, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); - web_buffer_strcat(wb, post_date); + if(printed) buffer_strcat(wb, "]},\n"); + buffer_strcat(wb, pre_date); + buffer_jsdate(wb, tm->tm_year + 1900, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); + buffer_strcat(wb, post_date); print_this = 1; } @@ -923,26 +923,26 @@ unsigned long rrd_stats_json(int type, RRDSET *st, struct web_buffer *wb, int po if(print_this) { if(annotate_reset) { annotation_count++; - web_buffer_strcat(wb, overflow_annotation); + buffer_strcat(wb, overflow_annotation); annotate_reset = 0; } else - web_buffer_strcat(wb, normal_annotation); + buffer_strcat(wb, normal_annotation); pc = 0; for(c = 0 ; c < dimensions ; c++) { if(found_non_existing[c] == group_count) { // all entries are non-existing pc++; - web_buffer_strcat(wb, pre_value); - web_buffer_strcat(wb, "null"); - web_buffer_strcat(wb, post_value); + buffer_strcat(wb, pre_value); + buffer_strcat(wb, "null"); + buffer_strcat(wb, post_value); } else if(!print_hidden[c]) { pc++; - web_buffer_strcat(wb, pre_value); - web_buffer_rrd_value(wb, group_values[c]); - web_buffer_strcat(wb, post_value); + buffer_strcat(wb, pre_value); + buffer_rrd_value(wb, group_values[c]); + buffer_strcat(wb, post_value); if(group_values[c]) found_non_zero[c]++; } @@ -954,9 +954,9 @@ unsigned long rrd_stats_json(int type, RRDSET *st, struct web_buffer *wb, int po // if all dimensions are hidden, print a null if(!pc) { - web_buffer_strcat(wb, pre_value); - web_buffer_strcat(wb, "null"); - web_buffer_strcat(wb, post_value); + buffer_strcat(wb, pre_value); + buffer_strcat(wb, "null"); + buffer_strcat(wb, post_value); } printed++; @@ -964,8 +964,8 @@ unsigned long rrd_stats_json(int type, RRDSET *st, struct web_buffer *wb, int po } } - if(printed) web_buffer_strcat(wb, "]}"); - web_buffer_strcat(wb, "\n ]\n}\n"); + if(printed) buffer_strcat(wb, "]}"); + buffer_strcat(wb, "\n ]\n}\n"); if(only_non_zero && max_loop > 1) { int changed = 0; @@ -979,7 +979,7 @@ unsigned long rrd_stats_json(int type, RRDSET *st, struct web_buffer *wb, int po } } - if(changed) web_buffer_flush(wb); + if(changed) buffer_flush(wb); else break; } else break; diff --git a/src/rrd2json.h b/src/rrd2json.h index 2454f1bb..d0dad28e 100755 --- a/src/rrd2json.h +++ b/src/rrd2json.h @@ -21,15 +21,15 @@ extern char *hostname; #define GROUP_MAX 1 #define GROUP_SUM 2 -extern void rrd_stats_api_v1_chart(RRDSET *st, struct web_buffer *wb); -extern void rrd_stats_api_v1_charts(struct web_buffer *wb); +extern void rrd_stats_api_v1_chart(RRDSET *st, BUFFER *wb); +extern void rrd_stats_api_v1_charts(BUFFER *wb); -extern unsigned long rrd_stats_one_json(RRDSET *st, char *options, struct web_buffer *wb); +extern unsigned long rrd_stats_one_json(RRDSET *st, char *options, BUFFER *wb); -extern void rrd_stats_graph_json(RRDSET *st, char *options, struct web_buffer *wb); +extern void rrd_stats_graph_json(RRDSET *st, char *options, BUFFER *wb); -extern void rrd_stats_all_json(struct web_buffer *wb); +extern void rrd_stats_all_json(BUFFER *wb); -extern unsigned long rrd_stats_json(int type, RRDSET *st, struct web_buffer *wb, int entries_to_show, int group, int group_method, time_t after, time_t before, int only_non_zero); +extern unsigned long rrd_stats_json(int type, RRDSET *st, BUFFER *wb, int entries_to_show, int group, int group_method, time_t after, time_t before, int only_non_zero); #endif /* NETDATA_RRD2JSON_H */ diff --git a/src/web_buffer.c b/src/web_buffer.c index cea5be61..1607696c 100755 --- a/src/web_buffer.c +++ b/src/web_buffer.c @@ -12,17 +12,51 @@ #include "web_buffer.h" #include "log.h" -void web_buffer_reset(struct web_buffer *wb) +#define BUFFER_OVERFLOW_EOF "EOF" + +static inline void buffer_overflow_init(BUFFER *b) +{ + b->buffer[b->size] = '\0'; + strcpy(&b->buffer[b->size + 1], BUFFER_OVERFLOW_EOF); +} + +#define buffer_overflow_check(b) _buffer_overflow_check(b, __FILE__, __FUNCTION__, __LINE__) + +static inline void _buffer_overflow_check(BUFFER *b, const char *file, const char *function, const unsigned long line) +{ + if(b->len > b->size) { + error("BUFFER: length %ld is above size %ld, at line %lu, at function %s() of file '%s'.", b->len, b->size, line, function, file); + b->len = b->size; + } + + if(b->buffer[b->size] != '\0' || strcmp(&b->buffer[b->size + 1], BUFFER_OVERFLOW_EOF)) { + error("BUFFER: detected overflow at line %lu, at function %s() of file '%s'.", line, function, file); + buffer_overflow_init(b); + } +} + + +void buffer_reset(BUFFER *wb) { - web_buffer_flush(wb); + buffer_flush(wb); - wb->sent = 0; - wb->rlen = 0; wb->contenttype = CT_TEXT_PLAIN; wb->date = 0; + + buffer_overflow_check(wb); } -void web_buffer_char_replace(struct web_buffer *wb, char from, char to) +const char *buffer_tostring(BUFFER *wb) +{ + buffer_need_bytes(wb, 1); + wb->buffer[wb->len] = '\0'; + + buffer_overflow_check(wb); + + return(wb->buffer); +} + +void buffer_char_replace(BUFFER *wb, char from, char to) { char *s = wb->buffer, *end = &wb->buffer[wb->len]; @@ -30,64 +64,128 @@ void web_buffer_char_replace(struct web_buffer *wb, char from, char to) if(*s == from) *s = to; s++; } + + buffer_overflow_check(wb); } -void web_buffer_strcat(struct web_buffer *wb, const char *txt) +void buffer_strcat(BUFFER *wb, const char *txt) { - char *buffer = wb->buffer; - const char *s = txt; - long bytes = wb->len, size = wb->size; + if(wb->size - wb->len < 512) + buffer_need_bytes(wb, 512); - while(*s && bytes < size) - buffer[bytes++] = *s++; + char *s = &wb->buffer[wb->len], *end = &wb->buffer[wb->size]; + long len = wb->len; - wb->len = bytes; + while(*txt && s != end) { + *s++ = *txt++; + len++; + } - if(*s) { - web_buffer_need_bytes(wb, strlen(s)); - web_buffer_strcat(wb, s); + wb->len = len; + buffer_overflow_check(wb); + + if(*txt) { + debug(D_WEB_BUFFER, "strcat(): increasing web_buffer at position %ld, size = %ld\n", wb->len, wb->size); + len = strlen(txt); + buffer_increase(wb, len); + buffer_strcat(wb, txt); } else { // terminate the string // without increasing the length - web_buffer_need_bytes(wb, 1); + buffer_need_bytes(wb, 1); wb->buffer[wb->len] = '\0'; } } -void web_buffer_snprintf(struct web_buffer *wb, size_t len, const char *fmt, ...) +void buffer_snprintf(BUFFER *wb, size_t len, const char *fmt, ...) { - web_buffer_need_bytes(wb, len+1); + buffer_need_bytes(wb, len+1); va_list args; va_start(args, fmt); wb->len += vsnprintf(&wb->buffer[wb->len], len+1, fmt, args); va_end(args); + buffer_overflow_check(wb); + // the buffer is \0 terminated by vsnprintf } +void buffer_vsprintf(BUFFER *wb, const char *fmt, va_list args) +{ + size_t len = wb->size - wb->len; + if(unlikely(!len)) return; + + wb->len += vsnprintf(&wb->buffer[wb->len], len, fmt, args); + + buffer_overflow_check(wb); -void web_buffer_rrd_value(struct web_buffer *wb, calculated_number value) + // the buffer is \0 terminated by vsnprintf +} + +void buffer_sprintf(BUFFER *wb, const char *fmt, ...) +{ + if(unlikely(wb->len > wb->size)) { + error("web_buffer_sprintf(): already overflown length %ld, size = %ld", wb->len, wb->size); + } + +// if(wb->size - wb->len < 512) +// web_buffer_need_bytes(wb, 512); + + size_t len = wb->size - wb->len, old_len = wb->len, wrote; + + buffer_need_bytes(wb, len); + + va_list args; + va_start(args, fmt); + wrote = vsnprintf(&wb->buffer[wb->len], len, fmt, args); + va_end(args); + + if(unlikely(wrote >= len)) { + // there is bug in vsnprintf() and it returns + // a number higher to len, but it does not + // overflow the buffer. + // our buffer overflow detector will log it + // if it does. + buffer_overflow_check(wb); + + debug(D_WEB_BUFFER, "web_buffer_sprintf(): increasing web_buffer at position %ld, size = %ld\n", wb->len, wb->size); + buffer_need_bytes(wb, len + WEB_DATA_LENGTH_INCREASE_STEP); + + va_start(args, fmt); + buffer_vsprintf(wb, fmt, args); + va_end(args); + } + else + wb->len += wrote; + + // the buffer is \0 terminated by vsnprintf +} + + +void buffer_rrd_value(BUFFER *wb, calculated_number value) { - web_buffer_need_bytes(wb, 50); + buffer_need_bytes(wb, 50); wb->len += print_calculated_number(&wb->buffer[wb->len], value); // terminate it - web_buffer_need_bytes(wb, 1); + buffer_need_bytes(wb, 1); wb->buffer[wb->len] = '\0'; + + buffer_overflow_check(wb); } // generate a javascript date, the fastest possible way... -void web_buffer_jsdate(struct web_buffer *wb, int year, int month, int day, int hours, int minutes, int seconds) +void buffer_jsdate(BUFFER *wb, int year, int month, int day, int hours, int minutes, int seconds) { // 10 20 30 = 35 // 01234567890123456789012345678901234 // Date(2014, 04, 01, 03, 28, 20, 065) - web_buffer_need_bytes(wb, 36); + buffer_need_bytes(wb, 36); char *b = &wb->buffer[wb->len]; @@ -127,44 +225,53 @@ void web_buffer_jsdate(struct web_buffer *wb, int year, int month, int day, int wb->len += i; // terminate it - web_buffer_need_bytes(wb, 1); + buffer_need_bytes(wb, 1); wb->buffer[wb->len] = '\0'; + + buffer_overflow_check(wb); } -struct web_buffer *web_buffer_create(long size) +BUFFER *buffer_create(long size) { - struct web_buffer *b; + BUFFER *b; debug(D_WEB_BUFFER, "Creating new web buffer of size %d.", size); - b = calloc(1, sizeof(struct web_buffer)); + b = calloc(1, sizeof(BUFFER)); if(!b) { error("Cannot allocate a web_buffer."); return NULL; } - b->buffer = malloc(size); + b->buffer = malloc(size + sizeof(BUFFER_OVERFLOW_EOF) + 2); if(!b->buffer) { - error("Cannot allocate a buffer of size %u.", size); + error("Cannot allocate a buffer of size %u.", size + sizeof(BUFFER_OVERFLOW_EOF) + 2); free(b); return NULL; } b->buffer[0] = '\0'; b->size = size; b->contenttype = CT_TEXT_PLAIN; + buffer_overflow_init(b); + buffer_overflow_check(b); + return(b); } -void web_buffer_free(struct web_buffer *b) +void buffer_free(BUFFER *b) { + buffer_overflow_check(b); + debug(D_WEB_BUFFER, "Freeing web buffer of size %d.", b->size); if(b->buffer) free(b->buffer); free(b); } -void web_buffer_increase(struct web_buffer *b, long free_size_required) +void buffer_increase(BUFFER *b, long free_size_required) { + buffer_overflow_check(b); + long left = b->size - b->len; if(left >= free_size_required) return; @@ -174,8 +281,11 @@ void web_buffer_increase(struct web_buffer *b, long free_size_required) debug(D_WEB_BUFFER, "Increasing data buffer from size %d to %d.", b->size, b->size + increase); - b->buffer = realloc(b->buffer, b->size + increase); - if(!b->buffer) fatal("Failed to increase data buffer from size %d to %d.", b->size, b->size + increase); + b->buffer = realloc(b->buffer, b->size + increase + sizeof(BUFFER_OVERFLOW_EOF) + 2); + if(!b->buffer) fatal("Failed to increase data buffer from size %d to %d.", b->size + sizeof(BUFFER_OVERFLOW_EOF) + 2, b->size + increase + sizeof(BUFFER_OVERFLOW_EOF) + 2); b->size += increase; + + buffer_overflow_init(b); + buffer_overflow_check(b); } diff --git a/src/web_buffer.h b/src/web_buffer.h index 93e4c065..af523943 100755 --- a/src/web_buffer.h +++ b/src/web_buffer.h @@ -5,17 +5,15 @@ #ifndef NETDATA_WEB_BUFFER_H #define NETDATA_WEB_BUFFER_H 1 -#define WEB_DATA_LENGTH_INCREASE_STEP 65536 +#define WEB_DATA_LENGTH_INCREASE_STEP 16384 -struct web_buffer { +typedef struct web_buffer { long size; // allocation size of buffer long len; // current data length in buffer - long sent; // current data length sent to output char *buffer; // the buffer int contenttype; - long rlen; // if non-zero, the excepted size of ifd time_t date; // the date this content has been generated -}; +} BUFFER; // content-types #define CT_APPLICATION_JSON 1 @@ -33,22 +31,27 @@ struct web_buffer { #define CT_APPLICATION_VND_MS_FONTOBJ 13 #define CT_IMAGE_SVG_XML 14 -#define web_buffer_need_bytes(buffer, needed_free_size) do { if(unlikely((buffer)->size - (buffer)->len < (needed_free_size))) web_buffer_increase((buffer), (needed_free_size)); } while(0) +#define buffer_strlen(wb) ((wb)->len) +extern const char *buffer_tostring(BUFFER *wb); -#define web_buffer_flush(wb) wb->buffer[wb->len = 0] = '\0' -void web_buffer_reset(struct web_buffer *wb); +#define buffer_need_bytes(buffer, needed_free_size) do { if(unlikely((buffer)->size - (buffer)->len < (needed_free_size))) buffer_increase((buffer), (needed_free_size)); } while(0) -void web_buffer_strcat(struct web_buffer *wb, const char *txt); -void web_buffer_rrd_value(struct web_buffer *wb, calculated_number value); +#define buffer_flush(wb) wb->buffer[wb->len = 0] = '\0' +extern void buffer_reset(BUFFER *wb); -void web_buffer_jsdate(struct web_buffer *wb, int year, int month, int day, int hours, int minutes, int seconds); +extern void buffer_strcat(BUFFER *wb, const char *txt); +extern void buffer_rrd_value(BUFFER *wb, calculated_number value); -struct web_buffer *web_buffer_create(long size); -void web_buffer_free(struct web_buffer *b); -void web_buffer_increase(struct web_buffer *b, long free_size_required); +extern void buffer_jsdate(BUFFER *wb, int year, int month, int day, int hours, int minutes, int seconds); -void web_buffer_snprintf(struct web_buffer *wb, size_t len, const char *fmt, ...); +extern BUFFER *buffer_create(long size); +extern void buffer_free(BUFFER *b); +extern void buffer_increase(BUFFER *b, long free_size_required); -void web_buffer_char_replace(struct web_buffer *wb, char from, char to); +extern void buffer_snprintf(BUFFER *wb, size_t len, const char *fmt, ...); +extern void buffer_vsprintf(BUFFER *wb, const char *fmt, va_list args); +extern void buffer_sprintf(BUFFER *wb, const char *fmt, ...); + +extern void buffer_char_replace(BUFFER *wb, char from, char to); #endif /* NETDATA_WEB_BUFFER_H */ diff --git a/src/web_client.c b/src/web_client.c index 1b8db57b..77e7aeab 100755 --- a/src/web_client.c +++ b/src/web_client.c @@ -26,7 +26,8 @@ #include "web_client.h" -#define INITIAL_WEB_DATA_LENGTH 65536 +#define INITIAL_WEB_DATA_LENGTH 16384 +#define WEB_REQUEST_LENGTH 16384 int web_client_timeout = DEFAULT_DISCONNECT_IDLE_WEB_CLIENTS_AFTER_SECONDS; int web_enable_gzip = 1; @@ -93,14 +94,33 @@ struct web_client *web_client_create(int listener) if(setsockopt(w->ifd, SOL_SOCKET, SO_KEEPALIVE, (char *) &flag, sizeof(int)) != 0) error("%llu: Cannot set SO_KEEPALIVE on socket.", w->id); } - w->data = web_buffer_create(INITIAL_WEB_DATA_LENGTH); - if(unlikely(!w->data)) { + w->response.data = buffer_create(INITIAL_WEB_DATA_LENGTH); + if(unlikely(!w->response.data)) { // no need for error log - web_buffer_create already logged the error close(w->ifd); free(w); return NULL; } + w->response.header = buffer_create(HTTP_RESPONSE_HEADER_SIZE); + if(unlikely(!w->response.header)) { + // no need for error log - web_buffer_create already logged the error + buffer_free(w->response.data); + close(w->ifd); + free(w); + return NULL; + } + + w->response.header_output = buffer_create(HTTP_RESPONSE_HEADER_SIZE); + if(unlikely(!w->response.header_output)) { + // no need for error log - web_buffer_create already logged the error + buffer_free(w->response.header); + buffer_free(w->response.data); + close(w->ifd); + free(w); + return NULL; + } + w->wait_receive = 1; if(web_clients) web_clients->prev = w; @@ -112,6 +132,69 @@ struct web_client *web_client_create(int listener) return(w); } +void web_client_reset(struct web_client *w) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + + long sent = (w->mode == WEB_CLIENT_MODE_FILECOPY)?w->response.rlen:w->response.data->len; + +#ifdef NETDATA_WITH_ZLIB + if(likely(w->response.zoutput)) sent = (long)w->response.zstream.total_out; +#endif + + long size = (w->mode == WEB_CLIENT_MODE_FILECOPY)?w->response.rlen:w->response.data->len; + + if(likely(w->last_url[0])) + log_access("%llu: (sent/all = %ld/%ld bytes %0.0f%%, prep/sent/total = %0.2f/%0.2f/%0.2f ms) %s: '%s'", + w->id, + sent, size, -((size>0)?((float)(size-sent)/(float)size * 100.0):0.0), + (float)usecdiff(&w->tv_ready, &w->tv_in) / 1000.0, + (float)usecdiff(&tv, &w->tv_ready) / 1000.0, + (float)usecdiff(&tv, &w->tv_in) / 1000.0, + (w->mode == WEB_CLIENT_MODE_FILECOPY)?"filecopy":"data", + w->last_url + ); + + debug(D_WEB_CLIENT, "%llu: Reseting client.", w->id); + + if(unlikely(w->mode == WEB_CLIENT_MODE_FILECOPY)) { + debug(D_WEB_CLIENT, "%llu: Closing filecopy input file.", w->id); + close(w->ifd); + w->ifd = w->ofd; + } + + w->last_url[0] = '\0'; + + w->mode = WEB_CLIENT_MODE_NORMAL; + + buffer_reset(w->response.header_output); + buffer_reset(w->response.header); + buffer_reset(w->response.data); + w->response.rlen = 0; + w->response.sent = 0; + + w->wait_receive = 1; + w->wait_send = 0; + + w->response.zoutput = 0; + + // if we had enabled compression, release it +#ifdef NETDATA_WITH_ZLIB + if(w->response.zinitialized) { + debug(D_DEFLATE, "%llu: Reseting compression.", w->id); + deflateEnd(&w->response.zstream); + w->response.zsent = 0; + w->response.zhave = 0; + w->response.zstream.avail_in = 0; + w->response.zstream.avail_out = 0; + w->response.zstream.total_in = 0; + w->response.zstream.total_out = 0; + w->response.zinitialized = 0; + } +#endif // NETDATA_WITH_ZLIB +} + struct web_client *web_client_free(struct web_client *w) { struct web_client *n = w->next; @@ -123,7 +206,8 @@ struct web_client *web_client_free(struct web_client *w) if(w == web_clients) web_clients = w->next; - if(w->data) web_buffer_free(w->data); + if(w->response.header) buffer_free(w->response.header); + if(w->response.data) buffer_free(w->response.data); close(w->ifd); if(w->ofd != w->ifd) close(w->ofd); free(w); @@ -152,7 +236,7 @@ int mysendfile(struct web_client *w, char *filename) // if the filename contains a / or a .., refuse to serve it if(strchr(filename, '/') != 0 || strstr(filename, "..") != 0) { debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not acceptable.", w->id, filename); - web_buffer_snprintf(w->data, FILENAME_MAX + 1024, "File '%s' cannot be served. Filenames cannot contain / or ..", filename); + buffer_sprintf(w->response.data, "File '%s' cannot be served. Filenames cannot contain / or ..", filename); return 400; } @@ -164,14 +248,14 @@ int mysendfile(struct web_client *w, char *filename) struct stat stat; if(lstat(webfilename, &stat) != 0) { error("%llu: File '%s' is not found.", w->id, webfilename); - web_buffer_snprintf(w->data, FILENAME_MAX + 1024, "File '%s' does not exist, or is not accessible.", filename); + buffer_sprintf(w->response.data, "File '%s' does not exist, or is not accessible.", filename); return 404; } // check if the file is owned by us if(stat.st_uid != getuid() && stat.st_uid != geteuid()) { error("%llu: File '%s' is owned by user %d (I run as user %d). Access Denied.", w->id, webfilename, stat.st_uid, getuid()); - web_buffer_snprintf(w->data, FILENAME_MAX + 1024, "Access to file '%s' is not permitted.", filename); + buffer_sprintf(w->response.data, "Access to file '%s' is not permitted.", filename); return 403; } @@ -182,152 +266,92 @@ int mysendfile(struct web_client *w, char *filename) if(errno == EBUSY || errno == EAGAIN) { error("%llu: File '%s' is busy, sending 307 Moved Temporarily to force retry.", w->id, webfilename); - snprintf(w->response_header, MAX_HTTP_HEADER_SIZE, "Location: /" WEB_PATH_FILE "/%s\r\n", filename); - web_buffer_snprintf(w->data, FILENAME_MAX + 1024, "The file '%s' is currently busy. Please try again later.", filename); + buffer_sprintf(w->response.header, "Location: /" WEB_PATH_FILE "/%s\r\n", filename); + buffer_sprintf(w->response.data, "The file '%s' is currently busy. Please try again later.", filename); return 307; } else { error("%llu: Cannot open file '%s'.", w->id, webfilename); - web_buffer_snprintf(w->data, FILENAME_MAX + 1024, "Cannot open file '%s'.", filename); + buffer_sprintf(w->response.data, "Cannot open file '%s'.", filename); return 404; } } // pick a Content-Type for the file - if(strstr(filename, ".html") != NULL) w->data->contenttype = CT_TEXT_HTML; - else if(strstr(filename, ".js") != NULL) w->data->contenttype = CT_APPLICATION_X_JAVASCRIPT; - else if(strstr(filename, ".css") != NULL) w->data->contenttype = CT_TEXT_CSS; - else if(strstr(filename, ".xml") != NULL) w->data->contenttype = CT_TEXT_XML; - else if(strstr(filename, ".xsl") != NULL) w->data->contenttype = CT_TEXT_XSL; - else if(strstr(filename, ".txt") != NULL) w->data->contenttype = CT_TEXT_PLAIN; - else if(strstr(filename, ".svg") != NULL) w->data->contenttype = CT_IMAGE_SVG_XML; - else if(strstr(filename, ".ttf") != NULL) w->data->contenttype = CT_APPLICATION_X_FONT_TRUETYPE; - else if(strstr(filename, ".otf") != NULL) w->data->contenttype = CT_APPLICATION_X_FONT_OPENTYPE; - else if(strstr(filename, ".woff") != NULL) w->data->contenttype = CT_APPLICATION_FONT_WOFF; - else if(strstr(filename, ".eot") != NULL) w->data->contenttype = CT_APPLICATION_VND_MS_FONTOBJ; - else w->data->contenttype = CT_APPLICATION_OCTET_STREAM; + if(strstr(filename, ".html") != NULL) w->response.data->contenttype = CT_TEXT_HTML; + else if(strstr(filename, ".js") != NULL) w->response.data->contenttype = CT_APPLICATION_X_JAVASCRIPT; + else if(strstr(filename, ".css") != NULL) w->response.data->contenttype = CT_TEXT_CSS; + else if(strstr(filename, ".xml") != NULL) w->response.data->contenttype = CT_TEXT_XML; + else if(strstr(filename, ".xsl") != NULL) w->response.data->contenttype = CT_TEXT_XSL; + else if(strstr(filename, ".txt") != NULL) w->response.data->contenttype = CT_TEXT_PLAIN; + else if(strstr(filename, ".svg") != NULL) w->response.data->contenttype = CT_IMAGE_SVG_XML; + else if(strstr(filename, ".ttf") != NULL) w->response.data->contenttype = CT_APPLICATION_X_FONT_TRUETYPE; + else if(strstr(filename, ".otf") != NULL) w->response.data->contenttype = CT_APPLICATION_X_FONT_OPENTYPE; + else if(strstr(filename, ".woff") != NULL) w->response.data->contenttype = CT_APPLICATION_FONT_WOFF; + else if(strstr(filename, ".eot") != NULL) w->response.data->contenttype = CT_APPLICATION_VND_MS_FONTOBJ; + else w->response.data->contenttype = CT_APPLICATION_OCTET_STREAM; debug(D_WEB_CLIENT_ACCESS, "%llu: Sending file '%s' (%ld bytes, ifd %d, ofd %d).", w->id, webfilename, stat.st_size, w->ifd, w->ofd); w->mode = WEB_CLIENT_MODE_FILECOPY; w->wait_receive = 1; w->wait_send = 0; - web_buffer_flush(w->data); - w->data->rlen = stat.st_size; - w->data->date = stat.st_mtim.tv_sec; + buffer_flush(w->response.data); + w->response.rlen = stat.st_size; + w->response.data->date = stat.st_mtim.tv_sec; return 200; } -void web_client_reset(struct web_client *w) -{ - struct timeval tv; - gettimeofday(&tv, NULL); - - long sent = (w->mode == WEB_CLIENT_MODE_FILECOPY)?w->data->rlen:w->data->len; - -#ifdef NETDATA_WITH_ZLIB - if(likely(w->zoutput)) sent = (long)w->zstream.total_out; -#endif - - long size = (w->mode == WEB_CLIENT_MODE_FILECOPY)?w->data->rlen:w->data->len; - - if(likely(w->last_url[0])) - log_access("%llu: (sent/all = %ld/%ld bytes %0.0f%%, prep/sent/total = %0.2f/%0.2f/%0.2f ms) %s: '%s'", - w->id, - sent, size, -((size>0)?((float)(size-sent)/(float)size * 100.0):0.0), - (float)usecdiff(&w->tv_ready, &w->tv_in) / 1000.0, - (float)usecdiff(&tv, &w->tv_ready) / 1000.0, - (float)usecdiff(&tv, &w->tv_in) / 1000.0, - (w->mode == WEB_CLIENT_MODE_FILECOPY)?"filecopy":"data", - w->last_url - ); - - debug(D_WEB_CLIENT, "%llu: Reseting client.", w->id); - - if(unlikely(w->mode == WEB_CLIENT_MODE_FILECOPY)) { - debug(D_WEB_CLIENT, "%llu: Closing filecopy input file.", w->id); - close(w->ifd); - w->ifd = w->ofd; - } - - w->last_url[0] = '\0'; - - w->mode = WEB_CLIENT_MODE_NORMAL; - - w->response_header[0] = '\0'; - - web_buffer_reset(w->data); - - w->wait_receive = 1; - w->wait_send = 0; - - w->zoutput = 0; - - // if we had enabled compression, release it -#ifdef NETDATA_WITH_ZLIB - if(w->zinitialized) { - debug(D_DEFLATE, "%llu: Reseting compression.", w->id); - deflateEnd(&w->zstream); - w->zsent = 0; - w->zhave = 0; - w->zstream.avail_in = 0; - w->zstream.avail_out = 0; - w->zstream.total_in = 0; - w->zstream.total_out = 0; - w->zinitialized = 0; - } -#endif // NETDATA_WITH_ZLIB -} #ifdef NETDATA_WITH_ZLIB void web_client_enable_deflate(struct web_client *w) { - if(w->zinitialized == 1) { + if(w->response.zinitialized == 1) { error("%llu: Compression has already be initialized for this client.", w->id); return; } - if(w->data->sent) { + if(w->response.sent) { error("%llu: Cannot enable compression in the middle of a conversation.", w->id); return; } - w->zstream.zalloc = Z_NULL; - w->zstream.zfree = Z_NULL; - w->zstream.opaque = Z_NULL; + w->response.zstream.zalloc = Z_NULL; + w->response.zstream.zfree = Z_NULL; + w->response.zstream.opaque = Z_NULL; - w->zstream.next_in = (Bytef *)w->data->buffer; - w->zstream.avail_in = 0; - w->zstream.total_in = 0; + w->response.zstream.next_in = (Bytef *)w->response.data->buffer; + w->response.zstream.avail_in = 0; + w->response.zstream.total_in = 0; - w->zstream.next_out = w->zbuffer; - w->zstream.avail_out = 0; - w->zstream.total_out = 0; + w->response.zstream.next_out = w->response.zbuffer; + w->response.zstream.avail_out = 0; + w->response.zstream.total_out = 0; - w->zstream.zalloc = Z_NULL; - w->zstream.zfree = Z_NULL; - w->zstream.opaque = Z_NULL; + w->response.zstream.zalloc = Z_NULL; + w->response.zstream.zfree = Z_NULL; + w->response.zstream.opaque = Z_NULL; -// if(deflateInit(&w->zstream, Z_DEFAULT_COMPRESSION) != Z_OK) { +// if(deflateInit(&w->response.zstream, Z_DEFAULT_COMPRESSION) != Z_OK) { // error("%llu: Failed to initialize zlib. Proceeding without compression.", w->id); // return; // } // Select GZIP compression: windowbits = 15 + 16 = 31 - if(deflateInit2(&w->zstream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY) != Z_OK) { + if(deflateInit2(&w->response.zstream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY) != Z_OK) { error("%llu: Failed to initialize zlib. Proceeding without compression.", w->id); return; } - w->zsent = 0; - w->zoutput = 1; - w->zinitialized = 1; + w->response.zsent = 0; + w->response.zoutput = 1; + w->response.zinitialized = 1; debug(D_DEFLATE, "%llu: Initialized compression.", w->id); } #endif // NETDATA_WITH_ZLIB -int rrdset_validate_dimensions(RRDSET *st, struct web_buffer *dimensions) +int rrdset_validate_dimensions(RRDSET *st, BUFFER *dimensions) { ; } @@ -354,8 +378,8 @@ int web_client_api_request_v1_data_format(char *name) int web_client_api_request_v1_charts(struct web_client *w, char *url) { - web_buffer_flush(w->data); - rrd_stats_api_v1_charts(w->data); + buffer_flush(w->response.data); + rrd_stats_api_v1_charts(w->response.data); return 200; } @@ -364,7 +388,7 @@ int web_client_api_request_v1_chart(struct web_client *w, char *url) int ret = 400; char *chart = NULL; - web_buffer_flush(w->data); + buffer_flush(w->response.data); while(url) { char *value = mystrsep(&url, "?&[]"); @@ -379,26 +403,26 @@ int web_client_api_request_v1_chart(struct web_client *w, char *url) if(!strcmp(name, "chart")) chart = value; else { - web_buffer_snprintf(w->data, 1024, "Unknown parameter '%s' in request.", name); + buffer_sprintf(w->response.data, "Unknown parameter '%s' in request.", name); goto cleanup; } } if(!chart || !*chart) { - web_buffer_snprintf(w->data, 1024, "No chart id is given at the request."); + buffer_sprintf(w->response.data, "No chart id is given at the request."); goto cleanup; } RRDSET *st = rrdset_find(chart); if(!st) st = rrdset_find_byname(chart); if(!st) { - web_buffer_snprintf(w->data, 1024, "Chart '%s' is not found.", chart); + buffer_sprintf(w->response.data, "Chart '%s' is not found.", chart); ret = 404; goto cleanup; } - w->data->contenttype = CT_APPLICATION_JSON; - rrd_stats_api_v1_chart(st, w->data); + w->response.data->contenttype = CT_APPLICATION_JSON; + rrd_stats_api_v1_chart(st, w->response.data); return 200; cleanup: @@ -408,9 +432,9 @@ cleanup: int web_client_api_request_v1_data(struct web_client *w, char *url) { int ret = 400; - struct web_buffer *dimensions = NULL; + BUFFER *dimensions = NULL; - web_buffer_flush(w->data); + buffer_flush(w->response.data); char *chart = NULL , *dim = NULL @@ -435,10 +459,10 @@ int web_client_api_request_v1_data(struct web_client *w, char *url) if(!strcmp(name, "chart")) chart = value; else if(!strcmp(name, "dimension") || !strcmp(name, "dim")) { - if(!dimensions) dimensions = web_buffer_create(strlen(value)); + if(!dimensions) dimensions = buffer_create(strlen(value)); if(dimensions) { - web_buffer_strcat(dimensions, "|"); - web_buffer_strcat(dimensions, value); + buffer_strcat(dimensions, "|"); + buffer_strcat(dimensions, value); } } else if(!strcmp(name, "after")) after_str = value; @@ -452,20 +476,20 @@ int web_client_api_request_v1_data(struct web_client *w, char *url) options = value; } else { - web_buffer_snprintf(w->data, 1024, "Unknown parameter '%s' in request.", name); + buffer_sprintf(w->response.data, "Unknown parameter '%s' in request.", name); goto cleanup; } } if(!chart || !*chart) { - web_buffer_snprintf(w->data, 1024, "No chart id is given at the request."); + buffer_sprintf(w->response.data, "No chart id is given at the request."); goto cleanup; } RRDSET *st = rrdset_find(chart); if(!st) st = rrdset_find_byname(chart); if(!st) { - web_buffer_snprintf(w->data, 1024, "Chart '%s' is not found.", chart); + buffer_sprintf(w->response.data, "Chart '%s' is not found.", chart); ret = 404; goto cleanup; } @@ -475,10 +499,10 @@ int web_client_api_request_v1_data(struct web_client *w, char *url) int points = (points_str && *points_str)?atoi(points_str):0; int group = (group_str && *group_str) ?atoi(group_str):0; - web_buffer_snprintf(w->data, 1024, "API command 'data' for chart '%s', dimensions '%s', after '%s', before '%s', points '%s', group '%s', format '%s', options '%s'", chart, dim, after_str, before_str, points_str, group_str, format, options); + buffer_sprintf(w->response.data, "API command 'data' for chart '%s', dimensions '%s', after '%s', before '%s', points '%s', group '%s', format '%s', options '%s'", chart, dim, after_str, before_str, points_str, group_str, format, options); cleanup: - if(dimensions) web_buffer_free(dimensions); + if(dimensions) buffer_free(dimensions); return ret; } @@ -495,8 +519,8 @@ int web_client_api_request_v1(struct web_client *w, char *url) else if(strcmp(tok, "charts") == 0) return web_client_api_request_v1_charts(w, url); - web_buffer_flush(w->data); - web_buffer_snprintf(w->data, 1024, "Unsupported v1 API command: %s", tok); + buffer_flush(w->response.data); + buffer_sprintf(w->response.data, "Unsupported v1 API command: %s", tok); return 404; } @@ -509,8 +533,8 @@ int web_client_api_request(struct web_client *w, char *url) if(strcmp(tok, "v1") == 0) return web_client_api_request_v1(w, url); - web_buffer_flush(w->data); - web_buffer_snprintf(w->data, 1024, "Unsupported API version: %s", tok); + buffer_flush(w->response.data); + buffer_sprintf(w->response.data, "Unsupported API version: %s", tok); return 404; } @@ -532,7 +556,7 @@ int web_client_data_request(struct web_client *w, char *url, int datasource_type if(!st) { // we don't have it // try to send a file with that name - web_buffer_flush(w->data); + buffer_flush(w->response.data); return(mysendfile(w, tok)); } @@ -585,8 +609,8 @@ int web_client_data_request(struct web_client *w, char *url, int datasource_type if(tok && strcmp(tok, "nonzero") == 0) nonzero = 1; } - w->data->contenttype = CT_APPLICATION_JSON; - web_buffer_flush(w->data); + w->response.data->contenttype = CT_APPLICATION_JSON; + buffer_flush(w->response.data); char *google_version = "0.6"; char *google_reqId = "0"; @@ -597,7 +621,7 @@ int web_client_data_request(struct web_client *w, char *url, int datasource_type unsigned long last_timestamp_in_data = 0; if(datasource_type == DATASOURCE_GOOGLE_JSON || datasource_type == DATASOURCE_GOOGLE_JSONP) { - w->data->contenttype = CT_APPLICATION_X_JAVASCRIPT; + w->response.data->contenttype = CT_APPLICATION_X_JAVASCRIPT; while(args) { tok = mystrsep(&args, "&"); @@ -638,7 +662,7 @@ int web_client_data_request(struct web_client *w, char *url, int datasource_type // check the client wants json if(strcmp(google_out, "json") != 0) { - web_buffer_snprintf(w->data, 65536, + buffer_sprintf(w->response.data, "%s({version:'%s',reqId:'%s',status:'error',errors:[{reason:'invalid_query',message:'output format is not supported',detailed_message:'the format %s requested is not supported by netdata.'}]});", google_responseHandler, google_version, google_reqId, google_out); return 200; @@ -647,21 +671,21 @@ int web_client_data_request(struct web_client *w, char *url, int datasource_type } if(datasource_type == DATASOURCE_GOOGLE_JSONP) { - web_buffer_snprintf(w->data, 65536, + buffer_sprintf(w->response.data, "%s({version:'%s',reqId:'%s',status:'ok',sig:'%lu',table:", google_responseHandler, google_version, google_reqId, st->last_updated.tv_sec); } debug(D_WEB_CLIENT_ACCESS, "%llu: Sending RRD data '%s' (id %s, %d lines, %d group, %d group_method, %lu after, %lu before).", w->id, st->name, st->id, lines, group_count, group_method, after, before); - unsigned long timestamp_in_data = rrd_stats_json(datasource_type, st, w->data, lines, group_count, group_method, after, before, nonzero); + unsigned long timestamp_in_data = rrd_stats_json(datasource_type, st, w->response.data, lines, group_count, group_method, after, before, nonzero); if(datasource_type == DATASOURCE_GOOGLE_JSONP) { if(timestamp_in_data > last_timestamp_in_data) - web_buffer_strcat(w->data, "});"); + buffer_strcat(w->response.data, "});"); else { // the client already has the latest data - web_buffer_snprintf(w->data, 65536, + buffer_sprintf(w->response.data, "%s({version:'%s',reqId:'%s',status:'error',errors:[{reason:'not_modified',message:'Data not modified'}]});", google_responseHandler, google_version, google_reqId); } @@ -682,9 +706,9 @@ int web_client_parse_request(struct web_client *w) { web_client_clean_request(w); - debug(D_WEB_DATA, "%llu: Processing data buffer of %d bytes: '%s'.", w->id, w->data->bytes, w->data->buffer); + debug(D_WEB_DATA, "%llu: Processing data buffer of %d bytes: '%s'.", w->id, w->response.data->bytes, w->response.data->buffer); - char *buf = w->data->buffer; + char *buf = w->response.data->buffer; char *line, *tok; // ------------------------------------------------------------------------ @@ -725,29 +749,29 @@ void web_client_process(struct web_client *w) { w->wait_receive = 0; // check if we have an empty line (end of HTTP header) - if(strstr(w->data->buffer, "\r\n\r\n")) { + if(strstr(w->response.data->buffer, "\r\n\r\n")) { global_statistics_lock(); global_statistics.web_requests++; global_statistics_unlock(); gettimeofday(&w->tv_in, NULL); - debug(D_WEB_DATA, "%llu: Processing data buffer of %d bytes: '%s'.", w->id, w->data->len, w->data->buffer); + debug(D_WEB_DATA, "%llu: Processing data buffer of %d bytes: '%s'.", w->id, w->response.data->len, w->response.data->buffer); // check if the client requested keep-alive HTTP - if(strcasestr(w->data->buffer, "Connection: keep-alive")) w->keepalive = 1; + if(strcasestr(w->response.data->buffer, "Connection: keep-alive")) w->keepalive = 1; else w->keepalive = 0; #ifdef NETDATA_WITH_ZLIB // check if the client accepts deflate - if(web_enable_gzip && strstr(w->data->buffer, "gzip")) + if(web_enable_gzip && strstr(w->response.data->buffer, "gzip")) web_client_enable_deflate(w); #endif // NETDATA_WITH_ZLIB int datasource_type = DATASOURCE_GOOGLE_JSONP; - //if(strstr(w->data->buffer, "X-DataSource-Auth")) + //if(strstr(w->response.data->buffer, "X-DataSource-Auth")) // datasource_type = DATASOURCE_GOOGLE_JSON; - char *buf = w->data->buffer; + char *buf = w->response.data->buffer; char *tok = strsep(&buf, " \r\n"); char *url = NULL; char *pointer_to_free = NULL; // keep url_decode() allocated buffer @@ -801,19 +825,19 @@ void web_client_process(struct web_client *w) { if(!st) { // we don't have it // try to send a file with that name - web_buffer_flush(w->data); + buffer_flush(w->response.data); code = mysendfile(w, tok); } else { code = 200; debug(D_WEB_CLIENT_ACCESS, "%llu: Sending %s.json of RRD_STATS...", w->id, st->name); - w->data->contenttype = CT_APPLICATION_JSON; - web_buffer_flush(w->data); - rrd_stats_graph_json(st, url, w->data); + w->response.data->contenttype = CT_APPLICATION_JSON; + buffer_flush(w->response.data); + rrd_stats_graph_json(st, url, w->response.data); } } else if(strcmp(tok, "debug") == 0) { - web_buffer_flush(w->data); + buffer_flush(w->response.data); // get the name of the data to show tok = mystrsep(&url, "/?&"); @@ -824,14 +848,14 @@ void web_client_process(struct web_client *w) { if(!st) st = rrdset_find(tok); if(!st) { code = 404; - web_buffer_snprintf(w->data, 1024, "Chart %s is not found.\r\n", tok); + buffer_sprintf(w->response.data, "Chart %s is not found.\r\n", tok); debug(D_WEB_CLIENT_ACCESS, "%llu: %s is not found.", w->id, tok); } else { code = 200; debug_flags |= D_RRD_STATS; st->debug = st->debug?0:1; - web_buffer_snprintf(w->data, 1024, "Chart %s has now debug %s.\r\n", tok, st->debug?"enabled":"disabled"); + buffer_sprintf(w->response.data, "Chart %s has now debug %s.\r\n", tok, st->debug?"enabled":"disabled"); debug(D_WEB_CLIENT_ACCESS, "%llu: debug for %s is %s.", w->id, tok, st->debug?"enabled":"disabled"); } } @@ -841,7 +865,7 @@ void web_client_process(struct web_client *w) { debug(D_WEB_CLIENT_ACCESS, "%llu: Mirroring...", w->id); // replace the zero bytes with spaces - web_buffer_char_replace(w->data, '\0', ' '); + buffer_char_replace(w->response.data, '\0', ' '); // just leave the buffer as is // it will be copied back to the client @@ -851,43 +875,43 @@ void web_client_process(struct web_client *w) { debug(D_WEB_CLIENT_ACCESS, "%llu: Sending list of RRD_STATS...", w->id); - web_buffer_flush(w->data); + buffer_flush(w->response.data); RRDSET *st = rrdset_root; for ( ; st ; st = st->next ) - web_buffer_snprintf(w->data, 1024, "%s\n", st->name); + buffer_sprintf(w->response.data, "%s\n", st->name); } else if(strcmp(tok, "all.json") == 0) { code = 200; debug(D_WEB_CLIENT_ACCESS, "%llu: Sending JSON list of all monitors of RRD_STATS...", w->id); - w->data->contenttype = CT_APPLICATION_JSON; - web_buffer_flush(w->data); - rrd_stats_all_json(w->data); + w->response.data->contenttype = CT_APPLICATION_JSON; + buffer_flush(w->response.data); + rrd_stats_all_json(w->response.data); } else if(strcmp(tok, "netdata.conf") == 0) { code = 200; debug(D_WEB_CLIENT_ACCESS, "%llu: Sending netdata.conf ...", w->id); - w->data->contenttype = CT_TEXT_PLAIN; - web_buffer_flush(w->data); - generate_config(w->data, 0); + w->response.data->contenttype = CT_TEXT_PLAIN; + buffer_flush(w->response.data); + generate_config(w->response.data, 0); } else if(strcmp(tok, WEB_PATH_FILE) == 0) { // "file" tok = mystrsep(&url, "/?&"); if(tok && *tok) code = mysendfile(w, tok); else { code = 400; - web_buffer_flush(w->data); - web_buffer_strcat(w->data, "You have to give a filename to get.\r\n"); + buffer_flush(w->response.data); + buffer_strcat(w->response.data, "You have to give a filename to get.\r\n"); } } else if(!tok[0]) { - web_buffer_flush(w->data); + buffer_flush(w->response.data); code = mysendfile(w, "index.html"); } else { - web_buffer_flush(w->data); + buffer_flush(w->response.data); code = mysendfile(w, tok); } @@ -898,21 +922,21 @@ void web_client_process(struct web_client *w) { if(buf) debug(D_WEB_CLIENT_ACCESS, "%llu: Cannot understand '%s'.", w->id, buf); code = 500; - web_buffer_flush(w->data); - web_buffer_strcat(w->data, "I don't understand you...\r\n"); + buffer_flush(w->response.data); + buffer_strcat(w->response.data, "I don't understand you...\r\n"); } // free url_decode() buffer if(pointer_to_free) free(pointer_to_free); } - else if(w->data->len > 8192) { + else if(w->response.data->len > 8192) { strcpy(w->last_url, "too big request"); debug(D_WEB_CLIENT_ACCESS, "%llu: Received request is too big.", w->id); code = 400; - web_buffer_flush(w->data); - web_buffer_strcat(w->data, "Received request is too big.\r\n"); + buffer_flush(w->response.data); + buffer_strcat(w->response.data, "Received request is too big.\r\n"); } else { // wait for more data @@ -920,19 +944,19 @@ void web_client_process(struct web_client *w) { return; } - if(w->data->len > w->data->size) { - error("%llu: memory overflow encountered (size is %ld, written %ld).", w->data->size, w->data->len); + if(w->response.data->len > w->response.data->size) { + error("%llu: memory overflow encountered (size is %ld, written %ld).", w->response.data->size, w->response.data->len); } gettimeofday(&w->tv_ready, NULL); - w->data->date = time(NULL); - w->data->sent = 0; + w->response.data->date = time(NULL); + w->response.sent = 0; // prepare the HTTP response header debug(D_WEB_CLIENT, "%llu: Generating HTTP header with response %d.", w->id, code); char *content_type_string = ""; - switch(w->data->contenttype) { + switch(w->response.data->contenttype) { case CT_TEXT_HTML: content_type_string = "text/html; charset=utf-8"; break; @@ -1019,15 +1043,10 @@ void web_client_process(struct web_client *w) { } char date[100]; - struct tm tm = *gmtime(&w->data->date); + struct tm tm = *gmtime(&w->response.data->date); strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %Z", &tm); - char custom_header[MAX_HTTP_HEADER_SIZE + 1] = ""; - if(w->response_header[0]) - strcpy(custom_header, w->response_header); - - int headerlen = 0; - headerlen += snprintf(&w->response_header[headerlen], MAX_HTTP_HEADER_SIZE - headerlen, + buffer_sprintf(w->response.header_output, "HTTP/1.1 %d %s\r\n" "Connection: %s\r\n" "Server: NetData Embedded HTTP Server\r\n" @@ -1040,50 +1059,54 @@ void web_client_process(struct web_client *w) { , date ); - if(custom_header[0]) - headerlen += snprintf(&w->response_header[headerlen], MAX_HTTP_HEADER_SIZE - headerlen, "%s", custom_header); + if(buffer_strlen(w->response.header)) + buffer_strcat(w->response.header_output, buffer_tostring(w->response.header)); if(w->mode == WEB_CLIENT_MODE_NORMAL) { - headerlen += snprintf(&w->response_header[headerlen], MAX_HTTP_HEADER_SIZE - headerlen, + buffer_sprintf(w->response.header_output, "Expires: %s\r\n" "Cache-Control: no-cache\r\n" - , date - ); - } - else { - headerlen += snprintf(&w->response_header[headerlen], MAX_HTTP_HEADER_SIZE - headerlen, - "Cache-Control: public\r\n" - ); + , date); } + else + buffer_strcat(w->response.header_output, "Cache-Control: public\r\n"); // if we know the content length, put it - if(!w->zoutput && (w->data->len || w->data->rlen)) - headerlen += snprintf(&w->response_header[headerlen], MAX_HTTP_HEADER_SIZE - headerlen, + if(!w->response.zoutput && (w->response.data->len || w->response.rlen)) + buffer_sprintf(w->response.header_output, "Content-Length: %ld\r\n" - , w->data->len?w->data->len:w->data->rlen + , w->response.data->len?w->response.data->len:w->response.rlen ); - else if(!w->zoutput) + else if(!w->response.zoutput) w->keepalive = 0; // content-length is required for keep-alive - if(w->zoutput) { - headerlen += snprintf(&w->response_header[headerlen], MAX_HTTP_HEADER_SIZE - headerlen, + if(w->response.zoutput) { + buffer_strcat(w->response.header_output, "Content-Encoding: gzip\r\n" "Transfer-Encoding: chunked\r\n" ); } - headerlen += snprintf(&w->response_header[headerlen], MAX_HTTP_HEADER_SIZE - headerlen, "\r\n"); + buffer_strcat(w->response.header_output, "\r\n"); // disable TCP_NODELAY, to buffer the header int flag = 0; - if(setsockopt(w->ofd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)) != 0) error("%llu: failed to disable TCP_NODELAY on socket.", w->id); + if(setsockopt(w->ofd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)) != 0) + error("%llu: failed to disable TCP_NODELAY on socket.", w->id); // sent the HTTP header - debug(D_WEB_DATA, "%llu: Sending response HTTP header of size %d: '%s'", w->id, headerlen, w->response_header); + debug(D_WEB_DATA, "%llu: Sending response HTTP header of size %d: '%s'" + , w->id + , buffer_strlen(w->response.header_output) + , buffer_tostring(w->response.header_output) + ); - bytes = send(w->ofd, w->response_header, headerlen, 0); - if(bytes != headerlen) - error("%llu: HTTP Header failed to be sent (I sent %d bytes but the system sent %d bytes).", w->id, headerlen, bytes); + bytes = send(w->ofd, buffer_tostring(w->response.header_output), buffer_strlen(w->response.header_output), 0); + if(bytes != buffer_strlen(w->response.header_output)) + error("%llu: HTTP Header failed to be sent (I sent %d bytes but the system sent %d bytes)." + , w->id + , buffer_strlen(w->response.header_output) + , bytes); else { global_statistics_lock(); global_statistics.bytes_sent += bytes; @@ -1095,18 +1118,18 @@ void web_client_process(struct web_client *w) { if(setsockopt(w->ofd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)) != 0) error("%llu: failed to enable TCP_NODELAY on socket.", w->id); // enable sending immediately if we have data - if(w->data->len) w->wait_send = 1; + if(w->response.data->len) w->wait_send = 1; else w->wait_send = 0; // pretty logging switch(w->mode) { case WEB_CLIENT_MODE_NORMAL: - debug(D_WEB_CLIENT, "%llu: Done preparing the response. Sending data (%d bytes) to client.", w->id, w->data->len); + debug(D_WEB_CLIENT, "%llu: Done preparing the response. Sending data (%d bytes) to client.", w->id, w->response.data->len); break; case WEB_CLIENT_MODE_FILECOPY: - if(w->data->rlen) { - debug(D_WEB_CLIENT, "%llu: Done preparing the response. Will be sending data file of %d bytes to client.", w->id, w->data->rlen); + if(w->response.rlen) { + debug(D_WEB_CLIENT, "%llu: Done preparing the response. Will be sending data file of %d bytes to client.", w->id, w->response.rlen); w->wait_receive = 1; /* @@ -1114,8 +1137,8 @@ void web_client_process(struct web_client *w) { // this block of code can be commented, without anything missing. // when it is commented, the program will copy the data using async I/O. { - long len = sendfile(w->ofd, w->ifd, NULL, w->data->rbytes); - if(len != w->data->rbytes) error("%llu: sendfile() should copy %ld bytes, but copied %ld. Falling back to manual copy.", w->id, w->data->rbytes, len); + 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); else web_client_reset(w); } */ @@ -1176,24 +1199,24 @@ long web_client_send_deflate(struct web_client *w) long len = 0, t = 0; // when using compression, - // w->data->sent is the amount of bytes passed through compression + // w->response.sent is the amount of bytes passed through compression - // debug(D_DEFLATE, "%llu: TEST w->data->bytes = %d, w->data->sent = %d, w->zhave = %d, w->zsent = %d, w->zstream.avail_in = %d, w->zstream.avail_out = %d, w->zstream.total_in = %d, w->zstream.total_out = %d.", w->id, w->data->bytes, w->data->sent, w->zhave, w->zsent, w->zstream.avail_in, w->zstream.avail_out, w->zstream.total_in, w->zstream.total_out); + debug(D_DEFLATE, "%llu: TEST w->response.data->len = %d, w->response.sent = %d, w->response.zhave = %d, w->response.zsent = %d, w->response.zstream.avail_in = %d, w->response.zstream.avail_out = %d, w->response.zstream.total_in = %d, w->response.zstream.total_out = %d.", 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->data->len - w->data->sent == 0 && w->zstream.avail_in == 0 && w->zhave == w->zsent && w->zstream.avail_out != 0) { + 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); // finalize the chunk - if(w->data->sent != 0) + if(w->response.sent != 0) t += web_client_send_chunk_finalize(w); // there can be two cases for this // A. we have done everything // B. we temporarily have nothing to send, waiting for the buffer to be filled by ifd - if(w->mode == WEB_CLIENT_MODE_FILECOPY && w->wait_receive && w->ifd != w->ofd && w->data->rlen && w->data->rlen > w->data->len) { + if(w->mode == WEB_CLIENT_MODE_FILECOPY && w->wait_receive && w->ifd != w->ofd && 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); w->wait_send = 0; @@ -1201,7 +1224,7 @@ long web_client_send_deflate(struct web_client *w) } if(w->keepalive == 0) { - debug(D_WEB_CLIENT, "%llu: Closing (keep-alive is not enabled). %ld bytes sent.", w->id, w->data->sent); + debug(D_WEB_CLIENT, "%llu: Closing (keep-alive is not enabled). %ld bytes sent.", w->id, w->response.sent); errno = 0; return(-1); } @@ -1212,28 +1235,28 @@ long web_client_send_deflate(struct web_client *w) return(0); } - if(w->zstream.avail_out == 0 && w->zhave == w->zsent) { + if(w->response.zhave == w->response.zsent) { // compress more input data // close the previous open chunk - if(w->data->sent != 0) t += web_client_send_chunk_close(w); + if(w->response.sent != 0) t += web_client_send_chunk_close(w); - debug(D_DEFLATE, "%llu: Compressing %d bytes starting from %d.", w->id, (w->data->len - w->data->sent), w->data->sent); + debug(D_DEFLATE, "%llu: Compressing %d bytes starting from %d.", w->id, (w->response.data->len - w->response.sent), w->response.sent); // give the compressor all the data not passed through the compressor yet - if(w->data->len > w->data->sent) { - w->zstream.next_in = (Bytef *)&w->data->buffer[w->data->sent]; - w->zstream.avail_in = (w->data->len - w->data->sent); + if(w->response.data->len > w->response.sent) { + w->response.zstream.next_in = (Bytef *)&w->response.data->buffer[w->response.sent]; + w->response.zstream.avail_in = (w->response.data->len - w->response.sent); } // reset the compressor output buffer - w->zstream.next_out = w->zbuffer; - w->zstream.avail_out = ZLIB_CHUNK; + w->response.zstream.next_out = w->response.zbuffer; + w->response.zstream.avail_out = ZLIB_CHUNK; // ask for FINISH if we have all the input int flush = Z_SYNC_FLUSH; if(w->mode == WEB_CLIENT_MODE_NORMAL - || (w->mode == WEB_CLIENT_MODE_FILECOPY && w->data->len == w->data->rlen)) { + || (w->mode == WEB_CLIENT_MODE_FILECOPY && w->response.data->len == w->response.rlen)) { flush = Z_FINISH; debug(D_DEFLATE, "%llu: Requesting Z_FINISH.", w->id); } @@ -1242,31 +1265,31 @@ long web_client_send_deflate(struct web_client *w) } // compress - if(deflate(&w->zstream, flush) == Z_STREAM_ERROR) { + if(deflate(&w->response.zstream, flush) == Z_STREAM_ERROR) { error("%llu: Compression failed. Closing down client.", w->id); web_client_reset(w); return(-1); } - w->zhave = ZLIB_CHUNK - w->zstream.avail_out; - w->zsent = 0; + w->response.zhave = ZLIB_CHUNK - w->response.zstream.avail_out; + w->response.zsent = 0; // keep track of the bytes passed through the compressor - w->data->sent = w->data->len; + w->response.sent = w->response.data->len; - debug(D_DEFLATE, "%llu: Compression produced %d bytes.", w->id, w->zhave); + debug(D_DEFLATE, "%llu: Compression produced %d bytes.", w->id, w->response.zhave); // open a new chunk - t += web_client_send_chunk_header(w, w->zhave); + t += web_client_send_chunk_header(w, w->response.zhave); } - len = send(w->ofd, &w->zbuffer[w->zsent], w->zhave - w->zsent, MSG_DONTWAIT); + len = send(w->ofd, &w->response.zbuffer[w->response.zsent], w->response.zhave - w->response.zsent, MSG_DONTWAIT); if(len > 0) { - w->zsent += len; + w->response.zsent += len; if(t > 0) len += t; debug(D_WEB_CLIENT, "%llu: Sent %d bytes.", w->id, len); } - else if(len == 0) debug(D_WEB_CLIENT, "%llu: Did not send any bytes to the client.", w->id); + else if(len == 0) debug(D_WEB_CLIENT, "%llu: Did not send any bytes to the client (zhave = %ld, zsent = %ld, need to send = %ld).", 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. Reason: %s", w->id, strerror(errno)); return(len); @@ -1276,12 +1299,12 @@ long web_client_send_deflate(struct web_client *w) long web_client_send(struct web_client *w) { #ifdef NETDATA_WITH_ZLIB - if(likely(w->zoutput)) return web_client_send_deflate(w); + if(likely(w->response.zoutput)) return web_client_send_deflate(w); #endif // NETDATA_WITH_ZLIB long bytes; - if(unlikely(w->data->len - w->data->sent == 0)) { + 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); @@ -1290,7 +1313,7 @@ long web_client_send(struct web_client *w) // A. we have done everything // B. we temporarily have nothing to send, waiting for the buffer to be filled by ifd - if(w->mode == WEB_CLIENT_MODE_FILECOPY && w->wait_receive && w->ifd != w->ofd && w->data->rlen && w->data->rlen > w->data->len) { + if(w->mode == WEB_CLIENT_MODE_FILECOPY && w->wait_receive && w->ifd != w->ofd && 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); w->wait_send = 0; @@ -1298,7 +1321,7 @@ long web_client_send(struct web_client *w) } if(unlikely(w->keepalive == 0)) { - debug(D_WEB_CLIENT, "%llu: Closing (keep-alive is not enabled). %ld bytes sent.", w->id, w->data->sent); + debug(D_WEB_CLIENT, "%llu: Closing (keep-alive is not enabled). %ld bytes sent.", w->id, w->response.sent); errno = 0; return(-1); } @@ -1308,9 +1331,9 @@ long web_client_send(struct web_client *w) return(0); } - bytes = send(w->ofd, &w->data->buffer[w->data->sent], w->data->len - w->data->sent, MSG_DONTWAIT); + bytes = send(w->ofd, &w->response.data->buffer[w->response.sent], w->response.data->len - w->response.sent, MSG_DONTWAIT); if(likely(bytes > 0)) { - w->data->sent += bytes; + w->response.sent += bytes; debug(D_WEB_CLIENT, "%llu: Sent %d bytes.", w->id, bytes); } else if(likely(bytes == 0)) debug(D_WEB_CLIENT, "%llu: Did not send any bytes to the client.", w->id); @@ -1322,27 +1345,27 @@ long web_client_send(struct web_client *w) long web_client_receive(struct web_client *w) { // do we have any space for more data? - web_buffer_need_bytes(w->data, WEB_DATA_LENGTH_INCREASE_STEP); + buffer_need_bytes(w->response.data, WEB_REQUEST_LENGTH); - long left = w->data->size - w->data->len; + long left = w->response.data->size - w->response.data->len; long bytes; if(unlikely(w->mode == WEB_CLIENT_MODE_FILECOPY)) - bytes = read(w->ifd, &w->data->buffer[w->data->len], (left-1)); + bytes = read(w->ifd, &w->response.data->buffer[w->response.data->len], (left-1)); else - bytes = recv(w->ifd, &w->data->buffer[w->data->len], left-1, MSG_DONTWAIT); + bytes = recv(w->ifd, &w->response.data->buffer[w->response.data->len], left-1, MSG_DONTWAIT); if(likely(bytes > 0)) { - int old = w->data->len; - w->data->len += bytes; - w->data->buffer[w->data->len] = '\0'; + int old = w->response.data->len; + w->response.data->len += bytes; + w->response.data->buffer[w->response.data->len] = '\0'; debug(D_WEB_CLIENT, "%llu: Received %d bytes.", w->id, bytes); - debug(D_WEB_DATA, "%llu: Received data: '%s'.", w->id, &w->data->buffer[old]); + debug(D_WEB_DATA, "%llu: Received data: '%s'.", w->id, &w->response.data->buffer[old]); if(w->mode == WEB_CLIENT_MODE_FILECOPY) { w->wait_send = 1; - if(w->data->rlen && w->data->len >= w->data->rlen) w->wait_receive = 0; + if(w->response.rlen && w->response.data->len >= w->response.rlen) w->wait_receive = 0; } } else if(likely(bytes == 0)) { diff --git a/src/web_client.h b/src/web_client.h index 534fcd53..a09fdd2a 100755 --- a/src/web_client.h +++ b/src/web_client.h @@ -1,5 +1,5 @@ -#ifndef NETDATA_WITHOUT_ZLIB +#ifdef NETDATA_WITH_ZLIB #include #endif @@ -24,27 +24,26 @@ extern int web_enable_gzip; #define URL_MAX 8192 #define ZLIB_CHUNK 16384 -#define MAX_HTTP_HEADER_SIZE 16384 - -/* -struct name_value { - char *name; - char *value; - unsigned long hash; - struct name_value *next; -}; +#define HTTP_RESPONSE_HEADER_SIZE 4096 + +struct response { + BUFFER *header; // our response header + BUFFER *header_output; // internal use + BUFFER *data; // our response data buffer -struct web_request { - char *protocol; - char *hostname; - char *path; - char *query_string; + long rlen; // if non-zero, the excepted size of ifd (input) + long sent; // current data length sent to output + + int zoutput; // if set to 1, web_client_send() will send compressed data +#ifdef NETDATA_WITH_ZLIB + z_stream zstream; // zlib stream for sending compressed output to client + Bytef zbuffer[ZLIB_CHUNK]; // temporary buffer for storing compressed output + long zsent; // the compressed bytes we have sent to the client + long zhave; // the compressed bytes that we have to send + int zinitialized; +#endif - struct name_value *headers; - struct name_value *query_parameters; - struct name_value *post_parameters; }; -*/ struct web_client { unsigned long long id; @@ -54,8 +53,6 @@ struct web_client { char last_url[URL_MAX+1]; - struct web_request *request; - struct timeval tv_in, tv_ready; int mode; @@ -69,22 +66,11 @@ struct web_client { int ifd; int ofd; - struct web_buffer *data; - - int zoutput; // if set to 1, web_client_send() will send compressed data -#ifndef NETDATA_WITHOUT_ZLIB - z_stream zstream; // zlib stream for sending compressed output to client - Bytef zbuffer[ZLIB_CHUNK]; // temporary buffer for storing compressed output - long zsent; // the compressed bytes we have sent to the client - long zhave; // the compressed bytes that we have to send - int zinitialized; -#endif + struct response response; int wait_receive; int wait_send; - char response_header[MAX_HTTP_HEADER_SIZE+1]; - struct web_client *prev; struct web_client *next; }; -- 2.39.2