From 5d005502d1a10a4e546e3e78419a58d019d8b60e Mon Sep 17 00:00:00 2001 From: "leonid.stashevsky" Date: Tue, 4 Oct 2022 09:34:37 +0200 Subject: [PATCH 1/4] KTOR-993 Add test to verify URI encoding --- .../test/io/ktor/tests/http/UrlEncodeTest.kt | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 ktor-http/common/test/io/ktor/tests/http/UrlEncodeTest.kt diff --git a/ktor-http/common/test/io/ktor/tests/http/UrlEncodeTest.kt b/ktor-http/common/test/io/ktor/tests/http/UrlEncodeTest.kt new file mode 100644 index 0000000000..8cb5470df3 --- /dev/null +++ b/ktor-http/common/test/io/ktor/tests/http/UrlEncodeTest.kt @@ -0,0 +1,110 @@ +/* + * Copyright 2014-2022 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +package io.ktor.tests.http + +import io.ktor.http.* +import kotlin.test.* + +class UrlEncodeTest { + + @Test + fun testUrlEncodePathKeepDigits() { + assertEquals("0123456789", "0123456789".encodeURLPath()) + } + + @Test + fun testUrlEncodeQueryComponentKeepDigits() { + assertEquals("0123456789", "0123456789".encodeURLQueryComponent()) + } + + @Test + fun testUrlKeepDigitsInPath() { + assertEquals("/0123456789/", Url("http://x.com/0123456789/").encodedPath) + } + + @Test + fun testUrlEncodePathKeepLetters() { + assertEquals( + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".encodeURLPath() + ) + } + + @Test + fun testUrlEncodeQueryComponentKeepLetters() { + assertEquals( + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".encodeURLQueryComponent() + ) + } + + @Test + fun testUrlKeepLettersInPath() { + assertEquals( + "/abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/", + Url("http://x.com/abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/").encodedPath + ) + } + + @Test + fun testUrlEncodePathKeepHyphen() { + assertEquals("-", "-".encodeURLPath()) + } + + @Test + fun testUrlEncodeQueryComponentKeepHyphen() { + assertEquals("-", "-".encodeURLQueryComponent()) + } + + @Test + fun testUrlKeepHyphenInPath() { + assertEquals("/-/", Url("http://x.com/-/").encodedPath) + } + + @Test + fun testUrlEncodePathKeepPeriod() { + assertEquals(".", ".".encodeURLPath()) + } + + @Test + fun testUrlEncodeQueryComponentKeepPeriod() { + assertEquals(".", ".".encodeURLQueryComponent()) + } + + @Test + fun testUrlKeepPeriodInPath() { + assertEquals("/./", Url("http://x.com/./").encodedPath) + } + + @Test + fun testUrlEncodePathKeepUnderscore() { + assertEquals("_", "_".encodeURLPath()) + } + + @Test + fun testUrlEncodeQueryComponentKeepUnderscore() { + assertEquals("_", "_".encodeURLQueryComponent()) + } + + @Test + fun testUrlKeepUnderscoreInPath() { + assertEquals("/_/", Url("http://x.com/_/").encodedPath) + } + + @Test + fun testUrlEncodePathKeepTilde() { + assertEquals("~", "~".encodeURLPath()) + } + + @Test + fun testUrlEncodeQueryComponentKeepTilde() { + assertEquals("~", "~".encodeURLQueryComponent()) + } + + @Test + fun testUrlKeepTildeInPath() { + assertEquals("/~/", Url("http://x.com/~/").encodedPath) + } +} From 9375eecaecbcf5fecc3f2b4c9791acff137d3a3f Mon Sep 17 00:00:00 2001 From: "leonid.stashevsky" Date: Tue, 4 Oct 2022 10:56:26 +0200 Subject: [PATCH 2/4] KTOR-917 Keep some characters in cookie URI encoding according to RFC 3986 --- ktor-http/common/src/io/ktor/http/Cookie.kt | 2 +- .../test/io/ktor/tests/http/CodecTest.kt | 2 +- .../plugins/Base64EncodingCookiesTest.kt | 32 +++ .../ktor/tests/server/plugins/CookiesTest.kt | 200 ------------------ .../plugins/DQuotesCookiesEncodingTest.kt | 31 +++ .../plugins/ParserServerSetCookieTest.kt | 131 ++++++++++++ .../tests/server/plugins/RawCookieTest.kt | 17 ++ .../server/plugins/URIEncodingCookiesTest.kt | 64 ++++++ 8 files changed, 277 insertions(+), 202 deletions(-) create mode 100644 ktor-server/ktor-server-tests/jvmAndNix/test/io/ktor/tests/server/plugins/Base64EncodingCookiesTest.kt create mode 100644 ktor-server/ktor-server-tests/jvmAndNix/test/io/ktor/tests/server/plugins/DQuotesCookiesEncodingTest.kt create mode 100644 ktor-server/ktor-server-tests/jvmAndNix/test/io/ktor/tests/server/plugins/ParserServerSetCookieTest.kt create mode 100644 ktor-server/ktor-server-tests/jvmAndNix/test/io/ktor/tests/server/plugins/RawCookieTest.kt create mode 100644 ktor-server/ktor-server-tests/jvmAndNix/test/io/ktor/tests/server/plugins/URIEncodingCookiesTest.kt diff --git a/ktor-http/common/src/io/ktor/http/Cookie.kt b/ktor-http/common/src/io/ktor/http/Cookie.kt index 2cde773406..95b94c03d2 100644 --- a/ktor-http/common/src/io/ktor/http/Cookie.kt +++ b/ktor-http/common/src/io/ktor/http/Cookie.kt @@ -183,7 +183,7 @@ public fun encodeCookieValue(value: String, encoding: CookieEncoding): String = else -> value } CookieEncoding.BASE64_ENCODING -> value.encodeBase64() - CookieEncoding.URI_ENCODING -> value.encodeURLQueryComponent(encodeFull = true, spaceToPlus = true) + CookieEncoding.URI_ENCODING -> value.encodeURLQueryComponent(encodeFull = false, spaceToPlus = true) } /** diff --git a/ktor-http/common/test/io/ktor/tests/http/CodecTest.kt b/ktor-http/common/test/io/ktor/tests/http/CodecTest.kt index a4ddad9895..7534f1fb07 100644 --- a/ktor-http/common/test/io/ktor/tests/http/CodecTest.kt +++ b/ktor-http/common/test/io/ktor/tests/http/CodecTest.kt @@ -141,7 +141,7 @@ class CodecTest { mapOf( "a" to listOf("b", "c", "d"), "1" to listOf("2"), - "x" to listOf("y", "z"), + "x" to listOf("y", "z") ).entries.formUrlEncodeTo(result) assertEquals("a=b&a=c&a=d&1=2&x=y&x=z", result.toString()) diff --git a/ktor-server/ktor-server-tests/jvmAndNix/test/io/ktor/tests/server/plugins/Base64EncodingCookiesTest.kt b/ktor-server/ktor-server-tests/jvmAndNix/test/io/ktor/tests/server/plugins/Base64EncodingCookiesTest.kt new file mode 100644 index 0000000000..e7f2e4c1ad --- /dev/null +++ b/ktor-server/ktor-server-tests/jvmAndNix/test/io/ktor/tests/server/plugins/Base64EncodingCookiesTest.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2014-2022 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +package io.ktor.tests.server.plugins + +import io.ktor.http.* +import kotlin.test.* + +class Base64EncodingCookiesTest { + @Test + fun `no bad characters`() { + testEncode("YWJj", "abc") + } + + @Test + fun `space inside`() { + testEncode("YWJjIDEyMw==", "abc 123") + } + + @Test + fun `equals inside`() { + testEncode("YWJjPTEyMw==", "abc=123") + } + + private fun testEncode(expected: String, value: String): String { + val encoded = encodeCookieValue(value, CookieEncoding.BASE64_ENCODING) + assertEquals(expected, encoded, "Encode failed") + assertEquals(value, decodeCookieValue(encoded, CookieEncoding.BASE64_ENCODING), "Decode failed") + return encoded + } +} diff --git a/ktor-server/ktor-server-tests/jvmAndNix/test/io/ktor/tests/server/plugins/CookiesTest.kt b/ktor-server/ktor-server-tests/jvmAndNix/test/io/ktor/tests/server/plugins/CookiesTest.kt index 369b9d3fe2..c2947b9223 100644 --- a/ktor-server/ktor-server-tests/jvmAndNix/test/io/ktor/tests/server/plugins/CookiesTest.kt +++ b/ktor-server/ktor-server-tests/jvmAndNix/test/io/ktor/tests/server/plugins/CookiesTest.kt @@ -177,203 +177,3 @@ class CookiesTest { private fun String.cutSetCookieHeader() = substringBeforeLast("\$x-enc").trimEnd().removeSuffix(";") } -class DQuotesEncodingTest { - @Test - fun `no bad characters`() { - testEncode("abc", "abc") - } - - @Test - fun `space inside`() { - testEncode("\"abc 123\"", "abc 123") - } - - @Test - fun `equals inside`() { - testEncode("abc=123", "abc=123") - } - - private fun testEncode(expected: String, value: String) { - val encoded = encodeCookieValue(value, CookieEncoding.DQUOTES) - assertEquals(expected, encoded, "Encode failed") - assertEquals(value, decodeCookieValue(encoded, CookieEncoding.DQUOTES), "Decode failed") - } -} - -class Base64EncodingTest { - @Test - fun `no bad characters`() { - testEncode("YWJj", "abc") - } - - @Test - fun `space inside`() { - testEncode("YWJjIDEyMw==", "abc 123") - } - - @Test - fun `equals inside`() { - testEncode("YWJjPTEyMw==", "abc=123") - } - - private fun testEncode(expected: String, value: String): String { - val encoded = encodeCookieValue(value, CookieEncoding.BASE64_ENCODING) - assertEquals(expected, encoded, "Encode failed") - assertEquals(value, decodeCookieValue(encoded, CookieEncoding.BASE64_ENCODING), "Decode failed") - return encoded - } -} - -class URIEncodingTest { - @Test - fun `no bad characters`() { - testEncode("abc", "abc") - } - - @Test - fun `space inside`() { - testEncode("abc+123", "abc 123") - } - - @Test - fun `equals inside`() { - testEncode("abc%3D123", "abc=123") - } - - private fun testEncode(expected: String, value: String) { - val encoded = encodeCookieValue(value, CookieEncoding.URI_ENCODING) - assertEquals(expected, encoded, "Encode failed") - assertEquals(value, decodeCookieValue(encoded, CookieEncoding.URI_ENCODING), "Decode failed") - } -} - -class RawCookieTest { - @Test - fun testRawEncodingWithEquals() { - val cookieValue = "my.value.key=my.value.value+my.value.id=5" - val encoded = encodeCookieValue(cookieValue, CookieEncoding.RAW) - assertEquals(cookieValue, encoded) - } -} - -class ParserServerSetCookieTest { - @Test - fun testSimpleParse() { - val header = "key=value; max-Age=999; \$extension=1; \$x-enc=URI_ENCODING" - val parsed = parseServerSetCookieHeader(header) - - assertEquals("key", parsed.name) - assertEquals("value", parsed.value) - assertEquals(999, parsed.maxAge) - assertEquals("1", parsed.extensions["\$extension"]) - assertEquals(CookieEncoding.URI_ENCODING, parsed.encoding) - } - - @Test - fun testSimpleParseCustomEncoding() { - val header = "key=value; max-Age=999; \$extension=1; \$x-enc=RAW" - val parsed = parseServerSetCookieHeader(header) - - assertEquals("key", parsed.name) - assertEquals("value", parsed.value) - assertEquals(999, parsed.maxAge) - assertEquals("1", parsed.extensions["\$extension"]) - assertEquals(CookieEncoding.RAW, parsed.encoding) - } - - @Test - fun testSimpleParseMissingEncoding() { - val header = "key=value; max-Age=999; \$extension=1; \$x-enc=URI_ENCODING" - val parsed = parseServerSetCookieHeader(header) - - assertEquals("key", parsed.name) - assertEquals("value", parsed.value) - assertEquals(999, parsed.maxAge) - assertEquals("1", parsed.extensions["\$extension"]) - assertEquals(CookieEncoding.URI_ENCODING, parsed.encoding) - } - - @Test - fun testSimpleParseVersionAtStart() { - val header = "\$Version=1; key=value; max-Age=999; \$extension=1; \$x-enc=URI_ENCODING" - val parsed = parseServerSetCookieHeader(header) - - assertEquals("key", parsed.name) - assertEquals("value", parsed.value) - assertEquals(999, parsed.maxAge) - assertEquals("1", parsed.extensions["\$extension"]) - assertEquals(CookieEncoding.URI_ENCODING, parsed.encoding) - } - - @Test - fun testParseWithQuotes() { - val header = "key=\"aaa; bbb = ccc\"; max-Age=999; \$extension=1; \$x-enc=URI_ENCODING" - val parsed = parseServerSetCookieHeader(header) - - assertEquals("key", parsed.name) - assertEquals("aaa; bbb = ccc", parsed.value) - assertEquals(999, parsed.maxAge) - assertEquals("1", parsed.extensions["\$extension"]) - assertEquals(CookieEncoding.URI_ENCODING, parsed.encoding) - } - - @Test - fun testParseExpires() { - val header = "SESSION=cart%3D%2523cl%26userId%3D%2523sid1; " + - "Expires=Sat, 16 Jan 2016 13:43:28 GMT; HttpOnly; \$x-enc=URI_ENCODING" - val parsed = parseServerSetCookieHeader(header) - - val expires = parsed.expires - assertNotNull(expires) - assertEquals(2016, expires.year) - assertEquals(io.ktor.util.date.Month.JANUARY, expires.month) - assertEquals(16, expires.dayOfMonth) - } - - @Test - fun testParseBase64() { - val header = "SESSION=MTIzCg==; \$x-enc=BASE64_ENCODING" - val parsed = parseServerSetCookieHeader(header) - assertEquals("123\n", parsed.value) - } - - @Test - fun testMaxAge() { - val header = "key=aaa; max-age=999" - val parsed = parseServerSetCookieHeader(header) - - assertEquals("key", parsed.name) - assertEquals("aaa", parsed.value) - assertEquals(999, parsed.maxAge) - } - - @Test - fun testMaxAgeNegative() { - val header = "key=aaa; max-age=-1" - val parsed = parseServerSetCookieHeader(header) - - assertEquals("key", parsed.name) - assertEquals("aaa", parsed.value) - assertEquals(0, parsed.maxAge) - } - - @Test - fun testMaxAgeTooLong() { - val header = "key=aaa; max-age=3153600000" - val parsed = parseServerSetCookieHeader(header) - - assertEquals("key", parsed.name) - assertEquals("aaa", parsed.value) - assertEquals(Int.MAX_VALUE, parsed.maxAge) - } - - @Test - fun testSlash() { - val header = "384f8bdb/sessid=GLU787LwmQa9uLqnM7nWHzBm; path=/" - val parsed = parseServerSetCookieHeader(header) - - assertEquals("384f8bdb/sessid", parsed.name) - assertEquals("GLU787LwmQa9uLqnM7nWHzBm", parsed.value) - assertEquals("/", parsed.path) - } -} diff --git a/ktor-server/ktor-server-tests/jvmAndNix/test/io/ktor/tests/server/plugins/DQuotesCookiesEncodingTest.kt b/ktor-server/ktor-server-tests/jvmAndNix/test/io/ktor/tests/server/plugins/DQuotesCookiesEncodingTest.kt new file mode 100644 index 0000000000..c925bed8d5 --- /dev/null +++ b/ktor-server/ktor-server-tests/jvmAndNix/test/io/ktor/tests/server/plugins/DQuotesCookiesEncodingTest.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2014-2022 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +package io.ktor.tests.server.plugins + +import io.ktor.http.* +import kotlin.test.* + +class DQuotesCookiesEncodingTest { + @Test + fun `no bad characters`() { + testEncode("abc", "abc") + } + + @Test + fun `space inside`() { + testEncode("\"abc 123\"", "abc 123") + } + + @Test + fun `equals inside`() { + testEncode("abc=123", "abc=123") + } + + private fun testEncode(expected: String, value: String) { + val encoded = encodeCookieValue(value, CookieEncoding.DQUOTES) + assertEquals(expected, encoded, "Encode failed") + assertEquals(value, decodeCookieValue(encoded, CookieEncoding.DQUOTES), "Decode failed") + } +} diff --git a/ktor-server/ktor-server-tests/jvmAndNix/test/io/ktor/tests/server/plugins/ParserServerSetCookieTest.kt b/ktor-server/ktor-server-tests/jvmAndNix/test/io/ktor/tests/server/plugins/ParserServerSetCookieTest.kt new file mode 100644 index 0000000000..c22f483b98 --- /dev/null +++ b/ktor-server/ktor-server-tests/jvmAndNix/test/io/ktor/tests/server/plugins/ParserServerSetCookieTest.kt @@ -0,0 +1,131 @@ +/* + * Copyright 2014-2022 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +package io.ktor.tests.server.plugins + +import io.ktor.http.* +import io.ktor.util.date.* +import kotlin.test.* + +class ParserServerSetCookieTest { + @Test + fun testSimpleParse() { + val header = "key=value; max-Age=999; \$extension=1; \$x-enc=URI_ENCODING" + val parsed = parseServerSetCookieHeader(header) + + assertEquals("key", parsed.name) + assertEquals("value", parsed.value) + assertEquals(999, parsed.maxAge) + assertEquals("1", parsed.extensions["\$extension"]) + assertEquals(CookieEncoding.URI_ENCODING, parsed.encoding) + } + + @Test + fun testSimpleParseCustomEncoding() { + val header = "key=value; max-Age=999; \$extension=1; \$x-enc=RAW" + val parsed = parseServerSetCookieHeader(header) + + assertEquals("key", parsed.name) + assertEquals("value", parsed.value) + assertEquals(999, parsed.maxAge) + assertEquals("1", parsed.extensions["\$extension"]) + assertEquals(CookieEncoding.RAW, parsed.encoding) + } + + @Test + fun testSimpleParseMissingEncoding() { + val header = "key=value; max-Age=999; \$extension=1; \$x-enc=URI_ENCODING" + val parsed = parseServerSetCookieHeader(header) + + assertEquals("key", parsed.name) + assertEquals("value", parsed.value) + assertEquals(999, parsed.maxAge) + assertEquals("1", parsed.extensions["\$extension"]) + assertEquals(CookieEncoding.URI_ENCODING, parsed.encoding) + } + + @Test + fun testSimpleParseVersionAtStart() { + val header = "\$Version=1; key=value; max-Age=999; \$extension=1; \$x-enc=URI_ENCODING" + val parsed = parseServerSetCookieHeader(header) + + assertEquals("key", parsed.name) + assertEquals("value", parsed.value) + assertEquals(999, parsed.maxAge) + assertEquals("1", parsed.extensions["\$extension"]) + assertEquals(CookieEncoding.URI_ENCODING, parsed.encoding) + } + + @Test + fun testParseWithQuotes() { + val header = "key=\"aaa; bbb = ccc\"; max-Age=999; \$extension=1; \$x-enc=URI_ENCODING" + val parsed = parseServerSetCookieHeader(header) + + assertEquals("key", parsed.name) + assertEquals("aaa; bbb = ccc", parsed.value) + assertEquals(999, parsed.maxAge) + assertEquals("1", parsed.extensions["\$extension"]) + assertEquals(CookieEncoding.URI_ENCODING, parsed.encoding) + } + + @Test + fun testParseExpires() { + val header = "SESSION=cart%3D%2523cl%26userId%3D%2523sid1; " + + "Expires=Sat, 16 Jan 2016 13:43:28 GMT; HttpOnly; \$x-enc=URI_ENCODING" + val parsed = parseServerSetCookieHeader(header) + + val expires = parsed.expires + assertNotNull(expires) + assertEquals(2016, expires.year) + assertEquals(Month.JANUARY, expires.month) + assertEquals(16, expires.dayOfMonth) + } + + @Test + fun testParseBase64() { + val header = "SESSION=MTIzCg==; \$x-enc=BASE64_ENCODING" + val parsed = parseServerSetCookieHeader(header) + assertEquals("123\n", parsed.value) + } + + @Test + fun testMaxAge() { + val header = "key=aaa; max-age=999" + val parsed = parseServerSetCookieHeader(header) + + assertEquals("key", parsed.name) + assertEquals("aaa", parsed.value) + assertEquals(999, parsed.maxAge) + } + + @Test + fun testMaxAgeNegative() { + val header = "key=aaa; max-age=-1" + val parsed = parseServerSetCookieHeader(header) + + assertEquals("key", parsed.name) + assertEquals("aaa", parsed.value) + assertEquals(0, parsed.maxAge) + } + + @Test + fun testMaxAgeTooLong() { + val header = "key=aaa; max-age=3153600000" + val parsed = parseServerSetCookieHeader(header) + + assertEquals("key", parsed.name) + assertEquals("aaa", parsed.value) + assertEquals(Int.MAX_VALUE, parsed.maxAge) + } + + @Test + fun testSlash() { + val header = "384f8bdb/sessid=GLU787LwmQa9uLqnM7nWHzBm; path=/" + val parsed = parseServerSetCookieHeader(header) + + assertEquals("384f8bdb/sessid", parsed.name) + assertEquals("GLU787LwmQa9uLqnM7nWHzBm", parsed.value) + assertEquals("/", parsed.path) + } +} diff --git a/ktor-server/ktor-server-tests/jvmAndNix/test/io/ktor/tests/server/plugins/RawCookieTest.kt b/ktor-server/ktor-server-tests/jvmAndNix/test/io/ktor/tests/server/plugins/RawCookieTest.kt new file mode 100644 index 0000000000..d05b6e0e37 --- /dev/null +++ b/ktor-server/ktor-server-tests/jvmAndNix/test/io/ktor/tests/server/plugins/RawCookieTest.kt @@ -0,0 +1,17 @@ +/* + * Copyright 2014-2022 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +package io.ktor.tests.server.plugins + +import io.ktor.http.* +import kotlin.test.* + +class RawCookieTest { + @Test + fun testRawEncodingWithEquals() { + val cookieValue = "my.value.key=my.value.value+my.value.id=5" + val encoded = encodeCookieValue(cookieValue, CookieEncoding.RAW) + assertEquals(cookieValue, encoded) + } +} diff --git a/ktor-server/ktor-server-tests/jvmAndNix/test/io/ktor/tests/server/plugins/URIEncodingCookiesTest.kt b/ktor-server/ktor-server-tests/jvmAndNix/test/io/ktor/tests/server/plugins/URIEncodingCookiesTest.kt new file mode 100644 index 0000000000..545923482e --- /dev/null +++ b/ktor-server/ktor-server-tests/jvmAndNix/test/io/ktor/tests/server/plugins/URIEncodingCookiesTest.kt @@ -0,0 +1,64 @@ +/* + * Copyright 2014-2022 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +package io.ktor.tests.server.plugins + +import io.ktor.http.* +import kotlin.test.* + +class URIEncodingCookiesTest { + @Test + fun `no bad characters`() { + testEncode("abc", "abc") + } + + @Test + fun `space inside`() { + testEncode("abc+123", "abc 123") + } + + @Test + fun `equals inside`() { + testEncode("abc=123", "abc=123") + } + + @Test + fun `encode keep digits`() { + testEncode("0123456789", "0123456789") + } + + @Test + fun `encode keep letters`() { + testEncode( + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + ) + } + + @Test + fun `encode keep hypen`() { + testEncode("abc-123", "abc-123") + } + + @Test + fun `encode keep underscore`() { + testEncode("abc_123", "abc_123") + } + + @Test + fun `encode keep period`() { + testEncode("abc.123", "abc.123") + } + + @Test + fun `encode keep tilde`() { + testEncode("abc~123", "abc~123") + } + + private fun testEncode(expected: String, value: String) { + val encoded = encodeCookieValue(value, CookieEncoding.URI_ENCODING) + assertEquals(expected, encoded, "Encode failed") + assertEquals(value, decodeCookieValue(encoded, CookieEncoding.URI_ENCODING), "Decode failed") + } +} From 4780474ec0077d1a2aa4778374be07509fcc7b15 Mon Sep 17 00:00:00 2001 From: "leonid.stashevsky" Date: Mon, 10 Oct 2022 11:21:05 +0200 Subject: [PATCH 3/4] fixup! KTOR-917 Keep some characters in cookie URI encoding according to RFC 3986 --- .../common/test/io/ktor/client/tests/plugins/CookiesTest.kt | 2 +- .../jvmAndNix/test/io/ktor/tests/server/plugins/CookiesTest.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/CookiesTest.kt b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/CookiesTest.kt index 152886f432..cb93ef708f 100644 --- a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/CookiesTest.kt +++ b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/CookiesTest.kt @@ -194,7 +194,7 @@ class CookiesTest : ClientLoader() { val response = httpResponse.bodyAsText() val cookieStrings = response.split("; ").filter { it.isNotBlank() } assertEquals(4, cookieStrings.size) - assertEquals("uri=first%2C+cookie", cookieStrings[0]) + assertEquals("uri=first,+cookie", cookieStrings[0]) assertEquals("raw=first%2C+cookie", cookieStrings[1]) assertEquals("base64=Zmlyc3QsIGNvb2tpZQ==", cookieStrings[2]) assertEquals("dquotes=\"first, cookie\"", cookieStrings[3]) diff --git a/ktor-server/ktor-server-tests/jvmAndNix/test/io/ktor/tests/server/plugins/CookiesTest.kt b/ktor-server/ktor-server-tests/jvmAndNix/test/io/ktor/tests/server/plugins/CookiesTest.kt index c2947b9223..0f3991c9a7 100644 --- a/ktor-server/ktor-server-tests/jvmAndNix/test/io/ktor/tests/server/plugins/CookiesTest.kt +++ b/ktor-server/ktor-server-tests/jvmAndNix/test/io/ktor/tests/server/plugins/CookiesTest.kt @@ -81,7 +81,7 @@ class CookiesTest { @Test fun `add cookies bad characters`() { - testSetCookies("AB=1%3A2") { + testSetCookies("AB=1:2") { cookies.append("AB", "1:2") } } From af136b34a8f19f03e1bb3c6e0657dc465b20f13f Mon Sep 17 00:00:00 2001 From: "leonid.stashevsky" Date: Tue, 11 Oct 2022 08:51:02 +0200 Subject: [PATCH 4/4] Fix codestyle warnings --- ktor-client/ktor-client-core/common/test/CookiesTest.kt | 4 ++-- .../ktor-server-test-host/jvm/test/TestApplicationTestJvm.kt | 1 - .../test/io/ktor/tests/server/plugins/CookiesTest.kt | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/ktor-client/ktor-client-core/common/test/CookiesTest.kt b/ktor-client/ktor-client-core/common/test/CookiesTest.kt index 9403b357b2..7b01ad7965 100644 --- a/ktor-client/ktor-client-core/common/test/CookiesTest.kt +++ b/ktor-client/ktor-client-core/common/test/CookiesTest.kt @@ -44,11 +44,11 @@ class CookiesTest { @Test fun testCookiesAreRenderedWithSpaceInBetween() = testSuspend { - var storage = AcceptAllCookiesStorage() + val storage = AcceptAllCookiesStorage() storage.addCookie("http://localhost/", Cookie("name1", "value1")) storage.addCookie("http://localhost/", Cookie("name2", "value2")) val feature = HttpCookies(storage, emptyList()) - var builder = HttpRequestBuilder() + val builder = HttpRequestBuilder() feature.sendCookiesWith(builder) diff --git a/ktor-server/ktor-server-test-host/jvm/test/TestApplicationTestJvm.kt b/ktor-server/ktor-server-test-host/jvm/test/TestApplicationTestJvm.kt index 6b87e1d1bd..6cdde8f510 100644 --- a/ktor-server/ktor-server-test-host/jvm/test/TestApplicationTestJvm.kt +++ b/ktor-server/ktor-server-test-host/jvm/test/TestApplicationTestJvm.kt @@ -242,7 +242,6 @@ class MyCalculatorPlugin { ): MyCalculatorPlugin { return MyCalculatorPlugin() } - } fun add(x: Int, y: Int): Int { diff --git a/ktor-server/ktor-server-tests/jvmAndNix/test/io/ktor/tests/server/plugins/CookiesTest.kt b/ktor-server/ktor-server-tests/jvmAndNix/test/io/ktor/tests/server/plugins/CookiesTest.kt index 0f3991c9a7..07b35c8ea7 100644 --- a/ktor-server/ktor-server-tests/jvmAndNix/test/io/ktor/tests/server/plugins/CookiesTest.kt +++ b/ktor-server/ktor-server-tests/jvmAndNix/test/io/ktor/tests/server/plugins/CookiesTest.kt @@ -176,4 +176,3 @@ class CookiesTest { private fun String.cutSetCookieHeader() = substringBeforeLast("\$x-enc").trimEnd().removeSuffix(";") } -