Skip to content

Commit

Permalink
Remove leading/trailing whitespaces from rendered authorization header (
Browse files Browse the repository at this point in the history
#2900)

* Fix client hanging when Authorization header contains trailing whitespace

* Allow parsing Authorization header without scheme

* Filter empty strings when parsing the authorization header
  • Loading branch information
kyri-petrou authored Jun 11, 2024
1 parent fcbbebf commit d1cb10d
Show file tree
Hide file tree
Showing 3 changed files with 21 additions and 4 deletions.
14 changes: 14 additions & 0 deletions zio-http/jvm/src/test/scala/zio/http/ClientSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,20 @@ object ClientSpec extends HttpRunnableSpec {
val resp = ZIO.scoped(ZClient.request(Request.get(url))).timeout(500.millis)
assertZIO(resp)(isNone)
} @@ timeout(5.seconds) @@ flaky(5),
test("authorization header without scheme") {
val app =
Handler
.fromFunction[Request] { req =>
req.headers.get(Header.Authorization) match {
case Some(h) => Response.text(h.renderedValue)
case None => Response.unauthorized("missing auth")
}
}
.toRoutes
val responseContent =
app.deploy(Request(headers = Headers(Header.Authorization.Unparsed("", "my-token")))).flatMap(_.body.asString)
assertZIO(responseContent)(equalTo("my-token"))
} @@ timeout(5.seconds),
)

override def spec = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ object AuthorizationSpec extends ZIOHttpSpec {
test("parsing of invalid Authorization values") {
assertTrue(
Authorization.parse("").isLeft,
Authorization.parse("something").isLeft,
Authorization.parse("something").isRight,
)
},
test("parsing and encoding is symmetrical") {
Expand Down
9 changes: 6 additions & 3 deletions zio-http/shared/src/main/scala/zio/http/Header.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1055,8 +1055,11 @@ object Header {
}

def parse(value: String): Either[String, Authorization] = {
val parts = value.split(" ")
if (parts.length >= 2) {
val parts = value.split(" ").filter(_.nonEmpty)
val nParts = parts.length
if (nParts == 1) {
Right(Unparsed("", parts(0)))
} else if (nParts >= 2) {
parts(0).toLowerCase match {
case "basic" => parseBasic(parts(1))
case "digest" => parseDigest(parts.tail.mkString(" "))
Expand All @@ -1074,7 +1077,7 @@ object Header {
s"""Digest response="$response",username="$username",realm="$realm",uri=${uri.toString},opaque="$opaque",algorithm=$algo,""" +
s"""qop=$qop,cnonce="$cnonce",nonce="$nonce",nc=$nc,userhash=${userhash.toString}"""
case Bearer(token) => s"Bearer ${token.value.asString}"
case Unparsed(scheme, params) => s"$scheme ${params.value.asString}"
case Unparsed(scheme, params) => s"$scheme ${params.value.asString}".strip()
}

private def parseBasic(value: String): Either[String, Authorization] = {
Expand Down

0 comments on commit d1cb10d

Please sign in to comment.