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/model/HttpRequestPort.kt b/http/src/main/kotlin/com/hexagonkt/http/model/HttpRequestPort.kt index ebfb1fdbe7..506d0e2a2f 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,33 @@ 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) + + 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 0c6b60f499..b93854e186 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,16 @@ 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) + + 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/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..5f19f804ea --- /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/model/HttpRequestTest.kt b/http/src/test/kotlin/com/hexagonkt/http/model/HttpRequestTest.kt index 5a8306a54e..890c8ce93d 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,54 @@ 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) + ) + 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( + 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..b076be0669 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,50 @@ 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) + ) + 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/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..fe42915f17 --- /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) + } + } +} 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 } 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