Issue #4334 - Improve testing of ErrorHandler behavior

+ Cleanup from PR review

Signed-off-by: Joakim Erdfelt <joakim.erdfelt@gmail.com>
This commit is contained in:
Joakim Erdfelt 2019-11-20 13:17:17 -06:00
parent 9e40fc9a6f
commit cf0df6e3ff
No known key found for this signature in database
GPG Key ID: 2D0E1FB8FE4B68B4
2 changed files with 35 additions and 31 deletions

View File

@ -482,28 +482,26 @@ public class ErrorHandler extends AbstractHandler
writer.append(json.entrySet().stream() writer.append(json.entrySet().stream()
.map(e -> QuotedStringTokenizer.quote(e.getKey()) + .map(e -> QuotedStringTokenizer.quote(e.getKey()) +
":" + ":" +
QuotedStringTokenizer.quote((e.getValue()))) QuotedStringTokenizer.quote(StringUtil.sanitizeXmlString((e.getValue()))))
.collect(Collectors.joining(",\n", "{\n", "\n}"))); .collect(Collectors.joining(",\n", "{\n", "\n}")));
} }
protected void writeErrorPageStacks(HttpServletRequest request, Writer writer) protected void writeErrorPageStacks(HttpServletRequest request, Writer writer)
throws IOException throws IOException
{ {
Throwable th = (Throwable)request.getAttribute(RequestDispatcher.ERROR_EXCEPTION); Throwable th = (Throwable)request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
while (th != null) if (th != null)
{ {
writer.write("<h3>Caused by:</h3><pre>"); writer.write("<h3>Caused by:</h3><pre>");
// You have to pre-generate and then use #write(writer, String) // You have to pre-generate and then use #write(writer, String)
StringWriter sw = new StringWriter(); try (StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw); PrintWriter pw = new PrintWriter(sw))
th.printStackTrace(pw); {
pw.flush(); th.printStackTrace(pw);
write(writer, sw.getBuffer().toString()); // IMPORTANT STEP pw.flush();
write(writer, sw.getBuffer().toString()); // sanitize
}
writer.write("</pre>\n"); writer.write("</pre>\n");
th = th.getCause();
} }
} }

View File

