Re: [PATCH] MINOR: cache: Add "Age" header.

2018-10-28 Thread Willy Tarreau
On Fri, Oct 26, 2018 at 03:05:12PM +0200, Frederic Lecaille wrote:
> On 10/26/2018 02:52 PM, Frederic Lecaille wrote:
> > Hello,
> > 
> > Here is a patch to handle the "Age" header for the cache.
> > Everything is in the commit log.
> 
> Here is a better patch with this diff between this latter one and the
> previous one:
(...)

I missed this patch, now applied, thanks Fred!

Willy



Re: [PATCH] MINOR: cache: Add "Age" header.

2018-10-26 Thread Frederic Lecaille

On 10/26/2018 02:52 PM, Frederic Lecaille wrote:

Hello,

Here is a patch to handle the "Age" header for the cache.
Everything is in the commit log.


Here is a better patch with this diff between this latter one and the 
previous one:


@@ -52,7 +52,7 @@
 +  age = 0;
 +  ctx.idx = 0;
 +  if (http_find_header2("Age", 3, ci_head(txn->rsp.chn), 
>hdr_idx, )) {
-+  if (!strl2llrc(ctx.line + ctx.val, ctx.vlen, _age) 
&& hdr_age > 0) {

++  if (!strl2llrc(ctx.line + ctx.val, ctx.vlen, _age)) {
 +  if (unlikely(hdr_age > CACHE_ENTRY_MAX_AGE))
 +  hdr_age = CACHE_ENTRY_MAX_AGE;
 +  age = hdr_age;



We check that the origin server "Age" header value is positive.


Regards,

Fred
>From 3979a2a0e3ad59ccc86175a97a3dccbcbe42321c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20L=C3=A9caille?= 
Date: Fri, 26 Oct 2018 14:29:22 +0200
Subject: [PATCH] MINOR: cache: Add "Age" header.

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 | 51 +++
 1 file changed, 47 insertions(+), 4 deletions(-)

diff --git a/src/cache.c b/src/cache.c
index b9ac2d50..96a251ae 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -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 = >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), >hdr_idx, )) {
+		if (!strl2llrc(ctx.line + ctx.val, ctx.vlen, _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, >hdr_idx, );
+	}
+
 	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_FWD2  /* Cache entry completely forwarded. */
+#define 	HTTP_CACHE_END3  /* 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  channel for this  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();
+	chunk_printf(, "Age: %u", age);
+
+	return ci_insert_line2(chn, ce->eoh, trash.area, trash.data);
+}
+

[PATCH] MINOR: cache: Add "Age" header.

2018-10-26 Thread Frederic Lecaille

Hello,

Here is a patch to handle the "Age" header for the cache.
Everything is in the commit log.

Regards,

Fred.
>From af5156e33de0a5a2f278cd6b8834e834c5401b35 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20L=C3=A9caille?= 
Date: Fri, 26 Oct 2018 14:29:22 +0200
Subject: [PATCH] MINOR: cache: Add "Age" header.

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 | 51 +++
 1 file changed, 47 insertions(+), 4 deletions(-)

diff --git a/src/cache.c b/src/cache.c
index b9ac2d50..6f90d895 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -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 = >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), >hdr_idx, )) {
+		if (!strl2llrc(ctx.line + ctx.val, ctx.vlen, _age)) {
+			if (unlikely(hdr_age > CACHE_ENTRY_MAX_AGE))
+hdr_age = CACHE_ENTRY_MAX_AGE;
+			age = hdr_age;
+		}
+		http_remove_header2(msg, >hdr_idx, );
+	}
+
 	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_FWD2  /* Cache entry completely forwarded. */
+#define 	HTTP_CACHE_END3  /* 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  channel for this  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();
+	chunk_printf(, "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;
-- 
2.11.0