]> arthur.barton.de Git - netdata.git/commitdiff
better handling of http responses expiration and cache control
authorCosta Tsaousis (ktsaou) <costa@tsaousis.gr>
Sat, 24 Sep 2016 21:49:55 +0000 (00:49 +0300)
committerCosta Tsaousis (ktsaou) <costa@tsaousis.gr>
Sat, 24 Sep 2016 21:49:55 +0000 (00:49 +0300)
src/rrd2json.c
src/web_buffer.c
src/web_buffer.h
src/web_client.c

index 1a84a266d3cb9bef095092af2474fe149d6fba96..474b5915db753f3becf23798bdf789d868249206 100644 (file)
@@ -1564,9 +1564,9 @@ int rrd2value(RRDSET *st, BUFFER *wb, calculated_number *n, const char *dimensio
     }
 
     if(r->result_options & RRDR_RESULT_OPTION_RELATIVE)
-        wb->options |= WB_CONTENT_NO_CACHEABLE;
+        buffer_no_cacheable(wb);
     else if(r->result_options & RRDR_RESULT_OPTION_ABSOLUTE)
-        wb->options |= WB_CONTENT_CACHEABLE;
+        buffer_cacheable(wb);
 
     options = rrdr_check_options(r, options, dimensions);
 
@@ -1592,9 +1592,9 @@ int rrd2format(RRDSET *st, BUFFER *wb, BUFFER *dimensions, uint32_t format, long
     }
 
     if(r->result_options & RRDR_RESULT_OPTION_RELATIVE)
-        wb->options |= WB_CONTENT_NO_CACHEABLE;
+        buffer_no_cacheable(wb);
     else if(r->result_options & RRDR_RESULT_OPTION_ABSOLUTE)
-        wb->options |= WB_CONTENT_CACHEABLE;
+        buffer_cacheable(wb);
 
     options = rrdr_check_options(r, options, (dimensions)?buffer_tostring(dimensions):NULL);
 
index 01a97ddc96410fd4c2ff80d556ec6f8eff8fa4ae..93ba782af279a05356765964862bdff770b07d4b 100644 (file)
@@ -35,6 +35,7 @@ void buffer_reset(BUFFER *wb)
     wb->contenttype = CT_TEXT_PLAIN;
     wb->options = 0;
     wb->date = 0;
+    wb->expires = 0;
 
     buffer_overflow_check(wb);
 }
index c4cd0563212c0a835baa93b8517081213c68af02..3cf2e5ec931661be6c9a0e18091e051284ce4f54 100644 (file)
@@ -4,12 +4,13 @@
 #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
@@ -39,6 +40,9 @@ typedef struct web_buffer {
 #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);
 
index 874aeaf4d89629c5c710653fa19496e9ed48026b..4426f6e96bf7d27146a52203ab50ae6673d37fa2 100644 (file)
@@ -407,6 +407,7 @@ int mysendfile(struct web_client *w, char *filename)
     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;
 }
@@ -759,8 +760,6 @@ int web_client_api_request_v1_badge(struct web_client *w, char *url) {
     int ret = 400;
     buffer_flush(w->response.data);
 
-    w->response.data->options |= WB_CONTENT_NO_CACHEABLE;
-
     BUFFER *dimensions = NULL;
     
     const char *chart = NULL
@@ -2084,19 +2083,38 @@ void web_client_process(struct web_client *w) {
     }
 
     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"
@@ -2148,44 +2166,37 @@ void web_client_process(struct web_client *w) {
             "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