玩 WS - 检查压缩 headers

Play WS - check compression headers

我按照 those instructions 为我的 Web 服务 (Play 2.4) 中的所有响应启用了 gzip 压缩。易于设置,我可以看到它的工作原理就像使用 curl 和 wireshark 检查发送的响应是压缩的一样。

现在我想成为一名优秀的开发人员并添加集成测试以确保下周没有人破坏 HTTP 压缩。这就是乐趣的开始!我的测试如下所示:

"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")
  }
}

但是,测试失败,因为 WS 的响应 headers 不包含内容编码信息,并且 body 以未压缩的纯文本形式返回。

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

在 运行 这个测试时检查 wireshark 中的流量 我可以清楚地看到服务器正在返回 gzip-encoded 响应,所以看起来 WS 以某种方式透明地解压缩响应并剥离 content-encodingheaders?有没有一种方法可以获得完整 headers 的纯压缩响应,以便我可以检查响应是否被压缩?

我认为你做不到。如果我没记错的话,这里的问题是 Netty return 内容已经解压,所以 header 也被删除了。

AsyncHTTPClient 中有一个配置来设置它 (setKeepEncoding),但不幸的是,这只适用于 2.0 及更新版本,Play 2.4 WS lib 使用版本 1.9.x.

不管怎样,Play给你的客户端已经配置好了,不知道你能不能调整一下。但是您可以创建一个新客户端来模拟该行为:

// 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("...") // (...)

同样,这只是模拟您需要的结果。此外,建议的逻辑可能比仅将 gzip 添加为 Content-Encoding(例如:将请求的第一个算法放在 "Accepts Encoding" 中)更聪明。

事实证明我们不能真正使用 Play-WS 进行此特定测试,因为它已经 returns 解压缩并剥离了 header 的内容(请参阅@Salem 有见地的回答),所以没有办法检查响应是否被压缩。

然而,使用标准 Java 类 编写检查 HTTP 压缩的测试非常容易。我们只关心服务器在发送带有 Accept-Encoding: gzip 的请求时是否以(有效的)GZIP 格式回答。这是我最终得到的结果:

  forAll(endPoints) { endPoint =>
    val url = new URL(Localhost + port + "/api/" + endPoint)
    val connection = url.openConnection().asInstanceOf[HttpURLConnection]
    connection.setRequestProperty("Accept-Encoding", "gzip")
    Try {
      new GZIPInputStream(connection.getInputStream)
    } must be a 'success
  }