@ -31,6 +31,7 @@ import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpTester; import org.eclipse.jetty.http.HttpTester;
import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.util.ajax.JSON; import org.eclipse.jetty.util.ajax.JSON;
import org.eclipse.jetty.util.log.StacklessLogging;
import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -38,6 +39,7 @@ import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource; import org.junit.jupiter.params.provider.ValueSource;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.anyOf;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
@ -47,12 +49,14 @@ import static org.hamcrest.Matchers.nullValue;
public class ErrorHandlerTest public class ErrorHandlerTest
{ {
static StacklessLogging stacklessLogging;
static Server server; static Server server;
static LocalConnector connector; static LocalConnector connector;
@BeforeAll @BeforeAll
public static void before() throws Exception public static void before() throws Exception
{ {
stacklessLogging = new StacklessLogging(HttpChannel.class);
server = new Server(); server = new Server();
connector = new LocalConnector(server); connector = new LocalConnector(server);
server.addConnector(connector); server.addConnector(connector);
@ -66,7 +70,7 @@ public class ErrorHandlerTest
if (baseRequest.getDispatcherType() == DispatcherType.ERROR) if (baseRequest.getDispatcherType() == DispatcherType.ERROR)
{ {
baseRequest.setHandled(true); baseRequest.setHandled(true);
response.sendError(((Integer)request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE)).intValue()); response.sendError((Integer)request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE));
return; return;
} }
@ -80,43 +84,40 @@ public class ErrorHandlerTest
if (target.startsWith("/badmessage/")) if (target.startsWith("/badmessage/"))
{ {
int code = Integer.valueOf(target.substring(target.lastIndexOf('/') + 1)); int code = Integer.parseInt(target.substring(target.lastIndexOf('/') + 1));
throw new ServletException(new BadMessageException(code)); throw new ServletException(new BadMessageException(code));
} }
// produce an exception with an JSON formatted cause message // produce an exception with an JSON formatted cause message
if (target.startsWith("/jsonmessage/")) if (target.startsWith("/jsonmessage/"))
{ {
StringBuilder message = new StringBuilder(); String message = "{\n \"glossary\": {\n \"title\": \"example\"\n }\n }";
message.append("{\n \"glossary\": {\n \"title\": \"example\"\n }\n }"); throw new ServletException(new RuntimeException(message));
throw new ServletException(new RuntimeException(message.toString()));
} }
// produce an exception with an XML cause message // produce an exception with an XML cause message
if (target.startsWith("/xmlmessage/")) if (target.startsWith("/xmlmessage/"))
{ {
StringBuilder message = new StringBuilder(); String message =
message.append("<!DOCTYPE glossary PUBLIC \"-//OASIS//DTD DocBook V3.1//EN\">\n"); "<!DOCTYPE glossary PUBLIC \"-//OASIS//DTD DocBook V3.1//EN\">\n" +
message.append(" <glossary>\n"); " <glossary>\n" +
message.append(" <title>example glossary</title>\n"); " <title>example glossary</title>\n" +
message.append(" </glossary>"); " </glossary>";
throw new ServletException(new RuntimeException(message.toString())); throw new ServletException(new RuntimeException(message));
} }
// produce an exception with an HTML cause message // produce an exception with an HTML cause message
if (target.startsWith("/htmlmessage/")) if (target.startsWith("/htmlmessage/"))
{ {
StringBuilder message = new StringBuilder(); String message = "<hr/><script>alert(42)</script>%3Cscript%3E";
message.append("<hr/><script>alert(42)</script>"); throw new ServletException(new RuntimeException(message));
throw new ServletException(new RuntimeException(message.toString()));
} }
// produce an exception with a UTF-8 cause message // produce an exception with a UTF-8 cause message
if (target.startsWith("/utf8message/")) if (target.startsWith("/utf8message/"))
{ {
StringBuilder message = new StringBuilder(); String message = "Euro is &euro; and \u20AC and %E2%82%AC";
message.append("Euro is &euro; and \u20AC and %E2%82%AC"); throw new ServletException(new RuntimeException(message));
throw new ServletException(new RuntimeException(message.toString()));
} }
} }
}); });
@ -127,6 +128,7 @@ public class ErrorHandlerTest
public static void after() throws Exception public static void after() throws Exception
{ {
server.stop(); server.stop();
stacklessLogging.close();
} }
@Test @Test
@ -469,12 +471,16 @@ public class ErrorHandlerTest
} }
else if (contentType.contains("text/json")) else if (contentType.contains("text/json"))
{ {
Map<Object, Object> jo = (Map)JSON.parse(response.getContent()); Map jo = (Map)JSON.parse(response.getContent());
assertThat("url field", jo.get("url"), is(notNullValue())); assertThat("url field", jo.get("url"), is(notNullValue()));
String expectedStatus = String.valueOf(response.getStatus()); String expectedStatus = String.valueOf(response.getStatus());
assertThat("status field", jo.get("status"), is(expectedStatus)); assertThat("status field", jo.get("status"), is(expectedStatus));
assertThat("message field", jo.get("message"), is(notNullValue())); String message = (String)jo.get("message");
assertThat("message field", message, is(notNullValue()));
assertThat("message field", message, anyOf(
not(containsString("<")),
not(containsString(">"))));
} }
else if (contentType.contains("text/plain")) else if (contentType.contains("text/plain"))
{ {
@ -528,7 +534,7 @@ public class ErrorHandlerTest
if (path.startsWith("/utf8")) if (path.startsWith("/utf8"))
{ {
// we are expecting UTF-8 output, look for it. // we are expecting UTF-8 output, look for it.
assertThat("content", content, containsString("Euro is &euro; and \u20AC and %E2%82%AC")); assertThat("content", content, containsString("Euro is &amp;euro; and \u20AC and %E2%82%AC"));
} }
} }
} }