scalaplayframeworkplayframework-2.4

Play WS - check compression headers


I enabled gzip compression for all the responses in my web service (Play 2.4) by following those instructions. Easy to set up, and I can see it works like a charm having checked with curl and wireshark that the responses are sent compressed.

Now I want to be a good developer and add an integration test to make sure no one breaks HTTP compression next week. Here's where the fun begins! My test looks like this:

"use HTTP compression" in {
  forAll(endPoints) { endPoint =>
    val response = await(
      WS.url(Localhost + port + "/api" + endPoint).withHeaders("Accept-Encoding" -> "gzip").get()
    )
    response.header("Content-Encoding") mustBe Some("gzip")
  }
}

However, the test fails as WS's response headers don't include content enconding information, and the body is returned as plain text, uncompressed.

[info] - should use HTTP compression *** FAILED ***
[info]   forAll failed, because:
[info]     at index 0, None was not equal to Some("gzip") (ApplicationSpec.scala:566)

Checking the traffic in wireshark when running this test I can clearly see the server is returning a gzip-encoded response, so it looks like WS is somehow transparently decompressing the response and stripping the content-encoding headers? Is there a way I can get the plain, compressed response with full headers so I can check whether the response is compressed or not?


Solution

  • I don't think you can do that. If I'm not mistaken , the problem here is that Netty return the content already uncompressed, so the header is removed also.

    There is a configuration in AsyncHTTPClient to set that (setKeepEncoding), but unfortunately this only works in version 2.0 and newer, and Play 2.4 WS lib uses version 1.9.x.

    Either way, the client Play gives you is already configured, and I don't know if you are able to tweak it. But you can create a new client to emulate that behavior:

    // Converted from Java code: I have never worked with those APi's in Scala
    val cfg = new AsyncHttpClientConfig.Builder().addResponseFilter(new ResponseFilter {
            override def filter[T](ctx: FilterContext[T]): FilterContext[T] = {
                val headers = ctx.getRequest.getHeaders
                if (headers.containsKey("Accept-Encoding")) {
                    ctx.getResponseHeaders.getHeaders.put("Content-Encoding", List("gzip"))
                }
                ctx
            }
        }).build()
    val client: NingWSClient = NingWSClient(cfg)
    client.url("...") // (...)
    

    Again, this is just emulating the result you need. Also, probably a more clever logic than just add gzip as Content-Encoding (ex: put the first algorithm requested in "Accepts Encoding") is advised.