From 5c94820183b6c84524de53ab57fc735ee9bac619 Mon Sep 17 00:00:00 2001 From: Ronald Holshausen Date: Mon, 18 Nov 2024 17:22:55 +1100 Subject: [PATCH] chore(pact-jvm-server): Converted MockProvider to kotlin --- pact-jvm-server/build.gradle | 13 +- .../server/KTorHttpsKeystoreMockProvider.kt | 65 +++++++++ .../com/dius/pact/server/KTorMockProvider.kt | 129 ++++++++++++++++++ .../au/com/dius/pact/server/MockProvider.kt | 89 ++++++++++++ .../au/com/dius/pact/server/PactSession.kt | 4 + .../au/com/dius/pact/server/Complete.scala | 14 +- .../au/com/dius/pact/server/Create.scala | 23 ++-- .../com/dius/pact/server/MockProvider.scala | 81 ----------- .../com/dius/pact/server/RequestRouter.scala | 4 +- .../UnfilteredHttpsKeystoreMockProvider.scala | 35 ----- .../server/UnfilteredHttpsMockProvider.scala | 36 ----- .../pact/server/UnfilteredMockProvider.scala | 33 ----- .../au/com/dius/pact/server/MainSpec.groovy | 7 +- .../src/test/resources/logback-test.xml | 13 ++ 14 files changed, 339 insertions(+), 207 deletions(-) create mode 100644 pact-jvm-server/src/main/kotlin/au/com/dius/pact/server/KTorHttpsKeystoreMockProvider.kt create mode 100644 pact-jvm-server/src/main/kotlin/au/com/dius/pact/server/KTorMockProvider.kt create mode 100644 pact-jvm-server/src/main/kotlin/au/com/dius/pact/server/MockProvider.kt delete mode 100644 pact-jvm-server/src/main/scala/au/com/dius/pact/server/MockProvider.scala delete mode 100644 pact-jvm-server/src/main/scala/au/com/dius/pact/server/UnfilteredHttpsKeystoreMockProvider.scala delete mode 100644 pact-jvm-server/src/main/scala/au/com/dius/pact/server/UnfilteredHttpsMockProvider.scala delete mode 100644 pact-jvm-server/src/main/scala/au/com/dius/pact/server/UnfilteredMockProvider.scala create mode 100644 pact-jvm-server/src/test/resources/logback-test.xml diff --git a/pact-jvm-server/build.gradle b/pact-jvm-server/build.gradle index 2015cc75d3..3e4def236b 100644 --- a/pact-jvm-server/build.gradle +++ b/pact-jvm-server/build.gradle @@ -11,8 +11,8 @@ mainClassName = 'au.com.dius.pact.server.Server' dependencies { implementation project(':consumer') implementation project(':core:pactbroker') - implementation 'ch.qos.logback:logback-core:1.4.4' - implementation 'ch.qos.logback:logback-classic:1.4.4' + implementation 'ch.qos.logback:logback-core:1.5.6' + implementation 'ch.qos.logback:logback-classic:1.5.6' implementation 'com.github.scopt:scopt_2.12:3.5.0' implementation('com.typesafe.scala-logging:scala-logging_2.12:3.7.2') { exclude group: 'org.scala-lang' @@ -21,10 +21,17 @@ dependencies { exclude module: 'netty-transport-native-kqueue' exclude module: 'netty-transport-native-epoll' } + implementation 'io.github.oshai:kotlin-logging-jvm' implementation 'org.apache.commons:commons-io:1.3.2' implementation 'org.apache.commons:commons-lang3' implementation 'org.apache.commons:commons-text' implementation 'org.apache.tika:tika-core' + implementation('io.netty:netty-handler') { + exclude module: 'netty-transport-native-kqueue' + } + implementation 'io.ktor:ktor-server-netty' + implementation 'io.ktor:ktor-network-tls-certificates' + implementation 'io.ktor:ktor-server-call-logging' testImplementation 'org.apache.groovy:groovy' testImplementation 'org.apache.groovy:groovy-json' @@ -49,7 +56,7 @@ compileScala { } test { - dependsOn(':pact-jvm-server:assembleDist') + dependsOn(':pact-jvm-server:installDist') systemProperty('appExecutable', (new File(buildDir, 'install/pact-jvm-server/bin/pact-jvm-server')).path) } diff --git a/pact-jvm-server/src/main/kotlin/au/com/dius/pact/server/KTorHttpsKeystoreMockProvider.kt b/pact-jvm-server/src/main/kotlin/au/com/dius/pact/server/KTorHttpsKeystoreMockProvider.kt new file mode 100644 index 0000000000..0acfdc0315 --- /dev/null +++ b/pact-jvm-server/src/main/kotlin/au/com/dius/pact/server/KTorHttpsKeystoreMockProvider.kt @@ -0,0 +1,65 @@ +package au.com.dius.pact.server + +import au.com.dius.pact.consumer.model.MockHttpsProviderConfig +import au.com.dius.pact.core.model.ContentType +import au.com.dius.pact.core.model.OptionalBody +import au.com.dius.pact.core.model.Response +import io.github.oshai.kotlinlogging.KotlinLogging +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.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.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 + } + + module { + install(CallLogging) + intercept(ApplicationCallPipeline.Call) { + if (context.request.httpMethod == HttpMethod.Options && context.request.headers.contains("X-PACT-BOOTCHECK")) { + context.response.header("X-PACT-BOOTCHECK", "true") + context.respond(HttpStatusCode.OK) + } else { + try { + val request = toPactRequest(context) + val response = handleRequest(request) + pactResponseToKTorResponse(response, context) + } catch (e: Exception) { + logger.error(e) { "Failed to generate response" } + pactResponseToKTorResponse( + Response(500, mutableMapOf("Content-Type" to listOf("application/json")), + OptionalBody.body("{\"error\": ${e.message}}".toByteArray(), ContentType.JSON)), context) + } + } + } + } + } + + init { + server = embeddedServer(Netty, environment = env, configure = {}) + } +} diff --git a/pact-jvm-server/src/main/kotlin/au/com/dius/pact/server/KTorMockProvider.kt b/pact-jvm-server/src/main/kotlin/au/com/dius/pact/server/KTorMockProvider.kt new file mode 100644 index 0000000000..c853a18a83 --- /dev/null +++ b/pact-jvm-server/src/main/kotlin/au/com/dius/pact/server/KTorMockProvider.kt @@ -0,0 +1,129 @@ +package au.com.dius.pact.server + +import au.com.dius.pact.consumer.model.MockProviderConfig +import au.com.dius.pact.core.model.ContentType +import au.com.dius.pact.core.model.IResponse +import au.com.dius.pact.core.model.OptionalBody +import au.com.dius.pact.core.model.Request +import au.com.dius.pact.core.model.Response +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.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.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.Dispatchers +import kotlinx.coroutines.withContext +import java.util.zip.DeflaterInputStream +import java.util.zip.GZIPInputStream + +private val logger = KotlinLogging.logger {} + +abstract class BaseKTorMockProvider(override val config: MockProviderConfig): StatefulMockProvider() { + + lateinit var server: NettyApplicationEngine + + suspend fun toPactRequest(call: ApplicationCall): Request { + val request = call.request + val headers = request.headers + val bodyContents = withContext(Dispatchers.IO) { + val stream = call.receiveStream() + when (bodyIsCompressed(headers["Content-Encoding"])) { + "gzip" -> GZIPInputStream(stream).readBytes() + "deflate" -> DeflaterInputStream(stream).readBytes() + else -> stream.readBytes() + } + } + val body = if (bodyContents.isEmpty()) { + OptionalBody.empty() + } else { + OptionalBody.body(bodyContents, ContentType.fromString(headers["Content-Type"]).or(ContentType.JSON)) + } + return Request(request.httpMethod.value, request.path(), + request.queryParameters.entries().associate { it.toPair() }.toMutableMap(), + headers.entries().associate { it.toPair() }.toMutableMap(), body) + } + + private fun bodyIsCompressed(encoding: String?): String? { + return if (COMPRESSED_ENCODINGS.contains(encoding)) encoding else null + } + + suspend fun pactResponseToKTorResponse(response: IResponse, call: ApplicationCall) { + response.headers.forEach { entry -> + entry.value.forEach { + call.response.headers.append(entry.key, it, safeOnly = false) + } + } + + val body = response.body + if (body.isPresent()) { + call.respondBytes(status = HttpStatusCode.fromValue(response.status), bytes = body.unwrap()) + } else { + call.respond(HttpStatusCode.fromValue(response.status)) + } + } + + override fun start() { + logger.debug { "Starting mock server" } + server.start() + logger.debug { "Mock server started: ${server.environment.connectors}" } + } + + override fun stop() { + server.stop(100) + logger.debug { "Mock server shutdown" } + } + + companion object { + private val COMPRESSED_ENCODINGS = setOf("gzip", "deflate") + } +} + +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 + } + + module { + install(CallLogging) + intercept(ApplicationCallPipeline.Call) { + if (context.request.httpMethod == HttpMethod.Options && context.request.headers.contains("X-PACT-BOOTCHECK")) { + context.response.header("X-PACT-BOOTCHECK", "true") + context.respond(HttpStatusCode.OK) + } else { + try { + val request = toPactRequest(context) + val response = handleRequest(request) + pactResponseToKTorResponse(response, context) + } catch (e: Exception) { + logger.error(e) { "Failed to generate response" } + pactResponseToKTorResponse( + Response(500, mutableMapOf("Content-Type" to listOf("application/json")), + OptionalBody.body("{\"error\": ${e.message}}".toByteArray(), ContentType.JSON)), context) + } + } + } + } + } + + init { + server = embeddedServer(Netty, environment = env, configure = {}) + } +} diff --git a/pact-jvm-server/src/main/kotlin/au/com/dius/pact/server/MockProvider.kt b/pact-jvm-server/src/main/kotlin/au/com/dius/pact/server/MockProvider.kt new file mode 100644 index 0000000000..5ee33b2f68 --- /dev/null +++ b/pact-jvm-server/src/main/kotlin/au/com/dius/pact/server/MockProvider.kt @@ -0,0 +1,89 @@ +package au.com.dius.pact.server + +import au.com.dius.pact.consumer.model.MockHttpsKeystoreProviderConfig +import au.com.dius.pact.consumer.model.MockHttpsProviderConfig +import au.com.dius.pact.consumer.model.MockProviderConfig +import au.com.dius.pact.core.model.IResponse +import au.com.dius.pact.core.model.Pact +import au.com.dius.pact.core.model.PactSpecVersion +import au.com.dius.pact.core.model.Request +import au.com.dius.pact.core.support.handleWith +import au.com.dius.pact.core.support.Result +import io.github.oshai.kotlinlogging.KotlinLogging + +private val logger = KotlinLogging.logger {} + +interface MockProvider { + val config: MockProviderConfig + val session: PactSession + fun start(pact: Pact) + fun run(code: () -> T): Result + fun runAndClose(pact: Pact, code: () -> T): Result, Exception> + fun stop() +} + +object DefaultMockProvider { + + fun withDefaultConfig(pactVersion: PactSpecVersion = PactSpecVersion.V3) = + apply(MockProviderConfig.createDefault(pactVersion)) + + // Constructor providing a default implementation of StatefulMockProvider. + // Users should not explicitly be forced to choose a variety. + fun apply(config: MockProviderConfig): StatefulMockProvider = + when (config) { + is MockHttpsProviderConfig -> KTorHttpsKeystoreMockProvider(config) +// is MockHttpsProviderConfig -> UnfilteredHttpsMockProvider(config) + else -> KTorMockProvider(config) + } +} + +abstract class StatefulMockProvider: MockProvider { + private var sessionVar = PactSession.empty + private var pactVar: Pact? = null + + private fun waitForRequestsToFinish() = Thread.sleep(100) + + override val session: PactSession + get() = sessionVar + val pact: Pact? + get() = pactVar + + abstract fun start() + + @Synchronized + override fun start(pact: Pact) { + pactVar = pact + sessionVar = PactSession.forPact(pact) + start() + } + + override fun run(code: () -> T): Result { + return handleWith { + val codeResult = code() + waitForRequestsToFinish() + codeResult + } + } + + override fun runAndClose(pact: Pact, code: () -> T): Result, Exception> { + return handleWith { + try { + start(pact) + val codeResult = code() + waitForRequestsToFinish() + (codeResult to session.remainingResults()) + } finally { + stop() + } + } + } + + @Synchronized + fun handleRequest(req: Request): IResponse { + logger.debug { "Received request: $req" } + val (response, newSession) = session.receiveRequest(req) + logger.debug { "Generating response: $response" } + sessionVar = newSession + return response + } +} diff --git a/pact-jvm-server/src/main/kotlin/au/com/dius/pact/server/PactSession.kt b/pact-jvm-server/src/main/kotlin/au/com/dius/pact/server/PactSession.kt index d9e01cd4b2..b61256357c 100644 --- a/pact-jvm-server/src/main/kotlin/au/com/dius/pact/server/PactSession.kt +++ b/pact-jvm-server/src/main/kotlin/au/com/dius/pact/server/PactSession.kt @@ -10,8 +10,11 @@ import au.com.dius.pact.core.model.OptionalBody 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 io.github.oshai.kotlinlogging.KotlinLogging import org.apache.commons.text.StringEscapeUtils +private val logger = KotlinLogging.logger {} + data class PactSession( val expected: Pact?, val results: PactSessionResults @@ -32,6 +35,7 @@ data class PactSession( (invalidResponse to recordUnexpected(req)) } } else { + logger.warn { "Expected Pact is not set!" } invalidResponse to this } } diff --git a/pact-jvm-server/src/main/scala/au/com/dius/pact/server/Complete.scala b/pact-jvm-server/src/main/scala/au/com/dius/pact/server/Complete.scala index b240cd799f..95226c46d1 100644 --- a/pact-jvm-server/src/main/scala/au/com/dius/pact/server/Complete.scala +++ b/pact-jvm-server/src/main/scala/au/com/dius/pact/server/Complete.scala @@ -1,13 +1,13 @@ package au.com.dius.pact.server import java.io.File - import au.com.dius.pact.core.model._ +import com.typesafe.scalalogging.StrictLogging import scala.collection.JavaConverters._ import scala.util.Success -object Complete { +object Complete extends StrictLogging { def getPort(j: Any): Option[String] = j match { case map: Map[AnyRef, AnyRef] => { @@ -32,17 +32,17 @@ object Complete { val result = for { port <- getPort(JsonUtils.parseJsonString(request.getBody.valueAsString())) mockProvider <- oldState.get(port) - sessionResults = mockProvider.session.remainingResults - pact <- mockProvider.pact + sessionResults = mockProvider.getSession.remainingResults + pact <- Option(mockProvider.getPact) } yield { mockProvider.stop() - writeIfMatching(pact, sessionResults, mockProvider.config.getPactVersion) match { + writeIfMatching(pact, sessionResults, mockProvider.getConfig.getPactVersion) match { case PactVerified => pactWritten(new Response(200, ResponseUtils.CrossSiteHeaders.asJava), - mockProvider.config.getPort.toString) + mockProvider.getConfig.getPort.toString) case error => pactWritten(new Response(400, Map("Content-Type" -> List("application/json").asJava).asJava, toJson(error)), - mockProvider.config.getPort.toString) + mockProvider.getConfig.getPort.toString) } } diff --git a/pact-jvm-server/src/main/scala/au/com/dius/pact/server/Create.scala b/pact-jvm-server/src/main/scala/au/com/dius/pact/server/Create.scala index bd3aa40eee..ea0d4bf8f0 100644 --- a/pact-jvm-server/src/main/scala/au/com/dius/pact/server/Create.scala +++ b/pact-jvm-server/src/main/scala/au/com/dius/pact/server/Create.scala @@ -1,13 +1,13 @@ package au.com.dius.pact.server -import java.io.IOException +import java.io.{File, IOException} import java.net.ServerSocket - -import au.com.dius.pact.consumer.model.{MockHttpsKeystoreProviderConfig, MockProviderConfig} +import au.com.dius.pact.consumer.model.{MockHttpsProviderConfig, MockProviderConfig} import au.com.dius.pact.core.model._ import com.typesafe.scalalogging.StrictLogging import org.apache.commons.lang3.RandomUtils +import java.security.KeyStore import scala.collection.JavaConverters._ object Create extends StrictLogging { @@ -17,18 +17,25 @@ object Create extends StrictLogging { val mockConfig : MockProviderConfig = { if (config.getKeystorePath.nonEmpty) { - MockHttpsKeystoreProviderConfig - .httpsKeystoreConfig(config.getHost, config.getSslPort, config.getKeystorePath, config.getKeystorePassword, - PactSpecVersion.fromInt(config.getPactVersion)) + val ks = KeyStore.getInstance(new File(config.getKeystorePath), config.getKeystorePassword.toCharArray) + new MockHttpsProviderConfig( + config.getHost, + config.getSslPort, + PactSpecVersion.fromInt(config.getPactVersion), + ks, + "localhost", + config.getKeystorePassword, + config.getKeystorePassword + ) } else { new MockProviderConfig(config.getHost, randomPort(config.getPortLowerBound, config.getPortUpperBound), PactSpecVersion.fromInt(config.getPactVersion)) } } - val server = DefaultMockProvider.apply(mockConfig) + val server = DefaultMockProvider.INSTANCE.apply(mockConfig) - val port = server.config.getPort + val port = server.getConfig.getPort val portEntry = port.toString -> server // Not very scala... diff --git a/pact-jvm-server/src/main/scala/au/com/dius/pact/server/MockProvider.scala b/pact-jvm-server/src/main/scala/au/com/dius/pact/server/MockProvider.scala deleted file mode 100644 index 5d54e08368..0000000000 --- a/pact-jvm-server/src/main/scala/au/com/dius/pact/server/MockProvider.scala +++ /dev/null @@ -1,81 +0,0 @@ -package au.com.dius.pact.server - -import au.com.dius.pact.consumer.model.{MockHttpsKeystoreProviderConfig, MockHttpsProviderConfig, MockProviderConfig} -import au.com.dius.pact.core.model.{IResponse, PactSpecVersion, Request, Response, Pact => PactModel} -import com.typesafe.scalalogging.StrictLogging - -import scala.util.Try - -trait MockProvider { - def config: MockProviderConfig - def session: PactSession - def start(pact: PactModel): Unit - def run[T](code: => T): Try[T] - def runAndClose[T](pact: PactModel)(code: => T): Try[(T, PactSessionResults)] - def stop(): Unit -} - -object DefaultMockProvider { - - def withDefaultConfig(pactVersion: PactSpecVersion = PactSpecVersion.V3) = - apply(MockProviderConfig.createDefault(pactVersion)) - - // Constructor providing a default implementation of StatefulMockProvider. - // Users should not explicitly be forced to choose a variety. - def apply(config: MockProviderConfig): StatefulMockProvider = - config match { - case httpsConfig: MockHttpsProviderConfig => new UnfilteredHttpsMockProvider(httpsConfig) - case httpsKeystoreConfig: MockHttpsKeystoreProviderConfig => new UnfilteredHttpsKeystoreMockProvider(httpsKeystoreConfig) - case _ => new UnfilteredMockProvider(config) - } -} - -// TODO: eliminate horrid state mutation and synchronisation. Reactive stuff to the rescue? -abstract class StatefulMockProvider extends MockProvider with StrictLogging { - private var sessionVar = PactSession.getEmpty - private var pactVar: Option[PactModel] = None - - private def waitForRequestsToFinish() = Thread.sleep(100) - - def session: PactSession = sessionVar - def pact: Option[PactModel] = pactVar - - def start(): Unit - - override def start(pact: PactModel): Unit = synchronized { - pactVar = Some(pact) - sessionVar = PactSession.forPact(pact) - start() - } - - override def run[T](code: => T): Try[T] = { - Try { - val codeResult = code - waitForRequestsToFinish() - codeResult - } - } - - override def runAndClose[T](pact: PactModel)(code: => T): Try[(T, PactSessionResults)] = { - Try { - try { - start(pact) - val codeResult = code - waitForRequestsToFinish() - (codeResult, session.remainingResults) - } finally { - stop() - } - } - } - - final def handleRequest(req: Request): IResponse = synchronized { - logger.debug("Received request: " + req) - val result = session.receiveRequest(req) - val response = result.getFirst - val newSession = result.getSecond - logger.debug("Generating response: " + response) - sessionVar = newSession - response - } -} diff --git a/pact-jvm-server/src/main/scala/au/com/dius/pact/server/RequestRouter.scala b/pact-jvm-server/src/main/scala/au/com/dius/pact/server/RequestRouter.scala index 275031fb29..2132a74979 100644 --- a/pact-jvm-server/src/main/scala/au/com/dius/pact/server/RequestRouter.scala +++ b/pact-jvm-server/src/main/scala/au/com/dius/pact/server/RequestRouter.scala @@ -1,12 +1,12 @@ package au.com.dius.pact.server import java.util - import au.com.dius.pact.core.model.{Request, Response, _} +import com.typesafe.scalalogging.StrictLogging import scala.collection.JavaConverters._ -object RequestRouter { +object RequestRouter extends StrictLogging { def matchPath(request: Request, oldState: ServerState): Option[StatefulMockProvider] = (for { k <- oldState.keys if request.getPath.startsWith(k) diff --git a/pact-jvm-server/src/main/scala/au/com/dius/pact/server/UnfilteredHttpsKeystoreMockProvider.scala b/pact-jvm-server/src/main/scala/au/com/dius/pact/server/UnfilteredHttpsKeystoreMockProvider.scala deleted file mode 100644 index f7d1888241..0000000000 --- a/pact-jvm-server/src/main/scala/au/com/dius/pact/server/UnfilteredHttpsKeystoreMockProvider.scala +++ /dev/null @@ -1,35 +0,0 @@ -package au.com.dius.pact.server - -import _root_.unfiltered.netty.{SslEngineProvider, cycle => unettyc} -import _root_.unfiltered.{netty => unetty, request => ureq, response => uresp} -import au.com.dius.pact.consumer.model.MockHttpsKeystoreProviderConfig -import au.com.dius.pact.core.model.{IResponse, Request} -import io.netty.channel.ChannelHandler.Sharable -import io.netty.handler.codec.{http => netty} - -class UnfilteredHttpsKeystoreMockProvider(val config: MockHttpsKeystoreProviderConfig) extends StatefulMockProvider { - type UnfilteredRequest = ureq.HttpRequest[unetty.ReceivedMessage] - type UnfilteredResponse = uresp.ResponseFunction[netty.HttpResponse] - - //def sslEngine: SslEngineProvider = SslEngineProvider.pathSysProperties() - def sslEngine: SslEngineProvider = SslEngineProvider.path(config.getKeystore, config.getPassword) - private val server = unetty.Server.httpsEngine(config.getPort, config.getHostname, sslEngine).chunked(1048576).handler(Routes) - - @Sharable - object Routes extends unettyc.Plan - with unettyc.SynchronousExecution - with unetty.ServerErrorResponse { - - override def intent: unettyc.Plan.Intent = { - case req => convertResponse(handleRequest(convertRequest(req))) - } - - def convertRequest(nr: UnfilteredRequest): Request = Conversions.unfilteredRequestToPactRequest(nr) - - def convertResponse(response: IResponse): UnfilteredResponse = Conversions.pactToUnfilteredResponse(response) - } - - def start(): Unit = server.start() - - def stop(): Unit = server.stop() -} diff --git a/pact-jvm-server/src/main/scala/au/com/dius/pact/server/UnfilteredHttpsMockProvider.scala b/pact-jvm-server/src/main/scala/au/com/dius/pact/server/UnfilteredHttpsMockProvider.scala deleted file mode 100644 index f0483b78b5..0000000000 --- a/pact-jvm-server/src/main/scala/au/com/dius/pact/server/UnfilteredHttpsMockProvider.scala +++ /dev/null @@ -1,36 +0,0 @@ -package au.com.dius.pact.server - -import _root_.unfiltered.netty.{SslContextProvider, cycle => unettyc} -import _root_.unfiltered.{netty => unetty, request => ureq, response => uresp} -import au.com.dius.pact.consumer.model.MockHttpsProviderConfig -import au.com.dius.pact.core.model.{IResponse, Request} -import io.netty.channel.ChannelHandler.Sharable -import io.netty.handler.codec.{http => netty} -import io.netty.handler.ssl.util.SelfSignedCertificate - -class UnfilteredHttpsMockProvider(val config: MockHttpsProviderConfig) extends StatefulMockProvider { - type UnfilteredRequest = ureq.HttpRequest[unetty.ReceivedMessage] - type UnfilteredResponse = uresp.ResponseFunction[netty.HttpResponse] - - def sslContext: SslContextProvider = SslContextProvider.selfSigned(new SelfSignedCertificate()) - - private val server = unetty.Server.https(config.getPort, config.getHostname, sslContext).chunked(1048576).handler(Routes) - - @Sharable - object Routes extends unettyc.Plan - with unettyc.SynchronousExecution - with unetty.ServerErrorResponse { - - override def intent: unettyc.Plan.Intent = { - case req => convertResponse(handleRequest(convertRequest(req))) - } - - def convertRequest(nr: UnfilteredRequest): Request = Conversions.unfilteredRequestToPactRequest(nr) - - def convertResponse(response: IResponse): UnfilteredResponse = Conversions.pactToUnfilteredResponse(response) - } - - def start(): Unit = server.start() - - def stop(): Unit = server.stop() -} diff --git a/pact-jvm-server/src/main/scala/au/com/dius/pact/server/UnfilteredMockProvider.scala b/pact-jvm-server/src/main/scala/au/com/dius/pact/server/UnfilteredMockProvider.scala deleted file mode 100644 index bcc64f3e83..0000000000 --- a/pact-jvm-server/src/main/scala/au/com/dius/pact/server/UnfilteredMockProvider.scala +++ /dev/null @@ -1,33 +0,0 @@ -package au.com.dius.pact.server - -import _root_.unfiltered.netty.{cycle => unettyc} -import _root_.unfiltered.{netty => unetty, request => ureq, response => uresp} -import au.com.dius.pact.consumer.model.MockProviderConfig -import au.com.dius.pact.core.model.{IResponse, Request} -import io.netty.channel.ChannelHandler.Sharable -import io.netty.handler.codec.{http => netty} - -class UnfilteredMockProvider(val config: MockProviderConfig) extends StatefulMockProvider { - type UnfilteredRequest = ureq.HttpRequest[unetty.ReceivedMessage] - type UnfilteredResponse = uresp.ResponseFunction[netty.HttpResponse] - - private val server = unetty.Server.http(config.getPort, config.getHostname).chunked(1048576).handler(Routes) - - @Sharable - object Routes extends unettyc.Plan - with unettyc.SynchronousExecution - with unetty.ServerErrorResponse { - - override def intent: unettyc.Plan.Intent = { - case req => convertResponse(handleRequest(convertRequest(req))) - } - - def convertRequest(nr: UnfilteredRequest): Request = Conversions.unfilteredRequestToPactRequest(nr) - - def convertResponse(response: IResponse): UnfilteredResponse = Conversions.pactToUnfilteredResponse(response) - } - - def start(): Unit = server.start() - - def stop(): Unit = server.stop() -} diff --git a/pact-jvm-server/src/test/groovy/au/com/dius/pact/server/MainSpec.groovy b/pact-jvm-server/src/test/groovy/au/com/dius/pact/server/MainSpec.groovy index 889c533899..aeeded403c 100644 --- a/pact-jvm-server/src/test/groovy/au/com/dius/pact/server/MainSpec.groovy +++ b/pact-jvm-server/src/test/groovy/au/com/dius/pact/server/MainSpec.groovy @@ -60,7 +60,7 @@ class MainSpec extends Specification { def 'create mock server test'() { given: def pact = MainSpec.getResourceAsStream('/create-pact.json').text - def process = invokeApp('--daemon', '--debug', '31311') + def process = invokeApp(true, '--daemon', '--debug', '31311') when: process.waitFor(500, TimeUnit.MILLISECONDS) @@ -104,11 +104,14 @@ class MainSpec extends Specification { process.destroyForcibly() } - Process invokeApp(String... args) { + Process invokeApp(boolean inheritIO = false, String... args) { def exec = System.getProperty('appExecutable') List command = [exec] command.addAll(args) ProcessBuilder pb = new ProcessBuilder(command) + if (inheritIO) { + pb.inheritIO() + } pb.start() } diff --git a/pact-jvm-server/src/test/resources/logback-test.xml b/pact-jvm-server/src/test/resources/logback-test.xml new file mode 100644 index 0000000000..cbe958bf92 --- /dev/null +++ b/pact-jvm-server/src/test/resources/logback-test.xml @@ -0,0 +1,13 @@ + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + +