/* * This file is part of BlueMap, licensed under the MIT License (MIT). * * Copyright (c) Blue (Lukas Rieger) * Copyright (c) contributors * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package io.gitlab.jfronny.libjf.web.impl.util.bluemapcore; import io.gitlab.jfronny.libjf.LibJf; import java.io.IOException; import java.net.*; import java.util.concurrent.*; public class HttpServer extends Thread { private int port; private int maxConnections; private final InetAddress bindAddress; private final HttpRequestHandler handler; private ThreadPoolExecutor connectionThreads; private final Runnable callback; private ServerSocket server; private State state = State.Initialized; public HttpServer(InetAddress bindAddress, int port, int maxConnections, HttpRequestHandler handler, Runnable startCallback) { super("LibJF HTTP Server"); this.port = port; this.maxConnections = maxConnections; this.bindAddress = bindAddress; this.handler = handler; connectionThreads = null; this.callback = startCallback; } @Override public void run() { close(); connectionThreads = new ThreadPoolExecutor(Math.min(maxConnections, 8), maxConnections, 10, TimeUnit.SECONDS, new LinkedBlockingQueue<>()); try { server = new ServerSocket(port, maxConnections, bindAddress); server.setSoTimeout(1000); } catch (IOException e){ state = State.Failed; LibJf.LOGGER.error("Error while starting the WebServer!", e); return; } callback.run(); LibJf.LOGGER.info("WebServer started."); while (!server.isClosed() && server.isBound()){ state = State.Running; try { Socket connection = server.accept(); try { connectionThreads.execute(new HttpConnection(server, connection, handler, 10, TimeUnit.SECONDS)); } catch (RejectedExecutionException e){ connection.close(); LibJf.LOGGER.warn("Dropped an incoming HttpConnection! (Too many connections?)"); } } catch (SocketException | SocketTimeoutException e) { // ignored } catch (IOException e) { LibJf.LOGGER.error("Error while creating a new HttpConnection!", e); } } state = State.Closed; LibJf.LOGGER.info("WebServer closed."); } public int getPort() { return server == null ? port : server.getLocalPort(); } public void setPort(int port) { this.port = port; } public void setMaxConnections(int maxConnections) { this.maxConnections = maxConnections; } public synchronized void close() { state = State.Closed; if (connectionThreads != null) connectionThreads.shutdown(); try { if (server != null && !server.isClosed()){ server.close(); } } catch (IOException e) { LibJf.LOGGER.error("Error while closing WebServer!", e); } } public void waitUntilReady() throws InterruptedException { while (state == State.Initialized) Thread.sleep(1L); } enum State { Initialized, Running, Closed, Failed } }