#define WEB_DATA_LENGTH_INCREASE_STEP 1024
typedef struct web_buffer {
- size_t size; // allocation size of buffer
- size_t len; // current data length in buffer
- char *buffer; // the buffer
- uint8_t contenttype;
- uint8_t options;
- time_t date; // the date this content has been generated
+ size_t size; // allocation size of buffer, in bytes
+ size_t len; // current data length in buffer, in bytes
+ char *buffer; // the buffer itself
+ uint8_t contenttype; // the content type of the data in the buffer
+ uint8_t options; // options related to the content
+ time_t date; // the timestamp this content has been generated
+ time_t expires; // the timestamp this content expires
} BUFFER;
// options
#define CT_IMAGE_ICNS 20
#define CT_IMAGE_BMP 21
+#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)
+#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)
+
#define buffer_strlen(wb) ((wb)->len)
extern const char *buffer_tostring(BUFFER *wb);
buffer_flush(w->response.data);
w->response.rlen = stat.st_size;
w->response.data->date = stat.st_mtim.tv_sec;
+ buffer_cacheable(w->response.data);
return 200;
}
int ret = 400;
buffer_flush(w->response.data);
- w->response.data->options |= WB_CONTENT_NO_CACHEABLE;
-
BUFFER *dimensions = NULL;
const char *chart = NULL
}
gettimeofday(&w->tv_ready, NULL);
- w->response.data->date = time(NULL);
w->response.sent = 0;
w->response.code = code;
+ // set a proper last modified date
+ if(unlikely(!w->response.data->date))
+ w->response.data->date = w->tv_ready.tv_sec;
+
+ // set a proper expiration date, if not already set
+ if(unlikely(!w->response.data->expires)) {
+ if(w->response.data->options & WB_CONTENT_NO_CACHEABLE)
+ w->response.data->expires = w->tv_ready.tv_sec + rrd_update_every;
+ else
+ w->response.data->expires = w->tv_ready.tv_sec + 86400;
+ }
+
// prepare the HTTP response header
debug(D_WEB_CLIENT, "%llu: Generating HTTP header with response %d.", w->id, code);
const char *content_type_string = web_content_type_to_string(w->response.data->contenttype);
const char *code_msg = web_response_code_to_string(code);
- char date[32];
- struct tm tmbuf, *tm = gmtime_r(&w->response.data->date, &tmbuf);
- strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %Z", tm);
+ // prepare the last modified and expiration dates
+ char date[32], edate[32];
+ {
+ struct tm tmbuf, *tm;
+
+ tm = gmtime_r(&w->response.data->date, &tmbuf);
+ strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %Z", tm);
+
+ tm = gmtime_r(&w->response.data->expires, &tmbuf);
+ strftime(edate, sizeof(edate), "%a, %d %b %Y %H:%M:%S %Z", tm);
+ }
buffer_sprintf(w->response.header_output,
"HTTP/1.1 %d %s\r\n"
"Access-Control-Max-Age: 1209600\r\n" // 86400 * 14
);
}
-
- if(buffer_strlen(w->response.header))
- buffer_strcat(w->response.header_output, buffer_tostring(w->response.header));
-
- if(w->mode == WEB_CLIENT_MODE_NORMAL && (w->response.data->options & WB_CONTENT_NO_CACHEABLE)) {
- buffer_sprintf(w->response.header_output,
- "Expires: %s\r\n"
- "Cache-Control: no-cache\r\n"
- , date);
- }
- else if(w->mode != WEB_CLIENT_MODE_OPTIONS) {
- char edate[32];
- time_t et = w->response.data->date + 86400;
- struct tm etmbuf, *etm = gmtime_r(&et, &etmbuf);
- strftime(edate, sizeof(edate), "%a, %d %b %Y %H:%M:%S %Z", etm);
-
+ else {
buffer_sprintf(w->response.header_output,
- "Expires: %s\r\n"
- "Cache-Control: public\r\n"
- , edate);
+ "Cache-Control: %s\r\n"
+ "Expires: %s\r\n",
+ (w->response.data->options & WB_CONTENT_NO_CACHEABLE)?"no-cache":"public",
+ edate);
}
- // if we know the content length, put it
- if(!w->response.zoutput && (w->response.data->len || w->response.rlen))
- buffer_sprintf(w->response.header_output,
- "Content-Length: %zu\r\n"
- , w->response.data->len? w->response.data->len: w->response.rlen
- );
- else if(!w->response.zoutput)
- w->keepalive = 0; // content-length is required for keep-alive
+ // copy a possibly available custom header
+ if(unlikely(buffer_strlen(w->response.header)))
+ buffer_strcat(w->response.header_output, buffer_tostring(w->response.header));
- if(w->response.zoutput) {
+ // headers related to the transfer method
+ if(likely(w->response.zoutput)) {
buffer_strcat(w->response.header_output,
"Content-Encoding: gzip\r\n"
"Transfer-Encoding: chunked\r\n"
);
}
+ else {
+ if(likely((w->response.data->len || w->response.rlen))) {
+ // we know the content length, put it
+ buffer_sprintf(w->response.header_output, "Content-Length: %zu\r\n", w->response.data->len? w->response.data->len: w->response.rlen);
+ }
+ else {
+ // we don't know the content length, disable keep-alive
+ w->keepalive = 0;
+ }
+ }
+ // end of HTTP header
buffer_strcat(w->response.header_output, "\r\n");
// sent the HTTP header