Skip to content

Commit

Permalink
chore: Upgrade KTor to 3.0.1
Browse files Browse the repository at this point in the history
  • Loading branch information
rholshausen committed Dec 2, 2024
1 parent 78d3735 commit b410304
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 94 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ dependencies {
implementation 'org.apache.tika:tika-core:2.9.1'
implementation 'com.google.guava:guava:31.1-jre'
implementation 'org.slf4j:slf4j-api:1.7.36'
implementation 'io.ktor:ktor-http-jvm:2.3.8'
implementation 'io.ktor:ktor-server-netty:2.3.8'
implementation 'io.ktor:ktor-network-tls-certificates:2.3.8'
implementation 'io.ktor:ktor-server-call-logging:2.3.8'
implementation 'io.ktor:ktor-http-jvm:3.0.1'
implementation 'io.ktor:ktor-server-netty:3.0.1'
implementation 'io.ktor:ktor-network-tls-certificates:3.0.1'
implementation 'io.ktor:ktor-server-call-logging:3.0.1'
implementation 'io.netty:netty-handler:4.1.108.Final'
implementation 'org.apache.groovy:groovy:4.0.23'
implementation 'org.apache.groovy:groovy-json:4.0.23'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,55 +10,43 @@ import au.com.dius.pact.core.model.Pact
import au.com.dius.pact.core.model.Request
import au.com.dius.pact.core.model.Response
import au.com.dius.pact.core.support.Result
import io.github.oshai.kotlinlogging.KotlinLogging
import io.ktor.http.HttpMethod
import io.ktor.http.HttpStatusCode
import io.ktor.server.application.ApplicationCall
import io.ktor.server.application.ApplicationCallPipeline
import io.ktor.server.application.install
import io.ktor.server.engine.applicationEngineEnvironment
import io.ktor.server.application.serverConfig
import io.ktor.server.engine.EngineConnectorConfig
import io.ktor.server.engine.connector
import io.ktor.server.engine.embeddedServer
import io.ktor.server.engine.sslConnector
import io.ktor.server.netty.Netty
import io.ktor.server.netty.NettyApplicationEngine
import io.ktor.server.plugins.callloging.CallLogging
import io.ktor.server.plugins.calllogging.CallLogging
import io.ktor.server.request.httpMethod
import io.ktor.server.request.path
import io.ktor.server.request.receiveStream
import io.ktor.server.response.header
import io.ktor.server.response.respond
import io.ktor.server.response.respondBytes
import io.ktor.util.network.hostname
import io.ktor.util.network.port
import io.netty.channel.Channel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import io.github.oshai.kotlinlogging.KLogging
import java.net.SocketAddress
import java.net.URI
import java.util.zip.DeflaterInputStream
import java.util.zip.GZIPInputStream

private val logger = KotlinLogging.logger {}

class KTorMockServer @JvmOverloads constructor(
pact: BasePact,
config: MockProviderConfig,
private val stopTimeout: Long = 20000
) : BaseMockServer(pact, config) {
private var localAddress: EngineConnectorConfig? = null

private val env = applicationEngineEnvironment {
if (config is MockHttpsProviderConfig) {
sslConnector(keyStore = config.keyStore!!, keyAlias = config.keyStoreAlias,
keyStorePassword = { config.keystorePassword.toCharArray() },
privateKeyPassword = { config.privateKeyPassword.toCharArray() }) {
host = config.hostname
port = config.port
}
} else {
connector {
host = config.hostname
port = config.port
}
}

private val serverProperties = serverConfig {
module {
install(CallLogging)
intercept(ApplicationCallPipeline.Call) {
Expand All @@ -81,8 +69,21 @@ class KTorMockServer @JvmOverloads constructor(
}
}
}

private var server: NettyApplicationEngine = embeddedServer(Netty, environment = env, configure = {})
private var server = embeddedServer(Netty, serverProperties) {
if (config is MockHttpsProviderConfig) {
sslConnector(keyStore = config.keyStore!!, keyAlias = config.keyStoreAlias,
keyStorePassword = { config.keystorePassword.toCharArray() },
privateKeyPassword = { config.privateKeyPassword.toCharArray() }) {
host = config.hostname
port = config.port
}
} else {
connector {
host = config.hostname
port = config.port
}
}
}

private suspend fun pactResponseToKTorResponse(response: IResponse, call: ApplicationCall) {
response.headers.forEach { entry ->
Expand Down Expand Up @@ -121,30 +122,29 @@ class KTorMockServer @JvmOverloads constructor(
}

override fun getUrl(): String {
val address = socketAddress()
return if (address != null) {
val connectorConfig = server.engine.configuration.connectors.first()
return if (localAddress != null) {
// Stupid GitHub Windows agents
val host = if (address.hostname.lowercase() == "miningmadness.com") {
config.hostname
val host = if (localAddress!!.host.lowercase() == "miningmadness.com") {
connectorConfig.host
} else {
address.hostname
localAddress!!.host
}
"${config.scheme}://$host:${address.port}"
URI(config.scheme, null, host, localAddress!!.port, null, null, null).toString()
} else {
val connectorConfig = server.environment.connectors.first()
"${config.scheme}://${connectorConfig.host}:${connectorConfig.port}"
URI(config.scheme, null, connectorConfig.host, connectorConfig.port, null, null, null).toString()
}
}

private fun socketAddress(): SocketAddress? {
val field = server.javaClass.getDeclaredField("channels")
field.isAccessible = true
val channels = field.get(server) as List<Channel>?
return channels?.first()?.localAddress()
override fun getPort(): Int {
return if (localAddress != null) {
localAddress!!.port
} else {
val connectorConfig = server.engine.configuration.connectors.first()
connectorConfig.port
}
}

override fun getPort() = socketAddress()?.port ?: server.environment.connectors.first().port

override fun updatePact(pact: Pact): Pact {
return if (pact.isV4Pact()) {
when (val p = pact.asV4Pact()) {
Expand All @@ -163,14 +163,17 @@ class KTorMockServer @JvmOverloads constructor(

override fun start() {
logger.debug { "Starting mock server" }

CoroutineScope(server.application.coroutineContext).launch {
localAddress = server.engine.resolvedConnectors().first()
}

server.start()
logger.debug { "Mock server started: ${server.environment.connectors}" }
logger.debug { "Mock server started: $localAddress" }
}

override fun stop() {
server.stop(100, stopTimeout)
logger.debug { "Mock server shutdown" }
}

companion object : KLogging()
}
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ class MockHttpServerSpec extends Specification {

then:
mockServer.url ==~ /http:\/\/[a-z0-9\-]+\:\d+/
mockServer.port > 0

cleanup:
mockServer.stop()
Expand All @@ -113,11 +114,36 @@ class MockHttpServerSpec extends Specification {
MockHttpsServer | '::1' | 1238
MockHttpsServer | 'ip6-localhost' | 0
MockHttpsServer | 'ip6-localhost' | 1239
// KTorMockServer | '[::1]' | 0 // KTor server does not do reverse lookups of the bound host
// KTorMockServer | '[::1]' | 2234
// KTorMockServer | '::1' | 0
// KTorMockServer | '::1' | 2235
KTorMockServer | 'ip6-localhost' | 0
KTorMockServer | 'ip6-localhost' | 2236
}

def 'KTor IP6 test'() {
given:
def pact = new RequestResponsePact(new Provider(), new Consumer(), [])
def config = new MockProviderConfig(hostname, port)

when:
def mockServer = mockServerClass.newInstance(pact, config)
mockServer.start()

then:
mockServer.url ==~ /http:\/\/\[::1]:\d+/
mockServer.port > 0

cleanup:
mockServer.stop()

where:

mockServerClass | hostname | port
KTorMockServer | '[::1]' | 0
KTorMockServer | '[::1]' | 2234
KTorMockServer | '::1' | 0
KTorMockServer | '::1' | 2235
KTorMockServer | 'ip6-localhost' | 0
KTorMockServer | 'ip6-localhost' | 2236
}
}
2 changes: 1 addition & 1 deletion pact-jvm-server/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ dependencies {
implementation 'org.apache.commons:commons-text'
implementation 'org.apache.tika:tika-core'
implementation('io.netty:netty-handler') {
exclude module: 'netty-transport-native-kqueue'
// exclude module: 'netty-transport-native-kqueue'
}
implementation 'io.ktor:ktor-server-netty'
implementation 'io.ktor:ktor-network-tls-certificates'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,34 +9,19 @@ import io.ktor.http.HttpMethod
import io.ktor.http.HttpStatusCode
import io.ktor.server.application.ApplicationCallPipeline
import io.ktor.server.application.install
import io.ktor.server.engine.applicationEngineEnvironment
import io.ktor.server.application.serverConfig
import io.ktor.server.engine.embeddedServer
import io.ktor.server.engine.sslConnector
import io.ktor.server.netty.Netty
import io.ktor.server.plugins.callloging.CallLogging
import io.ktor.server.plugins.calllogging.CallLogging
import io.ktor.server.request.httpMethod
import io.ktor.server.response.header
import io.ktor.server.response.respond

private val logger = KotlinLogging.logger {}

class KTorHttpsKeystoreMockProvider(override val config: MockHttpsProviderConfig): BaseKTorMockProvider(config) {
private val serverHostname = config.hostname
private val serverPort = config.port
private val keyStore = config.keyStore!!
private val keyStoreAlias = config.keyStoreAlias
private val password = config.keystorePassword
private val privateKeyPassword = config.privateKeyPassword

private val env = applicationEngineEnvironment {
sslConnector(keyStore = keyStore,
keyAlias = keyStoreAlias,
keyStorePassword = { password.toCharArray() },
privateKeyPassword = { privateKeyPassword.toCharArray() }) {
host = serverHostname
port = serverPort
}

private val serverProperties = serverConfig {
module {
install(CallLogging)
intercept(ApplicationCallPipeline.Call) {
Expand All @@ -59,7 +44,13 @@ class KTorHttpsKeystoreMockProvider(override val config: MockHttpsProviderConfig
}
}

init {
server = embeddedServer(Netty, environment = env, configure = {})
override var server = embeddedServer(Netty, serverProperties) {
sslConnector(keyStore = config.keyStore!!,
keyAlias = config.keyStoreAlias,
keyStorePassword = { config.keystorePassword.toCharArray() },
privateKeyPassword = { config.privateKeyPassword.toCharArray() }) {
host = config.hostname
port = config.port
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,23 @@ import io.ktor.http.HttpStatusCode
import io.ktor.server.application.ApplicationCall
import io.ktor.server.application.ApplicationCallPipeline
import io.ktor.server.application.install
import io.ktor.server.engine.applicationEngineEnvironment
import io.ktor.server.application.serverConfig
import io.ktor.server.engine.EmbeddedServer
import io.ktor.server.engine.EngineConnectorConfig
import io.ktor.server.engine.connector
import io.ktor.server.engine.embeddedServer
import io.ktor.server.netty.Netty
import io.ktor.server.netty.NettyApplicationEngine
import io.ktor.server.plugins.callloging.CallLogging
import io.ktor.server.plugins.calllogging.CallLogging
import io.ktor.server.request.httpMethod
import io.ktor.server.request.path
import io.ktor.server.request.receiveStream
import io.ktor.server.response.header
import io.ktor.server.response.respond
import io.ktor.server.response.respondBytes
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.zip.DeflaterInputStream
import java.util.zip.GZIPInputStream
Expand All @@ -33,7 +37,8 @@ private val logger = KotlinLogging.logger {}

abstract class BaseKTorMockProvider(override val config: MockProviderConfig): StatefulMockProvider() {

lateinit var server: NettyApplicationEngine
open lateinit var server: EmbeddedServer<NettyApplicationEngine, NettyApplicationEngine.Configuration>
private lateinit var localAddress: EngineConnectorConfig

suspend fun toPactRequest(call: ApplicationCall): Request {
val request = call.request
Expand Down Expand Up @@ -77,8 +82,13 @@ abstract class BaseKTorMockProvider(override val config: MockProviderConfig): St

override fun start() {
logger.debug { "Starting mock server" }

CoroutineScope(server.application.coroutineContext).launch {
localAddress = server.engine.resolvedConnectors().first()
}

server.start()
logger.debug { "Mock server started: ${server.environment.connectors}" }
logger.debug { "Mock server started: $localAddress" }
}

override fun stop() {
Expand All @@ -92,15 +102,7 @@ abstract class BaseKTorMockProvider(override val config: MockProviderConfig): St
}

class KTorMockProvider(override val config: MockProviderConfig): BaseKTorMockProvider(config) {
private val serverHostname = config.hostname
private val serverPort = config.port

private val env = applicationEngineEnvironment {
connector {
host = serverHostname
port = serverPort
}

private val serverProperties = serverConfig {
module {
install(CallLogging)
intercept(ApplicationCallPipeline.Call) {
Expand All @@ -123,7 +125,10 @@ class KTorMockProvider(override val config: MockProviderConfig): BaseKTorMockPro
}
}

init {
server = embeddedServer(Netty, environment = env, configure = {})
override var server = embeddedServer(Netty, serverProperties) {
connector {
host = config.hostname
port = config.port
}
}
}
Loading

0 comments on commit b410304

Please sign in to comment.