laviua/komock

View on GitHub
komock-core/src/main/kotlin/ua/com/lavi/komock/http/server/AbstractMockServer.kt

Summary

Maintainability
A
3 hrs
Test Coverage
package ua.com.lavi.komock.http.server

import org.eclipse.jetty.server.HttpConfiguration
import org.eclipse.jetty.server.Server
import org.eclipse.jetty.server.ServerConnector
import org.eclipse.jetty.server.handler.ContextHandler
import org.eclipse.jetty.server.handler.HandlerList
import org.eclipse.jetty.util.thread.QueuedThreadPool
import org.slf4j.LoggerFactory
import org.slf4j.MDC
import ua.com.lavi.komock.http.handler.after.AfterResponseHandler
import ua.com.lavi.komock.http.handler.after.EmptyAfterResponseHandlerImpl
import ua.com.lavi.komock.http.handler.after.LogAfterResponseHandlerImpl
import ua.com.lavi.komock.http.handler.before.BeforeResponseHandler
import ua.com.lavi.komock.http.handler.before.EmptyBeforeResponseHandlerImpl
import ua.com.lavi.komock.http.handler.before.LogBeforeResponseHandlerImpl
import ua.com.lavi.komock.http.handler.callback.CallbackHandler
import ua.com.lavi.komock.http.handler.callback.CallbackHandlerImpl
import ua.com.lavi.komock.http.handler.callback.EmptyCallbackHandlerImpl
import ua.com.lavi.komock.http.handler.response.ResponseHandler
import ua.com.lavi.komock.http.handler.response.RoutedResponseHandlerImpl
import ua.com.lavi.komock.model.HttpMethod
import ua.com.lavi.komock.model.config.http.CapturedData
import ua.com.lavi.komock.model.config.http.HttpServerProperties
import ua.com.lavi.komock.http.server.handler.CaptureHttpHandler
import ua.com.lavi.komock.model.config.http.RouteProperties
import ua.com.lavi.komock.http.server.handler.HttpHandler
import ua.com.lavi.komock.http.server.handler.RoutingTable
import java.util.concurrent.TimeUnit

/**
 * Created by Oleksandr Loushkin
 */

abstract class AbstractMockServer(val serverProps: HttpServerProperties) : MockServer {

    private val log = LoggerFactory.getLogger(this.javaClass)
    private var isStarted: Boolean = false
    private val routingTable: RoutingTable = RoutingTable()

    var jettyServer: Server = Server(NamedQueuedThreadPool(
            serverProps.maxThreads,
            serverProps.minThreads,
            serverProps.idleTimeout,
            serverProps.name)
    )

    init {
        val contextHandler = ContextHandler(serverProps.contextPath)
        contextHandler.virtualHosts = serverProps.virtualHosts.toTypedArray()
        if (serverProps.capture.enabled) {
            contextHandler.handler = CaptureHttpHandler(serverProps.capture, routingTable)
        } else {
            contextHandler.handler = HttpHandler(routingTable)
        }
        val handlerList = HandlerList()
        handlerList.handlers = arrayOf(contextHandler)
        jettyServer.handler = handlerList

        val serverConnector = buildServerConnector()
        serverConnector.idleTimeout = TimeUnit.HOURS.toMillis(1) // 3600000
        serverConnector.soLingerTime = -1 // linger time disabled
        serverConnector.host = serverProps.host
        serverConnector.port = serverProps.port
        jettyServer.connectors = arrayOf(serverConnector)

        //register only enabled routes
        serverProps.routes.filter { it.enabled }.forEach { addRoute(it) }
    }

    override fun start() {
        if (!isStarted) {
            log.info("Starting server: ${serverProps.name}")
            jettyServer.start()
            log.info("Started server: ${serverProps.name} on port: ${serverProps.port}. " +
                    "VirtualHosts: ${serverProps.virtualHosts}. " +
                    "MaxThreads: ${serverProps.maxThreads}. " +
                    "MinThreads: ${serverProps.minThreads}. " +
                    "Idle timeout: ${serverProps.idleTimeout} ms")
            MDC.put("serverName", serverProps.name)
            isStarted = true
        } else {
            log.info("Unable to start because server is already started!")
        }
    }

