From fc07b4ac1c67c9a096fe1d63bf5049eabff8b6c1 Mon Sep 17 00:00:00 2001 From: adamw Date: Mon, 27 Nov 2017 11:59:43 +0100 Subject: Properly handling ipv6 in the uri interpolator --- .../com/softwaremill/sttp/UriInterpolator.scala | 39 +++++++++++++++++----- .../softwaremill/sttp/UriInterpolatorTests.scala | 5 +++ 2 files changed, 35 insertions(+), 9 deletions(-) (limited to 'core/src') diff --git a/core/src/main/scala/com/softwaremill/sttp/UriInterpolator.scala b/core/src/main/scala/com/softwaremill/sttp/UriInterpolator.scala index f0dbcba..fba3eaa 100644 --- a/core/src/main/scala/com/softwaremill/sttp/UriInterpolator.scala +++ b/core/src/main/scala/com/softwaremill/sttp/UriInterpolator.scala @@ -129,6 +129,17 @@ object UriInterpolator { } object Authority extends Tokenizer { + private val IpV6InAuthorityPattern = "\\[[0-9a-fA-F:]+\\]".r + + private def ipv6parser(a: String): Option[Vector[Token]] = { + a match { + case IpV6InAuthorityPattern() => + // removing the [] which are used to surround ipv6 adresses in URLs + Some(Vector(StringToken(a.substring(1, a.length - 1)))) + case _ => None + } + } + override def tokenize(s: String): (Tokenizer, Vector[Token]) = tokenizeTerminatedFragment( s, @@ -136,7 +147,8 @@ object UriInterpolator { Set('/', '?', '#'), Map(':' -> ColonInAuthority, '@' -> AtInAuthority, - '.' -> DotInAuthority) + '.' -> DotInAuthority), + ipv6parser ) } @@ -172,19 +184,28 @@ object UriInterpolator { * * The rest of the string, after the terminators, is tokenized using * a tokenizer determined by the type of the terminator. + * + * @param extraFragmentParser A context-specific parser which is given the + * option to tokenize a fragment (without terminators). */ private def tokenizeTerminatedFragment( s: String, current: Tokenizer, terminators: Set[Char], - separatorsToTokens: Map[Char, Token]): (Tokenizer, Vector[Token]) = { + separatorsToTokens: Map[Char, Token], + extraFragmentParser: String => Option[Vector[Token]] = _ => None) + : (Tokenizer, Vector[Token]) = { def tokenizeFragment(f: String): Vector[Token] = { - splitPreserveSeparators(f, separatorsToTokens.keySet).map { t => - t.headOption.flatMap(separatorsToTokens.get) match { - case Some(token) => token - case None => StringToken(t) - } + extraFragmentParser(f) match { + case None => + splitPreserveSeparators(f, separatorsToTokens.keySet).map { t => + t.headOption.flatMap(separatorsToTokens.get) match { + case Some(token) => token + case None => StringToken(t) + } + } + case Some(tt) => tt } } @@ -316,9 +337,9 @@ object UriInterpolator { case e: ExpressionToken => val es = tokensToString(Vector(e)) es.split(":", 2) match { - case Array(_) => Vector(e) - case Array(h, p) => + case Array(h, p) if p.matches("\\d+") => Vector(StringToken(h), ColonInAuthority, StringToken(p)) + case _ => Vector(e) } case t => Vector(t) } diff --git a/core/src/test/scala/com/softwaremill/sttp/UriInterpolatorTests.scala b/core/src/test/scala/com/softwaremill/sttp/UriInterpolatorTests.scala index 1d6f838..92c90c3 100644 --- a/core/src/test/scala/com/softwaremill/sttp/UriInterpolatorTests.scala +++ b/core/src/test/scala/com/softwaremill/sttp/UriInterpolatorTests.scala @@ -60,6 +60,11 @@ class UriInterpolatorTests extends FunSuite with Matchers { (uri"http://192.168.1.2/x", s"http://192.168.1.2/x"), (uri"http://${"192.168.1.2"}/x", s"http://192.168.1.2/x") ), + "ipv6" -> List( + (uri"http://[::1]/x", s"http://[::1]/x"), + (uri"http://${"::1"}/x", s"http://[::1]/x"), + (uri"http://${"::1"}:${8080}/x", s"http://[::1]:8080/x") + ), "ports" -> List( (uri"http://example.com:8080", s"http://example.com:8080"), (uri"http://example.com:${8080}", s"http://example.com:8080"), -- cgit v1.2.3