PageRenderTime 3274ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/src/main/java/org/dynmap/web/HttpServer.java

http://github.com/webbukkit/dynmap
Java | 201 lines | 177 code | 19 blank | 5 comment | 27 complexity | 57d19abc0e14c381f4bce9f4f8e3d13f MD5 | raw file
Possible License(s): Apache-2.0
  1. package org.dynmap.web;
  2. import java.io.BufferedReader;
  3. import java.io.File;
  4. import java.io.FileReader;
  5. import java.io.IOException;
  6. import java.net.InetAddress;
  7. import java.net.InetSocketAddress;
  8. import java.net.ServerSocket;
  9. import java.net.Socket;
  10. import java.net.SocketAddress;
  11. import java.util.Collections;
  12. import java.util.HashMap;
  13. import java.util.HashSet;
  14. import java.util.IdentityHashMap;
  15. import java.util.Map;
  16. import java.util.SortedMap;
  17. import java.util.TreeMap;
  18. import java.util.logging.Logger;
  19. import org.dynmap.Log;
  20. public class HttpServer extends Thread {
  21. protected static final Logger log = Logger.getLogger("Minecraft");
  22. private ServerSocket sock = null;
  23. private Thread listeningThread;
  24. private InetAddress bindAddress;
  25. private int port;
  26. private boolean check_banned_ips;
  27. private int max_sessions;
  28. public SortedMap<String, HttpHandler> handlers = new TreeMap<String, HttpHandler>(Collections.reverseOrder());
  29. private Object lock = new Object();
  30. private HashSet<HttpServerConnection> active_connections = new HashSet<HttpServerConnection>();
  31. private HashSet<HttpServerConnection> keepalive_connections = new HashSet<HttpServerConnection>();
  32. private static Map<String, String> headers = new HashMap<String,String>();
  33. public HttpServer(InetAddress bindAddress, int port, boolean check_banned_ips, int max_sessions) {
  34. this.bindAddress = bindAddress;
  35. this.port = port;
  36. this.check_banned_ips = check_banned_ips;
  37. this.max_sessions = max_sessions;
  38. }
  39. public InetAddress getAddress() {
  40. return bindAddress;
  41. }
  42. public int getPort() {
  43. return port;
  44. }
  45. public void startServer() throws IOException {
  46. sock = new ServerSocket(port, 50, bindAddress); /* 5 too low - more than a couple users during render will get connect errors on some tile loads */
  47. listeningThread = this;
  48. start();
  49. Log.info("Dynmap WebServer started on " + bindAddress + ":" + port);
  50. }
  51. public void run() {
  52. try {
  53. ServerSocket s = sock;
  54. while (listeningThread == Thread.currentThread()) {
  55. try {
  56. Socket socket = s.accept();
  57. if(checkForBannedIp(socket.getRemoteSocketAddress())) {
  58. try { socket.close(); } catch (IOException iox) {}
  59. socket = null;
  60. }
  61. HttpServerConnection requestThread = new HttpServerConnection(socket, this);
  62. synchronized(lock) {
  63. active_connections.add(requestThread);
  64. requestThread.start();
  65. /* If we're at limit, wait here until we're free to accept another */
  66. while((listeningThread == Thread.currentThread()) &&
  67. (active_connections.size() >= max_sessions)) {
  68. lock.wait(500);
  69. }
  70. }
  71. } catch (IOException e) {
  72. if(listeningThread != null) /* Only report this if we didn't initiate the shutdown */
  73. Log.info("map WebServer.run() stops with IOException");
  74. break;
  75. }
  76. }
  77. Log.info("Webserver shut down.");
  78. } catch (Exception ex) {
  79. Log.severe("Exception on WebServer-thread", ex);
  80. }
  81. }
  82. public void shutdown() {
  83. Log.info("Shutting down webserver...");
  84. listeningThread = null;
  85. try {
  86. if (sock != null) {
  87. sock.close();
  88. sock = null;
  89. }
  90. /* And kill off the active connections */
  91. HashSet<HttpServerConnection> sc;
  92. synchronized(lock) {
  93. sc = new HashSet<HttpServerConnection>(active_connections);
  94. }
  95. for(HttpServerConnection c : sc) {
  96. c.shutdownConnection();
  97. }
  98. } catch (IOException e) {
  99. Log.warning("Exception while closing socket for webserver shutdown", e);
  100. }
  101. }
  102. public boolean canKeepAlive(HttpServerConnection c) {
  103. synchronized(lock) {
  104. /* If less than half of our limit are keep-alive, approve */
  105. if(keepalive_connections.size() < (max_sessions/2)) {
  106. keepalive_connections.add(c);
  107. return true;
  108. }
  109. }
  110. return false;
  111. }
  112. public void connectionEnded(HttpServerConnection c) {
  113. synchronized(lock) {
  114. active_connections.remove(c);
  115. keepalive_connections.remove(c);
  116. lock.notifyAll();
  117. }
  118. }
  119. private HashSet<String> banned_ips = new HashSet<String>();
  120. private HashSet<String> banned_ips_notified = new HashSet<String>();
  121. private long last_loaded = 0;
  122. private long lastmod = 0;
  123. private static final long BANNED_RELOAD_INTERVAL = 15000; /* Every 15 seconds */
  124. private void loadBannedIPs() {
  125. banned_ips.clear();
  126. banned_ips_notified.clear();
  127. File f = new File("banned-ips.txt");
  128. if(f.exists() == false)
  129. return;
  130. if(f.lastModified() == lastmod) {
  131. return;
  132. }
  133. lastmod = f.lastModified();
  134. BufferedReader rdr = null;
  135. try {
  136. rdr = new BufferedReader(new FileReader(f));
  137. String line;
  138. while((line = rdr.readLine()) != null) {
  139. line = line.trim().toLowerCase(); /* Trim it and case normalize it */
  140. if((line.length() == 0) || (line.charAt(0) == '#')) { /* Blank or comment? */
  141. continue;
  142. }
  143. banned_ips.add(line);
  144. }
  145. } catch (IOException iox) {
  146. Log.severe("Error reading banned-ips.txt!");
  147. } finally {
  148. if(rdr != null) {
  149. try { rdr.close(); } catch (IOException iox) {}
  150. rdr = null;
  151. }
  152. }
  153. }
  154. /* Return true if address is banned */
  155. public boolean checkForBannedIp(SocketAddress socketAddress) {
  156. if(!check_banned_ips)
  157. return false;
  158. long t = System.currentTimeMillis();
  159. if((t < last_loaded) || ((t-last_loaded) > BANNED_RELOAD_INTERVAL)) {
  160. loadBannedIPs();
  161. last_loaded = t;
  162. }
  163. /* Follow same technique as MC uses - toString the SocketAddress and clip out string between "/" and ":" */
  164. String ip = socketAddress.toString();
  165. ip = ip.substring(ip.indexOf("/") + 1);
  166. ip = ip.substring(0, ip.indexOf(":"));
  167. if(banned_ips.contains(ip)) {
  168. if(banned_ips_notified.contains(ip) == false) {
  169. Log.info("Rejected connection by banned IP address - " + socketAddress.toString());
  170. banned_ips_notified.add(ip);
  171. }
  172. return true;
  173. }
  174. return false;
  175. }
  176. public static Map<String,String> getCustomHeaders() {
  177. return headers;
  178. }
  179. public static void setCustomHeaders(Map<String,String> hdrs) {
  180. headers = hdrs;
  181. }
  182. }