package org.yamcs.web; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.yamcs.web.rest.Router; import org.yamcs.web.websocket.WebSocketResourceProvider; import com.google.common.util.concurrent.AbstractService; import io.netty.bootstrap.ServerBootstrap; import io.netty.buffer.PooledByteBufAllocator; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LoggingHandler; import io.netty.util.concurrent.Future; /** * Server wide HTTP server based on Netty that provides a number of * Yamcs web services: * * <ul> * <li>REST API * <li>WebSocket API * <li>Static file serving * </ul> */ public class HttpServer extends AbstractService { private static final Logger log = LoggerFactory.getLogger(HttpServer.class); private EventLoopGroup bossGroup; private Router apiRouter = new Router(); private List<WebSocketResourceProvider> webSocketResourceProviders = new CopyOnWriteArrayList<>(); private WebConfig config; public HttpServer() { this(WebConfig.getInstance()); } public HttpServer(WebConfig config) { this.config = config; } @Override protected void doStart() { try { startServer(); notifyStarted(); } catch (InterruptedException e) { notifyFailed(e); } } public void startServer() throws InterruptedException { StaticFileHandler.init(); int port = config.getPort(); bossGroup = new NioEventLoopGroup(1); //Note that while the thread pools created with this method are unbounded, netty will limit the number //of workers to 2*number of CPU cores EventLoopGroup workerGroup = new NioEventLoopGroup(); ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .handler(new LoggingHandler(HttpServer.class, LogLevel.DEBUG)) .childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT) .childHandler(new HttpServerChannelInitializer(apiRouter)); // Bind and start to accept incoming connections. bootstrap.bind(new InetSocketAddress(port)).sync(); try { log.info("Web address: http://{}:{}/", InetAddress.getLocalHost().getHostName(), port); } catch (UnknownHostException e) { log.info("Web address: http://localhost:{}/", port); } } public Future<?> stopServer() { return bossGroup.shutdownGracefully(); } public WebConfig getConfig() { return config; } public void registerRouteHandler(String yamcsInstance, RouteHandler routeHandler) { apiRouter.registerRouteHandler(yamcsInstance, routeHandler); } public void registerWebSocketRouteHandler(WebSocketResourceProvider provider) { webSocketResourceProviders.add(provider); } public List<WebSocketResourceProvider> getWebSocketResourceProviders() { return webSocketResourceProviders; } @Override protected void doStop() { stopServer(); notifyStopped(); } }