dropwizard / dropwizard/dropwizard
JerseyClient with gzipEnabled=true double-decompresses gzip responses → ZipException (HttpClient5 + GZipDecoder)
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 8.6k
- Forks
- 3.4k
- Avg merge
- 2d 17h
- Merged PRs (30d)
- 17
Description
Version: Dropwizard 5.0.0 (dropwizard-client)
What happens
With JerseyClientConfiguration.gzipEnabled = true (the default), calling readEntity on a response that the upstream gzipped throws java.util.zip.ZipException: Not in GZIP format:
java.util.zip.ZipException: Not in GZIP format
at java.base/java.util.zip.GZIPInputStream.readHeader(GZIPInputStream.java:199)
at io.dropwizard.jersey.gzip.GZipDecoder.aroundReadFrom(GZipDecoder.java:36)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor.proceed(...)
at org.glassfish.jersey.client.ClientResponse.readEntity(ClientResponse.java:307)
...
at io.dropwizard.client.DropwizardApacheConnector.apply(DropwizardApacheConnector.java)
Cause
With gzipEnabled = true, JerseyClientBuilder:
- calls
HttpClientBuilder.disableContentCompression(false)— so Apache HttpClient 5'sContentCompressionExecadvertisesAccept-Encodingand transparently decompresses the response entity; and - registers Jersey's
GZipDecoderreader interceptor.
So two decoders are active. DropwizardApacheConnector.apply() copies the raw HttpClient response headers — including Content-Encoding: gzip — into the Jersey ClientResponse, while handing over the entity stream HttpClient has already decompressed. GZipDecoder then sees the stale Content-Encoding header and wraps the already-plain stream in a GZIPInputStream, so readEntity fails.
Both the HttpClient decoder and GZipDecoder are gated on the single gzipEnabled flag, so there's no configuration that yields "one decoder while still negotiating gzip on the wire": it's either both (double decode) or neither (gzipEnabled: false, no compression at all).
Expected
Exactly one decode. When HttpClient's transparent content decompression is active, DropwizardApacheConnector should drop the now-inconsistent Content-Encoding (and Content-Length) headers before they reach Jersey — the standard behavior of an HTTP client that auto-decompresses — so that GZipDecoder correctly no-ops.
Reproduction
- Build a
JerseyClientviaJerseyClientBuilderwithgzipEnabled = true. - GET an endpoint that returns a gzipped body with
Content-Encoding: gzip(e.g. any Jetty/Dropwizard upstream over itsminimumEntitySize, or WireMock's auto-gzip). response.readEntity(String.class)throwsProcessingExceptioncaused byZipException: Not in GZIP format.
Workaround
Per outbound call, request identity encoding: .request().acceptEncoding("identity"). This keeps the response uncompressed so neither decoder engages — but it opts out of compression per call site rather than fixing the double-decode.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in DropwizardApacheConnector.apply(), then inspect JerseyClientBuilder's gzip configuration and GZipDecoder to trace how response headers and the entity stream are passed to Jersey. Reproduce the failure with a gzipped upstream and verify that readEntity(String.class) succeeds with consistent headers and exactly one decompression step.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 65/100