jvmMain.assembly.RaptorKtorServerComponent.kt Maven / Gradle / Ivy
The newest version!
package io.fluidsonic.raptor.ktor
import io.fluidsonic.raptor.*
import io.fluidsonic.raptor.di.*
import io.fluidsonic.raptor.transactions.*
import io.ktor.server.application.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.netty.handler.codec.http.*
import java.io.*
import kotlinx.coroutines.*
public class RaptorKtorServerComponent internal constructor(
internal val forceEncryptedConnection: Boolean,
) : RaptorComponent.Base(RaptorKtorPlugin),
RaptorTaggableComponent,
RaptorTransactionBoundary {
private var configuration: KtorServerConfiguration? = null
private val connectors: MutableList = mutableListOf()
private val customConfigurations: MutableList Unit> = mutableListOf()
private var engineEnvironmentFactory: ((configure: ApplicationEngineEnvironmentBuilder.() -> Unit) -> ApplicationEngineEnvironment)? = null
private var engineFactory: ((environment: ApplicationEngineEnvironment) -> ApplicationEngine)? = null
private val plugins: MutableList = mutableListOf()
private var startStopDispatcher: CoroutineDispatcher? = null
internal fun complete() =
checkNotNull(configuration)
@RaptorDsl
public fun custom(configuration: RaptorKtorInitializationScope.() -> Unit) {
customConfigurations += configuration
}
@RaptorDsl
public fun engineEnvironmentFactory(factory: (configure: ApplicationEngineEnvironmentBuilder.() -> Unit) -> ApplicationEngineEnvironment) {
check(engineEnvironmentFactory == null) { "Factory already set." }
engineEnvironmentFactory = factory
}
@RaptorDsl
public fun engineFactory(factory: (environment: ApplicationEngineEnvironment) -> ApplicationEngine) {
check(engineFactory == null) { "Factory already set." }
engineFactory = factory
}
@RaptorDsl
public fun httpConnector(host: String = "0.0.0.0", port: Int = 80) {
connectors += KtorServerConfiguration.Connector.Http(
host = host,
port = port
)
}
@RaptorDsl
public fun httpsConnector(
host: String = "0.0.0.0",
port: Int = 443,
keyAlias: String,
keyStoreFile: File,
keyStorePassword: String,
privateKeyPassword: String,
) {
connectors += KtorServerConfiguration.Connector.Https(
host = host,
port = port,
keyAlias = keyAlias,
keyStoreFile = keyStoreFile,
keyStorePassword = keyStorePassword,
privateKeyPassword = privateKeyPassword
)
}
@RaptorDsl
public fun install(plugin: RaptorKtorServerPlugin) {
if (plugins.add(plugin))
with(plugin) {
ConfigurationStartScope().onConfigurationStarted()
}
}
@RaptorDsl
internal fun startStopDispatcher(dispatcher: CoroutineDispatcher) {
check(startStopDispatcher == null) { "Dispatcher already set." }
startStopDispatcher = dispatcher
}
@RaptorDsl
public val routes: RaptorKtorRoutesComponent.Root
get() = componentRegistry.oneOrRegister(Keys.rootRoutesComponent) { RaptorKtorRoutesComponent.Root() }
override fun RaptorComponentConfigurationEndScope.onConfigurationEnded() {
if (plugins.isNotEmpty()) {
val scope = ConfigurationEndScope(parent = this)
for (plugin in plugins)
with(plugin) {
scope.onConfigurationEnded()
}
}
val rootRouteConfiguration = componentRegistry.oneOrNull(Keys.rootRoutesComponent)?.complete()?.let {
KtorRouteConfiguration(
children = it,
customConfigurations = emptyList(),
host = null,
path = "",
properties = RaptorPropertySet.empty(),
transactionFactory = null,
wrapper = null,
)
}
configuration = KtorServerConfiguration(
connectors = connectors.toList(),
customConfigurations = customConfigurations.toList(),
engineEnvironmentFactory = engineEnvironmentFactory ?: ::applicationEngineEnvironment,
engineFactory = engineFactory ?: {
// TODO make configurable
embeddedServer(Netty, it) {
responseWriteTimeoutSeconds = 30
httpServerCodec = {
HttpServerCodec(
4 * 4096, // for mmpt-k2-server project
HttpObjectDecoder.DEFAULT_MAX_HEADER_SIZE,
HttpObjectDecoder.DEFAULT_MAX_CHUNK_SIZE,
)
}
}
},
forceEncryptedConnection = forceEncryptedConnection,
rootRouteConfiguration = rootRouteConfiguration,
startStopDispatcher = startStopDispatcher ?: Dispatchers.Default,
tags = tags(),
transactionFactory = transactionFactory(),
)
}
override fun RaptorComponentConfigurationStartScope.onConfigurationStarted() {
transactions.di.provide {
// TODO improve
get().ktorCall ?: error("Cannot find Ktor ApplicationCall.")
}
}
private class ConfigurationEndScope(parent: RaptorComponentConfigurationEndScope) : RaptorKtorServerPluginConfigurationEndScope {
private val serverScope = object :
RaptorKtorServerPluginConfigurationEndScope.ServerScope,
RaptorComponentConfigurationEndScope by parent {}
override fun server(configuration: RaptorKtorServerPluginConfigurationEndScope.ServerScope.() -> Unit) {
serverScope.configuration()
}
}
private inner class ConfigurationStartScope : RaptorKtorServerPluginConfigurationStartScope {
override val server: RaptorKtorServerComponent
get() = this@RaptorKtorServerComponent
}
}
@RaptorDsl
public fun RaptorAssemblyQuery.custom(configuration: RaptorKtorInitializationScope.() -> Unit) {
this {
custom(configuration)
}
}
@RaptorDsl
public fun RaptorAssemblyQuery.engineEnvironmentFactory(
factory: (
configure: ApplicationEngineEnvironmentBuilder.() -> Unit,
) -> ApplicationEngineEnvironment,
) {
this {
engineEnvironmentFactory(factory)
}
}
@RaptorDsl
public fun RaptorAssemblyQuery.engineFactory(factory: (environment: ApplicationEngineEnvironment) -> ApplicationEngine) {
this {
engineFactory(factory)
}
}
@RaptorDsl
public fun RaptorAssemblyQuery.httpConnector(host: String = "0.0.0.0", port: Int = 80) {
this {
httpConnector(host = host, port = port)
}
}
@RaptorDsl
public fun RaptorAssemblyQuery.httpsConnector(
host: String = "0.0.0.0",
port: Int = 443,
keyAlias: String,
keyStoreFile: File,
keyStorePassword: String,
privateKeyPassword: String,
) {
this {
httpsConnector(
host = host,
port = port,
keyAlias = keyAlias,
keyStoreFile = keyStoreFile,
keyStorePassword = keyStorePassword,
privateKeyPassword = privateKeyPassword
)
}
}
@RaptorDsl
public fun RaptorAssemblyQuery.install(plugin: RaptorKtorServerPlugin) {
this {
install(plugin)
}
}
@RaptorDsl
public val RaptorAssemblyQuery.routes: RaptorAssemblyQuery
get() = map { it.routes }
@RaptorDsl
internal fun RaptorAssemblyQuery.startStopDispatcher(dispatcher: CoroutineDispatcher) {
this {
startStopDispatcher(dispatcher)
}
}