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