]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: cache: Add "Age" header.
authorFrédéric Lécaille <flecaille@haproxy.com>
Fri, 26 Oct 2018 12:29:22 +0000 (14:29 +0200)
committerWilly Tarreau <w@1wt.eu>
Sun, 28 Oct 2018 18:06:59 +0000 (19:06 +0100)
This patch makes the cache capable of adding an "Age" header as defined by
rfc7234.

During the storage of new HTTP objects we memorize ->eoh value and
the value of the "Age" header coming from the origin server.
These information may then be reused to return the cached HTTP objects
with a new "Age" header.

May be backported to 1.8.

src/cache.c

index b9ac2d50b8b6fc1138c660d925d6f733e7c4cb7c..96a251ae0c310b4d41346a06197ec61d0b3a7db5 100644 (file)
@@ -65,12 +65,15 @@ struct cache_st {
 struct cache_entry {
        unsigned int latest_validation;     /* latest validation date */
        unsigned int expire;      /* expiration date */
+       unsigned int age;         /* Origin server "Age" header value */
+       unsigned int eoh;         /* Origin server end of headers offset. */
        struct eb32_node eb;     /* ebtree node used to hold the cache object */
        char hash[20];
        unsigned char data[0];
 };
 
 #define CACHE_BLOCKSIZE 1024
+#define CACHE_ENTRY_MAX_AGE 2147483648
 
 static struct list caches = LIST_HEAD_INIT(caches);
 static struct cache *tmp_cache_config = NULL;
@@ -411,6 +414,8 @@ static void cache_free_blocks(struct shared_block *first, struct shared_block *b
 enum act_return http_action_store_cache(struct act_rule *rule, struct proxy *px,
                                               struct session *sess, struct stream *s, int flags)
 {
+       unsigned int age;
+       long long hdr_age;
        struct http_txn *txn = s->txn;
        struct http_msg *msg = &txn->rsp;
        struct filter *filter;
@@ -454,6 +459,17 @@ enum act_return http_action_store_cache(struct act_rule *rule, struct proxy *px,
        if (!(txn->flags & TX_CACHEABLE) || !(txn->flags & TX_CACHE_COOK))
                goto out;
 
+       age = 0;
+       ctx.idx = 0;
+       if (http_find_header2("Age", 3, ci_head(txn->rsp.chn), &txn->hdr_idx, &ctx)) {
+               if (!strl2llrc(ctx.line + ctx.val, ctx.vlen, &hdr_age) && hdr_age > 0) {
+                       if (unlikely(hdr_age > CACHE_ENTRY_MAX_AGE))
+                               hdr_age = CACHE_ENTRY_MAX_AGE;
+                       age = hdr_age;
+               }
+               http_remove_header2(msg, &txn->hdr_idx, &ctx);
+       }
+
        shctx_lock(shctx);
        first = shctx_row_reserve_hot(shctx, NULL, sizeof(struct cache_entry) + msg->sov);
        if (!first) {
@@ -468,6 +484,8 @@ enum act_return http_action_store_cache(struct act_rule *rule, struct proxy *px,
        object = (struct cache_entry *)first->data;
        object->eb.node.leaf_p = NULL;
        object->eb.key = 0;
+       object->age = age;
+       object->eoh = msg->eoh;
 
        /* reserve space for the cache_entry structure */
        first->len = sizeof(struct cache_entry);
@@ -529,9 +547,10 @@ out:
        return ACT_RET_CONT;
 }
 
-#define        HTTP_CACHE_INIT 0
-#define        HTTP_CACHE_FWD 1
-#define        HTTP_CACHE_END 2
+#define        HTTP_CACHE_INIT   0  /* Initial state. */
+#define        HTTP_CACHE_HEADER 1  /* Cache entry headers forwarded. */
+#define        HTTP_CACHE_FWD    2  /* Cache entry completely forwarded. */
+#define        HTTP_CACHE_END    3  /* Cache entry treatment terminated. */
 
 static void http_cache_applet_release(struct appctx *appctx)
 {
@@ -544,6 +563,27 @@ static void http_cache_applet_release(struct appctx *appctx)
        shctx_unlock(shctx_ptr(cache));
 }
 
+/*
+ * Append an "Age" header into <chn> channel for this <ce> cache entry.
+ * This is the responsability of the caller to insure there is enough
+ * data in the channel.
+ *
+ * Returns the number of bytes inserted if succeeded, 0 if failed.
+ */
+static int cache_channel_append_age_header(struct cache_entry *ce, struct channel *chn)
+{
+       unsigned int age;
+
+       age = MAX(0, (int)(now.tv_sec - ce->latest_validation)) + ce->age;
+       if (unlikely(age > CACHE_ENTRY_MAX_AGE))
+               age = CACHE_ENTRY_MAX_AGE;
+
+       chunk_reset(&trash);
+       chunk_printf(&trash, "Age: %u", age);
+
+       return ci_insert_line2(chn, ce->eoh, trash.area, trash.data);
+}
+
 static int cache_channel_row_data_get(struct appctx *appctx, int len)
 {
        int ret, total;
@@ -612,7 +652,7 @@ static void http_cache_io_handler(struct appctx *appctx)
                appctx->st0 = HTTP_CACHE_END;
 
        /* buffer are aligned there, should be fine */
-       if (appctx->st0 == HTTP_CACHE_INIT) {
+       if (appctx->st0 == HTTP_CACHE_HEADER || appctx->st0 == HTTP_CACHE_INIT) {
                int len = first->len - *sent - sizeof(struct cache_entry);
 
                if (len > 0) {
@@ -623,6 +663,9 @@ static void http_cache_io_handler(struct appctx *appctx)
                                appctx->st0 = HTTP_CACHE_END;
                        else
                                *sent += ret;
+                       if (appctx->st0 == HTTP_CACHE_INIT && *sent > cache_ptr->eoh &&
+                               cache_channel_append_age_header(cache_ptr, res))
+                               appctx->st0 = HTTP_CACHE_HEADER;
                }
                else {
                        *sent = 0;