@cryptotaxi247 / netdata-1 / commits / 83b0c68b3

better handling of http responses expiration and cache control

Costa Tsaousis (ktsaou) committed Sep 25, 2016 at 00:49 UTC 83b0c68b31e369364e6cc6c595f1151516ad978f
4 files changed +60 -44
src/rrd2json.c
+4 -4
@@ -1564,9 +1564,9 @@ int rrd2value(RRDSET *st, BUFFER *wb, calculated_number *n, const char *dimensio
1564 }
1565
1566 if(r->result_options & RRDR_RESULT_OPTION_RELATIVE)
1567 - wb->options |= WB_CONTENT_NO_CACHEABLE;
1567 + buffer_no_cacheable(wb);
1568 else if(r->result_options & RRDR_RESULT_OPTION_ABSOLUTE)
1569 - wb->options |= WB_CONTENT_CACHEABLE;
1569 + buffer_cacheable(wb);
1570
1571 options = rrdr_check_options(r, options, dimensions);
1572
@@ -1592,9 +1592,9 @@ int rrd2format(RRDSET *st, BUFFER *wb, BUFFER *dimensions, uint32_t format, long
1592 }
1593
1594 if(r->result_options & RRDR_RESULT_OPTION_RELATIVE)
1595 - wb->options |= WB_CONTENT_NO_CACHEABLE;
1595 + buffer_no_cacheable(wb);
1596 else if(r->result_options & RRDR_RESULT_OPTION_ABSOLUTE)
1597 - wb->options |= WB_CONTENT_CACHEABLE;
1597 + buffer_cacheable(wb);
1598
1599 options = rrdr_check_options(r, options, (dimensions)?buffer_tostring(dimensions):NULL);
1600
src/web_buffer.c
+1
@@ -35,6 +35,7 @@ void buffer_reset(BUFFER *wb)
35 wb->contenttype = CT_TEXT_PLAIN;
36 wb->options = 0;
37 wb->date = 0;
38 + wb->expires = 0;
39
40 buffer_overflow_check(wb);
41 }
src/web_buffer.h
+10 -6
@@ -4,12 +4,13 @@
4 #define WEB_DATA_LENGTH_INCREASE_STEP 1024
5
6 typedef struct web_buffer {
7 - size_t size; // allocation size of buffer
8 - size_t len; // current data length in buffer
9 - char *buffer; // the buffer
10 - uint8_t contenttype;
11 - uint8_t options;
12 - time_t date; // the date this content has been generated
7 + size_t size; // allocation size of buffer, in bytes
8 + size_t len; // current data length in buffer, in bytes
9 + char *buffer; // the buffer itself
10 + uint8_t contenttype; // the content type of the data in the buffer
11 + uint8_t options; // options related to the content
12 + time_t date; // the timestamp this content has been generated
13 + time_t expires; // the timestamp this content expires
14 } BUFFER;
15
16 // options
@@ -39,6 +40,9 @@ typedef struct web_buffer {
40 #define CT_IMAGE_ICNS 20
41 #define CT_IMAGE_BMP 21
42
43 +#define buffer_cacheable(wb) do { (wb)->options |= WB_CONTENT_CACHEABLE; if((wb)->options & WB_CONTENT_NO_CACHEABLE) (wb)->options &= ~WB_CONTENT_NO_CACHEABLE; } while(0)
44 +#define buffer_no_cacheable(wb) do { (wb)->options |= WB_CONTENT_NO_CACHEABLE; if((wb)->options & WB_CONTENT_CACHEABLE) (wb)->options &= ~WB_CONTENT_CACHEABLE; } while(0)
45 +
46 #define buffer_strlen(wb) ((wb)->len)
47 extern const char *buffer_tostring(BUFFER *wb);
48
src/web_client.c
+45 -34
@@ -407,6 +407,7 @@ int mysendfile(struct web_client *w, char *filename)
407 buffer_flush(w->response.data);
408 w->response.rlen = stat.st_size;
409 w->response.data->date = stat.st_mtim.tv_sec;
410 + buffer_cacheable(w->response.data);
411
412 return 200;
413 }
@@ -759,8 +760,6 @@ int web_client_api_request_v1_badge(struct web_client *w, char *url) {
760 int ret = 400;
761 buffer_flush(w->response.data);
762
762 - w->response.data->options |= WB_CONTENT_NO_CACHEABLE;
763 -
763 BUFFER *dimensions = NULL;
764
765 const char *chart = NULL
@@ -2084,19 +2083,38 @@ void web_client_process(struct web_client *w) {
2083 }
2084
2085 gettimeofday(&w->tv_ready, NULL);
2087 - w->response.data->date = time(NULL);
2086 w->response.sent = 0;
2087 w->response.code = code;
2088
2089 + // set a proper last modified date
2090 + if(unlikely(!w->response.data->date))
2091 + w->response.data->date = w->tv_ready.tv_sec;
2092 +
2093 + // set a proper expiration date, if not already set
2094 + if(unlikely(!w->response.data->expires)) {
2095 + if(w->response.data->options & WB_CONTENT_NO_CACHEABLE)
2096 + w->response.data->expires = w->tv_ready.tv_sec + rrd_update_every;
2097 + else
2098 + w->response.data->expires = w->tv_ready.tv_sec + 86400;
2099 + }
2100 +
2101 // prepare the HTTP response header
2102 debug(D_WEB_CLIENT, "%llu: Generating HTTP header with response %d.", w->id, code);
2103
2104 const char *content_type_string = web_content_type_to_string(w->response.data->contenttype);
2105 const char *code_msg = web_response_code_to_string(code);
2106
2097 - char date[32];
2098 - struct tm tmbuf, *tm = gmtime_r(&w->response.data->date, &tmbuf);
2099 - strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %Z", tm);
2107 + // prepare the last modified and expiration dates
2108 + char date[32], edate[32];
2109 + {
2110 + struct tm tmbuf, *tm;
2111 +
2112 + tm = gmtime_r(&w->response.data->date, &tmbuf);
2113 + strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %Z", tm);
2114 +
2115 + tm = gmtime_r(&w->response.data->expires, &tmbuf);
2116 + strftime(edate, sizeof(edate), "%a, %d %b %Y %H:%M:%S %Z", tm);
2117 + }
2118
2119 buffer_sprintf(w->response.header_output,
2120 "HTTP/1.1 %d %s\r\n"
@@ -2148,44 +2166,37 @@ void web_client_process(struct web_client *w) {
2166 "Access-Control-Max-Age: 1209600\r\n" // 86400 * 14
2167 );
2168 }
2151 -
2152 - if(buffer_strlen(w->response.header))
2153 - buffer_strcat(w->response.header_output, buffer_tostring(w->response.header));
2154 -
2155 - if(w->mode == WEB_CLIENT_MODE_NORMAL && (w->response.data->options & WB_CONTENT_NO_CACHEABLE)) {
2156 - buffer_sprintf(w->response.header_output,
2157 - "Expires: %s\r\n"
2158 - "Cache-Control: no-cache\r\n"
2159 - , date);
2160 - }
2161 - else if(w->mode != WEB_CLIENT_MODE_OPTIONS) {
2162 - char edate[32];
2163 - time_t et = w->response.data->date + 86400;
2164 - struct tm etmbuf, *etm = gmtime_r(&et, &etmbuf);
2165 - strftime(edate, sizeof(edate), "%a, %d %b %Y %H:%M:%S %Z", etm);
2166 -
2169 + else {
2170 buffer_sprintf(w->response.header_output,
2168 - "Expires: %s\r\n"
2169 - "Cache-Control: public\r\n"
2170 - , edate);
2171 + "Cache-Control: %s\r\n"
2172 + "Expires: %s\r\n",
2173 + (w->response.data->options & WB_CONTENT_NO_CACHEABLE)?"no-cache":"public",
2174 + edate);
2175 }
2176
2173 - // if we know the content length, put it
2174 - if(!w->response.zoutput && (w->response.data->len || w->response.rlen))
2175 - buffer_sprintf(w->response.header_output,
2176 - "Content-Length: %zu\r\n"
2177 - , w->response.data->len? w->response.data->len: w->response.rlen
2178 - );
2179 - else if(!w->response.zoutput)
2180 - w->keepalive = 0; // content-length is required for keep-alive
2177 + // copy a possibly available custom header
2178 + if(unlikely(buffer_strlen(w->response.header)))
2179 + buffer_strcat(w->response.header_output, buffer_tostring(w->response.header));
2180
2182 - if(w->response.zoutput) {
2181 + // headers related to the transfer method
2182 + if(likely(w->response.zoutput)) {
2183 buffer_strcat(w->response.header_output,
2184 "Content-Encoding: gzip\r\n"
2185 "Transfer-Encoding: chunked\r\n"
2186 );
2187 }
2188 + else {
2189 + if(likely((w->response.data->len || w->response.rlen))) {
2190 + // we know the content length, put it
2191 + buffer_sprintf(w->response.header_output, "Content-Length: %zu\r\n", w->response.data->len? w->response.data->len: w->response.rlen);
2192 + }
2193 + else {
2194 + // we don't know the content length, disable keep-alive
2195 + w->keepalive = 0;
2196 + }
2197 + }
2198
2199 + // end of HTTP header
2200 buffer_strcat(w->response.header_output, "\r\n");
2201
2202 // sent the HTTP header