From 4fd5b1570c006a7ad5e022cadb9f41decf51d866 Mon Sep 17 00:00:00 2001 From: adamw Date: Wed, 2 Aug 2017 14:02:13 +0200 Subject: Special encoding for fragment --- .../src/main/scala/com/softwaremill/sttp/Uri.scala | 29 +++++++++++++++------- .../scala/com/softwaremill/sttp/UriTests.scala | 4 ++- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/core/src/main/scala/com/softwaremill/sttp/Uri.scala b/core/src/main/scala/com/softwaremill/sttp/Uri.scala index f66793a..ce758a1 100644 --- a/core/src/main/scala/com/softwaremill/sttp/Uri.scala +++ b/core/src/main/scala/com/softwaremill/sttp/Uri.scala @@ -123,7 +123,7 @@ case class Uri(scheme: String, val queryS = encodeQueryFragments(queryFragments.toList, previousWasPlain = true, new StringBuilder()) - val fragS = fragment.fold("")("#" + _) + val fragS = fragment.fold("")("#" + encodeFragment(_)) s"$schemeS://$userInfoS$hostS$portS$pathPrefixS$pathS$queryPrefixS$queryS$fragS" } @@ -139,19 +139,30 @@ case class Uri(scheme: String, else URLEncoder.encode(String.valueOf(s), "UTF-8") - private val relaxedQueryAllowedCharacters = { - // https://stackoverflow.com/questions/2322764/what-characters-must-be-escaped-in-an-http-query-string - val alphanum = (('a' to 'z') ++ ('A' to 'Z') ++ ('0' to '9')).toSet - val special = Set('/', '?', ':', '@', '-', '.', '_', '~', '!', '$', '&', - '\'', '(', ')', '*', '+', ',', ';', '=') - alphanum ++ special + private object Rfc3986 { + val AlphaNum: Set[Char] = + (('a' to 'z') ++ ('A' to 'Z') ++ ('0' to '9')).toSet + val Unreserved: Set[Char] = AlphaNum ++ Set('-', '.', '_', '~') + val SubDelims: Set[Char] = + Set('!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=') + val PChar: Set[Char] = Unreserved ++ SubDelims ++ Set(':', '@') + val Query: Set[Char] = PChar ++ Set('/', '?') + val Fragment: Set[Char] = Query } - private def encodeQueryRelaxed(s: String): String = { + // https://stackoverflow.com/questions/2322764/what-characters-must-be-escaped-in-an-http-query-string + private def encodeQueryRelaxed(s: String): String = + encode(s, Rfc3986.Query) + + // https://stackoverflow.com/questions/2053132/is-a-colon-safe-for-friendly-url-use/2053640#2053640 + private def encodeFragment(s: String): String = + encode(s, Rfc3986.Fragment) + + private def encode(s: String, allowedCharacters: Set[Char]): String = { val sb = new StringBuilder() // based on https://gist.github.com/teigen/5865923 for (c <- s) { - if (relaxedQueryAllowedCharacters(c)) sb.append(c) + if (allowedCharacters(c)) sb.append(c) else { for (b <- c.toString.getBytes("UTF-8")) { sb.append("%") diff --git a/core/src/test/scala/com/softwaremill/sttp/UriTests.scala b/core/src/test/scala/com/softwaremill/sttp/UriTests.scala index 26e7936..fb55468 100644 --- a/core/src/test/scala/com/softwaremill/sttp/UriTests.scala +++ b/core/src/test/scala/com/softwaremill/sttp/UriTests.scala @@ -39,7 +39,9 @@ class UriTests extends FunSuite with Matchers { Nil, Nil, None) -> - "http://us%26er:pa%20ss@example.com" + "http://us%26er:pa%20ss@example.com", + Uri("http", None, "example.com", None, Nil, Nil, Some("f:g/h i")) -> + "http://example.com#f:g/h%20i", ) for { -- cgit v1.2.3