HttpPredicate.kt
package com.hexagonkt.http.handlers
import com.hexagonkt.handlers.Context
import com.hexagonkt.core.logging.Logger
import com.hexagonkt.http.patterns.LiteralPathPattern
import com.hexagonkt.http.model.HttpMethod
import com.hexagonkt.http.model.HttpStatus
import com.hexagonkt.http.patterns.PathPattern
import com.hexagonkt.http.patterns.createPathPattern
import com.hexagonkt.http.model.HttpCall
import kotlin.reflect.KClass
data class HttpPredicate(
val methods: Set<HttpMethod> = emptySet(),
val pathPattern: PathPattern = LiteralPathPattern(),
val exception: KClass<out Exception>? = null,
val status: HttpStatus? = null,
) : (Context<HttpCall>) -> Boolean {
private companion object {
val logger: Logger = Logger(HttpPredicate::class)
}
private fun PathPattern.isEmpty(): Boolean =
pattern.isEmpty()
val predicate: (Context<HttpCall>) -> Boolean =
if (methods.isEmpty()) log(::filterWithoutMethod)
else log(::filterWithMethod)
constructor(
methods: Set<HttpMethod> = emptySet(),
pattern: String = "",
exception: KClass<out Exception>? = null,
status: HttpStatus? = null,
prefix: Boolean = false,
) :
this(methods, createPathPattern(pattern, prefix), exception, status)
override fun invoke(context: Context<HttpCall>): Boolean =
predicate(context)
fun clearMethods(): HttpPredicate =
copy(methods = emptySet())
private fun log(
predicate: (Context<HttpCall>) -> Boolean
): (Context<HttpCall>) -> Boolean = {
val allowed = predicate(it)
logger.debug { "${describe()} -> ${if (allowed) "ALLOWED" else "DENIED"}" }
allowed
}
private fun filterMethod(context: Context<HttpCall>): Boolean =
context.event.request.method in methods
private fun filterPattern(context: Context<HttpCall>): Boolean =
if (pathPattern.isEmpty() && context.event.request.path == "/") true
else pathPattern.matches(context.event.request.path)
private fun filterException(context: Context<HttpCall>): Boolean {
val exceptionClass = context.exception?.javaClass ?: return false
return exception?.java?.isAssignableFrom(exceptionClass) ?: false
}
private fun filterStatus(context: Context<HttpCall>): Boolean =
status == context.event.response.status
private fun filterWithoutMethod(context: Context<HttpCall>): Boolean =
filterPattern(context)
&& (exception == null || filterException(context))
&& (status == null || filterStatus(context))
private fun filterWithMethod(context: Context<HttpCall>): Boolean =
filterMethod(context) && filterWithoutMethod(context)
fun addPrefix(prefix: String): HttpPredicate =
copy(pathPattern = pathPattern.addPrefix(prefix))
fun describe(): String =
methods
.map { it.name }
.ifEmpty { listOf("ANY") }
.joinToString(
separator = ", ",
postfix = pathPattern.describe().prependIndent(" "),
transform = { it }
)
}