2020package net.ccbluex.netty.http
2121
2222import io.netty.bootstrap.ServerBootstrap
23+ import io.netty.channel.Channel
2324import io.netty.channel.ChannelOption
25+ import io.netty.channel.EventLoopGroup
2426import io.netty.channel.epoll.Epoll
2527import io.netty.channel.epoll.EpollEventLoopGroup
2628import io.netty.channel.epoll.EpollServerSocketChannel
@@ -32,6 +34,9 @@ import net.ccbluex.netty.http.middleware.Middleware
3234import net.ccbluex.netty.http.rest.RouteController
3335import net.ccbluex.netty.http.websocket.WebSocketController
3436import org.apache.logging.log4j.LogManager
37+ import java.net.InetSocketAddress
38+ import java.util.concurrent.locks.ReentrantLock
39+ import kotlin.concurrent.withLock
3540
3641
3742/* *
@@ -44,22 +49,32 @@ class HttpServer {
4449 val routeController = RouteController ()
4550 val webSocketController = WebSocketController ()
4651
47- val middlewares = mutableListOf<Middleware >()
52+ private val lock = ReentrantLock ()
53+
54+ internal val middlewares = mutableListOf<Middleware >()
55+
56+ private var bossGroup: EventLoopGroup ? = null
57+ private var workerGroup: EventLoopGroup ? = null
58+ private var serverChannel: Channel ? = null
4859
4960 companion object {
5061 internal val logger = LogManager .getLogger(" HttpServer" )
5162 }
5263
53- fun middleware (middleware : Middleware ) {
64+ fun middleware (middleware : Middleware ) = apply {
5465 middlewares + = middleware
5566 }
5667
5768 /* *
5869 * Starts the Netty server on the specified port.
70+ *
71+ * @param port The port of HTTP server. `0` means to auto select one.
72+ *
73+ * @return actual port of server.
5974 */
60- fun start (port : Int ) {
61- val bossGroup = if (Epoll .isAvailable()) EpollEventLoopGroup () else NioEventLoopGroup ()
62- val workerGroup = if (Epoll .isAvailable()) EpollEventLoopGroup () else NioEventLoopGroup ()
75+ fun start (port : Int ): Int = lock.withLock {
76+ bossGroup = if (Epoll .isAvailable()) EpollEventLoopGroup (1 ) else NioEventLoopGroup (1 )
77+ workerGroup = if (Epoll .isAvailable()) EpollEventLoopGroup () else NioEventLoopGroup ()
6378
6479 try {
6580 logger.info(" Starting Netty server..." )
@@ -70,21 +85,35 @@ class HttpServer {
7085 .handler(LoggingHandler (LogLevel .INFO ))
7186 .childHandler(HttpChannelInitializer (this ))
7287 val ch = b.bind(port).sync().channel()
88+ serverChannel = ch
7389
7490 logger.info(" Netty server started on port $port ." )
75- ch.closeFuture().sync()
76- } catch (e: InterruptedException ) {
77- logger.error(" Netty server interrupted" , e)
91+
92+ return @withLock (ch.localAddress() as InetSocketAddress ).port
7893 } catch (t: Throwable ) {
7994 logger.error(" Netty server failed - $port " , t)
80-
95+ stop()
8196 // Forward the exception because we ran into an unexpected error
8297 throw t
83- } finally {
84- bossGroup.shutdownGracefully()
85- workerGroup.shutdownGracefully()
8698 }
99+ }
87100
101+ /* *
102+ * Stops the Netty server gracefully.
103+ */
104+ fun stop () = lock.withLock {
105+ logger.info(" Shutting down Netty server..." )
106+ try {
107+ serverChannel?.close()?.sync()
108+ bossGroup?.shutdownGracefully()?.sync()
109+ workerGroup?.shutdownGracefully()?.sync()
110+ } catch (e: Exception ) {
111+ logger.warn(" Error during shutdown" , e)
112+ } finally {
113+ serverChannel = null
114+ bossGroup = null
115+ workerGroup = null
116+ }
88117 logger.info(" Netty server stopped." )
89118 }
90119
0 commit comments