--- /dev/null
+varnishtest "A successful unsafe method (POST for instance) on a cached entry must disable it."
+
+#REQUIRE_VERSION=2.3
+
+feature ignore_unknown_macro
+
+server s1 {
+ rxreq
+ expect req.url == "/cached"
+ txresp -hdr "Cache-Control: max-age=5" \
+ -bodylen 150
+
+ rxreq
+ expect req.url == "/cached"
+ expect req.method == "POST"
+ txresp
+
+ rxreq
+ expect req.url == "/cached"
+ txresp -hdr "Cache-Control: max-age=5" \
+ -bodylen 100
+
+} -start
+
+haproxy h1 -conf {
+ defaults
+ mode http
+ ${no-htx} option http-use-htx
+ timeout connect 1s
+ timeout client 1s
+ timeout server 1s
+
+ frontend fe
+ bind "fd@${fe}"
+ default_backend test
+
+ backend test
+ http-request cache-use my_cache
+ server www ${s1_addr}:${s1_port}
+ http-response cache-store my_cache
+ http-response set-header X-Cache-Hit %[res.cache_hit]
+
+ cache my_cache
+ total-max-size 3
+ max-age 20
+ max-object-size 3072
+} -start
+
+
+client c1 -connect ${h1_fe_sock} {
+ txreq -url "/cached"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 150
+
+ txreq -method "POST" -url "/cached" -bodylen 100
+ rxresp
+ expect resp.status == 200
+ expect resp.http.X-Cache-Hit == 0
+
+ txreq -url "/cached"
+ rxresp
+ expect resp.status == 200
+ expect resp.bodylen == 100
+ expect resp.http.X-Cache-Hit == 0
+} -run
goto out;
/* cache only GET method */
- if (txn->meth != HTTP_METH_GET)
+ if (txn->meth != HTTP_METH_GET) {
+ /* In case of successful unsafe method on a stored resource, the
+ * cached entry must be invalidated (see RFC7234#4.4).
+ * A "non-error response" is one with a 2xx (Successful) or 3xx
+ * (Redirection) status code. */
+ if (txn->status >= 200 && txn->status < 400) {
+ switch (txn->meth) {
+ case HTTP_METH_OPTIONS:
+ case HTTP_METH_GET:
+ case HTTP_METH_HEAD:
+ case HTTP_METH_TRACE:
+ break;
+
+ default: /* Any unsafe method */
+ /* Discard any corresponding entry in case of sucessful
+ * unsafe request (such as PUT, POST or DELETE). */
+ shctx_lock(shctx);
+
+ old = entry_exist(cconf->c.cache, txn->cache_hash);
+ if (old) {
+ eb32_delete(&old->eb);
+ old->eb.key = 0;
+ }
+ shctx_unlock(shctx);
+ }
+ }
goto out;
+ }
/* cache key was not computed */
if (!key)
trash = get_trash_chunk();
ctx.blk = NULL;
- switch (txn->meth) {
- case HTTP_METH_HEAD:
- case HTTP_METH_GET:
- chunk_memcat(trash, "GET", 3);
- break;
- default:
- return 0;
- }
-
sl = http_get_stline(htx);
uri = htx_sl_req_uri(sl); // whole uri
if (!uri.len)
http_check_request_for_cacheability(s, &s->req);
- if ((s->txn->flags & (TX_CACHE_IGNORE|TX_CACHEABLE)) == TX_CACHE_IGNORE)
+ /* The request's hash has to be calculated for all requests, even POSTs
+ * or PUTs for instance because RFC7234 specifies that a sucessful
+ * "unsafe" method on a stored resource must invalidate it
+ * (see RFC7234#4.4). */
+ if (!sha1_hosturi(s))
return ACT_RET_CONT;
- if (!sha1_hosturi(s))
+ if ((s->txn->flags & (TX_CACHE_IGNORE|TX_CACHEABLE)) == TX_CACHE_IGNORE)
return ACT_RET_CONT;
if (s->txn->flags & TX_CACHE_IGNORE)