Why gzip compression is not disabled for strong etag
Jetty version(s) 12.0.12
Jetty Environment ee 10
Java version/vendor Amazon correto 21
OS type/version WSL
Description I believe GzipHandler handles the gzip compression, but I do not know why it does not disable compression for strong etags.
Similar issue for cowboy webserver: https://github.com/ninenines/cowboy/issues/1546
How to reproduce?
Enable compression on jetty and fire a request with a strong ETag, and the compression is applied to the response
Compression adds the suffix of the compression on the ETag response for endpoints that generate the content and care about etags, maintaining the original non-weak etag, but with compression indicated (to satisfy http caching requirement)
Example:
If the original request is ...
GET /context/data HTTP/1.1
Host: localhost:8080
Connection: close
And that request passes through GzipHandler reaching a webapp DataServlet.
That DataServlet produces a response, and also adds the ETag: 2341olla response header.
Since that request didn't have an Accept-Encoding, there's no compression applied, and the response will be roughly ...
HTTP/1.1 200 OK
Content-Type: application/octet-stream
Content-Length: 5123
ETag: 2341olla
<lots of response data here>
Now, if that same client makes a request, with that Etag ...
GET /context/data HTTP/1.1
Host: localhost:8080
If-None-Match: 2341olla
Connection: close
Then the response will again, go through GzipHandler, reach the webapp DataServlet.
That DataServlet checks the If-None-Match etag and decides, that it won't produce a response, but instead responds with a 304.
It goes back up through the response chain, past GzipHandler on the way out and the response seen by the client is ...
HTTP/1.1 304 Not Modified
Content-Type: application/octet-stream
ETag: 2341olla
However, if the client suddenly decides that it can support gzip, like this ...
GET /context/data HTTP/1.1
Host: localhost:8080
If-None-Match: 2341olla
Accept-Encoding: gzip
Connection: close
That will pass through the GzipHandler, it will see the support for Gzip in the request headers and prepare for Gzip response.
The request reaches the webapp DataServlet.
That DataServlet checks the If-None-Match etag and decides, that it won't produce a response, but instead responds with a 304.
The GzipHandler will see the 304 and not compress the response, and not mess with the ETag.
Producing a response like ...
HTTP/1.1 304 Not Modified
Content-Type: application/octet-stream
ETag: 2341olla
Nothing surprising yet, right?
Now, lets look at a new client, that has never asked for the resource, but supports ETag too.
Initial request ...
GET /context/data HTTP/1.1
Host: localhost:8080
Connection: close
And that request passes through GzipHandler reaching a webapp DataServlet.
That DataServlet produces a response, and also adds the ETag: 2341olla response header.
Since that request did have an Accept-Encoding, there is compression applied, and the ETag will have a --gzip suffix applied by the GzipHandler.
HTTP/1.1 200 OK
Content-Type: application/octet-stream
Content-Length: 413
Content-Encoding: gzip
ETag: 2341olla--gzip
<lots of compressed response data here>
The next request, by this new client will include the ETag in its full form, not being aware of the GzipHandler behavior.
GET /context/data HTTP/1.1
Host: localhost:8080
Accept-Encoding: gzip
If-None-Match: 2341olla--gzip
Connection: close
Then the response will again, go through GzipHandler, and it strips the --gzip suffix on the request If-None-Match header, sending the new modified request to the webapp DataServlet.
That DataServlet checks the If-None-Match etag (which it sees without the --gzip suffix) and decides, that it won't produce a response, but instead responds with a 304.
It goes back up through the response chain, past GzipHandler on the way out and the response seen by the client is ...
HTTP/1.1 304 Not Modified
Content-Type: application/octet-stream
ETag: 2341olla
@joakime, thank you for your reply. You are saying that the calculation/comparison of the ETag is done on the decompressed response, and I agree that compression will work properly in that case.
To give you a little bit of context, I recently migrated from Tomcat to Jetty, and compression is disabled by default for strong tags for Tomcat.
When I deployed the application after the migration, the production (iOS app that requests gzip compressed response) went down because we are heavily using ETags for caching, and Tomcat did not compress the response and did not send headers like 'Vary'.
I have overcome the issue, and I am just saying if disabling compression for strong etags is part of a standard, then Jetty should do the same as Tomcat, and other developers will not have the same issue when doing tomcat-jetty migration.