Skip to content

Instantly share code, notes, and snippets.

@daviddenton
Created May 8, 2020 20:27
Show Gist options
  • Save daviddenton/63942d3bafb4c64445ed949bec8819ec to your computer and use it in GitHub Desktop.
Save daviddenton/63942d3bafb4c64445ed949bec8819ec to your computer and use it in GitHub Desktop.
package org.http4k.h4k.example.lib
import org.http4k.cloudnative.env.Port
import org.http4k.core.HttpHandler
import org.http4k.server.Http4kServer
import org.http4k.server.SunHttp
import org.http4k.server.asServer
interface Discovery<ServiceId> {
fun lookup(id: ServiceId): HttpHandler
}
class H4KCluster<ServiceId> : Discovery<ServiceId> {
private val services = mutableMapOf<ServiceId, HttpHandler>()
private val servers = mutableListOf<Pair<ServiceId, Http4kServer>>()
override fun lookup(id: ServiceId) = services[id]
?: throw IllegalStateException("$id is not registered in this cluster")
fun install(id: ServiceId, appFn: (Discovery<ServiceId>) -> HttpHandler) = apply {
val app = appFn(this)
services[id] = app
}
fun expose(id: ServiceId, port: Port) = apply {
servers += id to lookup(id).asServer(SunHttp(port.value))
}
fun start() = apply {
servers.forEach {
it.second.start()
println("Bound ${it.first} to ${it.second.port()}")
}
}
fun stop() = apply {
servers.forEach {
it.second.stop()
println("Unbound ${it.first}")
}
}
}
package org.http4k.h4k.example.main
import org.http4k.core.HttpHandler
import org.http4k.core.Method
import org.http4k.core.Request
import org.http4k.core.Response
import org.http4k.core.Status
data class InternalServiceId(val name: String)
data class ExternalServiceId(val name: String)
/**
* Proxies requests to the App service
*/
object Proxy {
val ID = InternalServiceId("proxy")
operator fun invoke(appHttp: HttpHandler) = { req: Request -> appHttp(req) }
}
/**
* This is a particular application which uses the 3rd party Reverser service
*/
object App {
val ID = InternalServiceId("app")
operator fun invoke(reverserHttp: HttpHandler): HttpHandler {
val reverser = Reverser.Client(reverserHttp)
return { _: Request -> Response(Status.OK).body(reverser.reverse("hello world")) }
}
}
/**
* Domain client for the 3rd party Reverser service
*/
object Reverser {
val ID = ExternalServiceId("reverser")
class Client(private val http: HttpHandler) {
fun reverse(input: String) = http(Request(Method.GET, "/").body(input)).bodyString()
}
}
package org.http4k.h4k.example.test
import org.http4k.client.OkHttp
import org.http4k.cloudnative.env.Port
import org.http4k.core.HttpHandler
import org.http4k.core.Method
import org.http4k.core.Request
import org.http4k.core.Response
import org.http4k.core.Status
import org.http4k.core.Uri
import org.http4k.core.then
import org.http4k.filter.ClientFilters
import org.http4k.h4k.example.lib.H4KCluster
import org.http4k.h4k.example.main.App
import org.http4k.h4k.example.main.ExternalServiceId
import org.http4k.h4k.example.main.InternalServiceId
import org.http4k.h4k.example.main.Proxy
import org.http4k.h4k.example.main.Reverser
fun FakeReverserApp(): HttpHandler = { req: Request -> Response(Status.OK).body(req.bodyString().reversed()) }
fun main() {
// this is our "fakes" cluster
val egress = H4KCluster<ExternalServiceId>()
.install(Reverser.ID) { FakeReverserApp() }
.expose(Reverser.ID, Port(10000))
.start()
// this is our service cluster
val cluster = H4KCluster<InternalServiceId>()
.install(App.ID) { App(egress.lookup(Reverser.ID)) }
.install(Proxy.ID) { discovery -> Proxy(discovery.lookup(App.ID)) }
.expose(Proxy.ID, Port(8000))
.start()
// look up the service HttpHandler by ID
println(cluster.lookup(Proxy.ID)(Request(Method.GET, "")))
// because we've exposed it, we can also go over the wire
val client = ClientFilters.SetBaseUriFrom(Uri.of("http://localhost:8000")).then(OkHttp())
println(client(Request(Method.GET, "")))
cluster.stop()
egress.stop()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment