Scenario is reporting server timeout when receiving gzip response from the server

I am facing a problem when receiving a gzip response from the server in my Gatling test. I am using the Java SDK.

I am build an HttpProtocolBuilder by specifying acceptEncodingHeader(“gzip”)

Next I build a scenario to send a GET request to the server and check for HTTP status code 200.

Then I feed this scenario to a simulation.

The problem I am facing is when I specify acceptEncodingHeader(“gzip”), gatling reports a server timeout error after 60 seconds. If I omit the acceptEncodingHeader(“gzip”), then the test runs just fine. Since I control the server, I have verified that the server sends a gzip response correctly when it sees “Accept-Encoding” header as “gzip”. Nothing is wrong on the server side.

Why is gatling reporting a timeout error with this encoding? I am missing something in the scenario to handle a gzip request?

I am using gatling-app version 3.9.0

public class AcceptEncodingTest extends Simulation {

  {
    HttpProtocolBuilder httpProtocol = HttpDsl.http
      .baseUrl("https://localhost:8080")
      .acceptEncodingHeader("gzip")
      .authorizationHeader(ImsUtil.getAuthorizationHeader())
      .header("Content-Type", "application/json");

    FeederBuilder.Batchable<String> cachedArtifactIdFeeder = CoreDsl.csv("IDs.csv").random();
    ChainBuilder fetchCachedArtifactsWithIds = CoreDsl.feed(cachedArtifactIdFeeder)
      .exec(HttpDsl.http("Fetch artifacts from cache")
        .get("/schema/id/#{artifactId}/sandbox/#{sandbox}")
        .header("Accept", "application/json")
        .check(HttpDsl.status().is(200)));

    ScenarioBuilder fetchArtifactsScenario = CoreDsl.scenario("Fetch artifacts Scenario").exec(fetchCachedArtifactsWithIds);
    setUp(fetchArtifactsScenario.injectOpen(CoreDsl.constantUsersPerSec(1)
      .during(10))).protocols(httpProtocol);
  }

}

The debug output on console looks like this:

=========================
HTTP response:
<<<<<<<<<<<<<<<<<<<<<<<<<
23:35:24.861 [DEBUG] i.g.c.a.Exit - End user #4
23:35:24.861 [DEBUG] i.g.c.c.i.Injector - End user #Fetch artifacts Scenario
23:35:25.859 [DEBUG] i.g.h.e.GatlingHttpListener$ - Request 'Fetch artifacts from cache' failed for user 5
io.gatling.http.client.impl.RequestTimeoutException: Request timeout to localhost/127.0.0.1:8080 after 60000 ms
23:35:25.859 [DEBUG] i.g.h.e.r.DefaultStatsProcessor - Request 'Fetch artifacts from cache' failed for user 5: Request timeout to localhost/127.0.0.1:8080 after 60000 ms
23:35:25.859 [DEBUG] i.g.h.e.r.DefaultStatsProcessor - 
>>>>>>>>>>>>>>>>>>>>>>>>>>
Request:
Fetch unions from cache with IDs: KO Request timeout to localhost/127.0.0.1:8080 after 60000 ms
=========================

Greatly appreciate your help.

Could you please provide a way to reproduce your issue, like a sample application?

I suspect your server is not properly terminating the transfer-encoding: chunked stream so the client keeps on waiting until it ultimately times out.

Thanks @slandelle , you nailed it. Adding “Transfer-Encoding: chunked” in the response solved the problem. Previously the code was only adding “Content-Encoding: gzip” header in the response.

Old code:

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

    HttpServletRequest httpRequest = (HttpServletRequest) request;
    HttpServletResponse httpResponse = (HttpServletResponse) response;

    if ("GET".equals(httpRequest.getMethod()) && acceptsGZipEncoding(httpRequest)) {
      httpResponse.addHeader("Content-Encoding", "gzip");
      GZipResponseWrapper gzipResponse = new GZipResponseWrapper(httpResponse);
      chain.doFilter(request, gzipResponse);
      gzipResponse.close();
    } else {
      chain.doFilter(request, response);
    }
  }

New code:

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

    HttpServletRequest httpRequest = (HttpServletRequest) request;
    HttpServletResponse httpResponse = (HttpServletResponse) response;

    if ("GET".equals(httpRequest.getMethod()) && acceptsGZipEncoding(httpRequest)) {
      httpResponse.addHeader("Content-Encoding", "gzip");
      httpResponse.addHeader("Transfer-Encoding", "chunked");
      GZipResponseWrapper gzipResponse = new GZipResponseWrapper(httpResponse);
      chain.doFilter(request, gzipResponse);
      gzipResponse.close();
    } else {
      chain.doFilter(request, response);
    }
  }

Thanks again for your help!

1 Like

FYI, there are 3 ways to properly terminate the response:

  • set the content-length header when you already know the payload length (which you don’t know here because you’re compressing on the fly)
  • set the transfer-encoding: chunked when you don’t know the content-length beforehand
  • close the connection

Glad things work now.

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.