William,

find below a very dirty proof of concept patch for issue 821

    https://github.com/haproxy/haproxy/issues/821

With the following example configuration:

    listen fe
        mode http
        bind *:8080
        http-request cache-use foo
        http-response cache-store foo
        server foo localhost:8081
    frontend be
        mode http
        bind *:8081
        http-request return content-type text/plain lf-string stuff hdr etag 
'"stuff"'
    cache foo
        total-max-size 4

I get a proper 304 Not Modified without any valgrind complaints for some manual
example requests:

    $ http localhost:8080 'if-none-match: "stuff"'
    HTTP/1.1 200 OK
    content-length: 5
    content-type: text/plain
    etag: "stuff"
    
    stuff
    
    $ http localhost:8080 'if-none-match: "stuff"'
    HTTP/1.1 304 Not Modified
    age: 1
    content-length: 5
    content-type: text/plain
    etag: "stuff"
    
    
    
    $ http localhost:8080 'if-none-match: "stuff2"'
    HTTP/1.1 200 OK
    age: 3
    content-length: 5
    content-type: text/plain
    etag: "stuff"
    
    stuff

Before continuing cleaning this up and properly handling both strong and weak
ETags according to the RFC I'd like to have a first review to see whether I am
generally on the right track there or whether I am doing dumb stuff that will
cause issues under certain circumstances :-)

Best regards
Tim Düsterhus

Apply with `git am --scissors` to automatically cut the commit message.

-- >8 --
see GitHub issue #821
---
 src/cache.c | 25 ++++++++++++++++++++++---
 1 file changed, 22 insertions(+), 3 deletions(-)

diff --git a/src/cache.c b/src/cache.c
index 533b902bf..b8802bb06 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -4,6 +4,8 @@
  * Copyright 2017 HAProxy Technologies
  * William Lallemand <wlallem...@haproxy.com>
  *
+ * Copyright 2020 Tim Duesterhus <t...@bastelstu.be>
+ *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  * as published by the Free Software Foundation; either version
@@ -875,6 +877,7 @@ static void http_cache_io_handler(struct appctx *appctx)
        unsigned int len;
        size_t ret, total = 0;
 
+       req_htx = htxbuf(&req->buf);
        res_htx = htxbuf(&res->buf);
        total = res_htx->data;
 
@@ -899,15 +902,31 @@ static void http_cache_io_handler(struct appctx *appctx)
        }
 
        if (appctx->st0 == HTX_CACHE_HEADER) {
+               struct http_hdr_ctx inm_ctx = { .blk = NULL };
+               int etag_match = 0;
+
                /* Headers must be dump at once. Otherwise it is an error */
                len = first->len - sizeof(*cache_ptr) - appctx->ctx.cache.sent;
                ret = htx_cache_dump_msg(appctx, res_htx, len, HTX_BLK_EOH);
-               if (!ret || (htx_get_tail_type(res_htx) != HTX_BLK_EOH) ||
-                   !htx_cache_add_age_hdr(appctx, res_htx))
+               if (!ret || (htx_get_tail_type(res_htx) != HTX_BLK_EOH))
+                       goto error;
+
+               if (http_find_header(req_htx, ist("if-none-match"), &inm_ctx, 
0)) {
+                       struct http_hdr_ctx etag_ctx = { .blk = NULL };
+                       if (http_find_header(res_htx, ist("etag"), &etag_ctx, 
0)) {
+                               if (isteq(inm_ctx.value, etag_ctx.value)) {
+                                       etag_match = 1;
+                                       http_replace_res_status(res_htx, (const 
struct ist) IST("304"));
+                                       http_replace_res_reason(res_htx, (const 
struct ist) IST("Not Modified"));
+                               }
+                       }
+               }
+
+               if (!htx_cache_add_age_hdr(appctx, res_htx))
                        goto error;
 
                /* Skip response body for HEAD requests */
-               if (si_strm(si)->txn->meth == HTTP_METH_HEAD)
+               if (si_strm(si)->txn->meth == HTTP_METH_HEAD || etag_match)
                        appctx->st0 = HTX_CACHE_EOM;
                else
                        appctx->st0 = HTX_CACHE_DATA;
-- 
2.28.0


Reply via email to