#11249·dropwizard

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

Author: thiagohoraCreated Jul 9, 2026Updated Jul 15, 2026

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.