diff --git a/ktor-core-tests/test/org/jetbrains/ktor/tests/routing/RoutingResolveTest.kt b/ktor-core-tests/test/org/jetbrains/ktor/tests/routing/RoutingResolveTest.kt index 766c363d49..3c81862b8f 100644 --- a/ktor-core-tests/test/org/jetbrains/ktor/tests/routing/RoutingResolveTest.kt +++ b/ktor-core-tests/test/org/jetbrains/ktor/tests/routing/RoutingResolveTest.kt @@ -179,7 +179,7 @@ class RoutingResolveTest { @Test fun `routing foo with optional parameter`() { val root = routing() - val paramEntry = root.selectHandle(UriPartConstantRouteSelector("foo")) + val paramEntry = root.select(UriPartConstantRouteSelector("foo")) .selectHandle(UriPartOptionalParameterRouteSelector("param")) on("resolving /foo/value") { @@ -241,7 +241,7 @@ class RoutingResolveTest { @Test fun `routing foo with anonymous tailcard`() { val root = routing() - val paramEntry = root.selectHandle(UriPartConstantRouteSelector("foo")) + val paramEntry = root.select(UriPartConstantRouteSelector("foo")) .selectHandle(UriPartTailcardRouteSelector()) on("resolving /foo/value") { @@ -280,7 +280,7 @@ class RoutingResolveTest { @Test fun `routing foo with named tailcard`() { val root = routing() - val paramEntry = root.selectHandle(UriPartConstantRouteSelector("foo")) + val paramEntry = root.select(UriPartConstantRouteSelector("foo")) .selectHandle(UriPartTailcardRouteSelector("items")) on("resolving /foo/value") { @@ -376,7 +376,7 @@ class RoutingResolveTest { @Test fun `routing foo with quality`() { val root = routing() - val fooEntry = root.selectHandle(UriPartConstantRouteSelector("foo")) + val fooEntry = root.select(UriPartConstantRouteSelector("foo")) val paramEntry = fooEntry.selectHandle(UriPartParameterRouteSelector("name")) val constantEntry = fooEntry.selectHandle(UriPartConstantRouteSelector("admin")) @@ -460,4 +460,32 @@ class RoutingResolveTest { } } } + + @Test fun `select most specific route with root`() { + val routing = routing() + val rootEntry = routing.createRoute("/").apply { handle {} } + val varargEntry = routing.createRoute("/{...}").apply { handle {} } + + on("resolving /") { + val resolveResult = context(routing, "/").resolve() + + it("should successfully resolve") { + assertTrue(resolveResult.succeeded) + } + it("should resolve to rootEntry") { + assertEquals(rootEntry, resolveResult.entry) + } + } + on("resolving /path") { + val resolveResult = context(routing, "/path").resolve() + + it("should successfully resolve") { + assertTrue(resolveResult.succeeded) + } + it("should resolve to varargEntry") { + assertEquals(varargEntry, resolveResult.entry) + } + } + + } } diff --git a/ktor-core/src/org/jetbrains/ktor/routing/RouteSelector.kt b/ktor-core/src/org/jetbrains/ktor/routing/RouteSelector.kt index 8821bb9846..1d07c27403 100644 --- a/ktor-core/src/org/jetbrains/ktor/routing/RouteSelector.kt +++ b/ktor-core/src/org/jetbrains/ktor/routing/RouteSelector.kt @@ -14,8 +14,8 @@ data class RouteSelectorEvaluation(val succeeded: Boolean, val qualityConstant = 1.0 val qualityParameter = 0.8 - val qualityMissing = 0.5 - val qualityWildcard = 0.2 + val qualityWildcard = 0.5 + val qualityMissing = 0.2 } } @@ -136,11 +136,9 @@ object UriPartWildcardRouteSelector : RouteSelector { data class UriPartTailcardRouteSelector(val name: String = "") : RouteSelector { override fun evaluate(context: RoutingResolveContext, index: Int): RouteSelectorEvaluation { - if (index <= context.path.size) { - val values = if (name.isEmpty()) valuesOf() else valuesOf(name to context.path.drop(index).map { it }) - return RouteSelectorEvaluation(true, RouteSelectorEvaluation.qualityWildcard, values, segmentIncrement = context.path.size - index) - } - return RouteSelectorEvaluation.Failed + val values = if (name.isEmpty()) valuesOf() else valuesOf(name to context.path.drop(index).map { it }) + val quality = if (index < context.path.size) RouteSelectorEvaluation.qualityWildcard else RouteSelectorEvaluation.qualityMissing + return RouteSelectorEvaluation(true, quality, values, segmentIncrement = context.path.size - index) } override fun toString(): String = "{...}" diff --git a/ktor-core/src/org/jetbrains/ktor/routing/RoutingResolve.kt b/ktor-core/src/org/jetbrains/ktor/routing/RoutingResolve.kt index 5389ded65b..5a5c23c4e1 100644 --- a/ktor-core/src/org/jetbrains/ktor/routing/RoutingResolve.kt +++ b/ktor-core/src/org/jetbrains/ktor/routing/RoutingResolve.kt @@ -61,14 +61,15 @@ class RoutingResolveContext(val routing: Route, bestResult = RoutingResolveResult(true, subtreeResult.entry, combinedValues, combinedQuality) } - if (bestResult != null) - return bestResult - // no child matched, match is either current entry if path is done & there is a handler, or failure - if (segmentIndex == request.path.size && entry.handlers.isNotEmpty()) + if (segmentIndex == request.path.size && entry.handlers.isNotEmpty()) { + if (bestResult != null && bestResult.quality > RouteSelectorEvaluation.qualityMissing) + return bestResult + return RoutingResolveResult(true, entry, ValuesMap.Empty, 1.0) - else - return RoutingResolveResult(false, failEntry ?: entry, ValuesMap.Empty, 0.0) + } + + return bestResult ?: RoutingResolveResult(false, failEntry ?: entry, ValuesMap.Empty, 0.0) } }