播放WS - 检查压缩标头

时间:2016-09-08 12:17:36

标签: scala playframework playframework-2.4

我按照those instructions为我的网络服务(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的响应标头不包含内容enconding信息,并且正文以纯文本形式返回,未压缩。

[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编码的响应,所以看起来WS在某种程度上透明地解压缩响应并剥离内容编码头?有没有办法可以获得带有完整标题的普通压缩响应,这样我就可以检查响应是否被压缩了?

2 个答案:

答案 0 :(得分:1)

我认为你不能那样做。如果我没有弄错的话,这里的问题是Netty返回已经解压缩的内容,因此标题也会被删除。

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(例如:放置“接受编码”中请求的第一个算法)。

答案 1 :(得分:1)

事实证明,我们无法真正使用Play-WS进行此特定测试,因为它已经返回未压缩的内容并剥离了标题(请参阅@ 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
  }