org.http4k.chaos.ChaoticHttpHandler.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of http4k-testing-chaos Show documentation
Show all versions of http4k-testing-chaos Show documentation
Http4k support for chaos testing
package org.http4k.chaos
import org.http4k.chaos.ChaosBehaviours.ReturnStatus
import org.http4k.core.HttpHandler
import org.http4k.core.Request
import org.http4k.core.Response
import org.http4k.core.Status
import org.http4k.core.Status.Companion.INTERNAL_SERVER_ERROR
import org.http4k.core.Status.Companion.SERVICE_UNAVAILABLE
import org.http4k.core.Uri
import org.http4k.core.then
import org.http4k.filter.ServerFilters.CatchAll
import org.http4k.routing.RoutingHttpHandler
import org.http4k.routing.bind
import org.http4k.routing.routes
import org.http4k.server.ServerConfig
import org.http4k.server.SunHttp
import org.http4k.server.asServer
import java.lang.Integer.MAX_VALUE
import java.util.Random
import kotlin.reflect.KClass
/**
* Useful for creating HttpHandlers with a built-in Chaos Engine API for enabling and disabling chaos.
*/
abstract class ChaoticHttpHandler : HttpHandler {
protected abstract val app: HttpHandler
private val chaosEngine = ChaosEngine()
fun behave() = chaosEngine.disable()
fun misbehave(behaviour: Behaviour = ReturnStatus(INTERNAL_SERVER_ERROR)) = chaosEngine.enable(behaviour)
fun misbehave(stage: Stage) = chaosEngine.enable(stage)
fun returnStatus(status: Status) = misbehave(ReturnStatus(status))
override fun invoke(request: Request) = chaosEngine
.then(CatchAll())
.then(attachChaosIfSupported())(request)
private fun attachChaosIfSupported() = try {
app.withChaosApi(chaosEngine, controlsPath = "/chaos")
} catch (e: NoClassDefFoundError) {
routes(
routes("/chaos" bind { _: Request -> Response(SERVICE_UNAVAILABLE).body(e.stackTraceToString()) }),
when (app) {
is RoutingHttpHandler -> app as RoutingHttpHandler
else -> routes("/{path:.*}" bind app)
}
)
}
}
/**
* Convert this ChaoticHttpHandler into a running server, defaulting on a port dependent on the classname.
*/
fun ChaoticHttpHandler.start(
port: Int = this::class.defaultPort, serverConfig: (Int) -> ServerConfig = ::SunHttp
) =
asServer(serverConfig(port)).start().also {
println("Started ${this::class.simpleName ?: "server"} on $port")
}
/**
* Calculate a random standard port number for a ChaoticHttpHandler using the classname as a seed
*/
val KClass.defaultPort
get() = Random((simpleName.hashCode() % MAX_VALUE).toLong()).nextInt(65535 - 10000) + 10000
/**
* Local URI for this ChaoticHttpHandler
*/
val KClass.defaultLocalUri get() = Uri.of("http://localhost:$defaultPort")