]> arthur.barton.de Git - netdata.git/commitdiff
code cleanup and re-arrangements for better scalability
authorCosta Tsaousis (ktsaou) <costa@tsaousis.gr>
Sat, 28 Nov 2015 04:08:17 +0000 (06:08 +0200)
committerCosta Tsaousis (ktsaou) <costa@tsaousis.gr>
Sat, 28 Nov 2015 04:08:17 +0000 (06:08 +0200)
src/appconfig.c
src/appconfig.h
src/daemon.c
src/rrd2json.c
src/rrd2json.h
src/web_buffer.c
src/web_buffer.h
src/web_client.c
src/web_client.h

index 0f957e40e87b3ece815e634b4275df88f49c6266..1735dbdf8655f7c6015832da5630129904c340ea 100755 (executable)
@@ -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);
                                }
                        }
                }
index 79c22388e0c787beb9f0ed7df57c2c9e1eecd0ef..365d1e2f12bb2bb6bfd542f069beebb8cbfaba0a 100755 (executable)
@@ -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 */
index e2315f0b6b9392658d29a604a44bc2cc21473a72..652cc8618f5478a0668619241db5a9633d8e5508 100755 (executable)
@@ -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);
index 0583e01ba2e58cd2898f6fa470d51c691efef517..a12369ff451ea9eede665384b46873459c455e12 100755 (executable)
 #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;
index 2454f1bb1fae8b6f3f936ae8c1995ffbcdf071d8..d0dad28e09f7e620663505d5c257f0680ef10e7c 100755 (executable)
@@ -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 */
index cea5be61f03e2ceec479dc6be9fbfa4b90dac96e..1607696cf9d9e54150133b3fbd16c2a9188884eb 100755 (executable)
 #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);
 }
index 93e4c065b8a92837272e27c30ecf5a0f677b30ef..af5239437c7cc0d8e21d9a16a0d6013c4e7bdf01 100755 (executable)
@@ -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 */
index 1b8db57b8121e7ec46107334d207bf49ec40a2e7..77e7aeab1a6793e05be6ebfaf3cbd00a22000181 100755 (executable)
@@ -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)) {
index 534fcd539cd8a2d77b68b5b913b2e80e6361119f..a09fdd2a6ce304f722d8a4aa135a66cc0a36df71 100755 (executable)
@@ -1,5 +1,5 @@
 
-#ifndef NETDATA_WITHOUT_ZLIB
+#ifdef NETDATA_WITH_ZLIB
 #include <zlib.h>
 #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;
 };