/src/main/java/org/dynmap/web/HttpServer.java
Java | 201 lines | 177 code | 19 blank | 5 comment | 27 complexity | 57d19abc0e14c381f4bce9f4f8e3d13f MD5 | raw file
Possible License(s): Apache-2.0
- package org.dynmap.web;
- import java.io.BufferedReader;
- import java.io.File;
- import java.io.FileReader;
- import java.io.IOException;
- import java.net.InetAddress;
- import java.net.InetSocketAddress;
- import java.net.ServerSocket;
- import java.net.Socket;
- import java.net.SocketAddress;
- import java.util.Collections;
- import java.util.HashMap;
- import java.util.HashSet;
- import java.util.IdentityHashMap;
- import java.util.Map;
- import java.util.SortedMap;
- import java.util.TreeMap;
- import java.util.logging.Logger;
- import org.dynmap.Log;
- public class HttpServer extends Thread {
- protected static final Logger log = Logger.getLogger("Minecraft");
- private ServerSocket sock = null;
- private Thread listeningThread;
- private InetAddress bindAddress;
- private int port;
- private boolean check_banned_ips;
- private int max_sessions;
- public SortedMap<String, HttpHandler> handlers = new TreeMap<String, HttpHandler>(Collections.reverseOrder());
-
- private Object lock = new Object();
- private HashSet<HttpServerConnection> active_connections = new HashSet<HttpServerConnection>();
- private HashSet<HttpServerConnection> keepalive_connections = new HashSet<HttpServerConnection>();
- private static Map<String, String> headers = new HashMap<String,String>();
- public HttpServer(InetAddress bindAddress, int port, boolean check_banned_ips, int max_sessions) {
- this.bindAddress = bindAddress;
- this.port = port;
- this.check_banned_ips = check_banned_ips;
- this.max_sessions = max_sessions;
- }
- public InetAddress getAddress() {
- return bindAddress;
- }
-
- public int getPort() {
- return port;
- }
-
- public void startServer() throws IOException {
- sock = new ServerSocket(port, 50, bindAddress); /* 5 too low - more than a couple users during render will get connect errors on some tile loads */
- listeningThread = this;
- start();
- Log.info("Dynmap WebServer started on " + bindAddress + ":" + port);
- }
- public void run() {
- try {
- ServerSocket s = sock;
- while (listeningThread == Thread.currentThread()) {
- try {
- Socket socket = s.accept();
- if(checkForBannedIp(socket.getRemoteSocketAddress())) {
- try { socket.close(); } catch (IOException iox) {}
- socket = null;
- }
- HttpServerConnection requestThread = new HttpServerConnection(socket, this);
- synchronized(lock) {
- active_connections.add(requestThread);
- requestThread.start();
- /* If we're at limit, wait here until we're free to accept another */
- while((listeningThread == Thread.currentThread()) &&
- (active_connections.size() >= max_sessions)) {
- lock.wait(500);
- }
- }
- } catch (IOException e) {
- if(listeningThread != null) /* Only report this if we didn't initiate the shutdown */
- Log.info("map WebServer.run() stops with IOException");
- break;
- }
- }
- Log.info("Webserver shut down.");
- } catch (Exception ex) {
- Log.severe("Exception on WebServer-thread", ex);
- }
- }
- public void shutdown() {
- Log.info("Shutting down webserver...");
- listeningThread = null;
- try {
- if (sock != null) {
- sock.close();
- sock = null;
- }
- /* And kill off the active connections */
- HashSet<HttpServerConnection> sc;
- synchronized(lock) {
- sc = new HashSet<HttpServerConnection>(active_connections);
- }
- for(HttpServerConnection c : sc) {
- c.shutdownConnection();
- }
- } catch (IOException e) {
- Log.warning("Exception while closing socket for webserver shutdown", e);
- }
- }
- public boolean canKeepAlive(HttpServerConnection c) {
- synchronized(lock) {
- /* If less than half of our limit are keep-alive, approve */
- if(keepalive_connections.size() < (max_sessions/2)) {
- keepalive_connections.add(c);
- return true;
- }
- }
- return false;
- }
-
- public void connectionEnded(HttpServerConnection c) {
- synchronized(lock) {
- active_connections.remove(c);
- keepalive_connections.remove(c);
- lock.notifyAll();
- }
- }
-
- private HashSet<String> banned_ips = new HashSet<String>();
- private HashSet<String> banned_ips_notified = new HashSet<String>();
- private long last_loaded = 0;
- private long lastmod = 0;
- private static final long BANNED_RELOAD_INTERVAL = 15000; /* Every 15 seconds */
-
- private void loadBannedIPs() {
- banned_ips.clear();
- banned_ips_notified.clear();
- File f = new File("banned-ips.txt");
- if(f.exists() == false)
- return;
- if(f.lastModified() == lastmod) {
- return;
- }
- lastmod = f.lastModified();
- BufferedReader rdr = null;
- try {
- rdr = new BufferedReader(new FileReader(f));
- String line;
- while((line = rdr.readLine()) != null) {
- line = line.trim().toLowerCase(); /* Trim it and case normalize it */
- if((line.length() == 0) || (line.charAt(0) == '#')) { /* Blank or comment? */
- continue;
- }
- banned_ips.add(line);
- }
- } catch (IOException iox) {
- Log.severe("Error reading banned-ips.txt!");
- } finally {
- if(rdr != null) {
- try { rdr.close(); } catch (IOException iox) {}
- rdr = null;
- }
- }
- }
- /* Return true if address is banned */
- public boolean checkForBannedIp(SocketAddress socketAddress) {
- if(!check_banned_ips)
- return false;
-
- long t = System.currentTimeMillis();
- if((t < last_loaded) || ((t-last_loaded) > BANNED_RELOAD_INTERVAL)) {
- loadBannedIPs();
- last_loaded = t;
- }
- /* Follow same technique as MC uses - toString the SocketAddress and clip out string between "/" and ":" */
- String ip = socketAddress.toString();
- ip = ip.substring(ip.indexOf("/") + 1);
- ip = ip.substring(0, ip.indexOf(":"));
- if(banned_ips.contains(ip)) {
- if(banned_ips_notified.contains(ip) == false) {
- Log.info("Rejected connection by banned IP address - " + socketAddress.toString());
- banned_ips_notified.add(ip);
- }
- return true;
- }
- return false;
- }
- public static Map<String,String> getCustomHeaders() {
- return headers;
- }
- public static void setCustomHeaders(Map<String,String> hdrs) {
- headers = hdrs;
- }
- }