forked from I2P_Developers/i2p.i2p
First cut at migrating to Jetty 6 and prep for using an external
Jetty 6 package. - Add several jars from the Jetty 6 distribution - Update jetty.xml - Add context XML files - Update WorkingDir to migrate the content XML files - Update RouterConsoleRunner and LocaleWebAppHandler - Remove all old Jetty 5.1.15 local mods; this will break Seedless using a custom Server() constructor - Update I2PRequestLog to be a mod of NCSARequestLog from 6.1.26 - Put I2PRequestLog in its own jar - Copy MultiPartRequest and other required classes from Jetty 5.1.15 and add it to susimail, as the replacement MultiPartFilter in Jetty 6 is difficult to migrate to, and does not support content-type - Update i2psnark for Jetty 6 - Disable i2psnark RunStandalone, unused and instantiated Jetty 5 - Fix up all webapp build.xml to reference new jars Not yet working: Plugin/webapp run detection and stopping, eepsite CGI Not well tested: Plugins, classpaths, webapps
This commit is contained in:
@ -9,6 +9,7 @@ import java.security.KeyStore;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.apps.systray.SysTray;
|
||||
@ -24,25 +25,39 @@ import net.i2p.util.SecureFileOutputStream;
|
||||
import net.i2p.util.ShellCommand;
|
||||
import net.i2p.util.VersionComparator;
|
||||
|
||||
import org.mortbay.http.DigestAuthenticator;
|
||||
import org.mortbay.http.HashUserRealm;
|
||||
import org.mortbay.http.NCSARequestLog;
|
||||
import org.mortbay.http.SecurityConstraint;
|
||||
import org.mortbay.http.SocketListener;
|
||||
import org.mortbay.http.SslListener;
|
||||
import org.mortbay.http.handler.SecurityHandler;
|
||||
import org.mortbay.jetty.AbstractConnector;
|
||||
import org.mortbay.jetty.Connector;
|
||||
import org.mortbay.jetty.Handler;
|
||||
import org.mortbay.jetty.NCSARequestLog;
|
||||
import org.mortbay.jetty.Server;
|
||||
import org.mortbay.jetty.servlet.WebApplicationContext;
|
||||
import org.mortbay.jetty.servlet.WebApplicationHandler;
|
||||
import org.mortbay.util.InetAddrPort;
|
||||
import org.mortbay.jetty.handler.ContextHandlerCollection;
|
||||
import org.mortbay.jetty.handler.DefaultHandler;
|
||||
import org.mortbay.jetty.handler.HandlerCollection;
|
||||
import org.mortbay.jetty.handler.RequestLogHandler;
|
||||
import org.mortbay.jetty.nio.SelectChannelConnector;
|
||||
import org.mortbay.jetty.security.DigestAuthenticator;
|
||||
import org.mortbay.jetty.security.HashUserRealm;
|
||||
import org.mortbay.jetty.security.Constraint;
|
||||
import org.mortbay.jetty.security.ConstraintMapping;
|
||||
import org.mortbay.jetty.security.SecurityHandler;
|
||||
import org.mortbay.jetty.security.SslSelectChannelConnector;
|
||||
import org.mortbay.jetty.servlet.ServletHandler;
|
||||
import org.mortbay.jetty.servlet.SessionHandler;
|
||||
import org.mortbay.jetty.webapp.WebAppContext;
|
||||
import org.mortbay.thread.QueuedThreadPool;
|
||||
import org.mortbay.thread.concurrent.ThreadPool;
|
||||
|
||||
/**
|
||||
* Start the router console.
|
||||
*/
|
||||
public class RouterConsoleRunner {
|
||||
private Server _server;
|
||||
private static Server _server;
|
||||
private String _listenPort;
|
||||
private String _listenHost;
|
||||
private String _sslListenPort;
|
||||
private String _sslListenHost;
|
||||
private String _webAppsDir;
|
||||
|
||||
private static final String PROP_WEBAPP_CONFIG_FILENAME = "router.webappsConfigFile";
|
||||
private static final String DEFAULT_WEBAPP_CONFIG_FILENAME = "webapps.config";
|
||||
private static final DigestAuthenticator authenticator = new DigestAuthenticator();
|
||||
@ -57,6 +72,10 @@ public class RouterConsoleRunner {
|
||||
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]]";
|
||||
|
||||
private static final int MIN_THREADS = 1;
|
||||
private static final int MAX_THREADS = 24;
|
||||
private static final int MAX_IDLE_TIME = 90*1000;
|
||||
|
||||
static {
|
||||
System.setProperty("org.mortbay.http.Version.paranoid", "true");
|
||||
@ -135,6 +154,15 @@ public class RouterConsoleRunner {
|
||||
runner.startConsole();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
private static void startTrayApp() {
|
||||
try {
|
||||
//TODO: move away from routerconsole into a separate application.
|
||||
@ -158,6 +186,23 @@ public class RouterConsoleRunner {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* http://irc.codehaus.org/display/JETTY/Porting+to+jetty6
|
||||
*
|
||||
*<pre>
|
||||
* Server
|
||||
* HandlerCollection
|
||||
* ContextHandlerCollection
|
||||
* WebAppContext (i.e. ContextHandler)
|
||||
* SessionHandler
|
||||
* SecurityHandler
|
||||
* ServletHandler
|
||||
* WebAppContext
|
||||
* ...
|
||||
* DefaultHandler
|
||||
* RequestLogHandler (opt)
|
||||
*</pre>
|
||||
*/
|
||||
public void startConsole() {
|
||||
File workDir = new SecureDirectory(I2PAppContext.getGlobalContext().getTempDir(), "jetty-work");
|
||||
boolean workDirRemoved = FileUtil.rmdir(workDir, false);
|
||||
@ -171,14 +216,34 @@ public class RouterConsoleRunner {
|
||||
System.setProperty("jetty.class.path", I2PAppContext.getGlobalContext().getBaseDir() + "/lib/routerconsole.jar");
|
||||
_server = new Server();
|
||||
|
||||
/**** this doesn't work with NIO maybe?
|
||||
try {
|
||||
_server.setThreadPool(new ThreadPool(MIN_THREADS, MAX_THREADS, MAX_IDLE_TIME, TimeUnit.MILLISECONDS));
|
||||
} catch (Throwable t) {
|
||||
// class not found...
|
||||
System.out.println("INFO: Jetty concurrent ThreadPool unavailable, using QueuedThreadPool");
|
||||
QueuedThreadPool qtp = new QueuedThreadPool(MAX_THREADS);
|
||||
qtp.setMinThreads(MIN_THREADS);
|
||||
qtp.setMaxIdleTimeMs(MAX_IDLE_TIME);
|
||||
_server.setThreadPool(qtp);
|
||||
}
|
||||
****/
|
||||
HandlerCollection hColl = new HandlerCollection();
|
||||
ContextHandlerCollection chColl = new ContextHandlerCollection();
|
||||
_server.addHandler(hColl);
|
||||
hColl.addHandler(chColl);
|
||||
hColl.addHandler(new DefaultHandler());
|
||||
|
||||
String log = I2PAppContext.getGlobalContext().getProperty("routerconsole.log");
|
||||
if (log != null) {
|
||||
File logFile = new File(log);
|
||||
if (!logFile.isAbsolute())
|
||||
logFile = new File(I2PAppContext.getGlobalContext().getLogDir(), "logs/" + log);
|
||||
try {
|
||||
_server.setRequestLog(new NCSARequestLog(logFile.getAbsolutePath()));
|
||||
} catch (IOException ioe) {
|
||||
RequestLogHandler rhl = new RequestLogHandler();
|
||||
rhl.setRequestLog(new NCSARequestLog(logFile.getAbsolutePath()));
|
||||
hColl.addHandler(rhl);
|
||||
} catch (Exception ioe) {
|
||||
System.err.println("ERROR: Unable to create Jetty log: " + ioe);
|
||||
}
|
||||
}
|
||||
@ -202,7 +267,7 @@ public class RouterConsoleRunner {
|
||||
_webAppsDir += '/';
|
||||
|
||||
List<String> notStarted = new ArrayList();
|
||||
WebApplicationHandler baseHandler = null;
|
||||
WebAppContext baseHandler = null;
|
||||
try {
|
||||
int boundAddresses = 0;
|
||||
|
||||
@ -217,17 +282,17 @@ public class RouterConsoleRunner {
|
||||
// _server.addListener('[' + host + "]:" + _listenPort);
|
||||
//else
|
||||
// _server.addListener(host + ':' + _listenPort);
|
||||
InetAddrPort iap = new InetAddrPort(host, lport);
|
||||
SocketListener lsnr = new SocketListener(iap);
|
||||
lsnr.setMinThreads(1); // default 2
|
||||
lsnr.setMaxThreads(24); // default 256
|
||||
lsnr.setMaxIdleTimeMs(90*1000); // default 10 sec
|
||||
// Use AbstractConnector instead of Connector so we can do setName()
|
||||
AbstractConnector lsnr = new SelectChannelConnector();
|
||||
lsnr.setHost(host);
|
||||
lsnr.setPort(lport);
|
||||
lsnr.setMaxIdleTime(90*1000); // default 10 sec
|
||||
lsnr.setName("ConsoleSocket"); // all with same name will use the same thread pool
|
||||
_server.addListener(lsnr);
|
||||
_server.addConnector(lsnr);
|
||||
boundAddresses++;
|
||||
} catch (NumberFormatException nfe) {
|
||||
System.err.println("Unable to bind routerconsole to " + host + " port " + _listenPort + ' ' + nfe);
|
||||
} catch (IOException ioe) { // this doesn't seem to work, exceptions don't happen until start() below
|
||||
} catch (Exception ioe) { // this doesn't seem to work, exceptions don't happen until start() below
|
||||
System.err.println("Unable to bind routerconsole to " + host + " port " + _listenPort + ' ' + ioe);
|
||||
}
|
||||
}
|
||||
@ -252,19 +317,20 @@ public class RouterConsoleRunner {
|
||||
while (tok.hasMoreTokens()) {
|
||||
String host = tok.nextToken().trim();
|
||||
// doing it this way means we don't have to escape an IPv6 host with []
|
||||
InetAddrPort iap = new InetAddrPort(host, sslPort);
|
||||
try {
|
||||
SslListener ssll = new SslListener(iap);
|
||||
// TODO if class not found use SslChannelConnector
|
||||
// Sadly there's no common base class with the ssl methods in it
|
||||
SslSelectChannelConnector ssll = new SslSelectChannelConnector();
|
||||
ssll.setHost(host);
|
||||
ssll.setPort(sslPort);
|
||||
// the keystore path and password
|
||||
ssll.setKeystore(keyStore.getAbsolutePath());
|
||||
ssll.setPassword(ctx.getProperty(PROP_KEYSTORE_PASSWORD, DEFAULT_KEYSTORE_PASSWORD));
|
||||
// the X.509 cert password (if not present, verifyKeyStore() returned false)
|
||||
ssll.setKeyPassword(ctx.getProperty(PROP_KEY_PASSWORD, "thisWontWork"));
|
||||
ssll.setMinThreads(1); // default 2
|
||||
ssll.setMaxThreads(24); // default 256
|
||||
ssll.setMaxIdleTimeMs(90*1000); // default 10 sec
|
||||
ssll.setMaxIdleTime(90*1000); // default 10 sec
|
||||
ssll.setName("ConsoleSocket"); // all with same name will use the same thread pool
|
||||
_server.addListener(ssll);
|
||||
_server.addConnector(ssll);
|
||||
boundAddresses++;
|
||||
} catch (Exception e) { // probably no exceptions at this point
|
||||
System.err.println("Unable to bind routerconsole to " + host + " port " + sslPort + " for SSL: " + e);
|
||||
@ -280,15 +346,18 @@ public class RouterConsoleRunner {
|
||||
System.err.println("Unable to bind routerconsole to any address on port " + _listenPort + (sslPort > 0 ? (" or SSL port " + sslPort) : ""));
|
||||
return;
|
||||
}
|
||||
_server.setRootWebApp(ROUTERCONSOLE);
|
||||
WebApplicationContext wac = _server.addWebApplication("/", _webAppsDir + ROUTERCONSOLE + ".war");
|
||||
|
||||
baseHandler = new LocaleWebAppHandler(I2PAppContext.getGlobalContext(),
|
||||
"/", _webAppsDir + ROUTERCONSOLE + ".war");
|
||||
File tmpdir = new SecureDirectory(workDir, ROUTERCONSOLE + "-" +
|
||||
(_listenPort != null ? _listenPort : _sslListenPort));
|
||||
tmpdir.mkdir();
|
||||
wac.setTempDirectory(tmpdir);
|
||||
baseHandler = new LocaleWebAppHandler(I2PAppContext.getGlobalContext());
|
||||
wac.addHandler(0, baseHandler);
|
||||
initialize(wac);
|
||||
baseHandler.setTempDirectory(tmpdir);
|
||||
baseHandler.setSessionHandler(new SessionHandler());
|
||||
baseHandler.setServletHandler(new ServletHandler());
|
||||
initialize(baseHandler);
|
||||
chColl.addHandler(baseHandler);
|
||||
|
||||
File dir = new File(_webAppsDir);
|
||||
String fileNames[] = dir.list(WarFilenameFilter.instance());
|
||||
if (fileNames != null) {
|
||||
@ -300,7 +369,7 @@ public class RouterConsoleRunner {
|
||||
String path = new File(dir, fileNames[i]).getCanonicalPath();
|
||||
tmpdir = new SecureDirectory(workDir, appName + "-" +
|
||||
(_listenPort != null ? _listenPort : _sslListenPort));
|
||||
WebAppStarter.addWebApp(I2PAppContext.getGlobalContext(), _server, appName, path, tmpdir);
|
||||
WebAppStarter.addWebApp(I2PAppContext.getGlobalContext(), chColl, appName, path, tmpdir);
|
||||
|
||||
if (enabled == null) {
|
||||
// do this so configclients.jsp knows about all apps from reading the config
|
||||
@ -315,12 +384,14 @@ public class RouterConsoleRunner {
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
} catch (Exception ioe) {
|
||||
ioe.printStackTrace();
|
||||
}
|
||||
|
||||
if (rewrite)
|
||||
storeWebAppProperties(props);
|
||||
try {
|
||||
// start does a mapContexts()
|
||||
_server.start();
|
||||
} catch (Throwable me) {
|
||||
// NoClassFoundDefError from a webapp is a throwable, not an exception
|
||||
@ -337,8 +408,9 @@ public class RouterConsoleRunner {
|
||||
// map each not-started webapp to the error page
|
||||
for (int i = 0; i < notStarted.size(); i++) {
|
||||
try {
|
||||
baseHandler.mapPathToServlet('/' + notStarted.get(i) + "/*",
|
||||
"net.i2p.router.web.jsp.nowebapp_jsp");
|
||||
/////////////////////////////////////////////////
|
||||
//baseHandler.mapPathToServlet('/' + notStarted.get(i) + "/*",
|
||||
// "net.i2p.router.web.jsp.nowebapp_jsp");
|
||||
} catch (Throwable me) {
|
||||
System.err.println(me);
|
||||
}
|
||||
@ -454,18 +526,22 @@ public class RouterConsoleRunner {
|
||||
return success;
|
||||
}
|
||||
|
||||
static void initialize(WebApplicationContext context) {
|
||||
static void initialize(WebAppContext context) {
|
||||
SecurityHandler sec = new SecurityHandler();
|
||||
List<ConstraintMapping> constraints = new ArrayList(4);
|
||||
String password = getPassword();
|
||||
if (password != null) {
|
||||
HashUserRealm realm = new HashUserRealm("i2prouter");
|
||||
realm.put("admin", password);
|
||||
realm.addUserToRole("admin", "routerAdmin");
|
||||
context.setRealm(realm);
|
||||
context.setAuthenticator(authenticator);
|
||||
context.addHandler(0, new SecurityHandler());
|
||||
SecurityConstraint constraint = new SecurityConstraint("admin", "routerAdmin");
|
||||
sec.setUserRealm(realm);
|
||||
sec.setAuthenticator(authenticator);
|
||||
Constraint constraint = new Constraint("admin", "routerAdmin");
|
||||
constraint.setAuthenticate(true);
|
||||
context.addSecurityConstraint("/", constraint);
|
||||
ConstraintMapping cm = new ConstraintMapping();
|
||||
cm.setConstraint(constraint);
|
||||
cm.setPathSpec("/");
|
||||
constraints.add(cm);
|
||||
}
|
||||
|
||||
// This forces a '403 Forbidden' response for TRACE and OPTIONS unless the
|
||||
@ -477,12 +553,27 @@ public class RouterConsoleRunner {
|
||||
// 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
|
||||
SecurityConstraint sc = new SecurityConstraint();
|
||||
sc.setName("No trace or options");
|
||||
sc.addMethod("TRACE");
|
||||
sc.addMethod("OPTIONS");
|
||||
sc.setAuthenticate(true);
|
||||
context.addSecurityConstraint("/*", sc) ;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static String getPassword() {
|
||||
@ -507,11 +598,11 @@ public class RouterConsoleRunner {
|
||||
}
|
||||
|
||||
/** @since 0.8.8 */
|
||||
private class ServerShutdown implements Runnable {
|
||||
private static class ServerShutdown implements Runnable {
|
||||
public void run() {
|
||||
try {
|
||||
_server.stop();
|
||||
} catch (InterruptedException ie) {}
|
||||
} catch (Exception ie) {}
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user