From 4a1481735074841c146cca7f96c08dd933f41cae Mon Sep 17 00:00:00 2001 From: jaguililla Date: Thu, 18 May 2023 12:11:20 +0200 Subject: [PATCH 1/5] Optimize path patterns --- gradle.properties | 2 +- .../hexagonkt/http/patterns/PathPatterns.kt | 2 + .../http/patterns/TemplatePathPattern.kt | 2 +- .../http/patterns/WildcardPathPattern.kt | 18 +++++++++ .../http/patterns/WildcardPathPatternTest.kt | 39 +++++++++++++++++++ 5 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 http/src/main/kotlin/com/hexagonkt/http/patterns/WildcardPathPattern.kt create mode 100644 http/src/test/kotlin/com/hexagonkt/http/patterns/WildcardPathPatternTest.kt diff --git a/gradle.properties b/gradle.properties index 1b28b737d9..ddc665a01d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ org.gradle.warning.mode=all org.gradle.console=plain # Gradle -version=2.8.5 +version=2.8.6 group=com.hexagonkt description=The atoms of your platform diff --git a/http/src/main/kotlin/com/hexagonkt/http/patterns/PathPatterns.kt b/http/src/main/kotlin/com/hexagonkt/http/patterns/PathPatterns.kt index 35ed3fb0cc..66ce92cf6c 100644 --- a/http/src/main/kotlin/com/hexagonkt/http/patterns/PathPatterns.kt +++ b/http/src/main/kotlin/com/hexagonkt/http/patterns/PathPatterns.kt @@ -1,9 +1,11 @@ package com.hexagonkt.http.patterns import com.hexagonkt.core.assertEnabled +import com.hexagonkt.http.patterns.TemplatePathPattern.Companion.WILDCARD fun createPathPattern(pattern: String, prefix: Boolean): PathPattern = when { + pattern == WILDCARD -> WildcardPathPattern(prefix) TemplatePathPattern.isTemplate(pattern) -> TemplatePathPattern(pattern, prefix) else -> LiteralPathPattern(pattern, prefix) } diff --git a/http/src/main/kotlin/com/hexagonkt/http/patterns/TemplatePathPattern.kt b/http/src/main/kotlin/com/hexagonkt/http/patterns/TemplatePathPattern.kt index fc9b44ce3d..044db8047d 100644 --- a/http/src/main/kotlin/com/hexagonkt/http/patterns/TemplatePathPattern.kt +++ b/http/src/main/kotlin/com/hexagonkt/http/patterns/TemplatePathPattern.kt @@ -16,7 +16,7 @@ data class TemplatePathPattern( private const val PARAMETER_PREFIX = "{" private const val PARAMETER_SUFFIX = "}" - private const val WILDCARD = "*" + internal const val WILDCARD = "*" private const val PARAMETER = "\\$PARAMETER_PREFIX\\w+$PARAMETER_SUFFIX" private val REGEX_CHARACTERS = listOf('(', ')', '|', '?', '+', '[', ']') diff --git a/http/src/main/kotlin/com/hexagonkt/http/patterns/WildcardPathPattern.kt b/http/src/main/kotlin/com/hexagonkt/http/patterns/WildcardPathPattern.kt new file mode 100644 index 0000000000..b3cb7eb957 --- /dev/null +++ b/http/src/main/kotlin/com/hexagonkt/http/patterns/WildcardPathPattern.kt @@ -0,0 +1,18 @@ +package com.hexagonkt.http.patterns + +import com.hexagonkt.http.patterns.TemplatePathPattern.Companion.WILDCARD + +data class WildcardPathPattern(override val prefix: Boolean = false) : PathPattern { + + override val pattern: String = WILDCARD + + override fun addPrefix(prefix: String?): PathPattern = + if (prefix == null) this + else createPathPattern("$prefix/$WILDCARD", this.prefix) + + override fun matches(requestUrl: String): Boolean = + true + + override fun extractParameters(requestUrl: String): Map = + mapOf(1.toString() to requestUrl) +} diff --git a/http/src/test/kotlin/com/hexagonkt/http/patterns/WildcardPathPatternTest.kt b/http/src/test/kotlin/com/hexagonkt/http/patterns/WildcardPathPatternTest.kt new file mode 100644 index 0000000000..f4d002472d --- /dev/null +++ b/http/src/test/kotlin/com/hexagonkt/http/patterns/WildcardPathPatternTest.kt @@ -0,0 +1,39 @@ +package com.hexagonkt.http.patterns + +import kotlin.test.Test +import kotlin.test.* + +internal class WildcardPathPatternTest { + + @Test fun `Prefixes are matched if pattern is prefix`() { + val regexPath = WildcardPathPattern() + assert(regexPath.matches("/alpha/bravo/tango")) + + val regexPathPrefix = WildcardPathPattern(true) + assert(regexPathPrefix.matches("/alpha/bravo/tango")) + } + + @Test fun `Prefixes can be appended to patterns`() { + val regexPath = WildcardPathPattern() + assert(regexPath.addPrefix(null).matches("/alpha/bravo")) + assertFalse(regexPath.addPrefix("/prefix").matches("/alpha/bravo")) + assertTrue(regexPath.addPrefix("/prefix").matches("/prefix/alpha/bravo")) + } + + @Test fun `A wildcard path have a single parameter`() { + val pathWithoutData = WildcardPathPattern() + assertEquals("*", pathWithoutData.pattern) + assertEquals(mapOf("1" to "/alpha"), pathWithoutData.extractParameters("/alpha")) + } + + @Test fun `Adding a prefix keep the prefix flag`() { + WildcardPathPattern(true).addPrefix("/b").let { + assertEquals("/b/*", it.pattern) + assertTrue(it.prefix) + } + WildcardPathPattern(false).addPrefix("/b").let { + assertEquals("/b/*", it.pattern) + assertFalse(it.prefix) + } + } +} From e0701109f1712a5d5ef0df1fce5fd72cd1006fea Mon Sep 17 00:00:00 2001 From: jaguililla Date: Thu, 18 May 2023 12:26:20 +0200 Subject: [PATCH 2/5] Add HTTP model utilities --- .../hexagonkt/http/model/HttpRequestPort.kt | 15 ++++++++ .../hexagonkt/http/model/HttpResponsePort.kt | 6 ++++ .../hexagonkt/http/model/HttpRequestTest.kt | 34 ++++++++++++++++++ .../hexagonkt/http/model/HttpResponseTest.kt | 36 +++++++++++++------ 4 files changed, 81 insertions(+), 10 deletions(-) diff --git a/http/src/main/kotlin/com/hexagonkt/http/model/HttpRequestPort.kt b/http/src/main/kotlin/com/hexagonkt/http/model/HttpRequestPort.kt index ebfb1fdbe7..2a3a82c262 100644 --- a/http/src/main/kotlin/com/hexagonkt/http/model/HttpRequestPort.kt +++ b/http/src/main/kotlin/com/hexagonkt/http/model/HttpRequestPort.kt @@ -39,6 +39,21 @@ interface HttpRequestPort : HttpMessage { certificateChain: List = this.certificateChain, ): HttpRequestPort + operator fun plus(header: Header): HttpRequestPort = + with(headers = headers + header) + + operator fun plus(queryParameter: QueryParameter): HttpRequestPort = + with(queryParameters = queryParameters + queryParameter) + + operator fun plus(part: HttpPart): HttpRequestPort = + with(parts = parts + part) + + operator fun plus(formParameter: FormParameter): HttpRequestPort = + with(formParameters = formParameters + formParameter) + + operator fun plus(cookie: Cookie): HttpRequestPort = + with(cookies = cookies + cookie) + fun certificate(): X509Certificate? = certificateChain.firstOrNull() diff --git a/http/src/main/kotlin/com/hexagonkt/http/model/HttpResponsePort.kt b/http/src/main/kotlin/com/hexagonkt/http/model/HttpResponsePort.kt index 0c6b60f499..0140721e0b 100644 --- a/http/src/main/kotlin/com/hexagonkt/http/model/HttpResponsePort.kt +++ b/http/src/main/kotlin/com/hexagonkt/http/model/HttpResponsePort.kt @@ -27,4 +27,10 @@ interface HttpResponsePort : HttpMessage { onPong: WsSession.(data: ByteArray) -> Unit = this.onPong, onClose: WsSession.(status: Int, reason: String) -> Unit = this.onClose, ): HttpResponsePort + + operator fun plus(header: Header): HttpResponsePort = + with(headers = headers + header) + + operator fun plus(cookie: Cookie): HttpResponsePort = + with(cookies = cookies + cookie) } diff --git a/http/src/test/kotlin/com/hexagonkt/http/model/HttpRequestTest.kt b/http/src/test/kotlin/com/hexagonkt/http/model/HttpRequestTest.kt index 5a8306a54e..3022ab4a4f 100644 --- a/http/src/test/kotlin/com/hexagonkt/http/model/HttpRequestTest.kt +++ b/http/src/test/kotlin/com/hexagonkt/http/model/HttpRequestTest.kt @@ -245,4 +245,38 @@ internal class HttpRequestTest { testQueryParameters = QueryParameters() assertEquals(URL("http://localhost:9999/path"), TestRequest.url()) } + + @Test fun `HTTP Request operators work ok`() { + val httpRequest = httpRequestData() + + val header = Header("h", "v") + assertEquals( + httpRequest + header, + httpRequest.copy(headers = httpRequest.headers + header) + ) + + val queryParameter = QueryParameter("h", "v") + assertEquals( + httpRequest + queryParameter, + httpRequest.copy(queryParameters = httpRequest.queryParameters + queryParameter) + ) + + val httpPart = HttpPart("h", "v") + assertEquals( + httpRequest + httpPart, + httpRequest.copy(parts = httpRequest.parts + httpPart) + ) + + val formParameter = FormParameter("h", "v") + assertEquals( + httpRequest + formParameter, + httpRequest.copy(formParameters = httpRequest.formParameters + formParameter) + ) + + val cookie = Cookie("n", "v") + assertEquals( + httpRequest + cookie, + httpRequest.copy(cookies = httpRequest.cookies + cookie) + ) + } } diff --git a/http/src/test/kotlin/com/hexagonkt/http/model/HttpResponseTest.kt b/http/src/test/kotlin/com/hexagonkt/http/model/HttpResponseTest.kt index b206e29b9e..ad7e8b9a1d 100644 --- a/http/src/test/kotlin/com/hexagonkt/http/model/HttpResponseTest.kt +++ b/http/src/test/kotlin/com/hexagonkt/http/model/HttpResponseTest.kt @@ -19,26 +19,42 @@ internal class HttpResponseTest { ) @Test fun `HTTP Response comparison works ok`() { - val httpRequest = httpResponseData() + val httpResponse = httpResponseData() - assertEquals(httpRequest, httpRequest) + assertEquals(httpResponse, httpResponse) assertEquals(httpResponseData(), httpResponseData()) - assertFalse(httpRequest.equals("")) + assertFalse(httpResponse.equals("")) val headers = Headers(Header("h1", "v1")) val cookies = listOf(Cookie("p", "v")) val contentType = ContentType(TEXT_RICHTEXT) - assertNotEquals(httpRequest, httpRequest.with(body = "body")) - assertNotEquals(httpRequest, httpRequest.with(headers = headers)) - assertNotEquals(httpRequest, httpRequest.with(contentType = contentType)) - assertNotEquals(httpRequest, httpRequest.with(cookies = cookies)) - assertNotEquals(httpRequest, httpRequest.with(status = OK_200)) + assertNotEquals(httpResponse, httpResponse.with(body = "body")) + assertNotEquals(httpResponse, httpResponse.with(headers = headers)) + assertNotEquals(httpResponse, httpResponse.with(contentType = contentType)) + assertNotEquals(httpResponse, httpResponse.with(cookies = cookies)) + assertNotEquals(httpResponse, httpResponse.with(status = OK_200)) - assertEquals(httpRequest.hashCode(), httpResponseData().hashCode()) + assertEquals(httpResponse.hashCode(), httpResponseData().hashCode()) assertEquals( - httpRequest.copy(contentType = null).hashCode(), + httpResponse.copy(contentType = null).hashCode(), httpResponseData(null).hashCode() ) } + + @Test fun `HTTP Response operators work ok`() { + val httpResponse = httpResponseData() + + val header = Header("h", "v") + assertEquals( + httpResponse + header, + httpResponse.copy(headers = httpResponse.headers + header) + ) + + val cookie = Cookie("n", "v") + assertEquals( + httpResponse + cookie, + httpResponse.copy(cookies = httpResponse.cookies + cookie) + ) + } } From aace763d7f633d56aa23a18e1413c96e2d70e177 Mon Sep 17 00:00:00 2001 From: jaguililla Date: Thu, 18 May 2023 13:26:39 +0200 Subject: [PATCH 3/5] Fix --- .../kotlin/com/hexagonkt/http/patterns/WildcardPathPattern.kt | 2 +- .../com/hexagonkt/http/patterns/WildcardPathPatternTest.kt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/http/src/main/kotlin/com/hexagonkt/http/patterns/WildcardPathPattern.kt b/http/src/main/kotlin/com/hexagonkt/http/patterns/WildcardPathPattern.kt index b3cb7eb957..5f19f804ea 100644 --- a/http/src/main/kotlin/com/hexagonkt/http/patterns/WildcardPathPattern.kt +++ b/http/src/main/kotlin/com/hexagonkt/http/patterns/WildcardPathPattern.kt @@ -8,7 +8,7 @@ data class WildcardPathPattern(override val prefix: Boolean = false) : PathPatte override fun addPrefix(prefix: String?): PathPattern = if (prefix == null) this - else createPathPattern("$prefix/$WILDCARD", this.prefix) + else createPathPattern("$prefix$WILDCARD", this.prefix) override fun matches(requestUrl: String): Boolean = true diff --git a/http/src/test/kotlin/com/hexagonkt/http/patterns/WildcardPathPatternTest.kt b/http/src/test/kotlin/com/hexagonkt/http/patterns/WildcardPathPatternTest.kt index f4d002472d..fe42915f17 100644 --- a/http/src/test/kotlin/com/hexagonkt/http/patterns/WildcardPathPatternTest.kt +++ b/http/src/test/kotlin/com/hexagonkt/http/patterns/WildcardPathPatternTest.kt @@ -28,11 +28,11 @@ internal class WildcardPathPatternTest { @Test fun `Adding a prefix keep the prefix flag`() { WildcardPathPattern(true).addPrefix("/b").let { - assertEquals("/b/*", it.pattern) + assertEquals("/b*", it.pattern) assertTrue(it.prefix) } WildcardPathPattern(false).addPrefix("/b").let { - assertEquals("/b/*", it.pattern) + assertEquals("/b*", it.pattern) assertFalse(it.prefix) } } From 8e8ca0d54067f43f73ad7d03eda6ad67c0365cc6 Mon Sep 17 00:00:00 2001 From: jaguililla Date: Thu, 18 May 2023 17:31:23 +0200 Subject: [PATCH 4/5] Add utility methods --- .../com/hexagonkt/http/model/HttpRequestPort.kt | 12 ++++++++++++ .../com/hexagonkt/http/model/HttpResponsePort.kt | 6 ++++++ .../com/hexagonkt/http/model/HttpRequestTest.kt | 16 ++++++++++++++++ .../com/hexagonkt/http/model/HttpResponseTest.kt | 8 ++++++++ .../hexagonkt/http/test/examples/ClientTest.kt | 9 +++++---- .../hexagonkt/http/test/examples/SamplesTest.kt | 14 +++++++------- 6 files changed, 54 insertions(+), 11 deletions(-) diff --git a/http/src/main/kotlin/com/hexagonkt/http/model/HttpRequestPort.kt b/http/src/main/kotlin/com/hexagonkt/http/model/HttpRequestPort.kt index 2a3a82c262..506d0e2a2f 100644 --- a/http/src/main/kotlin/com/hexagonkt/http/model/HttpRequestPort.kt +++ b/http/src/main/kotlin/com/hexagonkt/http/model/HttpRequestPort.kt @@ -54,6 +54,18 @@ interface HttpRequestPort : HttpMessage { operator fun plus(cookie: Cookie): HttpRequestPort = with(cookies = cookies + cookie) + operator fun plus(headers: Headers): HttpRequestPort = + with(headers = this.headers + headers) + + operator fun plus(queryParameters: QueryParameters): HttpRequestPort = + with(queryParameters = this.queryParameters + queryParameters) + + operator fun plus(parts: List): HttpRequestPort = + with(parts = this.parts + parts) + + operator fun plus(formParameters: FormParameters): HttpRequestPort = + with(formParameters = this.formParameters + formParameters) + fun certificate(): X509Certificate? = certificateChain.firstOrNull() diff --git a/http/src/main/kotlin/com/hexagonkt/http/model/HttpResponsePort.kt b/http/src/main/kotlin/com/hexagonkt/http/model/HttpResponsePort.kt index 0140721e0b..b93854e186 100644 --- a/http/src/main/kotlin/com/hexagonkt/http/model/HttpResponsePort.kt +++ b/http/src/main/kotlin/com/hexagonkt/http/model/HttpResponsePort.kt @@ -33,4 +33,10 @@ interface HttpResponsePort : HttpMessage { operator fun plus(cookie: Cookie): HttpResponsePort = with(cookies = cookies + cookie) + + operator fun plus(headers: Headers): HttpResponsePort = + with(headers = this.headers + headers) + + operator fun plus(cookies: List): HttpResponsePort = + with(cookies = this.cookies + cookies) } diff --git a/http/src/test/kotlin/com/hexagonkt/http/model/HttpRequestTest.kt b/http/src/test/kotlin/com/hexagonkt/http/model/HttpRequestTest.kt index 3022ab4a4f..890c8ce93d 100644 --- a/http/src/test/kotlin/com/hexagonkt/http/model/HttpRequestTest.kt +++ b/http/src/test/kotlin/com/hexagonkt/http/model/HttpRequestTest.kt @@ -254,24 +254,40 @@ internal class HttpRequestTest { httpRequest + header, httpRequest.copy(headers = httpRequest.headers + header) ) + assertEquals( + httpRequest + Headers(header), + httpRequest.copy(headers = httpRequest.headers + header) + ) val queryParameter = QueryParameter("h", "v") assertEquals( httpRequest + queryParameter, httpRequest.copy(queryParameters = httpRequest.queryParameters + queryParameter) ) + assertEquals( + httpRequest + QueryParameters(queryParameter), + httpRequest.copy(queryParameters = httpRequest.queryParameters + queryParameter) + ) val httpPart = HttpPart("h", "v") assertEquals( httpRequest + httpPart, httpRequest.copy(parts = httpRequest.parts + httpPart) ) + assertEquals( + httpRequest + listOf(httpPart), + httpRequest.copy(parts = httpRequest.parts + httpPart) + ) val formParameter = FormParameter("h", "v") assertEquals( httpRequest + formParameter, httpRequest.copy(formParameters = httpRequest.formParameters + formParameter) ) + assertEquals( + httpRequest + FormParameters(formParameter), + httpRequest.copy(formParameters = httpRequest.formParameters + formParameter) + ) val cookie = Cookie("n", "v") assertEquals( diff --git a/http/src/test/kotlin/com/hexagonkt/http/model/HttpResponseTest.kt b/http/src/test/kotlin/com/hexagonkt/http/model/HttpResponseTest.kt index ad7e8b9a1d..b076be0669 100644 --- a/http/src/test/kotlin/com/hexagonkt/http/model/HttpResponseTest.kt +++ b/http/src/test/kotlin/com/hexagonkt/http/model/HttpResponseTest.kt @@ -50,11 +50,19 @@ internal class HttpResponseTest { httpResponse + header, httpResponse.copy(headers = httpResponse.headers + header) ) + assertEquals( + httpResponse + Headers(header), + httpResponse.copy(headers = httpResponse.headers + header) + ) val cookie = Cookie("n", "v") assertEquals( httpResponse + cookie, httpResponse.copy(cookies = httpResponse.cookies + cookie) ) + assertEquals( + httpResponse + listOf(cookie), + httpResponse.copy(cookies = httpResponse.cookies + cookie) + ) } } diff --git a/http_test/src/main/kotlin/com/hexagonkt/http/test/examples/ClientTest.kt b/http_test/src/main/kotlin/com/hexagonkt/http/test/examples/ClientTest.kt index 27eb9210b0..c5fd37f769 100644 --- a/http_test/src/main/kotlin/com/hexagonkt/http/test/examples/ClientTest.kt +++ b/http_test/src/main/kotlin/com/hexagonkt/http/test/examples/ClientTest.kt @@ -62,7 +62,10 @@ abstract class ClientTest( callback = { val contentType = ContentType(APPLICATION_JSON, charset = Charsets.UTF_8) val bodyString = request.bodyString() - val bodyHeader = if (bodyString.endsWith("\n") || bodyString.contains("{")) "json" else bodyString + val bodyHeader = + if (bodyString.endsWith("\n") || bodyString.contains("{")) "json" + else bodyString + ok( body = bodyString, headers = response.headers @@ -85,9 +88,7 @@ abstract class ClientTest( @Test fun `Form parameters are sent correctly`() { callback = { - val headers = Headers( - formParameters.httpFields.map { (k, v) -> Header(k, v.values) } - ) + val headers = Headers(formParameters.httpFields.map { (k, v) -> Header(k, v.values) }) ok(headers = headers) } diff --git a/http_test/src/main/kotlin/com/hexagonkt/http/test/examples/SamplesTest.kt b/http_test/src/main/kotlin/com/hexagonkt/http/test/examples/SamplesTest.kt index 750c6b9867..6601b2b924 100644 --- a/http_test/src/main/kotlin/com/hexagonkt/http/test/examples/SamplesTest.kt +++ b/http_test/src/main/kotlin/com/hexagonkt/http/test/examples/SamplesTest.kt @@ -377,24 +377,24 @@ abstract class SamplesTest( val server = HttpServer(serverAdapter()) { // filters - on("/*") { send(headers = response.headers + Header("b-all", "true")) } + on("/*") { send(response + Header("b-all", "true")) } - on("/filters/*") { send(headers = response.headers + Header("b-filters", "true")) } + on("/filters/*") { send(response + Header("b-filters", "true")) } get("/filters/route") { ok("filters route") } - after("/filters/*") { send(headers = response.headers + Header("a-filters", "true")) } + after("/filters/*") { send(response + Header("a-filters", "true")) } get("/filters") { ok("filters") } path("/nested") { - on("*") { send(headers = response.headers + Header("b-nested", "true")) } - on { send(headers = response.headers + Header("b-nested-2", "true")) } + on("*") { send(response + Header("b-nested", "true")) } + on { send(response + Header("b-nested-2", "true")) } get("/filters") { ok("nested filters") } get("/halted") { send(HttpStatus(499), "halted") } get { ok("nested also") } - after("*") { send(headers = response.headers + Header("a-nested", "true")) } + after("*") { send(response + Header("a-nested", "true")) } } - after("/*") { send(headers = response.headers + Header("a-all", "true")) } + after("/*") { send(response + Header("a-all", "true")) } // filters } From a2f5321dbe22f23b7f4740b5030159553f113b72 Mon Sep 17 00:00:00 2001 From: jaguililla Date: Thu, 18 May 2023 17:31:40 +0200 Subject: [PATCH 5/5] Fix parent POM --- starters/kotlin_pom.xml | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/starters/kotlin_pom.xml b/starters/kotlin_pom.xml index 0b3e2121ba..8b85571bea 100644 --- a/starters/kotlin_pom.xml +++ b/starters/kotlin_pom.xml @@ -42,26 +42,21 @@ ${project.basedir}/src/main/kotlin ${project.basedir}/src/test/kotlin + + + org.apache.maven.plugins + maven-resources-plugin + 3.3.1 + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + + - - org.apache.maven.plugins - maven-resources-plugin - 3.3.1 - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.11.0 - - - - org.apache.maven.plugins - maven-assembly-plugin - 3.6.0 - - org.jetbrains.kotlin kotlin-maven-plugin @@ -123,7 +118,7 @@ org.apache.maven.plugins maven-assembly-plugin - 3.5.0 + 3.6.0