This is an automated email from the ASF dual-hosted git repository.
mlibbey pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/master by this push:
new 0d52043048 Fix HTTP caching compliance by adding Vary headers for
compressible content (#12741)
0d52043048 is described below
commit 0d52043048bf31fee0dbd36958de12d9792caa77
Author: mlibbey <[email protected]>
AuthorDate: Mon Dec 15 16:38:24 2025 -0800
Fix HTTP caching compliance by adding Vary headers for compressible content
(#12741)
The compress plugin was only adding Vary: Accept-Encoding headers when
content
was actually going to be compressed, not when content could be compressed.
This
can cause downstream caches to never get the compressed version in cache.
Now adds Vary: Accept-Encoding headers for all compressible content
regardless
of whether compression is applied, ensuring proper HTTP cache behavior.
Co-authored-by: Claude <[email protected]>
---
plugins/compress/compress.cc | 209 +++++++++++++++------
tests/gold_tests/pluginTest/compress/compress.gold | 60 +++---
.../pluginTest/compress/compress.test.py | 25 +++
.../pluginTest/compress/compress_userver.gold | 4 +-
.../pluginTest/compress/compress_vary.gold | 2 +
5 files changed, 213 insertions(+), 87 deletions(-)
diff --git a/plugins/compress/compress.cc b/plugins/compress/compress.cc
index 88c52e0286..30cb749d7b 100644
--- a/plugins/compress/compress.cc
+++ b/plugins/compress/compress.cc
@@ -330,7 +330,7 @@ compress_transform_init(TSCont contp, Data *data)
}
if (content_encoding_header(bufp, hdr_loc, data->compression_type,
data->compression_algorithms) == TS_SUCCESS &&
- vary_header(bufp, hdr_loc) == TS_SUCCESS && etag_header(bufp, hdr_loc)
== TS_SUCCESS) {
+ etag_header(bufp, hdr_loc) == TS_SUCCESS) {
downstream_conn = TSTransformOutputVConnGet(contp);
data->downstream_buffer = TSIOBufferCreate();
data->downstream_reader = TSIOBufferReaderAlloc(data->downstream_buffer);
@@ -518,7 +518,7 @@ compress_transform(TSCont contp, TSEvent event, void * /*
edata ATS_UNUSED */)
}
static int
-transformable(TSHttpTxn txnp, bool server, HostConfiguration
*host_configuration, int *compress_type, int *algorithms)
+is_content_compressible(TSHttpTxn txnp, bool server, HostConfiguration
*host_configuration)
{
/* Server response header */
TSMBuffer bufp;
@@ -528,7 +528,6 @@ transformable(TSHttpTxn txnp, bool server,
HostConfiguration *host_configuration
/* Client request header */
TSMBuffer cbuf;
TSMLoc chdr;
- TSMLoc cfield;
const char *value;
int len;
@@ -570,6 +569,100 @@ transformable(TSHttpTxn txnp, bool server,
HostConfiguration *host_configuration
return 0;
}
+ // the only compressible method is currently GET or POST.
+ int method_length;
+ const char *method = TSHttpHdrMethodGet(cbuf, chdr, &method_length);
+
+ if (!((method_length == TS_HTTP_LEN_GET && memcmp(method,
TS_HTTP_METHOD_GET, TS_HTTP_LEN_GET) == 0) ||
+ (method_length == TS_HTTP_LEN_POST && memcmp(method,
TS_HTTP_METHOD_POST, TS_HTTP_LEN_POST) == 0))) {
+ debug("method is not GET or POST, not compressible");
+ TSHandleMLocRelease(cbuf, TS_NULL_MLOC, chdr);
+ TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
+ return 0;
+ }
+
+ TSHandleMLocRelease(cbuf, TS_NULL_MLOC, chdr);
+
+ /* If there already exists a content encoding then we don't want
+ to do anything. */
+ field_loc = TSMimeHdrFieldFind(bufp, hdr_loc,
TS_MIME_FIELD_CONTENT_ENCODING, -1);
+ if (field_loc) {
+ info("response is already content encoded, not compressible");
+ TSHandleMLocRelease(bufp, hdr_loc, field_loc);
+ TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
+ return 0;
+ }
+
+ field_loc = TSMimeHdrFieldFind(bufp, hdr_loc, TS_MIME_FIELD_CONTENT_LENGTH,
TS_MIME_LEN_CONTENT_LENGTH);
+ if (field_loc != TS_NULL_MLOC) {
+ unsigned int hdr_value = TSMimeHdrFieldValueUintGet(bufp, hdr_loc,
field_loc, -1);
+ TSHandleMLocRelease(bufp, hdr_loc, field_loc);
+ if (hdr_value == 0) {
+ info("response is 0-length, not compressible");
+ TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
+ return 0;
+ }
+
+ if (hdr_value < host_configuration->minimum_content_length()) {
+ info("response is smaller than minimum content length, not compressing");
+ TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
+ return 0;
+ }
+ }
+
+ // Check if content type is compressible based on configuration.
+ field_loc = TSMimeHdrFieldFind(bufp, hdr_loc, TS_MIME_FIELD_CONTENT_TYPE,
-1);
+ if (!field_loc) {
+ info("no content type header found, not compressible");
+ TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
+ return 0;
+ }
+
+ value = TSMimeHdrFieldValueStringGet(bufp, hdr_loc, field_loc, -1, &len);
+
+ int rv = host_configuration->is_content_type_compressible(value, len);
+
+ if (!rv) {
+ info("content-type [%.*s] not compressible", len, value);
+ }
+
+ TSHandleMLocRelease(bufp, hdr_loc, field_loc);
+ TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
+
+ return rv;
+}
+
+static int
+client_accepts_compression(TSHttpTxn txnp, bool server, HostConfiguration
*host_configuration, int *compress_type, int *algorithms)
+{
+ /* Server response header */
+ TSMBuffer bufp;
+ TSMLoc hdr_loc;
+
+ /* Client request header */
+ TSMBuffer cbuf;
+ TSMLoc chdr;
+ TSMLoc cfield;
+
+ const char *value;
+ int len;
+
+ if (server) {
+ if (TS_SUCCESS != TSHttpTxnServerRespGet(txnp, &bufp, &hdr_loc)) {
+ return 0;
+ }
+ } else {
+ if (TS_SUCCESS != TSHttpTxnCachedRespGet(txnp, &bufp, &hdr_loc)) {
+ return 0;
+ }
+ }
+
+ if (TS_SUCCESS != TSHttpTxnClientReqGet(txnp, &cbuf, &chdr)) {
+ info("cound not get client request");
+ TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
+ return 0;
+ }
+
// check Partial Object is transformable
if (host_configuration->range_request_ctl() ==
RangeRequestCtrl::NO_COMPRESSION) {
// check Range header in client request
@@ -592,21 +685,6 @@ transformable(TSHttpTxn txnp, bool server,
HostConfiguration *host_configuration
TSHandleMLocRelease(cbuf, TS_NULL_MLOC, chdr);
return 0;
}
-
- TSHandleMLocRelease(bufp, hdr_loc, content_range_hdr_field);
- TSHandleMLocRelease(cbuf, chdr, range_hdr_field);
- }
-
- // the only compressible method is currently GET.
- int method_length;
- const char *method = TSHttpHdrMethodGet(cbuf, chdr, &method_length);
-
- if (!((method_length == TS_HTTP_LEN_GET && memcmp(method,
TS_HTTP_METHOD_GET, TS_HTTP_LEN_GET) == 0) ||
- (method_length == TS_HTTP_LEN_POST && memcmp(method,
TS_HTTP_METHOD_POST, TS_HTTP_LEN_POST) == 0))) {
- debug("method is not GET or POST, not compressible");
- TSHandleMLocRelease(cbuf, TS_NULL_MLOC, chdr);
- TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
- return 0;
}
*algorithms = host_configuration->compression_algorithms();
@@ -647,10 +725,10 @@ transformable(TSHttpTxn txnp, bool server,
HostConfiguration *host_configuration
TSHandleMLocRelease(cbuf, chdr, cfield);
TSHandleMLocRelease(cbuf, TS_NULL_MLOC, chdr);
+ TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
if (!compression_acceptable) {
info("no acceptable encoding match found in request header, not
compressible");
- TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
return 0;
}
} else {
@@ -661,52 +739,48 @@ transformable(TSHttpTxn txnp, bool server,
HostConfiguration *host_configuration
return 0;
}
- /* If there already exists a content encoding then we don't want
- to do anything. */
- field_loc = TSMimeHdrFieldFind(bufp, hdr_loc,
TS_MIME_FIELD_CONTENT_ENCODING, -1);
- if (field_loc) {
- info("response is already content encoded, not compressible");
- TSHandleMLocRelease(bufp, hdr_loc, field_loc);
- TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
- return 0;
- }
-
- field_loc = TSMimeHdrFieldFind(bufp, hdr_loc, TS_MIME_FIELD_CONTENT_LENGTH,
TS_MIME_LEN_CONTENT_LENGTH);
- if (field_loc != TS_NULL_MLOC) {
- unsigned int hdr_value = TSMimeHdrFieldValueUintGet(bufp, hdr_loc,
field_loc, -1);
- TSHandleMLocRelease(bufp, hdr_loc, field_loc);
- if (hdr_value == 0) {
- info("response is 0-length, not compressible");
- return 0;
- }
-
- if (hdr_value < host_configuration->minimum_content_length()) {
- info("response is smaller than minimum content length, not compressing");
- return 0;
- }
- }
+ return 1;
+}
- /* We only want to do gzip compression on documents that have a
- content type of "text/" or "application/x-javascript". */
- field_loc = TSMimeHdrFieldFind(bufp, hdr_loc, TS_MIME_FIELD_CONTENT_TYPE,
-1);
- if (!field_loc) {
- info("no content type header found, not compressible");
- TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
+static int
+transformable(TSHttpTxn txnp, bool server, HostConfiguration
*host_configuration, int *compress_type, int *algorithms,
+ bool *content_is_compressible)
+{
+ // First check if content could be compressible
+ *content_is_compressible = is_content_compressible(txnp, server,
host_configuration);
+ if (!*content_is_compressible) {
return 0;
}
- value = TSMimeHdrFieldValueStringGet(bufp, hdr_loc, field_loc, -1, &len);
+ // Then check if client accepts compression
+ return client_accepts_compression(txnp, server, host_configuration,
compress_type, algorithms);
+}
- int rv = host_configuration->is_content_type_compressible(value, len);
+static void
+add_vary_header_for_compressible_content(TSHttpTxn txnp, bool server,
HostConfiguration * /* hc ATS_UNUSED */)
+{
+ TSMBuffer resp_buf;
+ TSMLoc resp_loc;
- if (!rv) {
- info("content-type [%.*s] not compressible", len, value);
+ // Get the response headers
+ if (server) {
+ if (TS_SUCCESS != TSHttpTxnServerRespGet(txnp, &resp_buf, &resp_loc)) {
+ return;
+ }
+ } else {
+ if (TS_SUCCESS != TSHttpTxnCachedRespGet(txnp, &resp_buf, &resp_loc)) {
+ return;
+ }
}
- TSHandleMLocRelease(bufp, hdr_loc, field_loc);
- TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
+ // Add Vary: Accept-Encoding header
+ if (vary_header(resp_buf, resp_loc) != TS_SUCCESS) {
+ error("failed to add Vary header for compressible content");
+ TSHandleMLocRelease(resp_buf, TS_NULL_MLOC, resp_loc);
+ return;
+ }
- return rv;
+ TSHandleMLocRelease(resp_buf, TS_NULL_MLOC, resp_loc);
}
static void
@@ -734,6 +808,21 @@ compress_transform_add(TSHttpTxn txnp, HostConfiguration
*hc, int compress_type,
TSHttpTxnHookAdd(txnp, TS_HTTP_RESPONSE_TRANSFORM_HOOK, connp);
}
+static void
+handle_compression_and_vary(TSHttpTxn txnp, bool server, HostConfiguration
*hc, int *compress_type, int *algorithms)
+{
+ // Check if content is compressible and add compression if client accepts it
+ bool content_is_compressible;
+ if (transformable(txnp, server, hc, compress_type, algorithms,
&content_is_compressible)) {
+ compress_transform_add(txnp, hc, *compress_type, *algorithms);
+ }
+
+ // Add Vary: Accept-Encoding for all compressible content to ensure proper
HTTP caching
+ if (content_is_compressible) {
+ add_vary_header_for_compressible_content(txnp, server, hc);
+ }
+}
+
HostConfiguration *
find_host_configuration(TSHttpTxn /* txnp ATS_UNUSED */, TSMBuffer bufp,
TSMLoc locp, Configuration *config)
{
@@ -778,9 +867,7 @@ transform_plugin(TSCont contp, TSEvent event, void *edata)
}
}
- if (transformable(txnp, true, hc, &compress_type, &algorithms)) {
- compress_transform_add(txnp, hc, compress_type, algorithms);
- }
+ handle_compression_and_vary(txnp, true, hc, &compress_type, &algorithms);
}
break;
@@ -806,9 +893,7 @@ transform_plugin(TSCont contp, TSEvent event, void *edata)
if (TS_ERROR != TSHttpTxnCacheLookupStatusGet(txnp, &obj_status) &&
(TS_CACHE_LOOKUP_HIT_FRESH == obj_status)) {
if (hc != nullptr) {
info("handling compression of cached object");
- if (transformable(txnp, false, hc, &compress_type, &algorithms)) {
- compress_transform_add(txnp, hc, compress_type, algorithms);
- }
+ handle_compression_and_vary(txnp, false, hc, &compress_type,
&algorithms);
}
} else {
// Prepare for going to origin
diff --git a/tests/gold_tests/pluginTest/compress/compress.gold
b/tests/gold_tests/pluginTest/compress/compress.gold
index 76ea2da492..223c32eb70 100644
--- a/tests/gold_tests/pluginTest/compress/compress.gold
+++ b/tests/gold_tests/pluginTest/compress/compress.gold
@@ -3,8 +3,8 @@
> Accept-Encoding: gzip, deflate, sdch, br, zstd
< HTTP/1.1 200 OK
< Content-Type: text/javascript
-< Content-Encoding: br
< Vary: Accept-Encoding
+< Content-Encoding: br
< Content-Length: 46
===
> GET http://ae-0/obj0 HTTP/1.1
@@ -12,8 +12,8 @@
> Accept-Encoding: gzip
< HTTP/1.1 200 OK
< Content-Type: text/javascript
-< Content-Encoding: gzip
< Vary: Accept-Encoding
+< Content-Encoding: gzip
< Content-Length: 7``
===
> GET http://ae-0/obj0 HTTP/1.1
@@ -21,8 +21,8 @@
> Accept-Encoding: br
< HTTP/1.1 200 OK
< Content-Type: text/javascript
-< Content-Encoding: br
< Vary: Accept-Encoding
+< Content-Encoding: br
< Content-Length: 46
===
> GET http://ae-0/obj0 HTTP/1.1
@@ -31,6 +31,7 @@
< HTTP/1.1 200 OK
< Content-Type: text/javascript
< Content-Length: 1049
+< Vary: Accept-Encoding
===
> GET http://ae-0/obj0 HTTP/1.1
> X-Ats-Compress-Test: 0/zstd
@@ -38,14 +39,15 @@
< HTTP/1.1 200 OK
< Content-Type: text/javascript
< Content-Length: 1049
+< Vary: Accept-Encoding
===
> GET http://ae-1/obj1 HTTP/1.1
> X-Ats-Compress-Test: 1/gzip, deflate, sdch, br, zstd
> Accept-Encoding: gzip, deflate, sdch, br, zstd
< HTTP/1.1 200 OK
< Content-Type: text/javascript
-< Content-Encoding: gzip
< Vary: Accept-Encoding
+< Content-Encoding: gzip
< Content-Length: 7``
===
> GET http://ae-1/obj1 HTTP/1.1
@@ -53,8 +55,8 @@
> Accept-Encoding: gzip
< HTTP/1.1 200 OK
< Content-Type: text/javascript
-< Content-Encoding: gzip
< Vary: Accept-Encoding
+< Content-Encoding: gzip
< Content-Length: 7``
===
> GET http://ae-1/obj1 HTTP/1.1
@@ -63,6 +65,7 @@
< HTTP/1.1 200 OK
< Content-Type: text/javascript
< Content-Length: 1049
+< Vary: Accept-Encoding
===
> GET http://ae-1/obj1 HTTP/1.1
> X-Ats-Compress-Test: 1/deflate
@@ -70,6 +73,7 @@
< HTTP/1.1 200 OK
< Content-Type: text/javascript
< Content-Length: 1049
+< Vary: Accept-Encoding
===
> GET http://ae-1/obj1 HTTP/1.1
> X-Ats-Compress-Test: 1/zstd
@@ -77,14 +81,15 @@
< HTTP/1.1 200 OK
< Content-Type: text/javascript
< Content-Length: 1049
+< Vary: Accept-Encoding
===
> GET http://ae-2/obj2 HTTP/1.1
> X-Ats-Compress-Test: 2/gzip, deflate, sdch, br, zstd
> Accept-Encoding: gzip, deflate, sdch, br, zstd
< HTTP/1.1 200 OK
< Content-Type: text/javascript
-< Content-Encoding: br
< Vary: Accept-Encoding
+< Content-Encoding: br
< Content-Length: 46
===
> GET http://ae-2/obj2 HTTP/1.1
@@ -92,8 +97,8 @@
> Accept-Encoding: gzip
< HTTP/1.1 200 OK
< Content-Type: text/javascript
-< Content-Encoding: gzip
< Vary: Accept-Encoding
+< Content-Encoding: gzip
< Content-Length: 7``
===
> GET http://ae-2/obj2 HTTP/1.1
@@ -101,8 +106,8 @@
> Accept-Encoding: br
< HTTP/1.1 200 OK
< Content-Type: text/javascript
-< Content-Encoding: br
< Vary: Accept-Encoding
+< Content-Encoding: br
< Content-Length: 46
===
> GET http://ae-2/obj2 HTTP/1.1
@@ -111,6 +116,7 @@
< HTTP/1.1 200 OK
< Content-Type: text/javascript
< Content-Length: 1049
+< Vary: Accept-Encoding
===
> GET http://ae-2/obj2 HTTP/1.1
> X-Ats-Compress-Test: 2/zstd
@@ -118,14 +124,15 @@
< HTTP/1.1 200 OK
< Content-Type: text/javascript
< Content-Length: 1049
+< Vary: Accept-Encoding
===
> GET http://ae-3/obj3 HTTP/1.1
> X-Ats-Compress-Test: 3/gzip, deflate, sdch, br, zstd
> Accept-Encoding: gzip, deflate, sdch, br, zstd
< HTTP/1.1 200 OK
< Content-Type: text/javascript
-< Content-Encoding: br
< Vary: Accept-Encoding
+< Content-Encoding: br
< Content-Length: 46
===
> GET http://ae-3/obj3 HTTP/1.1
@@ -133,8 +140,8 @@
> Accept-Encoding: gzip
< HTTP/1.1 200 OK
< Content-Type: text/javascript
-< Content-Encoding: gzip
< Vary: Accept-Encoding
+< Content-Encoding: gzip
< Content-Length: 7``
===
> GET http://ae-3/obj3 HTTP/1.1
@@ -142,8 +149,8 @@
> Accept-Encoding: br
< HTTP/1.1 200 OK
< Content-Type: text/javascript
-< Content-Encoding: br
< Vary: Accept-Encoding
+< Content-Encoding: br
< Content-Length: 46
===
> GET http://ae-3/obj3 HTTP/1.1
@@ -152,6 +159,7 @@
< HTTP/1.1 200 OK
< Content-Type: text/javascript
< Content-Length: 1049
+< Vary: Accept-Encoding
===
> GET http://ae-3/obj3 HTTP/1.1
> X-Ats-Compress-Test: 3/zstd
@@ -159,6 +167,7 @@
< HTTP/1.1 200 OK
< Content-Type: text/javascript
< Content-Length: 1049
+< Vary: Accept-Encoding
===
> GET http://ae-4/obj4 HTTP/1.1
> X-Ats-Compress-Test: 4/gzip, deflate, sdch, br, zstd
@@ -174,8 +183,8 @@
> Accept-Encoding: gzip
< HTTP/1.1 200 OK
< Content-Type: text/javascript
-< Content-Encoding: gzip
< Vary: Accept-Encoding
+< Content-Encoding: gzip
< Content-Length: 7``
===
> GET http://ae-4/obj4 HTTP/1.1
@@ -183,8 +192,8 @@
> Accept-Encoding: br
< HTTP/1.1 200 OK
< Content-Type: text/javascript
-< Content-Encoding: br
< Vary: Accept-Encoding
+< Content-Encoding: br
< Content-Length: 4``
===
> GET http://ae-4/obj4 HTTP/1.1
@@ -193,6 +202,7 @@
< HTTP/1.1 200 OK
< Content-Type: text/javascript
< Content-Length: 1049
+< Vary: Accept-Encoding
===
> GET http://ae-4/obj4 HTTP/1.1
> X-Ats-Compress-Test: 4/zstd
@@ -217,8 +227,8 @@
> Accept-Encoding: gzip
< HTTP/1.1 200 OK
< Content-Type: text/javascript
-< Content-Encoding: gzip
< Vary: Accept-Encoding
+< Content-Encoding: gzip
< Content-Length: 7``
===
> GET http://ae-5/obj5 HTTP/1.1
@@ -226,8 +236,8 @@
> Accept-Encoding: br
< HTTP/1.1 200 OK
< Content-Type: text/javascript
-< Content-Encoding: br
< Vary: Accept-Encoding
+< Content-Encoding: br
< Content-Length: 4``
===
> GET http://ae-5/obj5 HTTP/1.1
@@ -236,6 +246,7 @@
< HTTP/1.1 200 OK
< Content-Type: text/javascript
< Content-Length: 1049
+< Vary: Accept-Encoding
===
> GET http://ae-5/obj5 HTTP/1.1
> X-Ats-Compress-Test: 5/zstd
@@ -251,8 +262,8 @@
> Accept-Encoding: gzip;q=0.666
< HTTP/1.1 200 OK
< Content-Type: text/javascript
-< Content-Encoding: gzip
< Vary: Accept-Encoding
+< Content-Encoding: gzip
< Content-Length: 7``
===
> GET http://ae-0/obj0 HTTP/1.1
@@ -260,8 +271,8 @@
> Accept-Encoding: gzip;q=0.666x
< HTTP/1.1 200 OK
< Content-Type: text/javascript
-< Content-Encoding: gzip
< Vary: Accept-Encoding
+< Content-Encoding: gzip
< Content-Length: 7``
===
> GET http://ae-0/obj0 HTTP/1.1
@@ -269,8 +280,8 @@
> Accept-Encoding: gzip;q=#0.666
< HTTP/1.1 200 OK
< Content-Type: text/javascript
-< Content-Encoding: gzip
< Vary: Accept-Encoding
+< Content-Encoding: gzip
< Content-Length: 7``
===
> GET http://ae-0/obj0 HTTP/1.1
@@ -278,8 +289,8 @@
> Accept-Encoding: gzip; Q = 0.666
< HTTP/1.1 200 OK
< Content-Type: text/javascript
-< Content-Encoding: gzip
< Vary: Accept-Encoding
+< Content-Encoding: gzip
< Content-Length: 7``
===
> GET http://ae-0/obj0 HTTP/1.1
@@ -288,14 +299,15 @@
< HTTP/1.1 200 OK
< Content-Type: text/javascript
< Content-Length: 1049
+< Vary: Accept-Encoding
===
> GET http://ae-0/obj0 HTTP/1.1
> X-Ats-Compress-Test: 0/gzip;q=-0.1
> Accept-Encoding: gzip;q=-0.1
< HTTP/1.1 200 OK
< Content-Type: text/javascript
-< Content-Encoding: gzip
< Vary: Accept-Encoding
+< Content-Encoding: gzip
< Content-Length: 7``
===
> GET http://ae-0/obj0 HTTP/1.1
@@ -303,8 +315,8 @@
> Accept-Encoding: aaa, gzip;q=0.666, bbb
< HTTP/1.1 200 OK
< Content-Type: text/javascript
-< Content-Encoding: gzip
< Vary: Accept-Encoding
+< Content-Encoding: gzip
< Content-Length: 7``
===
> GET http://ae-0/obj0 HTTP/1.1
@@ -312,8 +324,8 @@
> Accept-Encoding: br ; q=0.666, bbb
< HTTP/1.1 200 OK
< Content-Type: text/javascript
-< Content-Encoding: br
< Vary: Accept-Encoding
+< Content-Encoding: br
< Content-Length: 46
===
> GET http://ae-0/obj0 HTTP/1.1
@@ -321,8 +333,8 @@
> Accept-Encoding: aaa, gzip;q=0.666 ,
< HTTP/1.1 200 OK
< Content-Type: text/javascript
-< Content-Encoding: gzip
< Vary: Accept-Encoding
+< Content-Encoding: gzip
< Content-Length: 7``
===
> POST http://ae-3/obj3 HTTP/1.1
@@ -330,7 +342,7 @@
> Accept-Encoding: gzip
< HTTP/1.1 200 OK
< Content-Type: text/javascript
-< Content-Encoding: gzip
< Vary: Accept-Encoding
+< Content-Encoding: gzip
< Content-Length: 7``
===
diff --git a/tests/gold_tests/pluginTest/compress/compress.test.py
b/tests/gold_tests/pluginTest/compress/compress.test.py
index 4359b64a5c..181f3595d9 100644
--- a/tests/gold_tests/pluginTest/compress/compress.test.py
+++ b/tests/gold_tests/pluginTest/compress/compress.test.py
@@ -281,6 +281,31 @@ tr = Test.AddTestRun(f'verify gzip post')
tr.ReturnCode = 0
tr.Processes.Default.Command = get_verify_command(out_path, "gunzip -k -c")
+# Test Vary header: compressible content without Accept-Encoding should get
Vary: Accept-Encoding
+tr = Test.AddTestRun('vary header test: no accept-encoding')
+tr.Processes.Default.ReturnCode = 0
+out_path = get_out_path()
+tr.MakeCurlCommand(
+ f"-o {out_path} --verbose --proxy http://127.0.0.1:{ts.Variables.port}"
+ f" --header 'X-Ats-Compress-Test: vary-no-accept-encoding'"
+ f" 'http://ae-0/obj0'"
+ " 2>> compress_vary.log",
+ ts=ts)
+
+# Test Vary header: compressible content with unsupported Accept-Encoding
should get Vary: Accept-Encoding
+tr = Test.AddTestRun('vary header test: unsupported accept-encoding')
+tr.Processes.Default.ReturnCode = 0
+out_path = get_out_path()
+tr.MakeCurlCommand(curl(ts, 0, "compress, identity",
out_path).replace("compress_long.log", "compress_vary.log"), ts=ts)
+
+# Verify Vary header is present in both cases
+tr = Test.AddTestRun('verify vary headers')
+tr.Processes.Default.ReturnCode = 0
+tr.Processes.Default.Command = (
+ r"tr -d '\r' < compress_vary.log | grep -i 'vary:.*accept-encoding' | sort
> compress_vary_short.log")
+f = tr.Disk.File("compress_vary_short.log")
+f.Content = "compress_vary.gold"
+
# compress_long.log contains all the output from the curl commands. The tr
removes the carriage returns for easier
# readability. Curl seems to have a bug, where it will neglect to output an
end of line before outputting an HTTP
# message header line. The sed command is a work-around for this problem.
greplog.sh uses the grep command to
diff --git a/tests/gold_tests/pluginTest/compress/compress_userver.gold
b/tests/gold_tests/pluginTest/compress/compress_userver.gold
index 693bd03905..6c7a1e49f9 100644
--- a/tests/gold_tests/pluginTest/compress/compress_userver.gold
+++ b/tests/gold_tests/pluginTest/compress/compress_userver.gold
@@ -36,5 +36,7 @@
0/gzip;q=-0.1
0/aaa, gzip;q=0.666, bbb
0/ br ; q=0.666, bbb
-0/aaa, gzip;q=0.666 ,
+0/aaa, gzip;q=0.666 ,
3/gzip
+vary-no-accept-encoding
+0/compress, identity
diff --git a/tests/gold_tests/pluginTest/compress/compress_vary.gold
b/tests/gold_tests/pluginTest/compress/compress_vary.gold
new file mode 100644
index 0000000000..1ba5ad4743
--- /dev/null
+++ b/tests/gold_tests/pluginTest/compress/compress_vary.gold
@@ -0,0 +1,2 @@
+< Vary: Accept-Encoding
+< Vary: Accept-Encoding