Skip to content

Commit

Permalink
KTOR-7479 Don't use anonymous objects for plugins
Browse files Browse the repository at this point in the history
  • Loading branch information
osipxd committed Oct 4, 2024
1 parent 57a81a0 commit 377d595
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 75 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2014-2021 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
* Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/

package io.ktor.client.plugins.api
Expand All @@ -13,16 +13,16 @@ import io.ktor.utils.io.core.*
* An instance of [ClientPlugin] that can be installed into [HttpClient].
*/
public class ClientPluginInstance<PluginConfig : Any> internal constructor(
internal val config: PluginConfig,
internal val name: String,
internal val body: ClientPluginBuilder<PluginConfig>.() -> Unit
private val key: AttributeKey<ClientPluginInstance<PluginConfig>>,
private val config: PluginConfig,
private val body: ClientPluginBuilder<PluginConfig>.() -> Unit
) : Closeable {

private var onClose: () -> Unit = {}

@InternalAPI
public fun install(scope: HttpClient) {
val pluginBuilder = ClientPluginBuilder(AttributeKey(name), scope, config).apply(body)
val pluginBuilder = ClientPluginBuilder(key, scope, config).apply(body)
this.onClose = pluginBuilder.onClose
pluginBuilder.hooks.forEach { it.install(scope) }
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2014-2021 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
* Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/

package io.ktor.client.plugins.api
Expand Down Expand Up @@ -48,20 +48,26 @@ public fun <PluginConfigT : Any> createClientPlugin(
name: String,
createConfiguration: () -> PluginConfigT,
body: ClientPluginBuilder<PluginConfigT>.() -> Unit
): ClientPlugin<PluginConfigT> =
object : ClientPlugin<PluginConfigT> {
override val key: AttributeKey<ClientPluginInstance<PluginConfigT>> = AttributeKey(name)
): ClientPlugin<PluginConfigT> = ClientPluginImpl(name, createConfiguration, body)

override fun prepare(block: PluginConfigT.() -> Unit): ClientPluginInstance<PluginConfigT> {
val config = createConfiguration().apply(block)
return ClientPluginInstance(config, name, body)
}
private class ClientPluginImpl<PluginConfigT : Any>(
name: String,
private val createConfiguration: () -> PluginConfigT,
private val body: ClientPluginBuilder<PluginConfigT>.() -> Unit
) : ClientPlugin<PluginConfigT> {

override val key: AttributeKey<ClientPluginInstance<PluginConfigT>> = AttributeKey(name)

override fun prepare(block: PluginConfigT.() -> Unit): ClientPluginInstance<PluginConfigT> {
val config = createConfiguration().apply(block)
return ClientPluginInstance(key, config, body)
}

@OptIn(InternalAPI::class)
override fun install(plugin: ClientPluginInstance<PluginConfigT>, scope: HttpClient) {
plugin.install(scope)
}
@OptIn(InternalAPI::class)
override fun install(plugin: ClientPluginInstance<PluginConfigT>, scope: HttpClient) {
plugin.install(scope)
}
}

/**
* Creates a [ClientPlugin] with empty config that can be installed into an [HttpClient].
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2014-2021 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
* Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/

package io.ktor.server.application
Expand Down Expand Up @@ -31,27 +31,14 @@ import io.ktor.util.pipeline.*
* Note that it may be modified later when a user of your plugin calls [Application.install].
* @param body Allows you to define handlers ([onCall], [onCallReceive], [onCallRespond] and so on) that
* can modify the behaviour of an [Application] where your plugin is installed.
**/
*/
public fun <PluginConfigT : Any> createApplicationPlugin(
name: String,
configurationPath: String,
createConfiguration: (config: ApplicationConfig) -> PluginConfigT,
body: PluginBuilder<PluginConfigT>.() -> Unit
): ApplicationPlugin<PluginConfigT> = object : ApplicationPlugin<PluginConfigT> {
override val key: AttributeKey<PluginInstance> = AttributeKey(name)

override fun install(
pipeline: Application,
configure: PluginConfigT.() -> Unit
): PluginInstance {
val config = try {
pipeline.environment.config.config(configurationPath)
} catch (_: Throwable) {
MapApplicationConfig()
}
return createPluginInstance(pipeline, pipeline, body, { createConfiguration(config) }, configure)
}
}
): ApplicationPlugin<PluginConfigT> =
ApplicationPluginImpl(name, createConfiguration.withConfig(configurationPath), body)

/**
* Creates an [ApplicationPlugin] that can be installed into an [Application].
Expand All @@ -74,13 +61,19 @@ public fun <PluginConfigT : Any> createApplicationPlugin(
* Note that it may be modified later when a user of your plugin calls [Application.install].
* @param body Allows you to define handlers ([onCall], [onCallReceive], [onCallRespond] and so on) that
* can modify the behaviour of an [Application] where your plugin is installed.
*
**/
*/
public fun <PluginConfigT : Any> createApplicationPlugin(
name: String,
createConfiguration: () -> PluginConfigT,
body: PluginBuilder<PluginConfigT>.() -> Unit
): ApplicationPlugin<PluginConfigT> = object : ApplicationPlugin<PluginConfigT> {
): ApplicationPlugin<PluginConfigT> = ApplicationPluginImpl(name, { createConfiguration() }, body)

private class ApplicationPluginImpl<PluginConfigT : Any>(
name: String,
private val createConfiguration: ApplicationCallPipeline.() -> PluginConfigT,
private val body: PluginBuilder<PluginConfigT>.() -> Unit
) : ApplicationPlugin<PluginConfigT> {

override val key: AttributeKey<PluginInstance> = AttributeKey(name)

override fun install(
Expand Down Expand Up @@ -115,28 +108,12 @@ public fun <PluginConfigT : Any> createApplicationPlugin(
* note that it may be modified later when a user of your plugin calls [install].
* @param body Allows you to define handlers ([onCall], [onCallReceive], [onCallRespond] and so on) that
* can modify the behaviour of an [Application] where your plugin is installed.
**/
*/
public fun <PluginConfigT : Any> createRouteScopedPlugin(
name: String,
createConfiguration: () -> PluginConfigT,
body: RouteScopedPluginBuilder<PluginConfigT>.() -> Unit
): RouteScopedPlugin<PluginConfigT> = object : RouteScopedPlugin<PluginConfigT> {

override val key: AttributeKey<PluginInstance> = AttributeKey(name)

override fun install(
pipeline: ApplicationCallPipeline,
configure: PluginConfigT.() -> Unit
): PluginInstance {
val application = when (pipeline) {
is RoutingNode -> pipeline.application
is Application -> pipeline
else -> error("Unsupported pipeline type: ${pipeline::class}")
}

return createRouteScopedPluginInstance(application, pipeline, body, createConfiguration, configure)
}
}
): RouteScopedPlugin<PluginConfigT> = RouteScopedPluginImpl(name, { createConfiguration() }, body)

/**
* Creates a [RouteScopedPlugin] that can be installed into a [io.ktor.server.routing.RoutingNode].
Expand All @@ -163,35 +140,34 @@ public fun <PluginConfigT : Any> createRouteScopedPlugin(
* note that it may be modified later when a user of your plugin calls [install].
* @param body Allows you to define handlers ([onCall], [onCallReceive], [onCallRespond] and so on) that
* can modify the behaviour of an [Application] where your plugin is installed.
**/
*/
public fun <PluginConfigT : Any> createRouteScopedPlugin(
name: String,
configurationPath: String,
createConfiguration: (config: ApplicationConfig) -> PluginConfigT,
body: RouteScopedPluginBuilder<PluginConfigT>.() -> Unit
): RouteScopedPlugin<PluginConfigT> = object : RouteScopedPlugin<PluginConfigT> {
): RouteScopedPlugin<PluginConfigT> =
RouteScopedPluginImpl(name, createConfiguration.withConfig(configurationPath), body)

private class RouteScopedPluginImpl<PluginConfigT : Any>(
name: String,
private val createConfiguration: ApplicationCallPipeline.() -> PluginConfigT,
private val body: RouteScopedPluginBuilder<PluginConfigT>.() -> Unit
) : RouteScopedPlugin<PluginConfigT> {

override val key: AttributeKey<PluginInstance> = AttributeKey(name)

override fun install(
pipeline: ApplicationCallPipeline,
configure: PluginConfigT.() -> Unit
): PluginInstance {
val environment = pipeline.environment

val config = try {
environment.config.config(configurationPath)
} catch (_: Throwable) {
MapApplicationConfig()
}

val application = when (pipeline) {
is RoutingNode -> pipeline.application
is Application -> pipeline
else -> error("Unsupported pipeline type: ${pipeline::class}")
}

return createRouteScopedPluginInstance(application, pipeline, body, { createConfiguration(config) }, configure)
return createRouteScopedPluginInstance(application, pipeline, body, createConfiguration, configure)
}
}

Expand All @@ -214,7 +190,7 @@ public fun <PluginConfigT : Any> createRouteScopedPlugin(
* @param name A name of a plugin that is used to get an instance of the plugin installed to the [Application].
* @param body Allows you to define handlers ([onCall], [onCallReceive], [onCallRespond] and so on) that
* can modify the behaviour of an [Application] where your plugin is installed.
**/
*/
public fun createApplicationPlugin(
name: String,
body: PluginBuilder<Unit>.() -> Unit
Expand All @@ -241,7 +217,7 @@ public fun createApplicationPlugin(
* @param name A name of a plugin that is used to get an instance of the plugin installed to the [io.ktor.server.routing.RoutingNode].
* @param body Allows you to define handlers ([onCall], [onCallReceive], [onCallRespond] and so on) that
* can modify the behaviour of an [Application] where your plugin is installed.
**/
*/
public fun createRouteScopedPlugin(
name: String,
body: RouteScopedPluginBuilder<Unit>.() -> Unit
Expand All @@ -254,10 +230,10 @@ private fun <
application: Application,
pipeline: ApplicationCallPipeline,
body: PluginBuilder<PluginConfigT>.() -> Unit,
createConfiguration: () -> PluginConfigT,
createConfiguration: ApplicationCallPipeline.() -> PluginConfigT,
configure: PluginConfigT.() -> Unit
): PluginInstance {
val config = createConfiguration().apply(configure)
val config = pipeline.createConfiguration().apply(configure)

val currentPlugin = this
val pluginBuilder = object : PluginBuilder<PluginConfigT>(currentPlugin.key) {
Expand All @@ -277,10 +253,10 @@ private fun <
application: Application,
pipeline: ApplicationCallPipeline,
body: RouteScopedPluginBuilder<PluginConfigT>.() -> Unit,
createConfiguration: () -> PluginConfigT,
createConfiguration: ApplicationCallPipeline.() -> PluginConfigT,
configure: PluginConfigT.() -> Unit
): PluginInstance {
val config = createConfiguration().apply(configure)
val config = pipeline.createConfiguration().apply(configure)

val currentPlugin = this
val pluginBuilder = object : RouteScopedPluginBuilder<PluginConfigT>(currentPlugin.key) {
Expand Down Expand Up @@ -317,3 +293,15 @@ private fun <Configuration : Any, Builder : PluginBuilder<Configuration>> Builde

hooks.forEach { it.install(pipeline) }
}

private fun <T> ((ApplicationConfig) -> T).withConfig(path: String): ApplicationCallPipeline.() -> T {
val createConfiguration = this
return {
val config = try {
environment.config.config(path)
} catch (_: Throwable) {
MapApplicationConfig()
}
createConfiguration(config)
}
}
4 changes: 1 addition & 3 deletions ktor-utils/common/src/io/ktor/util/Attributes.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,7 @@ public data class AttributeKey<T : Any> @JvmOverloads constructor(
private val type: TypeInfo = typeInfo<Any>(),
) {
init {
if (name.isEmpty()) {
throw IllegalStateException("Name can't be blank")
}
require(name.isNotBlank()) { "Name can't be blank" }
}

override fun toString(): String = "AttributeKey: $name"
Expand Down

0 comments on commit 377d595

Please sign in to comment.