All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.http4k.server.SunHttp.kt Maven / Gradle / Ivy

package org.http4k.server

import com.sun.net.httpserver.HttpExchange
import com.sun.net.httpserver.HttpServer
import org.http4k.core.HttpHandler
import org.http4k.core.Method
import org.http4k.core.Request
import org.http4k.core.RequestSource
import org.http4k.core.Response
import org.http4k.core.Status.Companion.NO_CONTENT
import org.http4k.core.Uri
import org.http4k.core.safeLong
import org.http4k.server.ServerConfig.StopMode
import java.net.InetSocketAddress
import java.util.concurrent.Executors.newWorkStealingPool
import java.util.concurrent.TimeUnit
import com.sun.net.httpserver.HttpHandler as SunHttpHandler

class HttpExchangeHandler(private val handler: HttpHandler) : SunHttpHandler {
    private fun HttpExchange.populate(httpResponse: Response) {
        httpResponse.headers.forEach { (key, value) -> responseHeaders.add(key, value ?: "") }
        if (requestMethod == "HEAD" || httpResponse.status == NO_CONTENT) {
            sendResponseHeaders(httpResponse.status.code, -1)
        } else {
            sendResponseHeaders(httpResponse.status.code, httpResponse.body.length ?: 0)
            httpResponse.body.stream.use { input -> responseBody.use { input.copyTo(it) } }
        }
    }

    private fun HttpExchange.toRequest(): Request =
        Request(Method.valueOf(requestMethod),
            requestURI.rawQuery?.let { Uri.of(requestURI.rawPath).query(requestURI.rawQuery) }
                ?: Uri.of(requestURI.rawPath))
            .body(requestBody, requestHeaders.getFirst("Content-Length").safeLong())
            .headers(requestHeaders.toList().flatMap { (key, values) -> values.map { key to it } })
            .source(RequestSource(localAddress.address.hostAddress, localAddress.port))

    override fun handle(exchange: HttpExchange) {
        with(exchange) {
            try {
                populate(handler(toRequest()))
            } catch (e: Exception) {
                sendResponseHeaders(500, -1)
            } finally {
                close()
            }
        }
    }
}

class SunHttp(val port: Int = 8000, override val stopMode: StopMode = StopMode.Immediate) : ServerConfig {
    constructor(port: Int = 8000): this(port, StopMode.Immediate)

    override fun toServer(http: HttpHandler): Http4kServer = object : Http4kServer {
        override fun port(): Int = if (port > 0) port else server.address.port

        private val executor = newWorkStealingPool()
        private val server = HttpServer.create(InetSocketAddress(port), 1000)
        override fun start(): Http4kServer = apply {
            server.createContext("/", HttpExchangeHandler(http))
            server.executor = executor
            server.start()
        }

        override fun stop() = apply {
            if (stopMode is StopMode.Graceful) {
                executor.shutdown()
                executor.awaitTermination(stopMode.timeout.toMillis(), TimeUnit.MILLISECONDS)
            }
            server.stop(0)
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy