Skip to content

Commit

Permalink
Merge pull request #151 from hmrc/BDOG-2254
Browse files Browse the repository at this point in the history
BDOG-2254 Preserve case insensitivity of response headers returned fr…
  • Loading branch information
colin-lamed authored Nov 4, 2022
2 parents 31d1276 + 4030f93 commit 93e018d
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 9 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
### Version 14.8.0

Fixes case-insensitivity of `HttpResponse#headers` for Scala 2.13

### Version 14.0.0

Improves hook-data model for auditing.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright 2022 HM Revenue & Customs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package uk.gov.hmrc.http

object CollectionUtils {
// play returns scala.collection.Seq, but default for Scala 2.13 is scala.collection.immutable.Seq
private [http] def forScala2_13(m: Map[String, scala.collection.Seq[String]]): Map[String, Seq[String]] =
// `m.mapValues(_.toSeq).toMap` by itself strips the ordering away
scala.collection.immutable.TreeMap[String, Seq[String]]()(scala.math.Ordering.comparatorToOrdering(String.CASE_INSENSITIVE_ORDER)) ++ m.mapValues(_.toSeq)

}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import com.typesafe.config.Config
import play.api.Configuration
import play.api.libs.ws.{BodyWritable, EmptyBody, InMemoryBody, SourceBody, WSClient, WSProxyServer, WSRequest, WSResponse}
import play.core.parsers.FormUrlEncodedParser
import uk.gov.hmrc.http.{BadGatewayException, BuildInfo, GatewayTimeoutException, HeaderCarrier, HttpReads, HttpResponse, Retries}
import uk.gov.hmrc.http.{BadGatewayException, BuildInfo, CollectionUtils, GatewayTimeoutException, HeaderCarrier, HttpReads, HttpResponse, Retries}
import uk.gov.hmrc.play.http.BodyCaptor
import uk.gov.hmrc.play.http.logging.Mdc
import uk.gov.hmrc.play.http.ws.WSProxyConfiguration
Expand Down Expand Up @@ -240,16 +240,11 @@ class ExecutorImpl(
)(implicit ec: ExecutionContext
): (Future[HttpResponse], Future[ResponseData]) = {
val auditResponseP = Promise[ResponseData]()

// play returns scala.collection, but default for Scala 2.13 is scala.collection.immutable
def forScala2_13(m: scala.collection.Map[String, scala.collection.Seq[String]]): Map[String, Seq[String]] =
m.mapValues(_.toSeq).toMap

val httpResponseF =
for {
response <- responseF
status = response.status
headers = forScala2_13(response.headers)
headers = CollectionUtils.forScala2_13(response.headers)
} yield {
if (isStream) {
val source =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ import uk.gov.hmrc.http.HttpResponse
@deprecated("Use WsHttpResponse.apply and HttpResponse instead", "11.0.0")
class WSHttpResponse(wsResponse: WSResponse) extends HttpResponse {

override def allHeaders: Map[String, Seq[String]] = wsResponse.headers.mapValues(_.toSeq).toMap
override def allHeaders: Map[String, Seq[String]] =
WSHttpResponse.forScala2_13(wsResponse.headers)

override def status: Int = wsResponse.status

Expand All @@ -40,6 +41,12 @@ object WSHttpResponse {
HttpResponse(
status = wsResponse.status,
body = wsResponse.body,
headers = wsResponse.headers.mapValues(_.toSeq).toMap
headers = forScala2_13(wsResponse.headers)
)

// play returns scala.collection.Seq, but default for Scala 2.13 is scala.collection.immutable.Seq
// duplicated from CollectionUtils since WSHttpResponse is not defined within uk.gov.hmrc.http package..
private def forScala2_13(m: Map[String, scala.collection.Seq[String]]): Map[String, Seq[String]] =
// `m.mapValues(_.toSeq).toMap` by itself strips the ordering away
scala.collection.immutable.TreeMap[String, Seq[String]]()(scala.math.Ordering.comparatorToOrdering(String.CASE_INSENSITIVE_ORDER)) ++ m.mapValues(_.toSeq)
}
Original file line number Diff line number Diff line change
Expand Up @@ -623,6 +623,28 @@ class HttpClientV2Spec
.withHeader("User-Agent", equalTo("ua2"))
)
}

"return case-insensitive headers" in new Setup {
implicit val hc = HeaderCarrier()

wireMockServer.stubFor(
WireMock.get(urlEqualTo("/"))
.willReturn(aResponse().withBody("\"res\"").withStatus(200).withHeader("k", "v"))
)

val res: HttpResponse =
httpClientV2
.get(url"$wireMockUrl/")
.withBody(Json.toJson(ReqDomain("req")))
.execute[HttpResponse]
.futureValue

res.headers.get("k") shouldBe Some(List("v"))
res.headers.get("K") shouldBe Some(List("v"))

res.header("k") shouldBe Some("v")
res.header("K") shouldBe Some("v")
}
}

trait Setup {
Expand Down

0 comments on commit 93e018d

Please sign in to comment.