diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ce114e..5acff18 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added +- [2020-06-14] Added new `client_hostname` selector to allow for custom address binding for Netty by [@lflare]. +- [2020-06-14] Added new `ui_hostname` selector to allow for custom address binding for WebUiNetty by [@lflare]. ### Changed diff --git a/src/main/kotlin/mdnet/base/Netty.kt b/src/main/kotlin/mdnet/base/Netty.kt index 5fc2c96..72dd867 100644 --- a/src/main/kotlin/mdnet/base/Netty.kt +++ b/src/main/kotlin/mdnet/base/Netty.kt @@ -102,7 +102,7 @@ class Netty(private val tls: ServerSettings.TlsCert, private val clientSettings: .option(ChannelOption.SO_BACKLOG, 1000) .childOption(ChannelOption.SO_KEEPALIVE, true) - val channel = bootstrap.bind(clientSettings.clientPort).sync().channel() + val channel = bootstrap.bind(InetSocketAddress(clientSettings.clientHostname, clientSettings.clientPort)).sync().channel() address = channel.localAddress() as InetSocketAddress closeFuture = channel.closeFuture() } diff --git a/src/main/kotlin/mdnet/base/WebUiNetty.kt b/src/main/kotlin/mdnet/base/WebUiNetty.kt new file mode 100755 index 0000000..bbb4af2 --- /dev/null +++ b/src/main/kotlin/mdnet/base/WebUiNetty.kt @@ -0,0 +1,62 @@ +package mdnet.base + +import io.netty.bootstrap.ServerBootstrap +import io.netty.channel.ChannelFactory +import io.netty.channel.ChannelFuture +import io.netty.channel.ChannelInitializer +import io.netty.channel.ChannelOption +import io.netty.channel.ServerChannel +import io.netty.channel.nio.NioEventLoopGroup +import io.netty.channel.socket.SocketChannel +import io.netty.channel.socket.nio.NioServerSocketChannel +import io.netty.handler.codec.http.HttpObjectAggregator +import io.netty.handler.codec.http.HttpServerCodec +import io.netty.handler.codec.http.HttpServerKeepAliveHandler +import io.netty.handler.stream.ChunkedWriteHandler +import java.net.InetSocketAddress +import java.util.concurrent.TimeUnit +import org.http4k.core.HttpHandler +import org.http4k.server.Http4kChannelHandler +import org.http4k.server.Http4kServer +import org.http4k.server.ServerConfig +import org.slf4j.LoggerFactory + +private val LOGGER = LoggerFactory.getLogger(WebUiNetty::class.java) + +class WebUiNetty(private val hostname: String, private val port: Int) : ServerConfig { + override fun toServer(httpHandler: HttpHandler): Http4kServer = object : Http4kServer { + private val masterGroup = NioEventLoopGroup() + private val workerGroup = NioEventLoopGroup() + private lateinit var closeFuture: ChannelFuture + private lateinit var address: InetSocketAddress + + override fun start(): Http4kServer = apply { + val bootstrap = ServerBootstrap() + bootstrap.group(masterGroup, workerGroup) + .channelFactory(ChannelFactory { NioServerSocketChannel() }) + .childHandler(object : ChannelInitializer() { + public override fun initChannel(ch: SocketChannel) { + ch.pipeline().addLast("codec", HttpServerCodec()) + ch.pipeline().addLast("keepAlive", HttpServerKeepAliveHandler()) + ch.pipeline().addLast("aggregator", HttpObjectAggregator(Int.MAX_VALUE)) + ch.pipeline().addLast("streamer", ChunkedWriteHandler()) + ch.pipeline().addLast("handler", Http4kChannelHandler(httpHandler)) + } + }) + .option(ChannelOption.SO_BACKLOG, 1000) + .childOption(ChannelOption.SO_KEEPALIVE, true) + + val channel = bootstrap.bind(InetSocketAddress(hostname, port)).sync().channel() + address = channel.localAddress() as InetSocketAddress + closeFuture = channel.closeFuture() + } + + override fun stop() = apply { + masterGroup.shutdownGracefully(5, 15, TimeUnit.SECONDS).sync() + workerGroup.shutdownGracefully(5, 15, TimeUnit.SECONDS).sync() + closeFuture.sync() + } + + override fun port(): Int = address.port + } +} diff --git a/src/main/kotlin/mdnet/base/server/WebUi.kt b/src/main/kotlin/mdnet/base/server/WebUi.kt index 794a377..f20af41 100644 --- a/src/main/kotlin/mdnet/base/server/WebUi.kt +++ b/src/main/kotlin/mdnet/base/server/WebUi.kt @@ -1,7 +1,10 @@ /* ktlint-disable no-wildcard-imports */ package mdnet.base.server +import java.time.Instant +import java.util.concurrent.atomic.AtomicReference import mdnet.base.Statistics +import mdnet.base.WebUiNetty import mdnet.base.settings.WebSettings import org.http4k.core.Body import org.http4k.core.Method @@ -9,16 +12,13 @@ import org.http4k.core.Response import org.http4k.core.Status import org.http4k.core.then import org.http4k.filter.ServerFilters +import org.http4k.format.Gson.auto import org.http4k.routing.ResourceLoader import org.http4k.routing.bind import org.http4k.routing.routes import org.http4k.routing.singlePageApp import org.http4k.server.Http4kServer -import org.http4k.server.Netty import org.http4k.server.asServer -import java.util.concurrent.atomic.AtomicReference -import org.http4k.format.Gson.auto -import java.time.Instant fun getUiServer( webSettings: WebSettings, @@ -43,5 +43,5 @@ fun getUiServer( singlePageApp(ResourceLoader.Classpath("/webui")) ) ) - .asServer(Netty(webSettings.uiPort)) + .asServer(WebUiNetty(webSettings.uiHostname, webSettings.uiPort)) } diff --git a/src/main/kotlin/mdnet/base/settings/ClientSettings.kt b/src/main/kotlin/mdnet/base/settings/ClientSettings.kt index 6e636e8..a1207f4 100644 --- a/src/main/kotlin/mdnet/base/settings/ClientSettings.kt +++ b/src/main/kotlin/mdnet/base/settings/ClientSettings.kt @@ -7,10 +7,14 @@ data class ClientSettings( @field:SerializedName("max_cache_size_mib") val maxCacheSizeMib: Long = 20480, @field:SerializedName("max_bandwidth_mib_per_hour") val maxBandwidthMibPerHour: Long = 0, @field:SerializedName("max_burst_rate_kib_per_second") val maxBurstRateKibPerSecond: Long = 0, + @field:SerializedName("client_hostname") val clientHostname: String = "0.0.0.0", @field:SerializedName("client_port") val clientPort: Int = 443, @field:Secret @field:SerializedName("client_secret") val clientSecret: String = "PASTE-YOUR-SECRET-HERE", @field:SerializedName("threads") val threads: Int = 32, @field:SerializedName("web_settings") val webSettings: WebSettings? = null ) -data class WebSettings(@field:SerializedName("ui_port") val uiPort: Int = 8080) +data class WebSettings( + @field:SerializedName("ui_hostname") val uiHostname: String = "127.0.0.1", + @field:SerializedName("ui_port") val uiPort: Int = 8080 +)