2004-07-24 02:06:07 +00:00
|
|
|
package net.i2p.router.web;
|
|
|
|
|
2011-02-11 19:12:19 +00:00
|
|
|
import java.awt.GraphicsEnvironment;
|
2004-09-29 19:34:02 +00:00
|
|
|
import java.io.File;
|
2008-06-16 12:26:36 +00:00
|
|
|
import java.io.FilenameFilter;
|
2004-07-24 02:06:07 +00:00
|
|
|
import java.io.IOException;
|
2016-01-06 17:50:06 +00:00
|
|
|
import java.io.Serializable;
|
2016-05-08 19:49:14 +00:00
|
|
|
import java.io.UnsupportedEncodingException;
|
2012-03-10 21:53:29 +00:00
|
|
|
import java.net.InetAddress;
|
|
|
|
import java.net.Inet4Address;
|
2012-03-26 14:07:38 +00:00
|
|
|
import java.net.InetSocketAddress;
|
2012-03-12 14:30:38 +00:00
|
|
|
import java.net.ServerSocket;
|
2016-01-06 17:50:06 +00:00
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.Collections;
|
|
|
|
import java.util.Comparator;
|
2012-01-18 01:54:34 +00:00
|
|
|
import java.util.HashMap;
|
2017-10-11 16:26:37 +00:00
|
|
|
import java.util.HashSet;
|
2004-08-10 19:51:11 +00:00
|
|
|
import java.util.List;
|
2012-01-18 01:54:34 +00:00
|
|
|
import java.util.Map;
|
2008-06-16 12:26:36 +00:00
|
|
|
import java.util.Properties;
|
2017-04-01 14:15:06 +00:00
|
|
|
import java.util.Set;
|
2013-11-21 11:31:50 +00:00
|
|
|
import java.util.SortedSet;
|
2009-04-08 01:34:12 +00:00
|
|
|
import java.util.StringTokenizer;
|
Console and Eepsite Jetty:
Switch back to QueuedThreadPool (ticket #1395)
In Jetty 5/6, the default QTP was not concurrent, so we switched to
ThreadPoolExecutor with a fixed-size queue, a set maxThreads,
and a RejectedExecutionPolicy of CallerRuns.
Unfortunately, CallerRuns causes lockups in Jetty NIO.
In addition, no flavor of TPE gives us what QTP does:
- TPE direct handoff (which we were using) never queues.
This doesn't provide any burst management when maxThreads is reached.
CallerRuns was an attempt to work around that.
- TPE unbounded queue does not adjust the number of threads.
This doesn't provide automatic resource management.
- TPE bounded queue does not add threads until the queue is full.
This doesn't provide good responsiveness to even small bursts.
QTP adds threads as soon as the queue is non-empty.
QTP as of Jetty 7 uses concurrent.
QTP unbounded queue is the default in Jetty.
So switch back to QTP with a bounded queue, which does what we want,
which is first expand the thread pool, then start queueing, then reject.
ref:
http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ThreadPoolExecutor.html
https://wiki.eclipse.org/Jetty/Howto/High_Load
2014-10-20 14:01:36 +00:00
|
|
|
import java.util.concurrent.LinkedBlockingQueue;
|
2004-08-10 19:51:11 +00:00
|
|
|
|
2005-03-24 01:19:52 +00:00
|
|
|
import net.i2p.I2PAppContext;
|
2012-10-13 13:54:30 +00:00
|
|
|
import net.i2p.app.ClientAppManager;
|
|
|
|
import net.i2p.app.ClientAppState;
|
|
|
|
import static net.i2p.app.ClientAppState.*;
|
2013-09-12 14:27:16 +00:00
|
|
|
import net.i2p.crypto.KeyStoreUtil;
|
2008-06-16 12:26:36 +00:00
|
|
|
import net.i2p.data.DataHelper;
|
2013-04-14 14:02:43 +00:00
|
|
|
import net.i2p.jetty.I2PLogger;
|
2008-07-16 13:42:54 +00:00
|
|
|
import net.i2p.router.RouterContext;
|
2012-10-13 13:54:30 +00:00
|
|
|
import net.i2p.router.app.RouterApp;
|
2015-09-15 13:33:29 +00:00
|
|
|
import net.i2p.router.news.NewsManager;
|
|
|
|
import net.i2p.router.update.ConsoleUpdateManager;
|
2012-03-10 21:53:29 +00:00
|
|
|
import net.i2p.util.Addresses;
|
2004-09-29 19:34:02 +00:00
|
|
|
import net.i2p.util.FileUtil;
|
2009-08-26 22:16:29 +00:00
|
|
|
import net.i2p.util.I2PAppThread;
|
2011-12-15 23:36:32 +00:00
|
|
|
import net.i2p.util.PortMapper;
|
2010-07-06 15:22:48 +00:00
|
|
|
import net.i2p.util.SecureDirectory;
|
2014-10-15 20:44:23 +00:00
|
|
|
import net.i2p.util.I2PSSLSocketFactory;
|
2012-10-14 13:54:08 +00:00
|
|
|
import net.i2p.util.SystemVersion;
|
2016-05-06 15:56:54 +00:00
|
|
|
|
2018-02-25 14:17:01 +00:00
|
|
|
import org.apache.http.conn.util.InetAddressUtils;
|
|
|
|
|
2012-11-21 20:49:18 +00:00
|
|
|
import org.eclipse.jetty.security.HashLoginService;
|
|
|
|
import org.eclipse.jetty.security.ConstraintMapping;
|
|
|
|
import org.eclipse.jetty.security.ConstraintSecurityHandler;
|
|
|
|
import org.eclipse.jetty.security.authentication.DigestAuthenticator;
|
|
|
|
import org.eclipse.jetty.server.AbstractConnector;
|
2015-08-09 13:36:24 +00:00
|
|
|
import org.eclipse.jetty.server.ConnectionFactory;
|
2012-11-21 20:49:18 +00:00
|
|
|
import org.eclipse.jetty.server.Connector;
|
2015-08-09 13:36:24 +00:00
|
|
|
import org.eclipse.jetty.server.HttpConfiguration;
|
|
|
|
import org.eclipse.jetty.server.HttpConnectionFactory;
|
2012-11-21 20:49:18 +00:00
|
|
|
import org.eclipse.jetty.server.NCSARequestLog;
|
2015-08-09 13:36:24 +00:00
|
|
|
import org.eclipse.jetty.server.SecureRequestCustomizer;
|
2012-11-21 20:49:18 +00:00
|
|
|
import org.eclipse.jetty.server.Server;
|
2016-11-06 17:20:35 +00:00
|
|
|
import org.eclipse.jetty.server.UserIdentity;
|
2015-08-09 13:36:24 +00:00
|
|
|
import org.eclipse.jetty.server.ServerConnector;
|
|
|
|
import org.eclipse.jetty.server.SslConnectionFactory;
|
2012-11-21 20:49:18 +00:00
|
|
|
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
|
|
|
|
import org.eclipse.jetty.server.handler.DefaultHandler;
|
|
|
|
import org.eclipse.jetty.server.handler.HandlerCollection;
|
|
|
|
import org.eclipse.jetty.server.handler.HandlerWrapper;
|
|
|
|
import org.eclipse.jetty.server.handler.RequestLogHandler;
|
|
|
|
import org.eclipse.jetty.servlet.ServletHandler;
|
|
|
|
import org.eclipse.jetty.servlet.ServletHolder;
|
|
|
|
import org.eclipse.jetty.webapp.WebAppContext;
|
2013-04-14 14:02:43 +00:00
|
|
|
import org.eclipse.jetty.util.log.Log;
|
2013-04-08 15:29:02 +00:00
|
|
|
import org.eclipse.jetty.util.resource.Resource;
|
2012-11-21 20:49:18 +00:00
|
|
|
import org.eclipse.jetty.util.security.Constraint;
|
|
|
|
import org.eclipse.jetty.util.security.Credential;
|
|
|
|
import org.eclipse.jetty.util.security.Credential.MD5;
|
2013-04-15 15:53:29 +00:00
|
|
|
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
2012-11-21 20:49:18 +00:00
|
|
|
import org.eclipse.jetty.util.thread.ExecutorThreadPool;
|
|
|
|
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
2017-03-11 16:27:37 +00:00
|
|
|
import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
|
2012-11-21 20:49:18 +00:00
|
|
|
import org.eclipse.jetty.util.thread.ThreadPool;
|
2004-07-24 02:06:07 +00:00
|
|
|
|
2016-05-06 15:56:54 +00:00
|
|
|
import org.tanukisoftware.wrapper.WrapperManager;
|
|
|
|
|
2011-12-23 00:56:48 +00:00
|
|
|
/**
|
|
|
|
* Start the router console.
|
|
|
|
*/
|
2012-10-13 13:54:30 +00:00
|
|
|
public class RouterConsoleRunner implements RouterApp {
|
2013-04-14 14:02:43 +00:00
|
|
|
|
|
|
|
static {
|
|
|
|
// To take effect, must be set before any Jetty classes are loaded
|
|
|
|
try {
|
|
|
|
Log.setLog(new I2PLogger());
|
|
|
|
} catch (Throwable t) {
|
|
|
|
System.err.println("INFO: I2P Jetty logging class not found, logging to wrapper log");
|
|
|
|
}
|
|
|
|
// This way it doesn't try to load Slf4jLog first
|
|
|
|
// This causes an NPE in AbstractLifeCycle
|
|
|
|
// http://dev.eclipse.org/mhonarc/lists/jetty-users/msg02587.html
|
|
|
|
//System.setProperty("org.eclipse.jetty.util.log.class", "net.i2p.jetty.I2PLogger");
|
|
|
|
}
|
|
|
|
|
2012-10-13 13:06:22 +00:00
|
|
|
private final RouterContext _context;
|
2012-10-13 13:54:30 +00:00
|
|
|
private final ClientAppManager _mgr;
|
|
|
|
private volatile ClientAppState _state = UNINITIALIZED;
|
2011-12-23 00:56:48 +00:00
|
|
|
private static Server _server;
|
2017-03-11 16:27:37 +00:00
|
|
|
private static ScheduledExecutorScheduler _jettyTimer;
|
2010-12-05 19:04:33 +00:00
|
|
|
private String _listenPort;
|
|
|
|
private String _listenHost;
|
|
|
|
private String _sslListenPort;
|
|
|
|
private String _sslListenHost;
|
|
|
|
private String _webAppsDir;
|
2011-12-23 00:56:48 +00:00
|
|
|
|
2008-06-16 12:26:36 +00:00
|
|
|
private static final String DEFAULT_WEBAPP_CONFIG_FILENAME = "webapps.config";
|
2012-10-13 13:06:22 +00:00
|
|
|
|
|
|
|
// Jetty Auth
|
2013-05-29 16:30:00 +00:00
|
|
|
private static final DigestAuthenticator authenticator = new DigestAuthenticator();
|
2013-04-23 18:22:48 +00:00
|
|
|
static {
|
|
|
|
// default changed from 0 (forever) in Jetty 6 to 60*1000 ms in Jetty 7
|
|
|
|
authenticator.setMaxNonceAge(7*24*60*60*1000L);
|
|
|
|
}
|
2012-10-13 15:41:57 +00:00
|
|
|
public static final String JETTY_REALM = "i2prouter";
|
2012-10-13 13:06:22 +00:00
|
|
|
private static final String JETTY_ROLE = "routerAdmin";
|
2012-10-13 15:41:57 +00:00
|
|
|
public static final String PROP_CONSOLE_PW = "routerconsole.auth." + JETTY_REALM;
|
2012-10-26 13:08:23 +00:00
|
|
|
public static final String PROP_PW_ENABLE = "routerconsole.auth.enable";
|
2017-07-15 13:57:40 +00:00
|
|
|
/** from Jetty Credential.java */
|
|
|
|
private static final String MD5_CREDENTIAL_TYPE = "MD5:";
|
2012-10-13 13:06:22 +00:00
|
|
|
|
2008-06-16 12:26:36 +00:00
|
|
|
public static final String ROUTERCONSOLE = "routerconsole";
|
|
|
|
public static final String PREFIX = "webapps.";
|
|
|
|
public static final String ENABLED = ".startOnLoad";
|
2010-12-05 19:04:33 +00:00
|
|
|
private static final String PROP_KEYSTORE_PASSWORD = "routerconsole.keystorePassword";
|
|
|
|
private static final String PROP_KEY_PASSWORD = "routerconsole.keyPassword";
|
2012-10-13 13:06:22 +00:00
|
|
|
public static final int DEFAULT_LISTEN_PORT = 7657;
|
2010-12-05 19:04:33 +00:00
|
|
|
private static final String DEFAULT_LISTEN_HOST = "127.0.0.1";
|
|
|
|
private static final String DEFAULT_WEBAPPS_DIR = "./webapps/";
|
|
|
|
private static final String USAGE = "Bad RouterConsoleRunner arguments, check clientApp.0.args in your clients.config file! " +
|
|
|
|
"Usage: [[port host[,host]] [-s sslPort [host[,host]]] [webAppsDir]]";
|
2011-12-23 00:56:48 +00:00
|
|
|
|
Console and Eepsite Jetty:
Switch back to QueuedThreadPool (ticket #1395)
In Jetty 5/6, the default QTP was not concurrent, so we switched to
ThreadPoolExecutor with a fixed-size queue, a set maxThreads,
and a RejectedExecutionPolicy of CallerRuns.
Unfortunately, CallerRuns causes lockups in Jetty NIO.
In addition, no flavor of TPE gives us what QTP does:
- TPE direct handoff (which we were using) never queues.
This doesn't provide any burst management when maxThreads is reached.
CallerRuns was an attempt to work around that.
- TPE unbounded queue does not adjust the number of threads.
This doesn't provide automatic resource management.
- TPE bounded queue does not add threads until the queue is full.
This doesn't provide good responsiveness to even small bursts.
QTP adds threads as soon as the queue is non-empty.
QTP as of Jetty 7 uses concurrent.
QTP unbounded queue is the default in Jetty.
So switch back to QTP with a bounded queue, which does what we want,
which is first expand the thread pool, then start queueing, then reject.
ref:
http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ThreadPoolExecutor.html
https://wiki.eclipse.org/Jetty/Howto/High_Load
2014-10-20 14:01:36 +00:00
|
|
|
/** this is for the handlers only. We will adjust for the connectors and acceptors below. */
|
2011-12-23 00:56:48 +00:00
|
|
|
private static final int MIN_THREADS = 1;
|
Console and Eepsite Jetty:
Switch back to QueuedThreadPool (ticket #1395)
In Jetty 5/6, the default QTP was not concurrent, so we switched to
ThreadPoolExecutor with a fixed-size queue, a set maxThreads,
and a RejectedExecutionPolicy of CallerRuns.
Unfortunately, CallerRuns causes lockups in Jetty NIO.
In addition, no flavor of TPE gives us what QTP does:
- TPE direct handoff (which we were using) never queues.
This doesn't provide any burst management when maxThreads is reached.
CallerRuns was an attempt to work around that.
- TPE unbounded queue does not adjust the number of threads.
This doesn't provide automatic resource management.
- TPE bounded queue does not add threads until the queue is full.
This doesn't provide good responsiveness to even small bursts.
QTP adds threads as soon as the queue is non-empty.
QTP as of Jetty 7 uses concurrent.
QTP unbounded queue is the default in Jetty.
So switch back to QTP with a bounded queue, which does what we want,
which is first expand the thread pool, then start queueing, then reject.
ref:
http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ThreadPoolExecutor.html
https://wiki.eclipse.org/Jetty/Howto/High_Load
2014-10-20 14:01:36 +00:00
|
|
|
/** this is for the handlers only. We will adjust for the connectors and acceptors below. */
|
2011-12-23 00:56:48 +00:00
|
|
|
private static final int MAX_THREADS = 24;
|
|
|
|
private static final int MAX_IDLE_TIME = 90*1000;
|
2011-12-24 17:09:01 +00:00
|
|
|
private static final String THREAD_NAME = "RouterConsole Jetty";
|
2016-05-06 17:54:27 +00:00
|
|
|
public static final String PROP_DTG_ENABLED = "desktopgui.enabled";
|
2017-10-11 16:26:37 +00:00
|
|
|
static final String PROP_ALLOWED_HOSTS = "routerconsole.allowedHosts";
|
|
|
|
|
2009-04-08 01:34:12 +00:00
|
|
|
/**
|
2010-12-05 19:04:33 +00:00
|
|
|
* <pre>
|
|
|
|
* non-SSL:
|
|
|
|
* RouterConsoleRunner
|
|
|
|
* RouterConsoleRunner 7657
|
|
|
|
* RouterConsoleRunner 7657 127.0.0.1
|
|
|
|
* RouterConsoleRunner 7657 127.0.0.1,::1
|
|
|
|
* RouterConsoleRunner 7657 127.0.0.1,::1 ./webapps/
|
|
|
|
*
|
|
|
|
* SSL:
|
|
|
|
* RouterConsoleRunner -s 7657
|
|
|
|
* RouterConsoleRunner -s 7657 127.0.0.1
|
|
|
|
* RouterConsoleRunner -s 7657 127.0.0.1,::1
|
|
|
|
* RouterConsoleRunner -s 7657 127.0.0.1,::1 ./webapps/
|
|
|
|
*
|
|
|
|
* If using both, non-SSL must be first:
|
|
|
|
* RouterConsoleRunner 7657 127.0.0.1 -s 7667
|
|
|
|
* RouterConsoleRunner 7657 127.0.0.1 -s 7667 127.0.0.1
|
|
|
|
* RouterConsoleRunner 7657 127.0.0.1,::1 -s 7667 127.0.0.1,::1
|
|
|
|
* RouterConsoleRunner 7657 127.0.0.1,::1 -s 7667 127.0.0.1,::1 ./webapps/
|
|
|
|
* </pre>
|
|
|
|
*
|
2009-04-08 01:34:12 +00:00
|
|
|
* @param args second arg may be a comma-separated list of bind addresses,
|
|
|
|
* for example ::1,127.0.0.1
|
|
|
|
* On XP, the other order (127.0.0.1,::1) fails the IPV6 bind,
|
|
|
|
* because 127.0.0.1 will bind ::1 also. But even though it's bound
|
|
|
|
* to both, we can't connect to [::1]:7657 for some reason.
|
|
|
|
* So the wise choice is ::1,127.0.0.1
|
|
|
|
*/
|
2012-10-13 13:54:30 +00:00
|
|
|
public RouterConsoleRunner(RouterContext ctx, ClientAppManager mgr, String args[]) {
|
|
|
|
_context = ctx;
|
|
|
|
_mgr = mgr;
|
2010-12-05 19:04:33 +00:00
|
|
|
if (args.length == 0) {
|
2010-12-14 14:34:27 +00:00
|
|
|
// _listenHost and _webAppsDir are defaulted below
|
2012-10-13 13:06:22 +00:00
|
|
|
_listenPort = Integer.toString(DEFAULT_LISTEN_PORT);
|
2010-12-05 19:04:33 +00:00
|
|
|
} else {
|
|
|
|
boolean ssl = false;
|
|
|
|
for (int i = 0; i < args.length; i++) {
|
|
|
|
if (args[i].equals("-s"))
|
|
|
|
ssl = true;
|
|
|
|
else if ((!ssl) && _listenPort == null)
|
|
|
|
_listenPort = args[i];
|
|
|
|
else if ((!ssl) && _listenHost == null)
|
|
|
|
_listenHost = args[i];
|
|
|
|
else if (ssl && _sslListenPort == null)
|
|
|
|
_sslListenPort = args[i];
|
|
|
|
else if (ssl && _sslListenHost == null)
|
|
|
|
_sslListenHost = args[i];
|
|
|
|
else if (_webAppsDir == null)
|
|
|
|
_webAppsDir = args[i];
|
|
|
|
else {
|
|
|
|
System.err.println(USAGE);
|
|
|
|
throw new IllegalArgumentException(USAGE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (_listenHost == null)
|
|
|
|
_listenHost = DEFAULT_LISTEN_HOST;
|
|
|
|
if (_sslListenHost == null)
|
|
|
|
_sslListenHost = _listenHost;
|
|
|
|
if (_webAppsDir == null)
|
|
|
|
_webAppsDir = DEFAULT_WEBAPPS_DIR;
|
|
|
|
// _listenPort and _sslListenPort are not defaulted, if one or the other is null, do not enable
|
|
|
|
if (_listenPort == null && _sslListenPort == null) {
|
|
|
|
System.err.println(USAGE);
|
|
|
|
throw new IllegalArgumentException(USAGE);
|
2004-07-24 02:06:07 +00:00
|
|
|
}
|
2012-10-13 13:54:30 +00:00
|
|
|
_state = INITIALIZED;
|
2004-07-24 02:06:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public static void main(String args[]) {
|
2012-10-13 13:06:22 +00:00
|
|
|
List<RouterContext> contexts = RouterContext.listContexts();
|
|
|
|
if (contexts == null || contexts.isEmpty())
|
|
|
|
throw new IllegalStateException("no router context");
|
2012-10-13 13:54:30 +00:00
|
|
|
RouterConsoleRunner runner = new RouterConsoleRunner(contexts.get(0), null, args);
|
|
|
|
runner.startup();
|
2004-07-24 02:06:07 +00:00
|
|
|
}
|
|
|
|
|
2012-10-13 13:54:30 +00:00
|
|
|
/////// ClientApp methods
|
|
|
|
|
|
|
|
/** @since 0.9.4 */
|
2013-04-24 15:45:15 +00:00
|
|
|
public synchronized void startup() {
|
2012-10-13 13:54:30 +00:00
|
|
|
changeState(STARTING);
|
2014-10-28 13:18:48 +00:00
|
|
|
checkJavaVersion();
|
2016-05-06 15:56:54 +00:00
|
|
|
startTrayApp();
|
2012-10-13 13:54:30 +00:00
|
|
|
startConsole();
|
2004-07-24 02:06:07 +00:00
|
|
|
}
|
2012-10-13 13:54:30 +00:00
|
|
|
|
|
|
|
/** @since 0.9.4 */
|
2013-04-24 15:45:15 +00:00
|
|
|
public synchronized void shutdown(String[] args) {
|
|
|
|
if (_state == STOPPED)
|
|
|
|
return;
|
2012-10-13 13:54:30 +00:00
|
|
|
changeState(STOPPING);
|
2013-04-24 15:45:15 +00:00
|
|
|
if (PluginStarter.pluginsEnabled(_context))
|
|
|
|
(new I2PAppThread(new PluginStopper(_context), "PluginStopper")).start();
|
2017-04-01 14:15:06 +00:00
|
|
|
stopAllWebApps();
|
2012-10-13 13:54:30 +00:00
|
|
|
try {
|
|
|
|
_server.stop();
|
|
|
|
} catch (Exception ie) {}
|
|
|
|
PortMapper portMapper = _context.portMapper();
|
|
|
|
portMapper.unregister(PortMapper.SVC_CONSOLE);
|
|
|
|
portMapper.unregister(PortMapper.SVC_HTTPS_CONSOLE);
|
2017-02-05 20:56:40 +00:00
|
|
|
synchronized(RouterConsoleRunner.class) {
|
|
|
|
if (_jettyTimer != null) {
|
2017-03-11 16:27:37 +00:00
|
|
|
try {
|
|
|
|
_jettyTimer.stop();
|
|
|
|
} catch (Exception e) {}
|
2017-02-05 20:56:40 +00:00
|
|
|
_jettyTimer = null;
|
|
|
|
}
|
|
|
|
}
|
2012-10-13 13:54:30 +00:00
|
|
|
changeState(STOPPED);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @since 0.9.4 */
|
|
|
|
public ClientAppState getState() {
|
|
|
|
return _state;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @since 0.9.4 */
|
|
|
|
public String getName() {
|
|
|
|
return "console";
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @since 0.9.4 */
|
|
|
|
public String getDisplayName() {
|
|
|
|
return "Router Console";
|
|
|
|
}
|
|
|
|
|
|
|
|
/////// end ClientApp methods
|
|
|
|
|
|
|
|
private synchronized void changeState(ClientAppState state) {
|
|
|
|
_state = state;
|
|
|
|
if (_mgr != null)
|
|
|
|
_mgr.notify(this, state, null, null);
|
|
|
|
}
|
|
|
|
|
2011-12-23 00:56:48 +00:00
|
|
|
/**
|
|
|
|
* SInce _server is now static
|
|
|
|
* @return may be null or stopped perhaps
|
|
|
|
* @since Jetty 6 since it doesn't have Server.getServers()
|
|
|
|
*/
|
|
|
|
static Server getConsoleServer() {
|
|
|
|
return _server;
|
|
|
|
}
|
|
|
|
|
2017-12-01 14:07:29 +00:00
|
|
|
/** @since 0.8.13, moved from LogsHelper in 0.9.33 */
|
|
|
|
public static String jettyVersion() {
|
|
|
|
return Server.getVersion();
|
|
|
|
}
|
|
|
|
|
2016-05-06 15:56:54 +00:00
|
|
|
private void startTrayApp() {
|
|
|
|
// if no permissions, don't even try
|
|
|
|
// isLaunchedAsService() always returns true on Linux
|
2017-01-12 15:54:06 +00:00
|
|
|
if (GraphicsEnvironment.isHeadless() || SystemVersion.isLinuxService() ||
|
2016-05-06 15:56:54 +00:00
|
|
|
(SystemVersion.isWindows() && _context.hasWrapper() && WrapperManager.isLaunchedAsService())) {
|
|
|
|
// required true for jrobin to work
|
|
|
|
System.setProperty("java.awt.headless", "true");
|
|
|
|
return;
|
|
|
|
}
|
2011-02-27 13:53:39 +00:00
|
|
|
try {
|
2017-11-19 00:40:50 +00:00
|
|
|
// default false for now, except on OSX and non-service windows
|
2016-09-24 14:54:30 +00:00
|
|
|
String sdtg = _context.getProperty(PROP_DTG_ENABLED);
|
|
|
|
boolean desktopguiEnabled = Boolean.parseBoolean(sdtg) ||
|
2017-11-19 00:40:50 +00:00
|
|
|
(sdtg == null && (SystemVersion.isWindows() || SystemVersion.isMac()));
|
2016-05-06 15:56:54 +00:00
|
|
|
if (desktopguiEnabled) {
|
2017-01-12 15:54:06 +00:00
|
|
|
System.setProperty("java.awt.headless", "false");
|
2016-05-06 15:56:54 +00:00
|
|
|
net.i2p.desktopgui.Main dtg = new net.i2p.desktopgui.Main(_context, _mgr, null);
|
|
|
|
dtg.startup();
|
2011-02-27 13:53:39 +00:00
|
|
|
} else {
|
|
|
|
// required true for jrobin to work
|
|
|
|
System.setProperty("java.awt.headless", "true");
|
2013-04-17 14:49:25 +00:00
|
|
|
// this check is in SysTray but do it here too
|
2016-10-25 23:59:20 +00:00
|
|
|
//if (SystemVersion.isWindows() && (!Boolean.getBoolean("systray.disable")) && (!SystemVersion.is64Bit()))
|
|
|
|
// SysTray.getInstance();
|
2011-02-27 13:53:39 +00:00
|
|
|
}
|
|
|
|
} catch (Throwable t) {
|
|
|
|
t.printStackTrace();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-28 13:18:48 +00:00
|
|
|
/** @since 0.9.17 */
|
|
|
|
private void checkJavaVersion() {
|
2014-11-02 16:42:35 +00:00
|
|
|
boolean noJava7 = !SystemVersion.isJava7();
|
2016-04-16 15:53:34 +00:00
|
|
|
boolean noPack200 = (PluginStarter.pluginsEnabled(_context) || !NewsHelper.isUpdateDisabled(_context)) &&
|
|
|
|
!FileUtil.isPack200Supported();
|
|
|
|
boolean openARM = SystemVersion.isARM() && SystemVersion.isOpenJDK();
|
2016-11-06 15:31:01 +00:00
|
|
|
boolean isJava9 = SystemVersion.isJava9();
|
|
|
|
if (noJava7 || noPack200 || openARM || isJava9) {
|
2014-10-28 13:18:48 +00:00
|
|
|
String s = "Java version: " + System.getProperty("java.version") +
|
|
|
|
" OS: " + System.getProperty("os.name") + ' ' +
|
|
|
|
System.getProperty("os.arch") + ' ' +
|
|
|
|
System.getProperty("os.version");
|
|
|
|
net.i2p.util.Log log = _context.logManager().getLog(RouterConsoleRunner.class);
|
|
|
|
log.logAlways(net.i2p.util.Log.WARN, s);
|
|
|
|
System.out.println("Warning: " + s);
|
2014-11-02 16:42:35 +00:00
|
|
|
if (noJava7) {
|
2016-04-16 15:53:34 +00:00
|
|
|
s = "Java 7 is now required, please upgrade";
|
2014-11-02 16:42:35 +00:00
|
|
|
log.logAlways(net.i2p.util.Log.WARN, s);
|
|
|
|
System.out.println("Warning: " + s);
|
|
|
|
}
|
|
|
|
if (noPack200) {
|
2016-04-16 15:53:34 +00:00
|
|
|
s = "Pack200 is required for plugins and automatic updates, please upgrade Java";
|
|
|
|
log.logAlways(net.i2p.util.Log.WARN, s);
|
|
|
|
System.out.println("Warning: " + s);
|
|
|
|
}
|
|
|
|
if (openARM) {
|
|
|
|
s = "OpenJDK is not recommended for ARM. Use Oracle Java 8";
|
2014-11-02 16:42:35 +00:00
|
|
|
log.logAlways(net.i2p.util.Log.WARN, s);
|
|
|
|
System.out.println("Warning: " + s);
|
|
|
|
}
|
2016-11-06 15:31:01 +00:00
|
|
|
if (isJava9) {
|
|
|
|
s = "Java 9 support is beta, and not recommended for general use";
|
|
|
|
log.logAlways(net.i2p.util.Log.WARN, s);
|
|
|
|
System.out.println("Warning: " + s);
|
|
|
|
}
|
2014-10-28 13:18:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-12-23 00:56:48 +00:00
|
|
|
/**
|
|
|
|
* http://irc.codehaus.org/display/JETTY/Porting+to+jetty6
|
|
|
|
*
|
|
|
|
*<pre>
|
|
|
|
* Server
|
|
|
|
* HandlerCollection
|
2017-10-11 16:26:37 +00:00
|
|
|
* HostCheckHandler
|
|
|
|
* ContextHandlerCollection
|
|
|
|
* WebAppContext (i.e. ContextHandler)
|
|
|
|
* SessionHandler
|
|
|
|
* SecurityHandler
|
|
|
|
* ServletHandler
|
|
|
|
* servlets...
|
|
|
|
* WebAppContext
|
|
|
|
* ...
|
2011-12-23 00:56:48 +00:00
|
|
|
* DefaultHandler
|
|
|
|
* RequestLogHandler (opt)
|
|
|
|
*</pre>
|
2015-08-09 13:36:24 +00:00
|
|
|
*
|
|
|
|
* Porting to Jetty 9:
|
|
|
|
*
|
|
|
|
* http://dev.eclipse.org/mhonarc/lists/jetty-dev/msg01952.html
|
|
|
|
* You are missing a few facts about Jetty 9.1 ...
|
|
|
|
* First, there are no longer any blocking connectors.
|
|
|
|
* Its all async / nio connectors now. (mainly because that's the direction that the servlet api 3.1 is taking)
|
|
|
|
*
|
|
|
|
* Next, there is only 1 connector. The ServerConnector.
|
|
|
|
* However, it takes 1 or more ConnectionFactory implementations to know how to handle the incoming connection.
|
|
|
|
* We have factories for HTTP (0.9 thru 1.1), SPDY, SSL-http, and SSL-npn so far.
|
|
|
|
* This list of factories will expand as the future of connectivity to web servers is ever growing (think HTTP/2)
|
|
|
|
*
|
|
|
|
* Use the embedded examples for help understanding this.
|
|
|
|
* http://git.eclipse.org/c/jetty/org.eclipse.jetty.project.git/tree/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyConnectors.java?id=jetty-9.1.0.RC0
|
|
|
|
*
|
2011-12-23 00:56:48 +00:00
|
|
|
*/
|
2004-07-24 02:06:07 +00:00
|
|
|
public void startConsole() {
|
2012-10-13 13:06:22 +00:00
|
|
|
File workDir = new SecureDirectory(_context.getTempDir(), "jetty-work");
|
2004-09-29 19:34:02 +00:00
|
|
|
boolean workDirRemoved = FileUtil.rmdir(workDir, false);
|
|
|
|
if (!workDirRemoved)
|
|
|
|
System.err.println("ERROR: Unable to remove Jetty temporary work directory");
|
|
|
|
boolean workDirCreated = workDir.mkdirs();
|
|
|
|
if (!workDirCreated)
|
|
|
|
System.err.println("ERROR: Unable to create Jetty temporary work directory");
|
2011-12-31 00:49:39 +00:00
|
|
|
|
2010-02-06 18:40:55 +00:00
|
|
|
// so Jetty can find WebAppConfiguration
|
2012-10-13 13:06:22 +00:00
|
|
|
System.setProperty("jetty.class.path", _context.getBaseDir() + "/lib/routerconsole.jar");
|
2015-08-09 13:36:24 +00:00
|
|
|
// FIXME
|
|
|
|
// http://dev.eclipse.org/mhonarc/lists/jetty-users/msg03487.html
|
|
|
|
//_server.setGracefulShutdown(1000);
|
2010-12-31 13:14:15 +00:00
|
|
|
|
Console and Eepsite Jetty:
Switch back to QueuedThreadPool (ticket #1395)
In Jetty 5/6, the default QTP was not concurrent, so we switched to
ThreadPoolExecutor with a fixed-size queue, a set maxThreads,
and a RejectedExecutionPolicy of CallerRuns.
Unfortunately, CallerRuns causes lockups in Jetty NIO.
In addition, no flavor of TPE gives us what QTP does:
- TPE direct handoff (which we were using) never queues.
This doesn't provide any burst management when maxThreads is reached.
CallerRuns was an attempt to work around that.
- TPE unbounded queue does not adjust the number of threads.
This doesn't provide automatic resource management.
- TPE bounded queue does not add threads until the queue is full.
This doesn't provide good responsiveness to even small bursts.
QTP adds threads as soon as the queue is non-empty.
QTP as of Jetty 7 uses concurrent.
QTP unbounded queue is the default in Jetty.
So switch back to QTP with a bounded queue, which does what we want,
which is first expand the thread pool, then start queueing, then reject.
ref:
http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ThreadPoolExecutor.html
https://wiki.eclipse.org/Jetty/Howto/High_Load
2014-10-20 14:01:36 +00:00
|
|
|
// In Jetty 6, QTP was not concurrent, so we switched to
|
|
|
|
// ThreadPoolExecutor with a fixed-size queue, a set maxThreads,
|
|
|
|
// and a RejectedExecutionPolicy of CallerRuns.
|
|
|
|
// Unfortunately, CallerRuns causes lockups in Jetty NIO (ticket #1395)
|
|
|
|
// In addition, no flavor of TPE gives us what QTP does:
|
|
|
|
// - TPE direct handoff (which we were using) never queues.
|
|
|
|
// This doesn't provide any burst management when maxThreads is reached.
|
|
|
|
// CallerRuns was an attempt to work around that.
|
|
|
|
// - TPE unbounded queue does not adjust the number of threads.
|
|
|
|
// This doesn't provide automatic resource management.
|
|
|
|
// - TPE bounded queue does not add threads until the queue is full.
|
|
|
|
// This doesn't provide good responsiveness to even small bursts.
|
|
|
|
// QTP adds threads as soon as the queue is non-empty.
|
|
|
|
// QTP as of Jetty 7 uses concurrent.
|
|
|
|
// QTP unbounded queue is the default in Jetty.
|
|
|
|
// So switch back to QTP with a bounded queue.
|
|
|
|
//
|
|
|
|
// ref:
|
|
|
|
// http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ThreadPoolExecutor.html
|
|
|
|
// https://wiki.eclipse.org/Jetty/Howto/High_Load
|
|
|
|
//
|
|
|
|
//try {
|
|
|
|
// ThreadPool ctp = new CustomThreadPoolExecutor();
|
|
|
|
// // Gone in Jetty 7
|
|
|
|
// //ctp.prestartAllCoreThreads();
|
|
|
|
// _server.setThreadPool(ctp);
|
|
|
|
//} catch (Throwable t) {
|
2011-12-23 00:56:48 +00:00
|
|
|
// class not found...
|
Console and Eepsite Jetty:
Switch back to QueuedThreadPool (ticket #1395)
In Jetty 5/6, the default QTP was not concurrent, so we switched to
ThreadPoolExecutor with a fixed-size queue, a set maxThreads,
and a RejectedExecutionPolicy of CallerRuns.
Unfortunately, CallerRuns causes lockups in Jetty NIO.
In addition, no flavor of TPE gives us what QTP does:
- TPE direct handoff (which we were using) never queues.
This doesn't provide any burst management when maxThreads is reached.
CallerRuns was an attempt to work around that.
- TPE unbounded queue does not adjust the number of threads.
This doesn't provide automatic resource management.
- TPE bounded queue does not add threads until the queue is full.
This doesn't provide good responsiveness to even small bursts.
QTP adds threads as soon as the queue is non-empty.
QTP as of Jetty 7 uses concurrent.
QTP unbounded queue is the default in Jetty.
So switch back to QTP with a bounded queue, which does what we want,
which is first expand the thread pool, then start queueing, then reject.
ref:
http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ThreadPoolExecutor.html
https://wiki.eclipse.org/Jetty/Howto/High_Load
2014-10-20 14:01:36 +00:00
|
|
|
//System.out.println("INFO: Jetty concurrent ThreadPool unavailable, using QueuedThreadPool");
|
|
|
|
LinkedBlockingQueue<Runnable> lbq = new LinkedBlockingQueue<Runnable>(4*MAX_THREADS);
|
2015-08-09 13:36:24 +00:00
|
|
|
// min and max threads will be reset below
|
|
|
|
QueuedThreadPool qtp = new QueuedThreadPool(MAX_THREADS, MIN_THREADS, MAX_IDLE_TIME, lbq);
|
Console and Eepsite Jetty:
Switch back to QueuedThreadPool (ticket #1395)
In Jetty 5/6, the default QTP was not concurrent, so we switched to
ThreadPoolExecutor with a fixed-size queue, a set maxThreads,
and a RejectedExecutionPolicy of CallerRuns.
Unfortunately, CallerRuns causes lockups in Jetty NIO.
In addition, no flavor of TPE gives us what QTP does:
- TPE direct handoff (which we were using) never queues.
This doesn't provide any burst management when maxThreads is reached.
CallerRuns was an attempt to work around that.
- TPE unbounded queue does not adjust the number of threads.
This doesn't provide automatic resource management.
- TPE bounded queue does not add threads until the queue is full.
This doesn't provide good responsiveness to even small bursts.
QTP adds threads as soon as the queue is non-empty.
QTP as of Jetty 7 uses concurrent.
QTP unbounded queue is the default in Jetty.
So switch back to QTP with a bounded queue, which does what we want,
which is first expand the thread pool, then start queueing, then reject.
ref:
http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ThreadPoolExecutor.html
https://wiki.eclipse.org/Jetty/Howto/High_Load
2014-10-20 14:01:36 +00:00
|
|
|
qtp.setName(THREAD_NAME);
|
|
|
|
qtp.setDaemon(true);
|
2015-08-09 13:36:24 +00:00
|
|
|
_server = new Server(qtp);
|
Console and Eepsite Jetty:
Switch back to QueuedThreadPool (ticket #1395)
In Jetty 5/6, the default QTP was not concurrent, so we switched to
ThreadPoolExecutor with a fixed-size queue, a set maxThreads,
and a RejectedExecutionPolicy of CallerRuns.
Unfortunately, CallerRuns causes lockups in Jetty NIO.
In addition, no flavor of TPE gives us what QTP does:
- TPE direct handoff (which we were using) never queues.
This doesn't provide any burst management when maxThreads is reached.
CallerRuns was an attempt to work around that.
- TPE unbounded queue does not adjust the number of threads.
This doesn't provide automatic resource management.
- TPE bounded queue does not add threads until the queue is full.
This doesn't provide good responsiveness to even small bursts.
QTP adds threads as soon as the queue is non-empty.
QTP as of Jetty 7 uses concurrent.
QTP unbounded queue is the default in Jetty.
So switch back to QTP with a bounded queue, which does what we want,
which is first expand the thread pool, then start queueing, then reject.
ref:
http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ThreadPoolExecutor.html
https://wiki.eclipse.org/Jetty/Howto/High_Load
2014-10-20 14:01:36 +00:00
|
|
|
//}
|
2011-12-24 17:09:01 +00:00
|
|
|
|
2011-12-23 00:56:48 +00:00
|
|
|
HandlerCollection hColl = new HandlerCollection();
|
|
|
|
ContextHandlerCollection chColl = new ContextHandlerCollection();
|
2017-10-11 16:26:37 +00:00
|
|
|
HostCheckHandler chCollWrapper = new HostCheckHandler(_context);
|
|
|
|
chCollWrapper.setHandler(chColl);
|
2012-11-21 20:49:18 +00:00
|
|
|
// gone in Jetty 7
|
|
|
|
//_server.addHandler(hColl);
|
|
|
|
_server.setHandler(hColl);
|
2017-10-11 16:26:37 +00:00
|
|
|
hColl.addHandler(chCollWrapper);
|
2011-12-23 00:56:48 +00:00
|
|
|
hColl.addHandler(new DefaultHandler());
|
2010-12-31 13:14:15 +00:00
|
|
|
|
2012-10-13 13:06:22 +00:00
|
|
|
String log = _context.getProperty("routerconsole.log");
|
2010-12-31 13:14:15 +00:00
|
|
|
if (log != null) {
|
|
|
|
File logFile = new File(log);
|
|
|
|
if (!logFile.isAbsolute())
|
2012-10-13 13:06:22 +00:00
|
|
|
logFile = new File(_context.getLogDir(), "logs/" + log);
|
2010-12-31 13:14:15 +00:00
|
|
|
try {
|
2011-12-23 00:56:48 +00:00
|
|
|
RequestLogHandler rhl = new RequestLogHandler();
|
|
|
|
rhl.setRequestLog(new NCSARequestLog(logFile.getAbsolutePath()));
|
|
|
|
hColl.addHandler(rhl);
|
|
|
|
} catch (Exception ioe) {
|
2010-12-31 13:14:15 +00:00
|
|
|
System.err.println("ERROR: Unable to create Jetty log: " + ioe);
|
|
|
|
}
|
|
|
|
}
|
2008-06-16 12:26:36 +00:00
|
|
|
boolean rewrite = false;
|
|
|
|
Properties props = webAppProperties();
|
2010-05-05 17:50:28 +00:00
|
|
|
if (props.isEmpty()) {
|
2008-06-16 12:26:36 +00:00
|
|
|
props.setProperty(PREFIX + ROUTERCONSOLE + ENABLED, "true");
|
|
|
|
rewrite = true;
|
|
|
|
}
|
2009-06-13 23:47:08 +00:00
|
|
|
|
|
|
|
// Get an absolute path with a trailing slash for the webapps dir
|
|
|
|
// We assume relative to the base install dir for backward compatibility
|
|
|
|
File app = new File(_webAppsDir);
|
|
|
|
if (!app.isAbsolute()) {
|
2012-10-13 13:06:22 +00:00
|
|
|
app = new File(_context.getBaseDir(), _webAppsDir);
|
2009-06-13 23:47:08 +00:00
|
|
|
try {
|
|
|
|
_webAppsDir = app.getCanonicalPath();
|
|
|
|
} catch (IOException ioe) {}
|
|
|
|
}
|
|
|
|
if (!_webAppsDir.endsWith("/"))
|
|
|
|
_webAppsDir += '/';
|
|
|
|
|
2017-10-11 16:26:37 +00:00
|
|
|
Set<String> listenHosts = new HashSet<String>(8);
|
2012-11-21 20:49:18 +00:00
|
|
|
HandlerWrapper rootWebApp = null;
|
2011-12-24 05:26:56 +00:00
|
|
|
ServletHandler rootServletHandler = null;
|
2013-11-21 11:31:50 +00:00
|
|
|
List<Connector> connectors = new ArrayList<Connector>(4);
|
2004-07-24 02:06:07 +00:00
|
|
|
try {
|
2009-04-08 01:34:12 +00:00
|
|
|
int boundAddresses = 0;
|
2013-11-21 11:31:50 +00:00
|
|
|
SortedSet<String> addresses = Addresses.getAllAddresses();
|
2012-03-10 22:07:15 +00:00
|
|
|
boolean hasIPV4 = addresses.contains("0.0.0.0");
|
|
|
|
boolean hasIPV6 = addresses.contains("0:0:0:0:0:0:0:0");
|
2010-12-05 19:04:33 +00:00
|
|
|
|
|
|
|
// add standard listeners
|
2012-03-10 21:53:29 +00:00
|
|
|
int lport = 0;
|
2010-12-05 19:04:33 +00:00
|
|
|
if (_listenPort != null) {
|
2012-03-10 21:53:29 +00:00
|
|
|
try {
|
|
|
|
lport = Integer.parseInt(_listenPort);
|
|
|
|
} catch (NumberFormatException nfe) {}
|
|
|
|
if (lport <= 0)
|
|
|
|
System.err.println("Bad routerconsole port " + _listenPort);
|
|
|
|
}
|
|
|
|
if (lport > 0) {
|
2016-01-06 17:50:06 +00:00
|
|
|
List<String> hosts = new ArrayList<String>(2);
|
2010-12-05 19:04:33 +00:00
|
|
|
StringTokenizer tok = new StringTokenizer(_listenHost, " ,");
|
|
|
|
while (tok.hasMoreTokens()) {
|
|
|
|
String host = tok.nextToken().trim();
|
|
|
|
try {
|
2012-03-10 21:53:29 +00:00
|
|
|
// Test before we add the connector, because Jetty 6 won't start if any of the
|
|
|
|
// connectors are bad
|
2018-02-25 14:17:01 +00:00
|
|
|
if ((!hasIPV6) && InetAddressUtils.isIPv6Address(host))
|
2012-03-12 14:30:38 +00:00
|
|
|
throw new IOException("IPv6 addresses unsupported");
|
2018-02-25 14:17:01 +00:00
|
|
|
if ((!hasIPV4) && InetAddressUtils.isIPv4Address(host))
|
2012-03-12 14:30:38 +00:00
|
|
|
throw new IOException("IPv4 addresses unsupported");
|
2012-03-26 14:07:38 +00:00
|
|
|
ServerSocket testSock = null;
|
2012-03-12 14:30:38 +00:00
|
|
|
try {
|
2012-03-26 14:07:38 +00:00
|
|
|
// On Windows, this was passing and Jetty was still failing,
|
|
|
|
// possibly due to %scope_id ???
|
|
|
|
// https://issues.apache.org/jira/browse/ZOOKEEPER-667
|
|
|
|
// so do exactly what Jetty does in SelectChannelConnector.open()
|
|
|
|
testSock = new ServerSocket();
|
|
|
|
InetSocketAddress isa = new InetSocketAddress(host, 0);
|
|
|
|
testSock.bind(isa);
|
2012-03-12 14:30:38 +00:00
|
|
|
} finally {
|
|
|
|
if (testSock != null) try { testSock.close(); } catch (IOException ioe) {}
|
|
|
|
}
|
2015-08-09 13:36:24 +00:00
|
|
|
HttpConfiguration httpConfig = new HttpConfiguration();
|
|
|
|
// number of acceptors, (default) number of selectors
|
|
|
|
ServerConnector lsnr = new ServerConnector(_server, 1, 0,
|
|
|
|
new HttpConnectionFactory(httpConfig));
|
|
|
|
//lsnr.setUseDirectBuffers(false); // default true seems to be leaky
|
2011-12-23 00:56:48 +00:00
|
|
|
lsnr.setHost(host);
|
|
|
|
lsnr.setPort(lport);
|
2015-08-09 13:36:24 +00:00
|
|
|
lsnr.setIdleTimeout(90*1000); // default 10 sec
|
2011-01-19 20:16:18 +00:00
|
|
|
lsnr.setName("ConsoleSocket"); // all with same name will use the same thread pool
|
2012-04-12 14:54:50 +00:00
|
|
|
//_server.addConnector(lsnr);
|
|
|
|
connectors.add(lsnr);
|
2010-12-05 19:04:33 +00:00
|
|
|
boundAddresses++;
|
2016-01-06 17:50:06 +00:00
|
|
|
hosts.add(host);
|
2012-03-12 14:30:38 +00:00
|
|
|
} catch (Exception ioe) {
|
|
|
|
System.err.println("Unable to bind routerconsole to " + host + " port " + _listenPort + ": " + ioe);
|
|
|
|
System.err.println("You may ignore this warning if the console is still available at http://localhost:" + _listenPort);
|
2010-12-05 19:04:33 +00:00
|
|
|
}
|
|
|
|
}
|
2016-01-06 17:50:06 +00:00
|
|
|
if (hosts.isEmpty()) {
|
|
|
|
_context.portMapper().register(PortMapper.SVC_CONSOLE, lport);
|
|
|
|
} else {
|
|
|
|
// put IPv4 first
|
|
|
|
Collections.sort(hosts, new HostComparator());
|
|
|
|
_context.portMapper().register(PortMapper.SVC_CONSOLE, hosts.get(0), lport);
|
|
|
|
// note that we could still fail in connector.start() below
|
2017-10-11 16:26:37 +00:00
|
|
|
listenHosts.addAll(hosts);
|
2016-01-06 17:50:06 +00:00
|
|
|
}
|
2010-12-05 19:04:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// add SSL listeners
|
|
|
|
int sslPort = 0;
|
|
|
|
if (_sslListenPort != null) {
|
2009-04-08 01:34:12 +00:00
|
|
|
try {
|
2010-12-05 19:04:33 +00:00
|
|
|
sslPort = Integer.parseInt(_sslListenPort);
|
|
|
|
} catch (NumberFormatException nfe) {}
|
|
|
|
if (sslPort <= 0)
|
|
|
|
System.err.println("Bad routerconsole SSL port " + _sslListenPort);
|
|
|
|
}
|
|
|
|
if (sslPort > 0) {
|
2012-10-13 13:06:22 +00:00
|
|
|
File keyStore = new File(_context.getConfigDir(), "keystore/console.ks");
|
2018-02-25 14:17:01 +00:00
|
|
|
// Put the list of hosts together early, so we can put it in the selfsigned cert.
|
|
|
|
StringTokenizer tok = new StringTokenizer(_sslListenHost, " ,");
|
|
|
|
Set<String> altNames = new HashSet<String>(4);
|
|
|
|
while (tok.hasMoreTokens()) {
|
|
|
|
String s = tok.nextToken().trim();
|
|
|
|
if (!s.equals("0.0.0.0") && !s.equals("::") &&
|
|
|
|
!s.equals("0:0:0:0:0:0:0:0"))
|
|
|
|
altNames.add(s);
|
|
|
|
}
|
|
|
|
String allowed = _context.getProperty(PROP_ALLOWED_HOSTS);
|
|
|
|
if (allowed != null) {
|
|
|
|
tok = new StringTokenizer(allowed, " ,");
|
|
|
|
while (tok.hasMoreTokens()) {
|
|
|
|
altNames.add(tok.nextToken().trim());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (verifyKeyStore(keyStore, altNames)) {
|
2013-04-15 15:53:29 +00:00
|
|
|
// the keystore path and password
|
|
|
|
SslContextFactory sslFactory = new SslContextFactory(keyStore.getAbsolutePath());
|
2017-12-07 14:31:28 +00:00
|
|
|
sslFactory.setKeyStorePassword(_context.getProperty(PROP_KEYSTORE_PASSWORD, KeyStoreUtil.DEFAULT_KEYSTORE_PASSWORD));
|
2013-04-15 15:53:29 +00:00
|
|
|
// the X.509 cert password (if not present, verifyKeyStore() returned false)
|
|
|
|
sslFactory.setKeyManagerPassword(_context.getProperty(PROP_KEY_PASSWORD, "thisWontWork"));
|
2014-10-15 20:44:23 +00:00
|
|
|
sslFactory.addExcludeProtocols(I2PSSLSocketFactory.EXCLUDE_PROTOCOLS.toArray(
|
|
|
|
new String[I2PSSLSocketFactory.EXCLUDE_PROTOCOLS.size()]));
|
2015-08-02 12:58:00 +00:00
|
|
|
sslFactory.addExcludeCipherSuites(I2PSSLSocketFactory.EXCLUDE_CIPHERS.toArray(
|
2014-10-15 20:44:23 +00:00
|
|
|
new String[I2PSSLSocketFactory.EXCLUDE_CIPHERS.size()]));
|
2016-01-06 17:50:06 +00:00
|
|
|
List<String> hosts = new ArrayList<String>(2);
|
2018-02-25 14:17:01 +00:00
|
|
|
tok = new StringTokenizer(_sslListenHost, " ,");
|
2010-12-05 19:04:33 +00:00
|
|
|
while (tok.hasMoreTokens()) {
|
|
|
|
String host = tok.nextToken().trim();
|
|
|
|
// doing it this way means we don't have to escape an IPv6 host with []
|
|
|
|
try {
|
2012-03-10 21:53:29 +00:00
|
|
|
// Test before we add the connector, because Jetty 6 won't start if any of the
|
|
|
|
// connectors are bad
|
2018-02-25 14:17:01 +00:00
|
|
|
if ((!hasIPV6) && InetAddressUtils.isIPv6Address(host))
|
2012-03-12 14:30:38 +00:00
|
|
|
throw new IOException("IPv6 addresses unsupported");
|
2018-02-25 14:17:01 +00:00
|
|
|
if ((!hasIPV4) && InetAddressUtils.isIPv4Address(host))
|
2012-03-12 14:30:38 +00:00
|
|
|
throw new IOException("IPv4 addresses unsupported");
|
|
|
|
ServerSocket testSock = null;
|
|
|
|
try {
|
2012-03-26 14:07:38 +00:00
|
|
|
// see comments above
|
|
|
|
testSock = new ServerSocket();
|
|
|
|
InetSocketAddress isa = new InetSocketAddress(host, 0);
|
|
|
|
testSock.bind(isa);
|
2012-03-12 14:30:38 +00:00
|
|
|
} finally {
|
|
|
|
if (testSock != null) try { testSock.close(); } catch (IOException ioe) {}
|
|
|
|
}
|
2015-08-09 13:36:24 +00:00
|
|
|
HttpConfiguration httpConfig = new HttpConfiguration();
|
|
|
|
httpConfig.setSecureScheme("https");
|
|
|
|
httpConfig.setSecurePort(sslPort);
|
|
|
|
httpConfig.addCustomizer(new SecureRequestCustomizer());
|
|
|
|
// number of acceptors, (default) number of selectors
|
|
|
|
ServerConnector ssll = new ServerConnector(_server, 1, 0,
|
|
|
|
new SslConnectionFactory(sslFactory, "http/1.1"),
|
|
|
|
new HttpConnectionFactory(httpConfig));
|
|
|
|
//sssll.setUseDirectBuffers(false); // default true seems to be leaky
|
2012-10-14 13:54:08 +00:00
|
|
|
ssll.setHost(host);
|
|
|
|
ssll.setPort(sslPort);
|
2015-08-09 13:36:24 +00:00
|
|
|
ssll.setIdleTimeout(90*1000); // default 10 sec
|
2011-01-19 20:16:18 +00:00
|
|
|
ssll.setName("ConsoleSocket"); // all with same name will use the same thread pool
|
2012-04-12 14:54:50 +00:00
|
|
|
//_server.addConnector(ssll);
|
|
|
|
connectors.add(ssll);
|
2010-12-05 19:04:33 +00:00
|
|
|
boundAddresses++;
|
2016-01-06 17:50:06 +00:00
|
|
|
hosts.add(host);
|
2012-03-12 14:30:38 +00:00
|
|
|
} catch (Exception e) {
|
2010-12-27 15:56:19 +00:00
|
|
|
System.err.println("Unable to bind routerconsole to " + host + " port " + sslPort + " for SSL: " + e);
|
2012-10-14 13:54:08 +00:00
|
|
|
if (SystemVersion.isGNU())
|
|
|
|
System.err.println("Probably because GNU classpath does not support Sun keystores");
|
2012-03-12 14:30:38 +00:00
|
|
|
System.err.println("You may ignore this warning if the console is still available at https://localhost:" + sslPort);
|
2010-12-05 19:04:33 +00:00
|
|
|
}
|
|
|
|
}
|
2016-01-06 17:50:06 +00:00
|
|
|
if (hosts.isEmpty()) {
|
|
|
|
_context.portMapper().register(PortMapper.SVC_HTTPS_CONSOLE, sslPort);
|
|
|
|
} else {
|
|
|
|
// put IPv4 first
|
|
|
|
Collections.sort(hosts, new HostComparator());
|
|
|
|
_context.portMapper().register(PortMapper.SVC_HTTPS_CONSOLE, hosts.get(0), sslPort);
|
|
|
|
// note that we could still fail in connector.start() below
|
2017-10-11 16:26:37 +00:00
|
|
|
listenHosts.addAll(hosts);
|
2016-01-06 17:50:06 +00:00
|
|
|
}
|
2010-12-05 19:04:33 +00:00
|
|
|
} else {
|
|
|
|
System.err.println("Unable to create or access keystore for SSL: " + keyStore.getAbsolutePath());
|
2009-04-08 01:34:12 +00:00
|
|
|
}
|
|
|
|
}
|
2010-12-05 19:04:33 +00:00
|
|
|
|
2009-04-08 01:34:12 +00:00
|
|
|
if (boundAddresses <= 0) {
|
2010-12-05 19:04:33 +00:00
|
|
|
System.err.println("Unable to bind routerconsole to any address on port " + _listenPort + (sslPort > 0 ? (" or SSL port " + sslPort) : ""));
|
2009-04-08 01:34:12 +00:00
|
|
|
return;
|
|
|
|
}
|
Console and Eepsite Jetty:
Switch back to QueuedThreadPool (ticket #1395)
In Jetty 5/6, the default QTP was not concurrent, so we switched to
ThreadPoolExecutor with a fixed-size queue, a set maxThreads,
and a RejectedExecutionPolicy of CallerRuns.
Unfortunately, CallerRuns causes lockups in Jetty NIO.
In addition, no flavor of TPE gives us what QTP does:
- TPE direct handoff (which we were using) never queues.
This doesn't provide any burst management when maxThreads is reached.
CallerRuns was an attempt to work around that.
- TPE unbounded queue does not adjust the number of threads.
This doesn't provide automatic resource management.
- TPE bounded queue does not add threads until the queue is full.
This doesn't provide good responsiveness to even small bursts.
QTP adds threads as soon as the queue is non-empty.
QTP as of Jetty 7 uses concurrent.
QTP unbounded queue is the default in Jetty.
So switch back to QTP with a bounded queue, which does what we want,
which is first expand the thread pool, then start queueing, then reject.
ref:
http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ThreadPoolExecutor.html
https://wiki.eclipse.org/Jetty/Howto/High_Load
2014-10-20 14:01:36 +00:00
|
|
|
// Each address spawns a Connector and an Acceptor thread
|
|
|
|
// If the min is less than this, we have no thread for the handlers or the expiration thread.
|
|
|
|
qtp.setMinThreads(MIN_THREADS + (2 * boundAddresses));
|
|
|
|
qtp.setMaxThreads(MAX_THREADS + (2 * boundAddresses));
|
2011-12-23 00:56:48 +00:00
|
|
|
|
2010-12-27 15:56:19 +00:00
|
|
|
File tmpdir = new SecureDirectory(workDir, ROUTERCONSOLE + "-" +
|
|
|
|
(_listenPort != null ? _listenPort : _sslListenPort));
|
Big directory rework.
Eliminate all uses of the current working directory, and
set up multiple directories specified by absolute paths for various uses.
Add a WorkingDir class to create a user config directory and
migrate files to it for new installs.
The directory will be $HOME/.i2p on linux and %APPDIR%\I2P on Windows,
or as specified in the system property -Di2p.dir.config=/path/to/i2pdir
All files except for the base install and temp files will be
in the config directory by default.
Temp files will be in a i2p-xxxxx subdirectory of the system temp directory
specified by the system property java.io.tmpdir.
Convert all file opens in the code to be relative to a specific directory,
as specified in the context. Code and applications should never open
files relative to the current working directory (e.g. new File("foo")).
All files should be accessed in the appropriate context directory,
e.g. new File(_context.getAppDir(), "foo").
The router.config file location may be specified as a system property on the
java command line with -Drouter.configLocation=/path/to/router.config
All directories may be specified as properties in the router.config file.
The migration will copy all files from an existing installation,
except i2psnark/, with the system property -Di2p.dir.migrate=true.
Otherwise it will just set up a new directory with a minimal configuration.
The migration will also create a modified wrapper.config and (on linux only)
a modified i2prouter script, and place them in the config directory.
There are no changes to the installer or the default i2prouter, i2prouter.bat,
i2prouter, wrapper.config, runplain.sh, windows service installer/uninstaller,
etc. in this checkin.
* Directories. These are all set at instantiation and will not be changed by
* subsequent property changes.
* All properties, if set, should be absolute paths.
*
* Name Property Method Files
* ----- -------- ----- -----
* Base i2p.dir.base getBaseDir() lib/, webapps/, docs/, geoip/, licenses/, ...
* Temp i2p.dir.temp getTempDir() Temporary files
* Config i2p.dir.config getConfigDir() *.config, hosts.txt, addressbook/, ...
*
* (the following all default to the same as Config)
*
* Router i2p.dir.router getRouterDir() netDb/, peerProfiles/, router.*, keyBackup/, ...
* Log i2p.dir.log getLogDir() wrapper.log*, logs/
* PID i2p.dir.pid getPIDDir() wrapper *.pid files, router.ping
* App i2p.dir.app getAppDir() eepsite/, ...
*
* Note that we can't control where the wrapper actually puts its files.
All these will be set appropriately in a Router Context.
In an I2P App Context, all except Temp will be the current working directory.
Lightly tested so far, needs much more testing.
2009-06-04 19:14:40 +00:00
|
|
|
tmpdir.mkdir();
|
2011-12-24 05:26:56 +00:00
|
|
|
rootServletHandler = new ServletHandler();
|
2012-11-21 20:49:18 +00:00
|
|
|
rootWebApp = new LocaleWebAppHandler(_context,
|
|
|
|
"/", _webAppsDir + ROUTERCONSOLE + ".war",
|
|
|
|
tmpdir, rootServletHandler);
|
2017-03-03 15:48:10 +00:00
|
|
|
try {
|
|
|
|
// Not sure who is supposed to call this, but unless we do,
|
|
|
|
// all the jsps die NPE, because JspFactory.getDefaultContext() returns null.
|
|
|
|
// We probably have to do this because we don't bundle the Jetty annotations jar and scanner.
|
|
|
|
// This is only with Tomcat 8, not with the Jetty (Eclipse) jsp impl.
|
|
|
|
// Got a clue from this ancient post for Tomcat 6:
|
|
|
|
// https://bz.apache.org/bugzilla/show_bug.cgi?id=39804
|
|
|
|
// see also apps/jetty/build.xml
|
|
|
|
Class.forName("org.eclipse.jetty.apache.jsp.JettyJasperInitializer");
|
|
|
|
} catch (ClassNotFoundException cnfe) {
|
|
|
|
System.err.println("Warning: JettyJasperInitializer not found");
|
|
|
|
}
|
2017-03-04 14:24:48 +00:00
|
|
|
WebAppContext wac = (WebAppContext)(rootWebApp.getHandler());
|
|
|
|
initialize(_context, wac);
|
|
|
|
WebAppStarter.setWebAppConfiguration(wac);
|
2011-12-24 05:26:56 +00:00
|
|
|
chColl.addHandler(rootWebApp);
|
2011-12-23 00:56:48 +00:00
|
|
|
|
2011-12-29 23:25:27 +00:00
|
|
|
} catch (Exception ioe) {
|
|
|
|
ioe.printStackTrace();
|
|
|
|
}
|
|
|
|
|
2017-10-11 16:26:37 +00:00
|
|
|
// fix up the allowed hosts set (see HostCheckHandler)
|
|
|
|
if (listenHosts.contains("0.0.0.0") ||
|
|
|
|
listenHosts.contains("::") ||
|
|
|
|
listenHosts.contains("0:0:0:0:0:0:0:0")) {
|
|
|
|
// empty set says all are valid
|
|
|
|
listenHosts.clear();
|
|
|
|
} else {
|
|
|
|
listenHosts.add("localhost");
|
|
|
|
listenHosts.add("127.0.0.1");
|
|
|
|
listenHosts.add("::1");
|
|
|
|
listenHosts.add("0:0:0:0:0:0:0:1");
|
|
|
|
String allowed = _context.getProperty(PROP_ALLOWED_HOSTS);
|
|
|
|
if (allowed != null) {
|
|
|
|
StringTokenizer tok = new StringTokenizer(allowed, " ,");
|
|
|
|
while (tok.hasMoreTokens()) {
|
|
|
|
listenHosts.add(tok.nextToken());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
chCollWrapper.setListenHosts(listenHosts);
|
|
|
|
|
2013-04-08 15:29:02 +00:00
|
|
|
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=364936
|
|
|
|
// WARN:oejw.WebAppContext:Failed startup of context o.e.j.w.WebAppContext{/,jar:file:/.../webapps/routerconsole.war!/},/.../webapps/routerconsole.war
|
|
|
|
// java.lang.IllegalStateException: zip file closed
|
|
|
|
Resource.setDefaultUseCaches(false);
|
2011-12-29 23:25:27 +00:00
|
|
|
try {
|
|
|
|
// start does a mapContexts()
|
|
|
|
_server.start();
|
|
|
|
} catch (Throwable me) {
|
|
|
|
// NoClassFoundDefError from a webapp is a throwable, not an exception
|
2012-04-12 14:54:50 +00:00
|
|
|
System.err.println("Error starting the Router Console server: " + me);
|
|
|
|
me.printStackTrace();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_server.isRunning()) {
|
|
|
|
// Add and start the connectors one-by-one
|
|
|
|
boolean error = false;
|
|
|
|
for (Connector conn : connectors) {
|
|
|
|
try {
|
|
|
|
_server.addConnector(conn);
|
|
|
|
// start after adding so it gets the right thread pool
|
|
|
|
conn.start();
|
|
|
|
} catch (Throwable me) {
|
|
|
|
try {
|
|
|
|
_server.removeConnector(conn);
|
|
|
|
} catch (Throwable t) {
|
|
|
|
t.printStackTrace();
|
|
|
|
}
|
|
|
|
System.err.println("WARNING: Error starting " + conn + ": " + me);
|
|
|
|
me.printStackTrace();
|
|
|
|
error = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (error) {
|
2015-11-22 16:44:26 +00:00
|
|
|
String port = (_listenPort != null) ? _listenPort : ((_sslListenPort != null) ? _sslListenPort : "7657");
|
2012-04-12 14:54:50 +00:00
|
|
|
System.err.println("WARNING: Error starting one or more listeners of the Router Console server.\n" +
|
2015-11-22 16:44:26 +00:00
|
|
|
"If your console is still accessible at http://127.0.0.1:" + port + "/,\n" +
|
2011-12-29 23:25:27 +00:00
|
|
|
"this may be a problem only with binding to the IPV6 address ::1.\n" +
|
|
|
|
"If so, you may ignore this error, or remove the\n" +
|
2012-04-12 14:54:50 +00:00
|
|
|
"\"::1,\" in the \"clientApp.0.args\" line of the clients.config file.");
|
|
|
|
}
|
2011-12-29 23:25:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Start all the other webapps after the server is up,
|
|
|
|
// so things start faster.
|
|
|
|
// Jetty 6 starts the connector before the router console is ready
|
|
|
|
// This also prevents one webapp from breaking the whole thing
|
2013-11-21 11:31:50 +00:00
|
|
|
List<String> notStarted = new ArrayList<String>();
|
2011-12-29 23:25:27 +00:00
|
|
|
if (_server.isRunning()) {
|
2009-06-13 23:47:08 +00:00
|
|
|
File dir = new File(_webAppsDir);
|
2008-06-16 12:26:36 +00:00
|
|
|
String fileNames[] = dir.list(WarFilenameFilter.instance());
|
|
|
|
if (fileNames != null) {
|
|
|
|
for (int i = 0; i < fileNames.length; i++) {
|
2011-12-29 23:25:27 +00:00
|
|
|
String appName = fileNames[i].substring(0, fileNames[i].lastIndexOf(".war"));
|
|
|
|
String enabled = props.getProperty(PREFIX + appName + ENABLED);
|
2017-03-18 14:34:59 +00:00
|
|
|
if (appName.equals("addressbook")) {
|
|
|
|
// addressbook.war is now empty, thread is started by SusiDNS
|
|
|
|
if (enabled != null) {
|
|
|
|
props.remove(PREFIX + "addressbook" + ENABLED);
|
|
|
|
rewrite = true;
|
|
|
|
}
|
|
|
|
} else if (! "false".equals(enabled)) {
|
2011-12-29 23:25:27 +00:00
|
|
|
try {
|
2008-06-16 12:26:36 +00:00
|
|
|
String path = new File(dir, fileNames[i]).getCanonicalPath();
|
2012-10-13 13:06:22 +00:00
|
|
|
WebAppStarter.startWebApp(_context, chColl, appName, path);
|
2008-06-16 12:26:36 +00:00
|
|
|
if (enabled == null) {
|
|
|
|
// do this so configclients.jsp knows about all apps from reading the config
|
|
|
|
props.setProperty(PREFIX + appName + ENABLED, "true");
|
|
|
|
rewrite = true;
|
|
|
|
}
|
2011-12-29 23:25:27 +00:00
|
|
|
} catch (Throwable t) {
|
|
|
|
System.err.println("ERROR: Failed to start " + appName + ' ' + t);
|
|
|
|
t.printStackTrace();
|
2009-11-27 13:26:38 +00:00
|
|
|
notStarted.add(appName);
|
2008-06-16 12:26:36 +00:00
|
|
|
}
|
2011-12-29 23:25:27 +00:00
|
|
|
} else {
|
|
|
|
notStarted.add(appName);
|
2008-06-16 12:26:36 +00:00
|
|
|
}
|
|
|
|
}
|
2012-10-13 13:54:30 +00:00
|
|
|
changeState(RUNNING);
|
2013-04-19 11:41:35 +00:00
|
|
|
if (_mgr != null)
|
|
|
|
_mgr.register(this);
|
2004-08-10 19:51:11 +00:00
|
|
|
}
|
2011-12-29 23:25:27 +00:00
|
|
|
} else {
|
|
|
|
System.err.println("ERROR: Router console did not start, not starting webapps");
|
2012-10-13 13:54:30 +00:00
|
|
|
changeState(START_FAILED);
|
2004-07-24 02:06:07 +00:00
|
|
|
}
|
2011-12-23 00:56:48 +00:00
|
|
|
|
2008-06-16 12:26:36 +00:00
|
|
|
if (rewrite)
|
2012-10-13 13:06:22 +00:00
|
|
|
storeWebAppProperties(_context, props);
|
2009-11-27 13:26:38 +00:00
|
|
|
|
2011-12-24 05:26:56 +00:00
|
|
|
if (rootServletHandler != null && notStarted.size() > 0) {
|
2009-11-27 13:26:38 +00:00
|
|
|
// map each not-started webapp to the error page
|
2011-12-24 05:26:56 +00:00
|
|
|
ServletHolder noWebApp = rootServletHandler.getServlet("net.i2p.router.web.jsp.nowebapp_jsp");
|
2009-11-27 13:26:38 +00:00
|
|
|
for (int i = 0; i < notStarted.size(); i++) {
|
2011-12-24 05:26:56 +00:00
|
|
|
// we want a new handler for each one since if the webapp is started we remove the handler???
|
2009-11-27 13:26:38 +00:00
|
|
|
try {
|
2011-12-24 05:26:56 +00:00
|
|
|
if (noWebApp != null) {
|
|
|
|
String path = '/' + notStarted.get(i);
|
|
|
|
// LocaleWebAppsHandler adds a .jsp
|
|
|
|
rootServletHandler.addServletWithMapping(noWebApp, path + ".jsp");
|
|
|
|
rootServletHandler.addServletWithMapping(noWebApp, path + "/*");
|
|
|
|
} else {
|
|
|
|
System.err.println("Can't find nowebapp.jsp?");
|
|
|
|
}
|
2009-11-27 13:26:38 +00:00
|
|
|
} catch (Throwable me) {
|
|
|
|
System.err.println(me);
|
2011-12-24 05:26:56 +00:00
|
|
|
me.printStackTrace();
|
2009-11-27 13:26:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-06-16 19:04:23 +00:00
|
|
|
Thread t = new I2PAppThread(new StatSummarizer(), "StatSummarizer", true);
|
2011-12-05 16:18:35 +00:00
|
|
|
t.setPriority(Thread.NORM_PRIORITY - 1);
|
2010-02-07 13:32:49 +00:00
|
|
|
t.start();
|
|
|
|
|
2014-02-07 15:40:23 +00:00
|
|
|
ConsoleUpdateManager um = new ConsoleUpdateManager(_context, _mgr, null);
|
Big refactor of the router console update subsystem, in preparation for
implementing out-of-console updaters like i2psnark.
- Add new update interfaces in net.i2p.update
- All update implementations moved to routerconsole update/
- Implement an UpdateManager that registers with the RouterContext
- UpdateManager handles multiple types of things to update
(router, plugins, news, ...) and methods of updating (HTTP, ...)
- UpdateManager maintains list of installed, downloaded, and available versions of everything
- Define Updaters that can check for a new version and/or download an item
- Individual Updaters register with the UpdateManager obtained from
I2PAppContext, identifying the type of update item and
update method they can handle.
- Updaters need only core libs, no router.jar or routerconsole access required.
- All checks and updates are initiated via the UpdateManager.
- All status on checks and updates in-progress or completed are
obtained from the UpdateManager. No more use of System properties
to broadcast update state.
- All update and checker tasks are intantiated on demand and threaded;
no more static references left over.
- Split out the Runners and Checkers from the Handlers and make the inheritance more sane.
- No more permanent NewsFetcher thread; run on the SimpleScheduler queue
and thread a checker task only to fetch the news.
- No more static NewsFetcher instance in routerconsole.
All helper methods that are still required are moved to NewsHelper.
The UpdateManager implements the policy for when to check and download.
All requests go through the UpdateManager.
For each update type, there's several parts:
- The xxxUpdateHandler implements the Updater
- The xxxUpdateChecker implements the UpdateTask for checking
- The xxxUpdateRunner implements the UpdateTask for downloading
New and moved classes:
web/ update/
---- -------
new ConsoleUpdateManager.java
new PluginUpdateChecker.java from PluginUpdateChecker
PluginUpdateChecker -> PluginUpdateHandler.java
PluginUpdateHandler.java -> PluginUpdateRunner
new UnsignedUpdateHandler.java
UnsignedUpdateHandler -> UnsignedUpdateRunner.java
new UnsignedUpdateChecker from NewsFetcher
UpdateHandler.java remains
new UpdateHandler.java
new UpdateRunner.java from UpdateHandler
move NewsHandler from NewsFetcher
new NewsFetcher
new NewsTimerTask
new DummyHandler
Initial checkin. Unfinished, untested, unpolished.
2012-06-18 22:09:45 +00:00
|
|
|
um.start();
|
2015-09-15 13:33:29 +00:00
|
|
|
NewsManager nm = new NewsManager(_context, _mgr, null);
|
|
|
|
nm.startup();
|
2011-11-09 18:38:39 +00:00
|
|
|
|
2012-10-13 13:06:22 +00:00
|
|
|
if (PluginStarter.pluginsEnabled(_context)) {
|
|
|
|
t = new I2PAppThread(new PluginStarter(_context), "PluginStarter", true);
|
2011-12-05 16:18:35 +00:00
|
|
|
t.setPriority(Thread.NORM_PRIORITY - 1);
|
2010-02-10 19:09:35 +00:00
|
|
|
t.start();
|
|
|
|
}
|
2011-06-16 19:04:23 +00:00
|
|
|
// stat summarizer registers its own hook
|
2013-04-24 15:45:15 +00:00
|
|
|
// RouterAppManager registers its own hook
|
|
|
|
if (_mgr == null)
|
|
|
|
_context.addShutdownTask(new ServerShutdown());
|
2012-10-13 13:06:22 +00:00
|
|
|
ConfigServiceHandler.registerSignalHandler(_context);
|
2004-07-24 02:06:07 +00:00
|
|
|
}
|
|
|
|
|
2010-12-05 19:04:33 +00:00
|
|
|
/**
|
|
|
|
* @return success if it exists and we have a password, or it was created successfully.
|
|
|
|
* @since 0.8.3
|
|
|
|
*/
|
2018-02-25 14:17:01 +00:00
|
|
|
private boolean verifyKeyStore(File ks, Set<String> altNames) {
|
2010-12-05 19:04:33 +00:00
|
|
|
if (ks.exists()) {
|
2012-10-13 13:06:22 +00:00
|
|
|
boolean rv = _context.getProperty(PROP_KEY_PASSWORD) != null;
|
2010-12-05 19:04:33 +00:00
|
|
|
if (!rv)
|
2012-10-13 13:06:22 +00:00
|
|
|
System.err.println("Console SSL error, must set " + PROP_KEY_PASSWORD + " in " + (new File(_context.getConfigDir(), "router.config")).getAbsolutePath());
|
2010-12-05 19:04:33 +00:00
|
|
|
return rv;
|
|
|
|
}
|
2018-02-25 14:17:01 +00:00
|
|
|
return createKeyStore(ks, altNames);
|
2010-12-05 19:04:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2018-02-25 14:17:01 +00:00
|
|
|
* Create a new keystore with a keypair in it.
|
2010-12-05 19:04:33 +00:00
|
|
|
*
|
|
|
|
* @return success
|
|
|
|
* @since 0.8.3
|
|
|
|
*/
|
2018-02-25 14:17:01 +00:00
|
|
|
private boolean createKeyStore(File ks, Set<String> altNames) {
|
2010-12-05 19:04:33 +00:00
|
|
|
// make a random 48 character password (30 * 8 / 5)
|
2013-09-12 14:27:16 +00:00
|
|
|
String keyPassword = KeyStoreUtil.randomString();
|
2018-02-19 19:40:04 +00:00
|
|
|
String cname = "localhost";
|
2018-02-25 14:17:01 +00:00
|
|
|
boolean success = KeyStoreUtil.createKeys(ks, "console", cname, altNames, "Console", keyPassword);
|
2010-12-05 19:04:33 +00:00
|
|
|
if (success) {
|
|
|
|
success = ks.exists();
|
|
|
|
if (success) {
|
|
|
|
try {
|
2013-11-21 11:31:50 +00:00
|
|
|
Map<String, String> changes = new HashMap<String, String>();
|
2017-12-07 14:31:28 +00:00
|
|
|
changes.put(PROP_KEYSTORE_PASSWORD, KeyStoreUtil.DEFAULT_KEYSTORE_PASSWORD);
|
2012-01-18 01:54:34 +00:00
|
|
|
changes.put(PROP_KEY_PASSWORD, keyPassword);
|
2012-10-13 13:06:22 +00:00
|
|
|
_context.router().saveConfig(changes, null);
|
2010-12-05 19:04:33 +00:00
|
|
|
} catch (Exception e) {} // class cast exception
|
2015-09-27 15:56:03 +00:00
|
|
|
// export cert, fails silently
|
|
|
|
File dir = new SecureDirectory(_context.getConfigDir(), "certificates");
|
|
|
|
dir.mkdir();
|
|
|
|
dir = new SecureDirectory(dir, "console");
|
|
|
|
dir.mkdir();
|
|
|
|
File certFile = new File(dir, "console.local.crt");
|
2017-12-07 14:31:28 +00:00
|
|
|
KeyStoreUtil.exportCert(ks, KeyStoreUtil.DEFAULT_KEYSTORE_PASSWORD, "console", certFile);
|
2010-12-05 19:04:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (success) {
|
|
|
|
System.err.println("Created self-signed certificate for " + cname + " in keystore: " + ks.getAbsolutePath() + "\n" +
|
2018-02-25 14:17:01 +00:00
|
|
|
"The certificate was generated randomly.\n" +
|
|
|
|
"Unless you have changed the default settings, the certificate is not associated with your " +
|
2010-12-05 19:04:33 +00:00
|
|
|
"IP address, host name, router identity, or destination keys.");
|
|
|
|
} else {
|
2013-09-12 14:27:16 +00:00
|
|
|
System.err.println("Failed to create console SSL keystore.\n" +
|
|
|
|
"This is for the Sun/Oracle keytool, others may be incompatible.\n" +
|
2010-12-05 19:04:33 +00:00
|
|
|
"If you create the keystore manually, you must add " + PROP_KEYSTORE_PASSWORD + " and " + PROP_KEY_PASSWORD +
|
2012-10-13 13:06:22 +00:00
|
|
|
" to " + (new File(_context.getConfigDir(), "router.config")).getAbsolutePath());
|
2010-12-05 19:04:33 +00:00
|
|
|
}
|
|
|
|
return success;
|
|
|
|
}
|
|
|
|
|
2012-10-13 13:06:22 +00:00
|
|
|
/**
|
|
|
|
* Set up basic security constraints for the webapp.
|
|
|
|
* Add all users and passwords.
|
|
|
|
*/
|
|
|
|
static void initialize(RouterContext ctx, WebAppContext context) {
|
2012-11-21 20:49:18 +00:00
|
|
|
ConstraintSecurityHandler sec = new ConstraintSecurityHandler();
|
2013-11-21 11:31:50 +00:00
|
|
|
List<ConstraintMapping> constraints = new ArrayList<ConstraintMapping>(4);
|
2012-10-13 13:06:22 +00:00
|
|
|
ConsolePasswordManager mgr = new ConsolePasswordManager(ctx);
|
2012-10-26 13:08:23 +00:00
|
|
|
boolean enable = ctx.getBooleanProperty(PROP_PW_ENABLE);
|
|
|
|
if (enable) {
|
|
|
|
Map<String, String> userpw = mgr.getMD5(PROP_CONSOLE_PW);
|
|
|
|
if (userpw.isEmpty()) {
|
|
|
|
enable = false;
|
2017-02-05 20:56:40 +00:00
|
|
|
ctx.router().saveConfig(PROP_PW_ENABLE, "false");
|
2012-10-26 13:08:23 +00:00
|
|
|
} else {
|
2016-11-06 17:20:35 +00:00
|
|
|
HashLoginService realm = new CustomHashLoginService(JETTY_REALM, context.getContextPath(),
|
|
|
|
ctx.logManager().getLog(RouterConsoleRunner.class));
|
2012-11-21 20:49:18 +00:00
|
|
|
sec.setLoginService(realm);
|
2012-10-26 13:08:23 +00:00
|
|
|
sec.setAuthenticator(authenticator);
|
2016-05-08 19:49:14 +00:00
|
|
|
String[] role = new String[] {JETTY_ROLE};
|
2012-10-26 13:08:23 +00:00
|
|
|
for (Map.Entry<String, String> e : userpw.entrySet()) {
|
|
|
|
String user = e.getKey();
|
|
|
|
String pw = e.getValue();
|
2017-07-15 13:57:40 +00:00
|
|
|
Credential cred = Credential.getCredential(MD5_CREDENTIAL_TYPE + pw);
|
2016-05-08 19:49:14 +00:00
|
|
|
realm.putUser(user, cred, role);
|
2012-10-26 13:08:23 +00:00
|
|
|
Constraint constraint = new Constraint(user, JETTY_ROLE);
|
|
|
|
constraint.setAuthenticate(true);
|
|
|
|
ConstraintMapping cm = new ConstraintMapping();
|
|
|
|
cm.setConstraint(constraint);
|
|
|
|
cm.setPathSpec("/");
|
|
|
|
constraints.add(cm);
|
2016-05-08 19:49:14 +00:00
|
|
|
// Jetty does auth checking only with ISO-8859-1,
|
|
|
|
// so register a 2nd and 3rd user with different encodings if necessary.
|
|
|
|
// Might work, might not...
|
|
|
|
// There's no standard and browser behavior varies.
|
|
|
|
// Chrome sends UTF-8. Firefox doesn't send anything.
|
|
|
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=41489
|
|
|
|
// see also RFC 7616/7617 (late 2015) and PasswordManager.md5Hex()
|
|
|
|
byte[] b1 = DataHelper.getUTF8(user);
|
|
|
|
byte[] b2 = DataHelper.getASCII(user);
|
|
|
|
if (!DataHelper.eq(b1, b2)) {
|
|
|
|
try {
|
|
|
|
// each char truncated to 8 bytes
|
|
|
|
String user2 = new String(b2, "ISO-8859-1");
|
|
|
|
realm.putUser(user2, cred, role);
|
|
|
|
constraint = new Constraint(user2, JETTY_ROLE);
|
|
|
|
constraint.setAuthenticate(true);
|
|
|
|
cm = new ConstraintMapping();
|
|
|
|
cm.setConstraint(constraint);
|
|
|
|
cm.setPathSpec("/");
|
|
|
|
constraints.add(cm);
|
|
|
|
|
|
|
|
// each UTF-8 byte as a char
|
|
|
|
// this is what chrome does
|
|
|
|
String user3 = new String(b1, "ISO-8859-1");
|
|
|
|
realm.putUser(user3, cred, role);
|
|
|
|
constraint = new Constraint(user3, JETTY_ROLE);
|
|
|
|
constraint.setAuthenticate(true);
|
|
|
|
cm = new ConstraintMapping();
|
|
|
|
cm.setConstraint(constraint);
|
|
|
|
cm.setPathSpec("/");
|
|
|
|
constraints.add(cm);
|
|
|
|
} catch (UnsupportedEncodingException uee) {}
|
|
|
|
}
|
2012-10-26 13:08:23 +00:00
|
|
|
}
|
2012-10-13 13:06:22 +00:00
|
|
|
}
|
2004-08-10 19:51:11 +00:00
|
|
|
}
|
2010-06-29 02:29:42 +00:00
|
|
|
|
|
|
|
// This forces a '403 Forbidden' response for TRACE and OPTIONS unless the
|
|
|
|
// WAC handler handles it.
|
|
|
|
// (LocaleWebAppHandler returns a '405 Method Not Allowed')
|
|
|
|
// TRACE and OPTIONS aren't really security issues...
|
|
|
|
// TRACE doesn't echo stuff unless you call setTrace(true)
|
|
|
|
// But it might bug some people
|
|
|
|
// The other strange methods - PUT, DELETE, MOVE - are disabled by default
|
|
|
|
// See also:
|
|
|
|
// http://old.nabble.com/Disable-HTTP-TRACE-in-Jetty-5.x-td12412607.html
|
2011-12-23 00:56:48 +00:00
|
|
|
|
|
|
|
Constraint sc = new Constraint();
|
|
|
|
sc.setName("No trace");
|
|
|
|
ConstraintMapping cm = new ConstraintMapping();
|
|
|
|
cm.setMethod("TRACE");
|
|
|
|
cm.setConstraint(sc);
|
|
|
|
cm.setPathSpec("/");
|
|
|
|
constraints.add(cm);
|
|
|
|
|
|
|
|
sc = new Constraint();
|
|
|
|
sc.setName("No options");
|
|
|
|
cm = new ConstraintMapping();
|
|
|
|
cm.setMethod("OPTIONS");
|
|
|
|
cm.setConstraint(sc);
|
|
|
|
cm.setPathSpec("/");
|
|
|
|
constraints.add(cm);
|
|
|
|
|
|
|
|
ConstraintMapping cmarr[] = constraints.toArray(new ConstraintMapping[constraints.size()]);
|
|
|
|
sec.setConstraintMappings(cmarr);
|
|
|
|
|
|
|
|
context.setSecurityHandler(sec);
|
2017-02-05 20:56:40 +00:00
|
|
|
|
|
|
|
// No, we can't share the ConstraintSecurityHandler across all webapps
|
|
|
|
// But we can force all webapps to use a single Timer thread
|
|
|
|
// see HashSessionManager javadoc
|
|
|
|
synchronized(RouterConsoleRunner.class) {
|
|
|
|
if (_jettyTimer == null) {
|
2017-03-11 16:27:37 +00:00
|
|
|
_jettyTimer = new ScheduledExecutorScheduler("Console HashSessionScavenger", true);
|
|
|
|
try {
|
|
|
|
_jettyTimer.start();
|
|
|
|
} catch (Exception e) {
|
|
|
|
System.err.println("Warning: ScheduledExecutorScheduler start failed: " + e);
|
|
|
|
}
|
2017-02-05 20:56:40 +00:00
|
|
|
}
|
|
|
|
context.getServletContext().setAttribute("org.eclipse.jetty.server.session.timer", _jettyTimer);
|
|
|
|
}
|
2004-08-10 19:51:11 +00:00
|
|
|
}
|
2017-02-05 20:56:40 +00:00
|
|
|
|
2016-11-06 17:20:35 +00:00
|
|
|
/**
|
|
|
|
* For logging authentication failures
|
|
|
|
* @since 0.9.28
|
|
|
|
*/
|
|
|
|
private static class CustomHashLoginService extends HashLoginService {
|
|
|
|
private final String _webapp;
|
|
|
|
private final net.i2p.util.Log _log;
|
|
|
|
|
|
|
|
public CustomHashLoginService(String realm, String webapp, net.i2p.util.Log log) {
|
|
|
|
super(realm);
|
|
|
|
_webapp = webapp;
|
|
|
|
_log = log;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public UserIdentity login(String username, Object credentials) {
|
|
|
|
UserIdentity rv = super.login(username, credentials);
|
|
|
|
if (rv == null)
|
|
|
|
//_log.logAlways(net.i2p.util.Log.WARN, "Console authentication failed, webapp: " + _webapp + ", user: " + username);
|
|
|
|
_log.logAlways(net.i2p.util.Log.WARN, "Console authentication failed, user: " + username);
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-06-16 19:04:23 +00:00
|
|
|
/** @since 0.8.8 */
|
2012-10-13 13:54:30 +00:00
|
|
|
private class ServerShutdown implements Runnable {
|
2011-06-16 19:04:23 +00:00
|
|
|
public void run() {
|
2012-10-13 13:54:30 +00:00
|
|
|
shutdown(null);
|
2004-08-10 19:51:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-13 13:06:22 +00:00
|
|
|
private Properties webAppProperties() {
|
|
|
|
return webAppProperties(_context.getConfigDir().getAbsolutePath());
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @since 0.9.4 */
|
|
|
|
public static Properties webAppProperties(I2PAppContext ctx) {
|
|
|
|
return webAppProperties(ctx.getConfigDir().getAbsolutePath());
|
2010-02-06 18:40:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public static Properties webAppProperties(String dir) {
|
2008-06-16 12:26:36 +00:00
|
|
|
Properties rv = new Properties();
|
2012-10-13 13:06:22 +00:00
|
|
|
// String webappConfigFile = _context.getProperty(PROP_WEBAPP_CONFIG_FILENAME, DEFAULT_WEBAPP_CONFIG_FILENAME);
|
2008-06-16 12:26:36 +00:00
|
|
|
String webappConfigFile = DEFAULT_WEBAPP_CONFIG_FILENAME;
|
2010-02-06 18:40:55 +00:00
|
|
|
File cfgFile = new File(dir, webappConfigFile);
|
2008-06-16 12:26:36 +00:00
|
|
|
|
|
|
|
try {
|
|
|
|
DataHelper.loadProps(rv, cfgFile);
|
|
|
|
} catch (IOException ioe) {
|
|
|
|
// _log.warn("Error loading the client app properties from " + cfgFile.getName(), ioe);
|
|
|
|
}
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2012-10-13 13:06:22 +00:00
|
|
|
public static void storeWebAppProperties(RouterContext ctx, Properties props) {
|
|
|
|
// String webappConfigFile = _context.getProperty(PROP_WEBAPP_CONFIG_FILENAME, DEFAULT_WEBAPP_CONFIG_FILENAME);
|
2008-06-16 12:26:36 +00:00
|
|
|
String webappConfigFile = DEFAULT_WEBAPP_CONFIG_FILENAME;
|
2012-10-13 13:06:22 +00:00
|
|
|
File cfgFile = new File(ctx.getConfigDir(), webappConfigFile);
|
2008-06-16 12:26:36 +00:00
|
|
|
|
|
|
|
try {
|
|
|
|
DataHelper.storeProps(props, cfgFile);
|
|
|
|
} catch (IOException ioe) {
|
|
|
|
// _log.warn("Error loading the client app properties from " + cfgFile.getName(), ioe);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-01 14:15:06 +00:00
|
|
|
/**
|
|
|
|
* Stops all but the root webapp (routerconsole.war)
|
|
|
|
* In Jetty 9, stopping the server doesn't stop the non-root webapps,
|
|
|
|
* so we must do it here.
|
|
|
|
* There should be a better way to do this, possibly by
|
|
|
|
* making the webapps "managed".
|
|
|
|
* @since 0.9.30
|
|
|
|
*/
|
|
|
|
private void stopAllWebApps() {
|
|
|
|
Properties props = webAppProperties(_context);
|
|
|
|
Set<String> keys = props.stringPropertyNames();
|
|
|
|
for (String name : keys) {
|
|
|
|
if (name.startsWith(PREFIX) && name.endsWith(ENABLED)) {
|
|
|
|
String app = name.substring(PREFIX.length(), name.lastIndexOf(ENABLED));
|
|
|
|
if (ROUTERCONSOLE.equals(app))
|
|
|
|
continue;
|
|
|
|
if (WebAppStarter.isWebAppRunning(app)) {
|
|
|
|
try {
|
|
|
|
WebAppStarter.stopWebApp(app);
|
|
|
|
} catch (Throwable t) { t.printStackTrace(); }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2010-02-07 13:32:49 +00:00
|
|
|
static class WarFilenameFilter implements FilenameFilter {
|
2008-06-16 12:26:36 +00:00
|
|
|
private static final WarFilenameFilter _filter = new WarFilenameFilter();
|
|
|
|
public static WarFilenameFilter instance() { return _filter; }
|
|
|
|
public boolean accept(File dir, String name) {
|
|
|
|
return (name != null) && (name.endsWith(".war") && !name.equals(ROUTERCONSOLE + ".war"));
|
|
|
|
}
|
|
|
|
}
|
2010-02-07 13:32:49 +00:00
|
|
|
|
2016-01-06 17:50:06 +00:00
|
|
|
/**
|
|
|
|
* Put IPv4 first
|
|
|
|
* @since 0.9.24
|
|
|
|
*/
|
|
|
|
private static class HostComparator implements Comparator<String>, Serializable {
|
|
|
|
public int compare(String l, String r) {
|
|
|
|
boolean l4 = l.contains(".");
|
|
|
|
boolean r4 = r.contains(".");
|
|
|
|
if (l4 && !r4)
|
|
|
|
return -1;
|
|
|
|
if (r4 && !l4)
|
|
|
|
return 1;
|
|
|
|
return l.compareTo(r);
|
|
|
|
}
|
|
|
|
}
|
2011-12-24 17:09:01 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Just to set the name and set Daemon
|
|
|
|
* @since Jetty 6
|
|
|
|
*/
|
Console and Eepsite Jetty:
Switch back to QueuedThreadPool (ticket #1395)
In Jetty 5/6, the default QTP was not concurrent, so we switched to
ThreadPoolExecutor with a fixed-size queue, a set maxThreads,
and a RejectedExecutionPolicy of CallerRuns.
Unfortunately, CallerRuns causes lockups in Jetty NIO.
In addition, no flavor of TPE gives us what QTP does:
- TPE direct handoff (which we were using) never queues.
This doesn't provide any burst management when maxThreads is reached.
CallerRuns was an attempt to work around that.
- TPE unbounded queue does not adjust the number of threads.
This doesn't provide automatic resource management.
- TPE bounded queue does not add threads until the queue is full.
This doesn't provide good responsiveness to even small bursts.
QTP adds threads as soon as the queue is non-empty.
QTP as of Jetty 7 uses concurrent.
QTP unbounded queue is the default in Jetty.
So switch back to QTP with a bounded queue, which does what we want,
which is first expand the thread pool, then start queueing, then reject.
ref:
http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ThreadPoolExecutor.html
https://wiki.eclipse.org/Jetty/Howto/High_Load
2014-10-20 14:01:36 +00:00
|
|
|
/*****
|
2012-11-21 20:49:18 +00:00
|
|
|
private static class CustomThreadPoolExecutor extends ExecutorThreadPool {
|
2011-12-24 17:09:01 +00:00
|
|
|
public CustomThreadPoolExecutor() {
|
2013-05-06 12:29:18 +00:00
|
|
|
super(new ThreadPoolExecutor(
|
|
|
|
MIN_THREADS, MAX_THREADS, MAX_IDLE_TIME, TimeUnit.MILLISECONDS,
|
2013-11-21 11:31:50 +00:00
|
|
|
new SynchronousQueue<Runnable>(),
|
2013-05-06 12:29:18 +00:00
|
|
|
new CustomThreadFactory(),
|
|
|
|
new ThreadPoolExecutor.CallerRunsPolicy())
|
2012-11-21 20:49:18 +00:00
|
|
|
);
|
2011-12-24 17:09:01 +00:00
|
|
|
}
|
|
|
|
}
|
Console and Eepsite Jetty:
Switch back to QueuedThreadPool (ticket #1395)
In Jetty 5/6, the default QTP was not concurrent, so we switched to
ThreadPoolExecutor with a fixed-size queue, a set maxThreads,
and a RejectedExecutionPolicy of CallerRuns.
Unfortunately, CallerRuns causes lockups in Jetty NIO.
In addition, no flavor of TPE gives us what QTP does:
- TPE direct handoff (which we were using) never queues.
This doesn't provide any burst management when maxThreads is reached.
CallerRuns was an attempt to work around that.
- TPE unbounded queue does not adjust the number of threads.
This doesn't provide automatic resource management.
- TPE bounded queue does not add threads until the queue is full.
This doesn't provide good responsiveness to even small bursts.
QTP adds threads as soon as the queue is non-empty.
QTP as of Jetty 7 uses concurrent.
QTP unbounded queue is the default in Jetty.
So switch back to QTP with a bounded queue, which does what we want,
which is first expand the thread pool, then start queueing, then reject.
ref:
http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ThreadPoolExecutor.html
https://wiki.eclipse.org/Jetty/Howto/High_Load
2014-10-20 14:01:36 +00:00
|
|
|
*****/
|
2011-12-24 17:09:01 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Just to set the name and set Daemon
|
|
|
|
* @since Jetty 6
|
|
|
|
*/
|
Console and Eepsite Jetty:
Switch back to QueuedThreadPool (ticket #1395)
In Jetty 5/6, the default QTP was not concurrent, so we switched to
ThreadPoolExecutor with a fixed-size queue, a set maxThreads,
and a RejectedExecutionPolicy of CallerRuns.
Unfortunately, CallerRuns causes lockups in Jetty NIO.
In addition, no flavor of TPE gives us what QTP does:
- TPE direct handoff (which we were using) never queues.
This doesn't provide any burst management when maxThreads is reached.
CallerRuns was an attempt to work around that.
- TPE unbounded queue does not adjust the number of threads.
This doesn't provide automatic resource management.
- TPE bounded queue does not add threads until the queue is full.
This doesn't provide good responsiveness to even small bursts.
QTP adds threads as soon as the queue is non-empty.
QTP as of Jetty 7 uses concurrent.
QTP unbounded queue is the default in Jetty.
So switch back to QTP with a bounded queue, which does what we want,
which is first expand the thread pool, then start queueing, then reject.
ref:
http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ThreadPoolExecutor.html
https://wiki.eclipse.org/Jetty/Howto/High_Load
2014-10-20 14:01:36 +00:00
|
|
|
/*****
|
2011-12-24 17:09:01 +00:00
|
|
|
private static class CustomThreadFactory implements ThreadFactory {
|
|
|
|
|
|
|
|
public Thread newThread(Runnable r) {
|
|
|
|
Thread rv = Executors.defaultThreadFactory().newThread(r);
|
|
|
|
rv.setName(THREAD_NAME);
|
|
|
|
rv.setDaemon(true);
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
}
|
Console and Eepsite Jetty:
Switch back to QueuedThreadPool (ticket #1395)
In Jetty 5/6, the default QTP was not concurrent, so we switched to
ThreadPoolExecutor with a fixed-size queue, a set maxThreads,
and a RejectedExecutionPolicy of CallerRuns.
Unfortunately, CallerRuns causes lockups in Jetty NIO.
In addition, no flavor of TPE gives us what QTP does:
- TPE direct handoff (which we were using) never queues.
This doesn't provide any burst management when maxThreads is reached.
CallerRuns was an attempt to work around that.
- TPE unbounded queue does not adjust the number of threads.
This doesn't provide automatic resource management.
- TPE bounded queue does not add threads until the queue is full.
This doesn't provide good responsiveness to even small bursts.
QTP adds threads as soon as the queue is non-empty.
QTP as of Jetty 7 uses concurrent.
QTP unbounded queue is the default in Jetty.
So switch back to QTP with a bounded queue, which does what we want,
which is first expand the thread pool, then start queueing, then reject.
ref:
http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ThreadPoolExecutor.html
https://wiki.eclipse.org/Jetty/Howto/High_Load
2014-10-20 14:01:36 +00:00
|
|
|
*****/
|
2011-12-24 17:09:01 +00:00
|
|
|
|
2004-07-24 02:06:07 +00:00
|
|
|
}
|