Console: Add error handler to all webapps (ticket #2155)

Fix up default servlet handling in i2ptunnel and susidns
This commit is contained in:
zzz
2018-03-14 18:33:14 +00:00
parent b9d5cdf2be
commit 2aceca5f93
10 changed files with 401 additions and 37 deletions

View File

@@ -18,6 +18,15 @@
<servlet-class>org.klomp.snark.web.I2PSnarkServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>net.i2p.servlet.ErrorServlet</servlet-name>
<servlet-class>net.i2p.servlet.ErrorServlet</servlet-class>
<init-param>
<param-name>name</param-name>
<param-value>I2PSnark</param-value>
</init-param>
</servlet>
<!-- precompiled servlets -->
@@ -25,6 +34,11 @@
<servlet-name>org.klomp.snark.web.I2PSnarkServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>net.i2p.servlet.ErrorServlet</servlet-name>
<url-pattern>/.error</url-pattern>
</servlet-mapping>
<!-- this webapp doesn't actually use sessions or cookies -->
<session-config>
@@ -36,4 +50,16 @@
</cookie-config>
</session-config>
<error-page>
<error-code>403</error-code>
<location>/.error</location>
</error-page>
<error-page>
<error-code>404</error-code>
<location>/.error</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/.error</location>
</error-page>
</web-app>

View File

@@ -13,13 +13,32 @@
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>net.i2p.servlet.ErrorServlet</servlet-name>
<servlet-class>net.i2p.servlet.ErrorServlet</servlet-class>
<init-param>
<param-name>name</param-name>
<param-value>Hidden Services Manager</param-value>
</init-param>
</servlet>
<!-- precompiled servlets -->
<!-- yeah we could do this in a handler but this is easier -->
<servlet-mapping>
<servlet-name>net.i2p.i2ptunnel.jsp.index_jsp</servlet-name>
<!-- this becomes the default so it also covers /index and /index.html -->
<url-pattern>/</url-pattern>
<!-- empty pattern handles the root -->
<url-pattern></url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>net.i2p.i2ptunnel.jsp.index_jsp</servlet-name>
<url-pattern>/index</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>net.i2p.i2ptunnel.jsp.index_jsp</servlet-name>
<url-pattern>/index.html</url-pattern>
</servlet-mapping>
<servlet-mapping>
@@ -37,6 +56,11 @@
<url-pattern>/register</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>net.i2p.servlet.ErrorServlet</servlet-name>
<url-pattern>/error</url-pattern>
</servlet-mapping>
<!-- this webapp doesn't actually use sessions or cookies -->
<session-config>
<session-timeout>
@@ -50,4 +74,17 @@
<welcome-file>index.html</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<error-page>
<error-code>403</error-code>
<location>/error</location>
</error-page>
<error-page>
<error-code>404</error-code>
<location>/error</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/error</location>
</error-page>
</web-app>

View File

@@ -21,7 +21,16 @@
<servlet-class>net.i2p.imagegen.RandomArtServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>net.i2p.servlet.ErrorServlet</servlet-name>
<servlet-class>net.i2p.servlet.ErrorServlet</servlet-class>
<init-param>
<param-name>name</param-name>
<param-value>imagegen</param-value>
</init-param>
</servlet>
<!-- precompiled servlets -->
<servlet-mapping>
@@ -38,6 +47,12 @@
<servlet-name>net.i2p.imagegen.RandomArtServlet</servlet-name>
<url-pattern>/ra</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>net.i2p.servlet.ErrorServlet</servlet-name>
<url-pattern>/error</url-pattern>
</servlet-mapping>
<!-- this webapp doesn't actually use sessions or cookies -->
<session-config>
@@ -49,4 +64,17 @@
</cookie-config>
</session-config>
<error-page>
<error-code>403</error-code>
<location>/error</location>
</error-page>
<error-page>
<error-code>404</error-code>
<location>/error</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/error</location>
</error-page>
</web-app>

View File

@@ -0,0 +1,238 @@
package net.i2p.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.charset.Charset;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.ServletException;
import net.i2p.CoreVersion;
import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper;
import net.i2p.util.NativeBigInteger;
import net.i2p.util.Translate;
import org.eclipse.jetty.server.Server;
/**
* Common servlet for errors
* This is intended for webapps and local plugins.
* It is not appropriate for eepsites or remotely-accessible plugins,
* as it uses local console resources.
*
* See http://www.eclipse.org/jetty/documentation/current/custom-error-pages.html
* for how to add to web.xml or see examples in bundled webapps.
*
* Init parameters:
* <ul>
* <li>CSSPath - absolue URL of CSS to reference, starting with /, defaults to console theme
* <li>name - webapp or plugin name, will be translated with bundle, e.g. SusiMail
* <li>bundle - package of translation bundle, e.g. net.i2p.susimail.web.messages
* </ul>
*
* Most strings are translated with the console bundle, as we're using the same strings
* as in error.jsp and error500.jsp.
*
* Supported error codes: 403, 404, and 500+.
* Others must be added here.
*
* @since 0.9.34 adapted from routerconsole error.jsp, error500.jsp, and CSSHelper
*/
public class ErrorServlet extends HttpServlet {
private static final long serialVersionUID = 99356750L;
private final I2PAppContext _context;
private static final String CONSOLE_BUNDLE_NAME = "net.i2p.router.web.messages";
private static final String PROP_THEME_NAME = "routerconsole.theme";
private static final String PROP_THEME_PFX = PROP_THEME_NAME + '.';
private static final String DEFAULT_THEME = "light";
private static final String BASE_THEME_PATH = "/themes/console/";
private static final String DEFAULT_ICO = "images/favicon.ico";
private static final String DEFAULT_CSS = "console.css";
/** to be added to head */
private final String _icoPath = BASE_THEME_PATH + '/' + DEFAULT_ICO;
private String _cssPath;
/** for webapp translation */
private String _webappName;
private String _bundleName;
private String _defaultBundle;
public ErrorServlet() {
super();
_context = I2PAppContext.getGlobalContext();
}
@Override
public void init() throws ServletException {
super.init();
_cssPath = getInitParameter("CSSPath");
if (_cssPath == null) {
String dir = BASE_THEME_PATH +
_context.getProperty(PROP_THEME_NAME, DEFAULT_THEME);
_cssPath = dir + '/' + DEFAULT_CSS;
}
_webappName = getInitParameter("name");
if (_webappName == null)
_webappName = "unknown";
_bundleName = getInitParameter("bundle");
_defaultBundle = _bundleName != null ? _bundleName : CONSOLE_BUNDLE_NAME;
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setCharacterEncoding("UTF-8");
resp.setHeader("X-Content-Type-Options", "nosniff");
resp.setHeader("Accept-Ranges", "none");
resp.setDateHeader("Expires", 0);
resp.setHeader("Cache-Control", "no-store, max-age=0, no-cache, must-revalidate");
resp.setHeader("Pragma", "no-cache");
resp.setHeader("Content-Security-Policy", "default-src 'self'; script-src 'none'");
Integer ERROR_CODE = (Integer) req.getAttribute("javax.servlet.error.status_code");
String ERROR_URI = (String) req.getAttribute("javax.servlet.error.request_uri");
String ERROR_MESSAGE = (String) req.getAttribute("javax.servlet.error.message");
Throwable ERROR_THROWABLE = (Throwable) req.getAttribute("javax.servlet.error.exception");
int errorCode = ERROR_CODE != null ? ERROR_CODE.intValue() : 0;
if (ERROR_CODE != null) {
resp.setStatus(errorCode);
}
if (ERROR_URI == null)
ERROR_URI = "";
else
ERROR_URI = DataHelper.escapeHTML(ERROR_URI);
if (ERROR_MESSAGE == null)
ERROR_MESSAGE = "";
else
ERROR_MESSAGE = DataHelper.escapeHTML(ERROR_MESSAGE);
if (errorCode == 404 &&
(ERROR_URI.endsWith(".png") ||
ERROR_URI.endsWith(".jpg") ||
ERROR_URI.endsWith(".gif") ||
ERROR_URI.endsWith(".ico") ||
ERROR_URI.endsWith(".svg") ||
ERROR_URI.endsWith(".txt") ||
ERROR_URI.endsWith(".css"))) {
// keep it simple
resp.setContentType("text/plain");
PrintWriter out = resp.getWriter();
out.println(_t("Error {0}", 404) + ": " + ERROR_URI + ' ' + _t("not found"));
out.close();
return;
}
resp.setContentType("text/html");
PrintWriter out = resp.getWriter();
out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");
out.print("<html><head><title>");
if (errorCode == 404)
out.print(_t("Page Not Found"));
else
out.print(_t("Internal Error"));
out.println("</title>");
out.println("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">");
if (_icoPath != null)
out.println("<link rel=\"icon\" href=\"" + _icoPath + "\">");
out.println("<link href=\"" + _cssPath + '?' + CoreVersion.VERSION + "\" rel=\"stylesheet\" type=\"text/css\">");
out.println("</head><body>");
out.println("<div class=\"routersummaryouter\"><div class=\"routersummary\">");
out.println("<a href=\"/\" title=\"" + _t("Router Console") +
"\"><img src=\"/themes/console/images/i2plogo.png\" alt=\"" +
_t("I2P Router Console") + "\" border=\"0\"></a><hr>");
out.println("<a href=\"/config\">" + _t("Configuration") + "</a> <a href=\"/help\">" + _t("Help") + "</a>");
out.println("</div></div>");
out.println("<div class=\"sorry\" id=\"warning\">");
out.println("<h2>" + _t("Error {0}", errorCode) + ":&nbsp;" + ERROR_MESSAGE + "</h2>");
out.println("<h3>" + _t("WebApp") + ": " + _w(_webappName) + "</h3>");
outputMessage(out, errorCode, ERROR_MESSAGE, ERROR_URI, ERROR_THROWABLE);
out.println("</div></body></html>");
out.close();
}
/**
* Override for specific cases.
* This supports 403, 404, and 500+.
* Output HTML that goes inside the div.
*
* @param errorCode e.g. 404
* @param errorMsg non-null, may be empty, already HTML-escaped
* @param errorURI non-null, may be empty, already HTML-escaped
* @param errorCause may be null
*/
protected void outputMessage(PrintWriter out, int errorCode, String errorMsg, String errorURI, Throwable errorCause) {
if (errorCode == 404) {
out.println(_t("Sorry! You appear to be requesting a non-existent Router Console page or resource."));
out.println("<hr>");
out.println(_t("Error {0}", 404) + ": " + errorURI + "&nbsp;" + _t("not found"));
} else if (errorCode == 403 || errorCode >= 500 || errorCause != null) {
out.println(_t("Sorry! There has been an internal error."));
out.println("<hr>");
out.println("<p>");
out.println(_t("Please report bugs on {0} or {1}.",
"<a href=\"http://trac.i2p2.i2p/newticket\">trac.i2p2.i2p</a>",
"<a href=\"https://trac.i2p2.de/newticket\">trac.i2p2.de</a>"));
out.println("<p>");
out.println(_t("Please include this information in bug reports") + ':');
out.print("</p></div><div class=\"sorry\" id=\"warning2\"><h3>");
out.print(_t("Error Details"));
out.println("</h3><p>");
out.println(_t("Error {0}", errorCode) + ": " + errorURI + "&nbsp;" + errorMsg);
out.println("</p><p>");
if (errorCause != null) {
StringWriter sw = new StringWriter(2048);
PrintWriter pw = new PrintWriter(sw);
errorCause.printStackTrace(pw);
pw.flush();
String trace = sw.toString();
trace = trace.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;");
trace = trace.replace("\n", "<br>&nbsp;&nbsp;&nbsp;&nbsp;\n");
out.print(trace);
}
out.print("</p><h3>");
out.println(_t("I2P Version and Running Environment"));
out.println("</h3><p>");
// router puts its version here
String version = System.getProperty("router.version", CoreVersion.VERSION);
out.println("<b>I2P version:</b> " + version + "<br>");
out.println("<b>Java version:</b> " + System.getProperty("java.vendor") + ' ' + System.getProperty("java.version") +
" (" + System.getProperty("java.runtime.name") + ' ' + System.getProperty("java.runtime.version") + ")<br>");
out.println("<b>Wrapper version:</b> " + System.getProperty("wrapper.version", "none") + "<br>");
try {
// wrap in case not running on Jetty
out.println("<b>Server version:</b> " + Server.getVersion() + "<br>");
} catch (Throwable t) {}
out.println("<b>Platform:</b> " + System.getProperty("os.name") + ' ' + System.getProperty("os.arch") +
' ' + System.getProperty("os.version") + "<br>");
out.println("<b>Processor:</b> " + NativeBigInteger.cpuModel() + " (" + NativeBigInteger.cpuType() + ")<br>");
out.println("<b>Jbigi:</b> " + NativeBigInteger.loadStatus() + "<br>");
out.println("<b>Encoding:</b> " + System.getProperty("file.encoding") + "<br>");
out.println("<b>Charset:</b> " + Charset.defaultCharset().name());
out.println("</p><p>");
out.println(_t("Note that system information, log timestamps, and log messages may provide clues to your location; please review everything you include in a bug report."));
out.println("</p>");
} else {
out.println("<p>Unsupported error " + errorCode + "</p>");
}
}
/** translate a string, with webapp bundle */
protected String _w(String s) {
return Translate.getString(s, _context, _defaultBundle);
}
/** translate a string, console bundle */
protected String _t(String s) {
return Translate.getString(s, _context, CONSOLE_BUNDLE_NAME);
}
/** translate a string, console bundle */
protected String _t(String s, Object o) {
return Translate.getString(s, o, _context, CONSOLE_BUNDLE_NAME);
}
/** translate a string, console bundle */
protected String _t(String s, Object o, Object o2) {
return Translate.getString(s, o, o2, _context, CONSOLE_BUNDLE_NAME);
}
}

View File

@@ -29,6 +29,15 @@
<servlet-class>i2p.susi.dns.TranslateSVGServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>net.i2p.servlet.ErrorServlet</servlet-name>
<servlet-class>net.i2p.servlet.ErrorServlet</servlet-class>
<init-param>
<param-name>name</param-name>
<param-value>Addressbook</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>addressbook-runner</servlet-name>
<url-pattern>/addressbook-runner</url-pattern>
@@ -63,6 +72,17 @@
<url-pattern>/index</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>i2p.susi.dns.jsp.index_jsp</servlet-name>
<url-pattern>/index.html</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>i2p.susi.dns.jsp.index_jsp</servlet-name>
<!-- empty pattern handles the root -->
<url-pattern></url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>i2p.susi.dns.jsp.export_jsp</servlet-name>
<url-pattern>/export</url-pattern>
@@ -73,6 +93,11 @@
<url-pattern>/images/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>net.i2p.servlet.ErrorServlet</servlet-name>
<url-pattern>/error</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>
30
@@ -83,4 +108,17 @@
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<error-page>
<error-code>403</error-code>
<location>/error</location>
</error-page>
<error-page>
<error-code>404</error-code>
<location>/error</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/error</location>
</error-page>
</web-app>

View File

@@ -106,7 +106,6 @@
<war destfile="${project}.war" webxml="WEB-INF/web-out.xml">
<fileset dir=".">
<include name="WEB-INF/**/*.class"/>
<include name="index.html"/>
<include name="svg/*"/>
</fileset>
<manifest>
@@ -123,7 +122,7 @@
<target name="warUpToDate">
<uptodate property="war.uptodate" targetfile="${project}.war">
<srcfiles dir= "." includes="WEB-INF/web-out.xml WEB-INF/**/*.class svg/* index.html" />
<srcfiles dir= "." includes="WEB-INF/web-out.xml WEB-INF/**/*.class svg/*" />
</uptodate>
<condition property="shouldListChanges" >
<and>

View File

@@ -1,11 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="refresh" content="0;url=index" />
<title>susidns</title>
</head>
<body>
<a href="index">Enter</a>
</body>
</html>

View File

@@ -23,10 +23,26 @@
<file-size-threshold>131072</file-size-threshold>
</multipart-config>
</servlet>
<servlet>
<servlet-name>net.i2p.servlet.ErrorServlet</servlet-name>
<servlet-class>net.i2p.servlet.ErrorServlet</servlet-class>
<init-param>
<param-name>name</param-name>
<param-value>SusiMail</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>SusiMail</servlet-name>
<url-pattern>/susimail</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>net.i2p.servlet.ErrorServlet</servlet-name>
<url-pattern>/error</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>1440</session-timeout>
<cookie-config>
@@ -48,25 +64,17 @@
<param-value>SUSIMAILJSESSIONID</param-value>
</context-param>
<!--
Jetty 6 mulipart form handling
See http://docs.codehaus.org/display/JETTY/File+Upload+in+jetty6
and RequestWrapper.java.
Unused because it doesn't support content-type until Jetty 8.
<filter>
<filter-name>fileuploadfilter</filter-name>
<filter-class>org.mortbay.servlet.MultiPartFilter</filter-class>
<init-param>
<param-name>deleteFiles</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>fileuploadfilter</filter-name>
<url-pattern>/susimail</url-pattern>
</filter-mapping>
-->
<error-page>
<error-code>403</error-code>
<location>/error</location>
</error-page>
<error-page>
<error-code>404</error-code>
<location>/error</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/error</location>
</error-page>
</web-app>

View File

@@ -2,6 +2,7 @@
* Console:
- Hide links to webapps and eepsite if not running (ticket #2161)
- Hide link to /configplugins if disabled
- Add error handler to webapps (ticket #2155)
* i2ptunnel: Hide links to webapps that are not runnning (ticket #2161)
* SusDNS: Translate svg image text (ticket #1749)

View File

@@ -18,7 +18,7 @@ public class RouterVersion {
/** deprecated */
public final static String ID = "Monotone";
public final static String VERSION = CoreVersion.VERSION;
public final static long BUILD = 16;
public final static long BUILD = 17;
/** for example "-test" */
public final static String EXTRA = "";