JettyServletHttpServer.kt

  1. package com.hexagontk.http.server.jetty

  2. import com.hexagontk.core.fieldsMapOf
  3. import com.hexagontk.http.HttpFeature
  4. import com.hexagontk.http.HttpFeature.*
  5. import com.hexagontk.http.model.HttpProtocol
  6. import com.hexagontk.http.model.HttpProtocol.*
  7. import com.hexagontk.http.server.HttpServer
  8. import com.hexagontk.http.server.HttpServerPort
  9. import com.hexagontk.http.server.HttpServerSettings
  10. import com.hexagontk.http.server.servlet.ServletFilter
  11. import jakarta.servlet.DispatcherType
  12. import jakarta.servlet.MultipartConfigElement
  13. import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory
  14. import org.eclipse.jetty.ee10.servlet.DefaultServlet
  15. import org.eclipse.jetty.ee10.servlet.ServletContextHandler
  16. import org.eclipse.jetty.ee10.servlet.ServletContextHandler.NO_SESSIONS
  17. import org.eclipse.jetty.ee10.servlet.ServletHolder
  18. import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory
  19. import org.eclipse.jetty.server.*
  20. import org.eclipse.jetty.server.handler.gzip.GzipHandler
  21. import org.eclipse.jetty.util.VirtualThreads.getDefaultVirtualThreadsExecutor
  22. import org.eclipse.jetty.util.ssl.SslContextFactory
  23. import org.eclipse.jetty.util.thread.ExecutorThreadPool
  24. import org.eclipse.jetty.util.thread.ThreadPool
  25. import java.security.KeyStore
  26. import java.util.*
  27. import org.eclipse.jetty.server.Server as JettyServer

  28. /**
  29.  * Implements [HttpServerPort] using [JettyServer].
  30.  */
  31. class JettyServletHttpServer(
  32.     private val maxThreads: Int = 200,
  33.     private val minThreads: Int = 8,

  34.     private val acceptors: Int = -1,
  35.     private val selectors: Int = -1,

  36.     private val sendDateHeader: Boolean = false,
  37.     private val sendServerVersion: Boolean = false,
  38.     private val sendXPoweredBy: Boolean = false,
  39. ) : HttpServerPort {

  40.     private var jettyServer: JettyServer? = null

  41.     constructor() : this(
  42.         maxThreads = 200,
  43.         minThreads = 8,

  44.         acceptors = -1,
  45.         selectors = -1,

  46.         sendDateHeader = false,
  47.         sendServerVersion = false,
  48.         sendXPoweredBy = false,
  49.     )

  50.     override fun runtimePort(): Int =
  51.         jettyConnector().localPort

  52.     override fun started() =
  53.         jettyServer?.isStarted ?: false

  54.     private fun jettyConnector(): ServerConnector =
  55.         jettyServer?.connectors?.get(0) as? ServerConnector ?: error("Jetty must have a connector")

  56.     private fun createThreadPool(): ThreadPool {
  57.         val virtualThreadPool = getDefaultVirtualThreadsExecutor()
  58.         val threadPool = ExecutorThreadPool(maxThreads, minThreads)
  59.         threadPool.virtualThreadsExecutor = virtualThreadPool
  60.         return threadPool
  61.     }

  62.     override fun startUp(server: HttpServer) {
  63.         val settings = server.settings
  64.         val serverInstance = JettyServer(createThreadPool())
  65.         jettyServer = serverInstance

  66.         val context = createServerContext(settings)
  67.         val filter = ServletFilter(server.handler)
  68.         val filterBind = context.servletContext.addFilter("filters", filter)
  69.         val dispatcherTypes = EnumSet.allOf(DispatcherType::class.java)
  70.         filterBind.addMappingForUrlPatterns(dispatcherTypes, true, "/*")

  71.         val servletHolder = ServletHolder("default", DefaultServlet())
  72.         servletHolder.registration.setMultipartConfig(MultipartConfigElement("/tmp"))
  73.         context.addServlet(servletHolder, "/*")

  74.         val serverConnector =
  75.             if (settings.sslSettings != null) setupSsl(settings, serverInstance)
  76.             else ServerConnector(serverInstance, acceptors, selectors)

  77.         serverConnector.host = settings.bindAddress.hostName
  78.         serverConnector.port = settings.bindPort
  79.         serverConnector.connectionFactories
  80.             .filterIsInstance<HttpConnectionFactory>()
  81.             .map { it.httpConfiguration }
  82.             .map {
  83.                 it.sendDateHeader = sendDateHeader
  84.                 it.sendServerVersion = sendServerVersion
  85.                 it.sendXPoweredBy = sendXPoweredBy
  86.             }

  87.         serverInstance.connectors = arrayOf(serverConnector)
  88.         serverInstance.handler = context
  89.         serverInstance.stopAtShutdown = true
  90.         serverInstance.start()
  91.     }

  92.     override fun shutDown() {
  93.         jettyServer?.stop()
  94.     }

  95.     override fun supportedProtocols(): Set<HttpProtocol> =
  96.         setOf(HTTP, HTTPS, HTTP2)

  97.     override fun supportedFeatures(): Set<HttpFeature> =
  98.         setOf(ZIP, COOKIES, MULTIPART)

  99.     override fun options(): Map<String, *> =
  100.         fieldsMapOf(
  101.             JettyServletHttpServer::maxThreads to maxThreads,
  102.             JettyServletHttpServer::minThreads to minThreads,
  103.             JettyServletHttpServer::acceptors to acceptors,
  104.             JettyServletHttpServer::selectors to selectors,
  105.             JettyServletHttpServer::sendDateHeader to sendDateHeader,
  106.             JettyServletHttpServer::sendServerVersion to sendServerVersion,
  107.             JettyServletHttpServer::sendXPoweredBy to sendXPoweredBy,
  108.         )

  109.     private fun createServerContext(settings: HttpServerSettings): ServletContextHandler {

  110.         val context = ServletContextHandler(NO_SESSIONS)

  111.         if (settings.zip)
  112.             context.insertHandler(GzipHandler())

  113.         return context
  114.     }

  115.     private fun setupSsl(
  116.         settings: HttpServerSettings, serverInstance: JettyServer): ServerConnector {

  117.         val httpConfiguration = HttpConfiguration()
  118.         httpConfiguration.secureScheme = "https"
  119.         httpConfiguration.securePort = settings.bindPort
  120.         httpConfiguration.addCustomizer(SecureRequestCustomizer())

  121.         val sslContextFactory = SslContextFactory.Server()
  122.         val sslSettings = settings.sslSettings ?: error("SSL settings cannot be 'null'")
  123.         sslContextFactory.needClientAuth = sslSettings.clientAuth

  124.         val keyStore = sslSettings.keyStore
  125.         if (keyStore != null) {
  126.             val keyStorePassword = sslSettings.keyStorePassword
  127.             val keyStoreStream = keyStore.openStream()
  128.             sslContextFactory.keyStore = KeyStore.getInstance("pkcs12")
  129.             sslContextFactory.keyStore.load(keyStoreStream, keyStorePassword.toCharArray())
  130.             sslContextFactory.keyStorePassword = keyStorePassword
  131.         }

  132.         val trustStore = sslSettings.trustStore
  133.         if (trustStore != null) {
  134.             val trustStorePassword = sslSettings.trustStorePassword
  135.             val trustStoreStream = trustStore.openStream()
  136.             sslContextFactory.trustStore = KeyStore.getInstance("pkcs12")
  137.             sslContextFactory.trustStore.load(trustStoreStream, trustStorePassword.toCharArray())
  138.             sslContextFactory.setTrustStorePassword(trustStorePassword)
  139.         }

  140.         return if (settings.protocol != HTTP2)
  141.             ServerConnector(
  142.                 serverInstance,
  143.                 SslConnectionFactory(sslContextFactory, "http/1.1"),
  144.                 HttpConnectionFactory(httpConfiguration)
  145.             )
  146.         else {
  147.             val alpn = ALPNServerConnectionFactory()
  148.             ServerConnector(
  149.                 serverInstance,
  150.                 SslConnectionFactory(sslContextFactory, alpn.protocol),
  151.                 alpn,
  152.                 HTTP2ServerConnectionFactory(httpConfiguration),
  153.                 HttpConnectionFactory(httpConfiguration)
  154.             )
  155.         }
  156.     }
  157. }