From 3369d0e185d5573059fc4516398d37c2933911e6 Mon Sep 17 00:00:00 2001 From: sabeeh Date: Tue, 2 Jan 2024 00:48:03 +0500 Subject: [PATCH 1/4] add subScheme support in URL.kt --- korge-core/src/korlibs/io/net/URL.kt | 27 +++++++++++--- korge-core/test/korlibs/io/net/URLTest.kt | 43 ++++++++++++++++++++++- 2 files changed, 64 insertions(+), 6 deletions(-) diff --git a/korge-core/src/korlibs/io/net/URL.kt b/korge-core/src/korlibs/io/net/URL.kt index a05da44c0..081dfd7b2 100644 --- a/korge-core/src/korlibs/io/net/URL.kt +++ b/korge-core/src/korlibs/io/net/URL.kt @@ -13,6 +13,7 @@ import korlibs.io.lang.* data class URL private constructor( val isOpaque: Boolean, val scheme: String?, + val subScheme: String?, val userInfo: String?, val host: String?, val path: String, @@ -43,6 +44,7 @@ data class URL private constructor( fun toUrlString(includeScheme: Boolean = true, out: StringBuilder = StringBuilder()): StringBuilder { if (includeScheme && scheme != null) { out.append("$scheme:") + if (subScheme != null) out.append("$subScheme:") if (!isOpaque) out.append("//") } if (userInfo != null) out.append("$userInfo@") @@ -58,7 +60,7 @@ data class URL private constructor( override fun toString(): String = fullUrl fun toComponentString(): String { - return "URL(" + listOf(::scheme, ::userInfo, ::host, ::path, ::query, ::fragment) + return "URL(" + listOf(::scheme, ::subScheme, ::userInfo, ::host, ::path, ::query, ::fragment) .map { it.name to it.get() } .filter { it.second != null } .joinToString(", ") { "${it.first}=${it.second}" } + ")" @@ -79,6 +81,7 @@ data class URL private constructor( operator fun invoke( scheme: String?, + subScheme: String?, userInfo: String?, host: String?, path: String, @@ -86,9 +89,19 @@ data class URL private constructor( fragment: String?, opaque: Boolean = false, port: Int = DEFAULT_PORT - ): URL = URL(opaque, scheme?.lowercase(), userInfo, host, path, query, fragment, port) - - private val schemeRegex = Regex("\\w+:") + ): URL = URL( + isOpaque = opaque, + scheme = scheme?.lowercase(), + subScheme = subScheme?.lowercase(), + userInfo = userInfo, + host = host, + path = path, + query = query, + fragment = fragment, + defaultPort = port + ) + + private val schemeRegex = Regex("^([a-zA-Z]+)(?::([a-zA-Z]+))?:") operator fun invoke(url: String): URL { val r = StrReader(url) @@ -97,7 +110,9 @@ data class URL private constructor( schemeColon != null -> { val isHierarchical = r.tryLit("//") != null val nonScheme = r.readRemaining() - val scheme = schemeColon.dropLast(1) + val schemeParts = schemeColon.dropLast(1).split(":") + val scheme = schemeParts[0] + val subScheme = schemeParts.getOrNull(1) val nonFragment = nonScheme.substringBefore('#') val fragment = nonScheme.substringAfterOrNull('#') @@ -117,6 +132,7 @@ data class URL private constructor( URL( opaque = !isHierarchical, scheme = scheme, + subScheme = subScheme, userInfo = userInfo, host = host.takeIf { it.isNotEmpty() }, path = if (path != null) "/$path" else "", @@ -133,6 +149,7 @@ data class URL private constructor( URL( opaque = false, scheme = null, + subScheme = null, userInfo = null, host = null, path = path, diff --git a/korge-core/test/korlibs/io/net/URLTest.kt b/korge-core/test/korlibs/io/net/URLTest.kt index b874768be..5780ecf30 100644 --- a/korge-core/test/korlibs/io/net/URLTest.kt +++ b/korge-core/test/korlibs/io/net/URLTest.kt @@ -111,10 +111,51 @@ class URLTest { assertEquals("q=1", url.query) assertEquals("https://Google.com:442/test?q=1", url.fullUrl) - val url2 = URL(scheme = "HTTPs", userInfo = null, host = "Google.com", path = "", query = null, fragment = null) + val url2 = URL(scheme = "HTTPs", subScheme = null, userInfo = null, host = "Google.com", path = "", query = null, fragment = null) assertEquals("https", url2.scheme) assertEquals(443, url2.port) assertEquals("https://Google.com", url2.fullUrl) + + val url3 = URL("https://username:password@example.com:8443/path/to/resource?q=1") + assertEquals("https", url3.scheme) + assertNull(url3.subScheme) + assertEquals("username:password", url3.userInfo) + assertEquals("username", url3.user) + assertEquals("password", url3.password) + assertEquals("example.com", url3.host) + assertEquals(8443, url3.port) + assertEquals("/path/to/resource", url3.path) + assertEquals("/path/to/resource?q=1", url3.pathWithQuery) + assertEquals("q=1", url3.query) + assertEquals("https://username:password@example.com:8443/path/to/resource?q=1", url3.fullUrl) + } + + @Test + fun testSubSchemeUrls(){ +// :://:/?# + val url = URL("jdbc:mysql://localhost:3306/database?useSSL=false") + assertEquals("jdbc", url.scheme) // always lowercase issue #2092 + assertEquals("mysql", url.subScheme) + assertEquals("localhost", url.host) + assertEquals(3306, url.port) + assertEquals("/database", url.path) + assertEquals("/database?useSSL=false", url.pathWithQuery) + assertEquals("useSSL=false", url.query) + assertEquals("jdbc:mysql://localhost:3306/database?useSSL=false", url.fullUrl) + + val url2 = URL(scheme = "jdbc", subScheme = "mysql", userInfo = null, host = "localhost", path = "/database", query = null, fragment = null, port = 3306) + assertEquals("jdbc:mysql://localhost:3306/database", url2.fullUrl) + + + val url3 = URL("jdbc:mysql://localhost:3306/database?useSSL=false") + assertEquals("jdbc", url3.scheme) // always lowercase issue #2092 + assertEquals("mysql", url3.subScheme) + assertEquals("localhost", url3.host) + assertEquals(3306, url3.port) + assertEquals("/database", url3.path) + assertEquals("/database?useSSL=false", url3.pathWithQuery) + assertEquals("useSSL=false", url3.query) + assertEquals("jdbc:mysql://localhost:3306/database?useSSL=false", url3.fullUrl) } @Test From d1b4385c2925c36735d61ae146eb5e96fd59e596 Mon Sep 17 00:00:00 2001 From: sabeeh Date: Tue, 2 Jan 2024 01:18:01 +0500 Subject: [PATCH 2/4] support for scheme standard according to rfc3986#section-3.1 --- korge-core/src/korlibs/io/net/URL.kt | 2 +- korge-core/test/korlibs/io/net/URLTest.kt | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/korge-core/src/korlibs/io/net/URL.kt b/korge-core/src/korlibs/io/net/URL.kt index 081dfd7b2..25625e65a 100644 --- a/korge-core/src/korlibs/io/net/URL.kt +++ b/korge-core/src/korlibs/io/net/URL.kt @@ -101,7 +101,7 @@ data class URL private constructor( defaultPort = port ) - private val schemeRegex = Regex("^([a-zA-Z]+)(?::([a-zA-Z]+))?:") + private val schemeRegex = Regex("^([a-zA-Z0-9+.-]+)(?::([a-zA-Z]+))?:") operator fun invoke(url: String): URL { val r = StrReader(url) diff --git a/korge-core/test/korlibs/io/net/URLTest.kt b/korge-core/test/korlibs/io/net/URLTest.kt index 5780ecf30..7c5e81f35 100644 --- a/korge-core/test/korlibs/io/net/URLTest.kt +++ b/korge-core/test/korlibs/io/net/URLTest.kt @@ -158,6 +158,20 @@ class URLTest { assertEquals("jdbc:mysql://localhost:3306/database?useSSL=false", url3.fullUrl) } + @Test + fun testUrlScheme() { +// test scheme standard (rfc3986#section-3.1): ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) + val url = URL("custom+scheme123://example.com/path/to/resource") + assertEquals("custom+scheme123", url.scheme) + assertEquals("example.com", url.host) + assertEquals("custom+scheme123://example.com/path/to/resource", url.fullUrl) + + val url2 = URL("alpha-numeric+scheme.123://example.com/path/to/resource") + assertEquals("alpha-numeric+scheme.123", url2.scheme) + assertEquals("example.com", url2.host) + assertEquals("alpha-numeric+scheme.123://example.com/path/to/resource", url2.fullUrl) + } + @Test fun testNormalize() { assertEquals("g", "./g/.".pathInfo.normalize()) From ed6f6351c0031413cfb6220788a4cd568f8e9805 Mon Sep 17 00:00:00 2001 From: sabeeh Date: Wed, 3 Jan 2024 14:18:42 +0500 Subject: [PATCH 3/4] URL.invoke overload function for backward compatibility. --- korge-core/src/korlibs/io/net/URL.kt | 21 +++++++++++++++++++++ korge-core/test/korlibs/io/net/URLTest.kt | 3 ++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/korge-core/src/korlibs/io/net/URL.kt b/korge-core/src/korlibs/io/net/URL.kt index 25625e65a..a73c9e009 100644 --- a/korge-core/src/korlibs/io/net/URL.kt +++ b/korge-core/src/korlibs/io/net/URL.kt @@ -101,6 +101,27 @@ data class URL private constructor( defaultPort = port ) + operator fun invoke( + scheme: String?, + userInfo: String?, + host: String?, + path: String, + query: String?, + fragment: String?, + opaque: Boolean = false, + port: Int = DEFAULT_PORT + ): URL = URL( + isOpaque = opaque, + scheme = scheme?.lowercase(), + subScheme = null, + userInfo = userInfo, + host = host, + path = path, + query = query, + fragment = fragment, + defaultPort = port + ) + private val schemeRegex = Regex("^([a-zA-Z0-9+.-]+)(?::([a-zA-Z]+))?:") operator fun invoke(url: String): URL { diff --git a/korge-core/test/korlibs/io/net/URLTest.kt b/korge-core/test/korlibs/io/net/URLTest.kt index 7c5e81f35..b82b0e773 100644 --- a/korge-core/test/korlibs/io/net/URLTest.kt +++ b/korge-core/test/korlibs/io/net/URLTest.kt @@ -111,9 +111,10 @@ class URLTest { assertEquals("q=1", url.query) assertEquals("https://Google.com:442/test?q=1", url.fullUrl) - val url2 = URL(scheme = "HTTPs", subScheme = null, userInfo = null, host = "Google.com", path = "", query = null, fragment = null) + val url2 = URL(scheme = "HTTPs", userInfo = null, host = "Google.com", path = "", query = null, fragment = null) assertEquals("https", url2.scheme) assertEquals(443, url2.port) + assertNull(url2.subScheme) assertEquals("https://Google.com", url2.fullUrl) val url3 = URL("https://username:password@example.com:8443/path/to/resource?q=1") From 4bafcd72a55c829cf61b6679debe6533a3dddc97 Mon Sep 17 00:00:00 2001 From: sabeeh Date: Wed, 3 Jan 2024 17:10:17 +0500 Subject: [PATCH 4/4] Deprecate URL.invoke with parameters and introduce replacement using URL.fromComponents --- korge-core/src/korlibs/io/net/URL.kt | 41 +++++++++++++---------- korge-core/test/korlibs/io/net/URLTest.kt | 4 +-- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/korge-core/src/korlibs/io/net/URL.kt b/korge-core/src/korlibs/io/net/URL.kt index a73c9e009..235e254c7 100644 --- a/korge-core/src/korlibs/io/net/URL.kt +++ b/korge-core/src/korlibs/io/net/URL.kt @@ -79,17 +79,17 @@ data class URL private constructor( else -> -1 } - operator fun invoke( - scheme: String?, - subScheme: String?, - userInfo: String?, - host: String?, - path: String, - query: String?, - fragment: String?, - opaque: Boolean = false, - port: Int = DEFAULT_PORT - ): URL = URL( + fun fromComponents( + scheme: String? = null, + subScheme: String? = null, + userInfo: String? = null, + host: String? = null, + path: String = "", + query: String? = null, + fragment: String? = null, + opaque: Boolean = false, + port: Int = DEFAULT_PORT + ): URL = URL( isOpaque = opaque, scheme = scheme?.lowercase(), subScheme = subScheme?.lowercase(), @@ -101,6 +101,10 @@ data class URL private constructor( defaultPort = port ) + @Deprecated( + message = "Use URL.fromComponents", + replaceWith = ReplaceWith("URL.fromComponents(scheme, subScheme, userInfo, host, path, query, fragment, opaque)") + ) operator fun invoke( scheme: String?, userInfo: String?, @@ -109,17 +113,18 @@ data class URL private constructor( query: String?, fragment: String?, opaque: Boolean = false, - port: Int = DEFAULT_PORT - ): URL = URL( - isOpaque = opaque, + port: Int = DEFAULT_PORT, + subScheme: String? = null, + ): URL = this.fromComponents( + opaque = opaque, scheme = scheme?.lowercase(), - subScheme = null, + subScheme = subScheme?.lowercase(), userInfo = userInfo, host = host, path = path, query = query, fragment = fragment, - defaultPort = port + port = port ) private val schemeRegex = Regex("^([a-zA-Z0-9+.-]+)(?::([a-zA-Z]+))?:") @@ -150,7 +155,7 @@ data class URL private constructor( val host = hostWithPort.substringBefore(':') val port = hostWithPort.substringAfterOrNull(':') - URL( + this.fromComponents( opaque = !isHierarchical, scheme = scheme, subScheme = subScheme, @@ -167,7 +172,7 @@ data class URL private constructor( val fragment = url.substringAfterOrNull('#') val path = nonFragment.substringBefore('?') val query = nonFragment.substringAfterOrNull('?') - URL( + this.fromComponents( opaque = false, scheme = null, subScheme = null, diff --git a/korge-core/test/korlibs/io/net/URLTest.kt b/korge-core/test/korlibs/io/net/URLTest.kt index b82b0e773..7fa6aa245 100644 --- a/korge-core/test/korlibs/io/net/URLTest.kt +++ b/korge-core/test/korlibs/io/net/URLTest.kt @@ -111,7 +111,7 @@ class URLTest { assertEquals("q=1", url.query) assertEquals("https://Google.com:442/test?q=1", url.fullUrl) - val url2 = URL(scheme = "HTTPs", userInfo = null, host = "Google.com", path = "", query = null, fragment = null) + val url2 = URL.fromComponents(scheme = "HTTPs", host = "Google.com") assertEquals("https", url2.scheme) assertEquals(443, url2.port) assertNull(url2.subScheme) @@ -144,7 +144,7 @@ class URLTest { assertEquals("useSSL=false", url.query) assertEquals("jdbc:mysql://localhost:3306/database?useSSL=false", url.fullUrl) - val url2 = URL(scheme = "jdbc", subScheme = "mysql", userInfo = null, host = "localhost", path = "/database", query = null, fragment = null, port = 3306) + val url2 = URL.fromComponents(scheme = "jdbc", subScheme = "mysql", host = "localhost", path = "/database", port = 3306) assertEquals("jdbc:mysql://localhost:3306/database", url2.fullUrl)