diff --git a/http4k-core/src/main/kotlin/org/http4k/routing/ParameterMatchRoutingHttpHandler.kt b/http4k-core/src/main/kotlin/org/http4k/routing/ParameterMatchRoutingHttpHandler.kt index 520fe7bb12..8d3249dbd7 100644 --- a/http4k-core/src/main/kotlin/org/http4k/routing/ParameterMatchRoutingHttpHandler.kt +++ b/http4k-core/src/main/kotlin/org/http4k/routing/ParameterMatchRoutingHttpHandler.kt @@ -5,6 +5,8 @@ import org.http4k.core.HttpHandler import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.then +import org.http4k.routing.RouterMatch.MatchingHandler +import org.http4k.routing.RouterMatch.Unmatched sealed class ParameterMatch(private val predicate: (Request) -> Boolean) : (Request) -> Boolean by predicate { infix fun bind(handler: HttpHandler): RoutingHttpHandler = PredicatedHandler(predicate, handler) @@ -18,12 +20,20 @@ sealed class ParameterMatch(private val predicate: (Request) -> Boolean) : (Requ } internal class PredicatedHandler(private val predicate: (Request) -> Boolean, private val handler: HttpHandler) : RoutingHttpHandler { - override fun withFilter(new: Filter) = throw UnsupportedOperationException("Not available at the top level") - override fun withBasePath(new: String) = throw UnsupportedOperationException("Not available at the top level") - override fun match(request: Request) = if (predicate(request)) RouterMatch.MatchingHandler(handler) else RouterMatch.Unmatched + override fun withFilter(new: Filter) = PredicatedHandler(predicate, when(handler) { + is RoutingHttpHandler -> handler.withFilter(new) + else -> new.then(handler) + }) + + override fun withBasePath(new: String) = when(handler) { + is RoutingHttpHandler -> handler.withBasePath(new) + else -> throw UnsupportedOperationException("Cannot apply new base path without binding to an HTTP verb") + } + + override fun match(request: Request) = if (predicate(request)) MatchingHandler(handler) else Unmatched override fun invoke(request: Request): Response = when (val matchResult = match(request)) { - is RouterMatch.MatchingHandler -> matchResult(request) + is MatchingHandler -> matchResult(request) else -> routeNotFoundHandler(request) } } @@ -34,17 +44,17 @@ internal data class ParameterMatchRoutingHttpHandler( private val notFoundHandler: HttpHandler = routeNotFoundHandler, private val methodNotAllowedHandler: HttpHandler = routeMethodNotAllowedHandler) : RoutingHttpHandler { - override fun match(request: Request) = if (!matched(request)) RouterMatch.Unmatched else httpHandler.match(request) + override fun match(request: Request) = if (!matched(request)) Unmatched else httpHandler.match(request) override fun invoke(request: Request): Response = when (val matchResult = match(request)) { - is RouterMatch.MatchingHandler -> matchResult(request) + is MatchingHandler -> matchResult(request) is RouterMatch.MethodNotMatched -> methodNotAllowedHandler(request) - is RouterMatch.Unmatched -> notFoundHandler(request) + is Unmatched -> notFoundHandler(request) } override fun withFilter(new: Filter): RoutingHttpHandler = ParameterMatchRoutingHttpHandler( matched, - new.then(httpHandler), + httpHandler.withFilter(new), new.then(notFoundHandler), new.then(methodNotAllowedHandler) ) diff --git a/http4k-core/src/main/kotlin/org/http4k/routing/TemplateRoutingHttpHandler.kt b/http4k-core/src/main/kotlin/org/http4k/routing/TemplateRoutingHttpHandler.kt index c999ea7820..2442906d36 100644 --- a/http4k-core/src/main/kotlin/org/http4k/routing/TemplateRoutingHttpHandler.kt +++ b/http4k-core/src/main/kotlin/org/http4k/routing/TemplateRoutingHttpHandler.kt @@ -30,7 +30,10 @@ internal data class TemplateRoutingHttpHandler( } override fun withFilter(new: Filter): RoutingHttpHandler = - copy(httpHandler = new.then(httpHandler), notFoundHandler = new.then(notFoundHandler), methodNotAllowedHandler = new.then(methodNotAllowedHandler)) + copy(httpHandler = new.then(httpHandler), + notFoundHandler = new.then(notFoundHandler), + methodNotAllowedHandler = new.then(methodNotAllowedHandler) + ) override fun withBasePath(new: String): RoutingHttpHandler = copy(template = UriTemplate.from("$new/$template")) } diff --git a/http4k-core/src/test/kotlin/org/http4k/routing/ParameterMatchRoutingHttpHandlerTest.kt b/http4k-core/src/test/kotlin/org/http4k/routing/ParameterMatchRoutingHttpHandlerTest.kt index 7334f2e974..f592623203 100644 --- a/http4k-core/src/test/kotlin/org/http4k/routing/ParameterMatchRoutingHttpHandlerTest.kt +++ b/http4k-core/src/test/kotlin/org/http4k/routing/ParameterMatchRoutingHttpHandlerTest.kt @@ -3,6 +3,7 @@ package org.http4k.routing import com.natpryce.hamkrest.and import com.natpryce.hamkrest.assertion.assertThat import com.natpryce.hamkrest.present +import com.natpryce.hamkrest.throws import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response @@ -31,6 +32,11 @@ class ParameterMatchRoutingHttpHandlerTest : RoutingHttpHandlerContract() { assertThat(withBasePath.matchAndInvoke(prefixReq), criteria) assertThat(withBasePath(prefixReq), criteria) } + + @Test + fun `attempt to bind param handler without a verb`() { + assertThat({ routes(prefix bind (headers("host") bind { Response(OK) })) }, throws()) + } } class ParameterMatchRoutingHttpHandlerAlternateTest : RoutingHttpHandlerContract() {