Merge pull request #322 from mikeprimm/master

Add max-sessions setting to configuration.txt - limits concurrent threads and sessions on internal web server
This commit is contained in:
mikeprimm 2011-07-25 19:22:34 -07:00
commit 1e9cfe3bc7
4 changed files with 37 additions and 4 deletions

View File

@ -275,11 +275,13 @@ public class DynmapPlugin extends JavaPlugin {
int port = configuration.getInteger("webserver-port", 8123); int port = configuration.getInteger("webserver-port", 8123);
boolean allow_symlinks = configuration.getBoolean("allow-symlinks", false); boolean allow_symlinks = configuration.getBoolean("allow-symlinks", false);
boolean checkbannedips = configuration.getBoolean("check-banned-ips", true); boolean checkbannedips = configuration.getBoolean("check-banned-ips", true);
int maxconnections = configuration.getInteger("max-sessions", 30);
if(maxconnections < 2) maxconnections = 2;
if(allow_symlinks) if(allow_symlinks)
Log.verboseinfo("Web server is permitting symbolic links"); Log.verboseinfo("Web server is permitting symbolic links");
else else
Log.verboseinfo("Web server is not permitting symbolic links"); Log.verboseinfo("Web server is not permitting symbolic links");
webServer = new HttpServer(bindAddress, port, checkbannedips); webServer = new HttpServer(bindAddress, port, checkbannedips, maxconnections);
webServer.handlers.put("/", new FilesystemHandler(getFile(configuration.getString("webpath", "web")), allow_symlinks)); webServer.handlers.put("/", new FilesystemHandler(getFile(configuration.getString("webpath", "web")), allow_symlinks));
webServer.handlers.put("/tiles/", new FilesystemHandler(tilesDirectory, allow_symlinks)); webServer.handlers.put("/tiles/", new FilesystemHandler(tilesDirectory, allow_symlinks));
webServer.handlers.put("/up/configuration", new ClientConfigurationHandler(this)); webServer.handlers.put("/up/configuration", new ClientConfigurationHandler(this));

View File

@ -27,16 +27,19 @@ public class HttpServer extends Thread {
private InetAddress bindAddress; private InetAddress bindAddress;
private int port; private int port;
private boolean check_banned_ips; private boolean check_banned_ips;
private int max_sessions;
public SortedMap<String, HttpHandler> handlers = new TreeMap<String, HttpHandler>(Collections.reverseOrder()); public SortedMap<String, HttpHandler> handlers = new TreeMap<String, HttpHandler>(Collections.reverseOrder());
private Object lock = new Object(); private Object lock = new Object();
private HashSet<HttpServerConnection> active_connections = new HashSet<HttpServerConnection>(); private HashSet<HttpServerConnection> active_connections = new HashSet<HttpServerConnection>();
private HashSet<HttpServerConnection> keepalive_connections = new HashSet<HttpServerConnection>();
public HttpServer(InetAddress bindAddress, int port, boolean check_banned_ips) { public HttpServer(InetAddress bindAddress, int port, boolean check_banned_ips, int max_sessions) {
this.bindAddress = bindAddress; this.bindAddress = bindAddress;
this.port = port; this.port = port;
this.check_banned_ips = check_banned_ips; this.check_banned_ips = check_banned_ips;
this.max_sessions = max_sessions;
} }
public InetAddress getAddress() { public InetAddress getAddress() {
@ -68,8 +71,13 @@ public class HttpServer extends Thread {
HttpServerConnection requestThread = new HttpServerConnection(socket, this); HttpServerConnection requestThread = new HttpServerConnection(socket, this);
synchronized(lock) { synchronized(lock) {
active_connections.add(requestThread); 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);
}
} }
requestThread.start();
} catch (IOException e) { } catch (IOException e) {
if(listeningThread != null) /* Only report this if we didn't initiate the shutdown */ if(listeningThread != null) /* Only report this if we didn't initiate the shutdown */
Log.info("map WebServer.run() stops with IOException"); Log.info("map WebServer.run() stops with IOException");
@ -102,10 +110,23 @@ public class HttpServer extends Thread {
Log.warning("Exception while closing socket for webserver shutdown", 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) { public void connectionEnded(HttpServerConnection c) {
synchronized(lock) { synchronized(lock) {
active_connections.remove(c); active_connections.remove(c);
keepalive_connections.remove(c);
lock.notifyAll();
} }
} }

View File

@ -26,6 +26,7 @@ public class HttpServerConnection extends Thread {
private Socket socket; private Socket socket;
private HttpServer server; private HttpServer server;
private boolean do_shutdown; private boolean do_shutdown;
private boolean can_keepalive;
private PrintStream printOut; private PrintStream printOut;
private StringWriter sw = new StringWriter(); private StringWriter sw = new StringWriter();
@ -36,6 +37,7 @@ public class HttpServerConnection extends Thread {
this.socket = socket; this.socket = socket;
this.server = server; this.server = server;
do_shutdown = false; do_shutdown = false;
can_keepalive = false;
} }
private final static void readLine(InputStream in, StringWriter sw) throws IOException { private final static void readLine(InputStream in, StringWriter sw) throws IOException {
@ -159,7 +161,9 @@ public class HttpServerConnection extends Thread {
boolean iskeepalive = false; boolean iskeepalive = false;
String keepalive = request.fields.get(HttpField.Connection); String keepalive = request.fields.get(HttpField.Connection);
if((keepalive != null) && (keepalive.toLowerCase().indexOf("keep-alive") >= 0)) { if((keepalive != null) && (keepalive.toLowerCase().indexOf("keep-alive") >= 0)) {
iskeepalive = true; /* See if we're clear to do keepalive */
if(!iskeepalive)
iskeepalive = server.canKeepAlive(this);
} }
// TODO: Optimize HttpHandler-finding by using a real path-aware tree. // TODO: Optimize HttpHandler-finding by using a real path-aware tree.
@ -193,6 +197,9 @@ public class HttpServerConnection extends Thread {
response.fields.put(HttpField.Connection, "keep-alive"); response.fields.put(HttpField.Connection, "keep-alive");
response.fields.put("Keep-Alive", "timeout=5"); response.fields.put("Keep-Alive", "timeout=5");
} }
else {
response.fields.put(HttpField.Connection, "close");
}
try { try {
handler.handle(relativePath, request, response); handler.handle(relativePath, request, response);
} catch (IOException e) { } catch (IOException e) {

View File

@ -116,6 +116,9 @@ webserver-bindaddress: 0.0.0.0
# The TCP-port the webserver will listen on. # The TCP-port the webserver will listen on.
webserver-port: 8123 webserver-port: 8123
# Maximum concurrent session on internal web server - limits resources used in Bukkit server
max-sessions: 30
# Disables Webserver portion of Dynmap (Advanced users only) # Disables Webserver portion of Dynmap (Advanced users only)
disable-webserver: false disable-webserver: false