    override fun stop() {
        if (isStarted) {
            log.info("Stopping server: ${serverProps.name}")
            jettyServer.stop()
            log.info("Stopped server: ${serverProps.name}")
            isStarted = false
        } else {
            log.info("Unable to stop because server was not started!")
        }
    }

    override fun addVirtualHosts(virtualHosts: List<String>) {
        getContextHandler().addVirtualHosts(virtualHosts.toTypedArray())
        log.info("Added virtual hosts: $virtualHosts")
    }

    override fun deleteVirtualHosts(virtualHosts: List<String>) {
        getContextHandler().removeVirtualHosts(virtualHosts.toTypedArray())
        log.info("Removed virtual hosts: $virtualHosts")
    }

    override fun addRoute(routeProperties: RouteProperties) {

        val beforeRouteHandler = if (routeProperties.logRequest) {
            LogBeforeResponseHandlerImpl(routeProperties)
        } else {
            EmptyBeforeResponseHandlerImpl()
        }
        val afterRouteHandler = if (routeProperties.logResponse) {
            LogAfterResponseHandlerImpl(routeProperties)
        } else {
            EmptyAfterResponseHandlerImpl()
        }

        val responseHandler = RoutedResponseHandlerImpl(routeProperties)
        val callbackHandler = CallbackHandlerImpl(routeProperties.callback)

        val httpMethod = HttpMethod.retrieveMethod(routeProperties.httpMethod)
        val url = routeProperties.url

        addRoute(url, httpMethod, responseHandler, beforeRouteHandler, afterRouteHandler, callbackHandler)
    }

    override fun addRoute(url: String,
                          httpMethod: HttpMethod,
                          responseHandler: ResponseHandler) {

        addRoute(url,
                httpMethod,
                responseHandler,
                EmptyBeforeResponseHandlerImpl(),
                EmptyAfterResponseHandlerImpl(),
                EmptyCallbackHandlerImpl())
    }

    override fun addRoute(url: String,
                          httpMethod: HttpMethod,
                          responseHandler: ResponseHandler,
                          callbackHandler: CallbackHandler) {

        addRoute(url,
                httpMethod,
                responseHandler,
                EmptyBeforeResponseHandlerImpl(),
                EmptyAfterResponseHandlerImpl(),
                callbackHandler)
    }

    override fun addRoute(url: String,
                          httpMethod: HttpMethod,
                          responseHandler: ResponseHandler,
                          beforeRouteHandler: BeforeResponseHandler,
                          afterRouteHandler: AfterResponseHandler,
                          callbackHandler: CallbackHandler) {

        routingTable.addRoute(url, httpMethod, responseHandler, beforeRouteHandler, afterRouteHandler, callbackHandler)

        log.info("Registered http route: $httpMethod $url")
    }

    override fun deleteRoute(url: String, httpMethod: HttpMethod) {
        routingTable.deleteRoute(url, httpMethod)
        log.info("Removed route: $httpMethod $url")
    }

    override fun deleteRoute(routeProperties: RouteProperties) {
        val url = routeProperties.url
        val httpMethod = HttpMethod.retrieveMethod(routeProperties.httpMethod)
        deleteRoute(url, httpMethod)
    }

    abstract fun buildServerConnector(): ServerConnector

    override fun getCapturedData(): List<CapturedData> {
        return if (serverProps.capture.enabled) {
            val captureHttpHandler = getContextHandler().handler as CaptureHttpHandler
            return captureHttpHandler.getCapturedData()
        } else {
            emptyList()
        }
    }

    override fun getName(): String {
        return serverProps.name
    }

    private fun getContextHandler(): ContextHandler {
        return (jettyServer.handler as HandlerList).handlers[0] as ContextHandler
    }

    open fun httpConfig(): HttpConfiguration {
        val httpConfig = HttpConfiguration()
        httpConfig.sendServerVersion = false // do not show jetty version
        return httpConfig
    }

    private inner class NamedQueuedThreadPool(maxThreads: Int, minThreads: Int, idleTimeout: Int, threadName: String) :
            QueuedThreadPool(maxThreads, minThreads, idleTimeout) {

        init {
            name = threadName
        }
    }
}