aboutsummaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authoradamw <adam@warski.org>2017-09-04 11:28:01 +0200
committeradamw <adam@warski.org>2017-09-04 11:28:01 +0200
commitf0f428c85ef6f24e98f9465248d9155d29dc5b4d (patch)
tree32b0921a5d4b5bed12fdd0225ac8dfdf6a696b8f /core
parentcaf69a9742bf6a8593208af30ec27b7006e91328 (diff)
downloadsttp-f0f428c85ef6f24e98f9465248d9155d29dc5b4d.tar.gz
sttp-f0f428c85ef6f24e98f9465248d9155d29dc5b4d.tar.bz2
sttp-f0f428c85ef6f24e98f9465248d9155d29dc5b4d.zip
#33: properly escape path segments in URIs
Diffstat (limited to 'core')
-rw-r--r--core/src/main/scala/com/softwaremill/sttp/Uri.scala27
-rw-r--r--core/src/test/scala/com/softwaremill/sttp/UriTests.scala10
2 files changed, 19 insertions, 18 deletions
diff --git a/core/src/main/scala/com/softwaremill/sttp/Uri.scala b/core/src/main/scala/com/softwaremill/sttp/Uri.scala
index ce758a1..ef94dd9 100644
--- a/core/src/main/scala/com/softwaremill/sttp/Uri.scala
+++ b/core/src/main/scala/com/softwaremill/sttp/Uri.scala
@@ -88,7 +88,8 @@ case class Uri(scheme: String,
override def toString: String = {
def encodeUserInfo(ui: UserInfo): String =
- encode(ui.username) + ui.password.fold("")(":" + encode(_))
+ encode(Rfc3986.UserInfo)(ui.username) + ui.password.fold("")(
+ ":" + encode(Rfc3986.UserInfo)(_))
@tailrec
def encodeQueryFragments(qfs: List[QueryFragment],
@@ -112,12 +113,12 @@ case class Uri(scheme: String,
encodeQueryFragments(t, previousWasPlain = false, sb)
}
- val schemeS = encode(scheme)
+ val schemeS = encode(Rfc3986.Scheme)(scheme)
val userInfoS = userInfo.fold("")(encodeUserInfo(_) + "@")
- val hostS = encode(host)
+ val hostS = encode(Rfc3986.Host)(host)
val portS = port.fold("")(":" + _)
val pathPrefixS = if (path.isEmpty) "" else "/"
- val pathS = path.map(encode).mkString("/")
+ val pathS = path.map(encode(Rfc3986.PathSegment)).mkString("/")
val queryPrefixS = if (queryFragments.isEmpty) "" else "?"
val queryS = encodeQueryFragments(queryFragments.toList,
@@ -127,13 +128,6 @@ case class Uri(scheme: String,
s"$schemeS://$userInfoS$hostS$portS$pathPrefixS$pathS$queryPrefixS$queryS$fragS"
}
- private def encode(s: Any): String = {
- // space is encoded as a +, which is only valid in the query;
- // in other contexts, it must be percent-encoded; see
- // https://stackoverflow.com/questions/2678551/when-to-encode-space-to-plus-or-20
- URLEncoder.encode(String.valueOf(s), "UTF-8").replaceAll("\\+", "%20")
- }
-
private def encodeQuery(s: String, relaxed: Boolean): String =
if (relaxed) encodeQueryRelaxed(s)
else
@@ -146,19 +140,24 @@ case class Uri(scheme: String,
val SubDelims: Set[Char] =
Set('!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=')
val PChar: Set[Char] = Unreserved ++ SubDelims ++ Set(':', '@')
+
+ val Scheme: Set[Char] = AlphaNum ++ Set('+', '-', '.')
+ val UserInfo: Set[Char] = Unreserved ++ SubDelims
+ val Host: Set[Char] = Unreserved ++ SubDelims
+ val PathSegment: Set[Char] = PChar
val Query: Set[Char] = PChar ++ Set('/', '?')
val Fragment: Set[Char] = Query
}
// 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)
+ encode(Rfc3986.Query)(s)
// 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)
+ encode(Rfc3986.Fragment)(s)
- private def encode(s: String, allowedCharacters: Set[Char]): String = {
+ private def encode(allowedCharacters: Set[Char])(s: String): String = {
val sb = new StringBuilder()
// based on https://gist.github.com/teigen/5865923
for (c <- s) {
diff --git a/core/src/test/scala/com/softwaremill/sttp/UriTests.scala b/core/src/test/scala/com/softwaremill/sttp/UriTests.scala
index c9bb8b5..f421413 100644
--- a/core/src/test/scala/com/softwaremill/sttp/UriTests.scala
+++ b/core/src/test/scala/com/softwaremill/sttp/UriTests.scala
@@ -31,17 +31,19 @@ class UriTests extends FunSuite with Matchers {
List("a b", "z", "ą:ę"),
List(QF.KeyValue("p:1", "v&v"), QF.KeyValue("p2", "v v")),
None) ->
- "http://exa%20mple.com/a%20b/z/%C4%85%3A%C4%99?p%3A1=v%26v&p2=v+v",
+ "http://exa%20mple.com/a%20b/z/%C4%85:%C4%99?p%3A1=v%26v&p2=v+v",
Uri("http",
- Some(UserInfo("us&er", Some("pa ss"))),
+ Some(UserInfo("us&e/r", Some("pa ss"))),
"example.com",
None,
Nil,
Nil,
None) ->
- "http://us%26er:pa%20ss@example.com",
+ "http://us&e%2Fr:pa%20ss@example.com",
Uri("http", None, "example.com", None, Nil, Nil, Some("f:g/h i")) ->
- "http://example.com#f:g/h%20i"
+ "http://example.com#f:g/h%20i",
+ Uri("http", None, "example.com", None, List("key=value"), Nil, None) ->
+ "http://example.com/key=value"
)
for {