dropwizard / dropwizard/dropwizard

JerseyClient with gzipEnabled=true double-decompresses gzip responses → ZipException (HttpClient5 + GZipDecoder)

Open
#11,249 8 comments 0 reactions 0 assignees View on GitHub

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:

  1. calls HttpClientBuilder.disableContentCompression(false) — so Apache HttpClient 5's ContentCompressionExec advertises Accept-Encoding and transparently decompresses the response entity; and
  2. registers Jersey's GZipDecoder reader 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
  1. Build a JerseyClient via JerseyClientBuilder with gzipEnabled = true.
  2. GET an endpoint that returns a gzipped body with Content-Encoding: gzip (e.g. any Jetty/Dropwizard upstream over its minimumEntitySize, or WireMock's auto-gzip).
  3. response.readEntity(String.class) throws ProcessingException caused by ZipException: 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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.