Merge branch `jetty-9.4.x` into `jetty-10.0.x`

Signed-off-by: Joakim Erdfelt <joakim.erdfelt@gmail.com>

# Conflicts:
#	VERSION.txt
#	aggregates/jetty-all-compact3/pom.xml
#	aggregates/jetty-all/pom.xml
#	apache-jsp/pom.xml
#	apache-jstl/pom.xml
#	build-resources/pom.xml
#	examples/async-rest/async-rest-jar/pom.xml
#	examples/async-rest/async-rest-webapp/pom.xml
#	examples/async-rest/pom.xml
#	examples/embedded/pom.xml
#	examples/pom.xml
#	jetty-alpn/jetty-alpn-client/pom.xml
#	jetty-alpn/jetty-alpn-conscrypt-client/pom.xml
#	jetty-alpn/jetty-alpn-conscrypt-server/pom.xml
#	jetty-alpn/jetty-alpn-java-client/pom.xml
#	jetty-alpn/jetty-alpn-java-server/pom.xml
#	jetty-alpn/jetty-alpn-openjdk8-client/pom.xml
#	jetty-alpn/jetty-alpn-openjdk8-server/pom.xml
#	jetty-alpn/jetty-alpn-server/pom.xml
#	jetty-alpn/pom.xml
#	jetty-annotations/pom.xml
#	jetty-ant/pom.xml
#	jetty-bom/pom.xml
#	jetty-cdi/pom.xml
#	jetty-client/pom.xml
#	jetty-continuation/pom.xml
#	jetty-deploy/pom.xml
#	jetty-distribution/pom.xml
#	jetty-documentation/pom.xml
#	jetty-fcgi/fcgi-client/pom.xml
#	jetty-fcgi/fcgi-server/pom.xml
#	jetty-fcgi/pom.xml
#	jetty-gcloud/jetty-gcloud-session-manager/pom.xml
#	jetty-gcloud/pom.xml
#	jetty-hazelcast/pom.xml
#	jetty-home/pom.xml
#	jetty-http-spi/pom.xml
#	jetty-http/pom.xml
#	jetty-http2/http2-alpn-tests/pom.xml
#	jetty-http2/http2-client/pom.xml
#	jetty-http2/http2-common/pom.xml
#	jetty-http2/http2-hpack/pom.xml
#	jetty-http2/http2-http-client-transport/pom.xml
#	jetty-http2/http2-server/pom.xml
#	jetty-http2/pom.xml
#	jetty-infinispan/infinispan-common/pom.xml
#	jetty-infinispan/infinispan-embedded-query/pom.xml
#	jetty-infinispan/infinispan-embedded/pom.xml
#	jetty-infinispan/infinispan-remote-query/pom.xml
#	jetty-infinispan/infinispan-remote/pom.xml
#	jetty-infinispan/pom.xml
#	jetty-io/pom.xml
#	jetty-jaas/pom.xml
#	jetty-jaspi/pom.xml
#	jetty-jmx/pom.xml
#	jetty-jndi/pom.xml
#	jetty-jspc-maven-plugin/pom.xml
#	jetty-maven-plugin/pom.xml
#	jetty-memcached/jetty-memcached-sessions/pom.xml
#	jetty-memcached/pom.xml
#	jetty-nosql/pom.xml
#	jetty-openid/pom.xml
#	jetty-osgi/jetty-osgi-alpn/pom.xml
#	jetty-osgi/jetty-osgi-boot-jsp/pom.xml
#	jetty-osgi/jetty-osgi-boot-warurl/pom.xml
#	jetty-osgi/jetty-osgi-boot/pom.xml
#	jetty-osgi/jetty-osgi-httpservice/pom.xml
#	jetty-osgi/pom.xml
#	jetty-osgi/test-jetty-osgi-context/pom.xml
#	jetty-osgi/test-jetty-osgi-fragment/pom.xml
#	jetty-osgi/test-jetty-osgi-server/pom.xml
#	jetty-osgi/test-jetty-osgi-webapp/pom.xml
#	jetty-osgi/test-jetty-osgi/pom.xml
#	jetty-plus/pom.xml
#	jetty-proxy/pom.xml
#	jetty-quickstart/pom.xml
#	jetty-rewrite/pom.xml
#	jetty-runner/pom.xml
#	jetty-security/pom.xml
#	jetty-server/pom.xml
#	jetty-server/src/test/java/org/eclipse/jetty/server/ErrorHandlerTest.java
#	jetty-servlet/pom.xml
#	jetty-servlets/pom.xml
#	jetty-spring/pom.xml
#	jetty-start/pom.xml
#	jetty-unixsocket/pom.xml
#	jetty-util-ajax/pom.xml
#	jetty-util/pom.xml
#	jetty-webapp/pom.xml
#	jetty-websocket/javax-websocket-client/pom.xml
#	jetty-websocket/javax-websocket-server/pom.xml
#	jetty-websocket/jetty-websocket-api/pom.xml
#	jetty-websocket/jetty-websocket-common/pom.xml
#	jetty-websocket/jetty-websocket-server/pom.xml
#	jetty-websocket/jetty-websocket-tests/pom.xml
#	jetty-websocket/pom.xml
#	jetty-websocket/websocket-core/pom.xml
#	jetty-websocket/websocket-servlet/pom.xml
#	jetty-xml/pom.xml
#	pom.xml
#	tests/jetty-jmh/pom.xml
#	tests/pom.xml
#	tests/test-continuation/pom.xml
#	tests/test-distribution/pom.xml
#	tests/test-http-client-transport/pom.xml
#	tests/test-integration/pom.xml
#	tests/test-jmx/jmx-webapp-it/pom.xml
#	tests/test-jmx/jmx-webapp/pom.xml
#	tests/test-jmx/pom.xml
#	tests/test-loginservice/pom.xml
#	tests/test-quickstart/pom.xml
#	tests/test-sessions/pom.xml
#	tests/test-sessions/test-file-sessions/pom.xml
#	tests/test-sessions/test-gcloud-sessions/pom.xml
#	tests/test-sessions/test-hazelcast-sessions/pom.xml
#	tests/test-sessions/test-infinispan-sessions/pom.xml
#	tests/test-sessions/test-jdbc-sessions/pom.xml
#	tests/test-sessions/test-memcached-sessions/pom.xml
#	tests/test-sessions/test-mongodb-sessions/pom.xml
#	tests/test-sessions/test-sessions-common/pom.xml
#	tests/test-webapps/pom.xml
#	tests/test-webapps/test-cdi-common-webapp/pom.xml
#	tests/test-webapps/test-felix-webapp/pom.xml
#	tests/test-webapps/test-http2-webapp/pom.xml
#	tests/test-webapps/test-jaas-webapp/pom.xml
#	tests/test-webapps/test-jetty-webapp/pom.xml
#	tests/test-webapps/test-jndi-webapp/pom.xml
#	tests/test-webapps/test-mock-resources/pom.xml
#	tests/test-webapps/test-owb-cdi-webapp/pom.xml
#	tests/test-webapps/test-proxy-webapp/pom.xml
#	tests/test-webapps/test-servlet-spec/pom.xml
#	tests/test-webapps/test-servlet-spec/test-container-initializer/pom.xml
#	tests/test-webapps/test-servlet-spec/test-spec-webapp/pom.xml
#	tests/test-webapps/test-servlet-spec/test-web-fragment/pom.xml
#	tests/test-webapps/test-simple-webapp/pom.xml
#	tests/test-webapps/test-webapp-rfc2616/pom.xml
#	tests/test-webapps/test-weld-cdi-webapp/pom.xml
This commit is contained in:
Joakim Erdfelt 2019-11-26 10:46:22 -06:00
commit 2777229867
No known key found for this signature in database
GPG Key ID: 2D0E1FB8FE4B68B4
7 changed files with 323 additions and 81 deletions

2
.github/stale.yml vendored
View File

@ -13,7 +13,7 @@ staleLabel: Stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has been a
full year without activit. It will be closed if no further activity occurs.
full year without activity. It will be closed if no further activity occurs.
Thank you for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: >

View File

@ -193,6 +193,12 @@ jetty-10.0.0-alpha0 - 11 July
+ 3849 ClosedChannelException from jetty-test-webapp javax websocket chat
example
jetty-9.4.24.v20191120 - 20 November 2019
+ 3083 The ini-template for jetty.console-capture.dir does not match the
default value
+ 4128 OpenIdCredetials can't decode JWT ID token
+ 4334 Better test ErrorHandler changes
jetty-9.4.23.v20191118 - 18 November 2019
+ 1485 Add systemd service file
+ 2266 Jetty maven plugin reload is triggered each time the

View File

@ -21,6 +21,7 @@ package org.eclipse.jetty.server.handler;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
@ -474,24 +475,24 @@ public class ErrorHandler extends AbstractHandler
writer.append(json.entrySet().stream()
.map(e -> QuotedStringTokenizer.quote(e.getKey()) +
":" +
QuotedStringTokenizer.quote((e.getValue())))
QuotedStringTokenizer.quote(StringUtil.sanitizeXmlString((e.getValue()))))
.collect(Collectors.joining(",\n", "{\n", "\n}")));
}
protected void writeErrorPageStacks(HttpServletRequest request, Writer writer)
throws IOException
{
Throwable th = (Throwable)request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
if (_showStacks && th != null)
if (th != null)
{
PrintWriter pw = writer instanceof PrintWriter ? (PrintWriter)writer : new PrintWriter(writer);
pw.write("<pre>");
while (th != null)
writer.write("<h3>Caused by:</h3><pre>");
// You have to pre-generate and then use #write(writer, String)
try (StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw))
{
th.printStackTrace(pw);
th = th.getCause();
pw.flush();
write(writer, sw.getBuffer().toString()); // sanitize
}
writer.write("</pre>\n");
}

View File

@ -19,8 +19,9 @@
package org.eclipse.jetty.server;
import java.io.IOException;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.servlet.DispatcherType;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
@ -28,30 +29,38 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.BadMessageException;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.tools.HttpTester;
import org.eclipse.jetty.server.handler.AbstractHandler;
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.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.anyOf;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.startsWith;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class ErrorHandlerTest
{
static StacklessLogging stacklessLogging;
static Server server;
static LocalConnector connector;
@BeforeAll
public static void before() throws Exception
{
stacklessLogging = new StacklessLogging(HttpChannel.class);
server = new Server();
connector = new LocalConnector(server);
server.addConnector(connector);
@ -64,7 +73,7 @@ public class ErrorHandlerTest
if (baseRequest.getDispatcherType() == DispatcherType.ERROR)
{
baseRequest.setHandled(true);
response.sendError(((Integer)request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE)).intValue());
response.sendError((Integer)request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE));
return;
}
@ -78,7 +87,40 @@ public class ErrorHandlerTest
if (target.startsWith("/badmessage/"))
{
throw new ServletException(new BadMessageException(Integer.parseInt(target.substring(12))));
int code = Integer.parseInt(target.substring(target.lastIndexOf('/') + 1));
throw new ServletException(new BadMessageException(code));
}
// produce an exception with an JSON formatted cause message
if (target.startsWith("/jsonmessage/"))
{
String message = "\"}, \"glossary\": {\n \"title\": \"example\"\n }\n {\"";
throw new ServletException(new RuntimeException(message));
}
// produce an exception with an XML cause message
if (target.startsWith("/xmlmessage/"))
{
String message =
"<!DOCTYPE glossary PUBLIC \"-//OASIS//DTD DocBook V3.1//EN\">\n" +
" <glossary>\n" +
" <title>example glossary</title>\n" +
" </glossary>";
throw new ServletException(new RuntimeException(message));
}
// produce an exception with an HTML cause message
if (target.startsWith("/htmlmessage/"))
{
String message = "<hr/><script>alert(42)</script>%3Cscript%3E";
throw new ServletException(new RuntimeException(message));
}
// produce an exception with a UTF-8 cause message
if (target.startsWith("/utf8message/"))
{
String message = "Euro is &euro; and \u20AC and %E2%82%AC";
throw new ServletException(new RuntimeException(message));
}
}
});
@ -89,193 +131,238 @@ public class ErrorHandlerTest
public static void after() throws Exception
{
server.stop();
stacklessLogging.close();
}
@Test
public void test404NoAccept() throws Exception
{
String response = connector.getResponse(
String rawResponse = connector.getResponse(
"GET / HTTP/1.1\r\n" +
"Host: Localhost\r\n" +
"\r\n");
assertThat(response, startsWith("HTTP/1.1 404 "));
assertThat(response, not(containsString("Content-Length: 0")));
assertThat(response, containsString("Content-Type: text/html;charset=iso-8859-1"));
HttpTester.Response response = HttpTester.parseResponse(rawResponse);
assertThat("Response status code", response.getStatus(), is(404));
assertThat("Response Content-Length", response.getField(HttpHeader.CONTENT_LENGTH).getIntValue(), greaterThan(0));
assertThat("Response Content-Type", response.get(HttpHeader.CONTENT_TYPE), containsString("text/html;charset=ISO-8859-1"));
assertContent(response);
}
@Test
public void test404EmptyAccept() throws Exception
{
String response = connector.getResponse(
String rawResponse = connector.getResponse(
"GET / HTTP/1.1\r\n" +
"Accept: \r\n" +
"Host: Localhost\r\n" +
"\r\n");
assertThat(response, startsWith("HTTP/1.1 404 "));
assertThat(response, containsString("Content-Length: 0"));
assertThat(response, not(containsString("Content-Type")));
HttpTester.Response response = HttpTester.parseResponse(rawResponse);
assertThat("Response status code", response.getStatus(), is(404));
assertThat("Response Content-Length", response.getField(HttpHeader.CONTENT_LENGTH).getIntValue(), is(0));
assertThat("Response Content-Type", response.getField(HttpHeader.CONTENT_TYPE), is(nullValue()));
}
@Test
public void test404UnAccept() throws Exception
{
String response = connector.getResponse(
String rawResponse = connector.getResponse(
"GET / HTTP/1.1\r\n" +
"Accept: text/*;q=0\r\n" +
"Host: Localhost\r\n" +
"\r\n");
HttpTester.Response response = HttpTester.parseResponse(rawResponse);
assertThat(response, startsWith("HTTP/1.1 404 "));
assertThat(response, containsString("Content-Length: 0"));
assertThat(response, not(containsString("Content-Type")));
assertThat("Response status code", response.getStatus(), is(404));
assertThat("Response Content-Length", response.getField(HttpHeader.CONTENT_LENGTH).getIntValue(), is(0));
assertThat("Response Content-Type", response.getField(HttpHeader.CONTENT_TYPE), is(nullValue()));
}
@Test
public void test404AllAccept() throws Exception
{
String response = connector.getResponse(
String rawResponse = connector.getResponse(
"GET / HTTP/1.1\r\n" +
"Host: Localhost\r\n" +
"Accept: */*\r\n" +
"\r\n");
HttpTester.Response response = HttpTester.parseResponse(rawResponse);
assertThat(response, startsWith("HTTP/1.1 404 "));
assertThat(response, not(containsString("Content-Length: 0")));
assertThat(response, containsString("Content-Type: text/html;charset=iso-8859-1"));
assertThat("Response status code", response.getStatus(), is(404));
assertThat("Response Content-Length", response.getField(HttpHeader.CONTENT_LENGTH).getIntValue(), greaterThan(0));
assertThat("Response Content-Type", response.get(HttpHeader.CONTENT_TYPE), containsString("text/html;charset=ISO-8859-1"));
assertContent(response);
}
@Test
public void test404HtmlAccept() throws Exception
{
String response = connector.getResponse(
String rawResponse = connector.getResponse(
"GET / HTTP/1.1\r\n" +
"Host: Localhost\r\n" +
"Accept: text/html\r\n" +
"\r\n");
assertThat(response, startsWith("HTTP/1.1 404 "));
assertThat(response, not(containsString("Content-Length: 0")));
assertThat(response, containsString("Content-Type: text/html;charset=iso-8859-1"));
HttpTester.Response response = HttpTester.parseResponse(rawResponse);
assertThat("Response status code", response.getStatus(), is(404));
assertThat("Response Content-Length", response.getField(HttpHeader.CONTENT_LENGTH).getIntValue(), greaterThan(0));
assertThat("Response Content-Type", response.get(HttpHeader.CONTENT_TYPE), containsString("text/html;charset=ISO-8859-1"));
assertContent(response);
}
@Test
public void testMoreSpecificAccept() throws Exception
{
String response = connector.getResponse(
String rawResponse = connector.getResponse(
"GET / HTTP/1.1\r\n" +
"Host: Localhost\r\n" +
"Accept: text/html, some/other;specific=true\r\n" +
"\r\n");
assertThat(response, startsWith("HTTP/1.1 404 "));
assertThat(response, not(containsString("Content-Length: 0")));
assertThat(response, containsString("Content-Type: text/html;charset=iso-8859-1"));
HttpTester.Response response = HttpTester.parseResponse(rawResponse);
assertThat("Response status code", response.getStatus(), is(404));
assertThat("Response Content-Length", response.getField(HttpHeader.CONTENT_LENGTH).getIntValue(), greaterThan(0));
assertThat("Response Content-Type", response.get(HttpHeader.CONTENT_TYPE), containsString("text/html;charset=ISO-8859-1"));
assertContent(response);
}
@Test
public void test404HtmlAcceptAnyCharset() throws Exception
{
String response = connector.getResponse(
String rawResponse = connector.getResponse(
"GET / HTTP/1.1\r\n" +
"Host: Localhost\r\n" +
"Accept: text/html\r\n" +
"Accept-Charset: *\r\n" +
"\r\n");
assertThat(response, startsWith("HTTP/1.1 404 "));
assertThat(response, not(containsString("Content-Length: 0")));
assertThat(response, containsString("Content-Type: text/html;charset=utf-8"));
HttpTester.Response response = HttpTester.parseResponse(rawResponse);
assertThat("Response status code", response.getStatus(), is(404));
assertThat("Response Content-Length", response.getField(HttpHeader.CONTENT_LENGTH).getIntValue(), greaterThan(0));
assertThat("Response Content-Type", response.get(HttpHeader.CONTENT_TYPE), containsString("text/html;charset=UTF-8"));
assertContent(response);
}
@Test
public void test404HtmlAcceptUtf8Charset() throws Exception
{
String response = connector.getResponse(
String rawResponse = connector.getResponse(
"GET / HTTP/1.1\r\n" +
"Host: Localhost\r\n" +
"Accept: text/html\r\n" +
"Accept-Charset: utf-8\r\n" +
"\r\n");
assertThat(response, startsWith("HTTP/1.1 404 "));
assertThat(response, not(containsString("Content-Length: 0")));
assertThat(response, containsString("Content-Type: text/html;charset=utf-8"));
HttpTester.Response response = HttpTester.parseResponse(rawResponse);
assertThat("Response status code", response.getStatus(), is(404));
assertThat("Response Content-Length", response.getField(HttpHeader.CONTENT_LENGTH).getIntValue(), greaterThan(0));
assertThat("Response Content-Type", response.get(HttpHeader.CONTENT_TYPE), containsString("text/html;charset=UTF-8"));
assertContent(response);
}
@Test
public void test404HtmlAcceptNotUtf8Charset() throws Exception
{
String response = connector.getResponse(
String rawResponse = connector.getResponse(
"GET / HTTP/1.1\r\n" +
"Host: Localhost\r\n" +
"Accept: text/html\r\n" +
"Accept-Charset: utf-8;q=0\r\n" +
"\r\n");
assertThat(response, startsWith("HTTP/1.1 404 "));
assertThat(response, not(containsString("Content-Length: 0")));
assertThat(response, containsString("Content-Type: text/html;charset=iso-8859-1"));
HttpTester.Response response = HttpTester.parseResponse(rawResponse);
assertThat("Response status code", response.getStatus(), is(404));
assertThat("Response Content-Length", response.getField(HttpHeader.CONTENT_LENGTH).getIntValue(), greaterThan(0));
assertThat("Response Content-Type", response.get(HttpHeader.CONTENT_TYPE), containsString("text/html;charset=ISO-8859-1"));
assertContent(response);
}
@Test
public void test404HtmlAcceptNotUtf8UnknownCharset() throws Exception
{
String response = connector.getResponse(
String rawResponse = connector.getResponse(
"GET / HTTP/1.1\r\n" +
"Host: Localhost\r\n" +
"Accept: text/html\r\n" +
"Accept-Charset: utf-8;q=0,unknown\r\n" +
"\r\n");
assertThat(response, startsWith("HTTP/1.1 404 "));
assertThat(response, containsString("Content-Length: 0"));
assertThat(response, not(containsString("Content-Type")));
HttpTester.Response response = HttpTester.parseResponse(rawResponse);
assertThat("Response status code", response.getStatus(), is(404));
assertThat("Response Content-Length", response.getField(HttpHeader.CONTENT_LENGTH).getIntValue(), is(0));
assertThat("Response Content-Type", response.getField(HttpHeader.CONTENT_TYPE), is(nullValue()));
}
@Test
public void test404HtmlAcceptUnknownUtf8Charset() throws Exception
{
String response = connector.getResponse(
String rawResponse = connector.getResponse(
"GET / HTTP/1.1\r\n" +
"Host: Localhost\r\n" +
"Accept: text/html\r\n" +
"Accept-Charset: utf-8;q=0.1,unknown\r\n" +
"\r\n");
assertThat(response, startsWith("HTTP/1.1 404 "));
assertThat(response, not(containsString("Content-Length: 0")));
assertThat(response, containsString("Content-Type: text/html;charset=utf-8"));
HttpTester.Response response = HttpTester.parseResponse(rawResponse);
assertThat("Response status code", response.getStatus(), is(404));
assertThat("Response Content-Length", response.getField(HttpHeader.CONTENT_LENGTH).getIntValue(), greaterThan(0));
assertThat("Response Content-Type", response.get(HttpHeader.CONTENT_TYPE), containsString("text/html;charset=UTF-8"));
assertContent(response);
}
@Test
public void test404PreferHtml() throws Exception
{
String response = connector.getResponse(
String rawResponse = connector.getResponse(
"GET / HTTP/1.1\r\n" +
"Host: Localhost\r\n" +
"Accept: text/html;q=1.0,text/json;q=0.5,*/*\r\n" +
"Accept-Charset: *\r\n" +
"\r\n");
assertThat(response, startsWith("HTTP/1.1 404 "));
assertThat(response, not(containsString("Content-Length: 0")));
assertThat(response, containsString("Content-Type: text/html;charset=utf-8"));
HttpTester.Response response = HttpTester.parseResponse(rawResponse);
assertThat("Response status code", response.getStatus(), is(404));
assertThat("Response Content-Length", response.getField(HttpHeader.CONTENT_LENGTH).getIntValue(), greaterThan(0));
assertThat("Response Content-Type", response.get(HttpHeader.CONTENT_TYPE), containsString("text/html;charset=UTF-8"));
assertContent(response);
}
@Test
public void test404PreferJson() throws Exception
{
String response = connector.getResponse(
String rawResponse = connector.getResponse(
"GET / HTTP/1.1\r\n" +
"Host: Localhost\r\n" +
"Accept: text/html;q=0.5,text/json;q=1.0,*/*\r\n" +
"Accept-Charset: *\r\n" +
"\r\n");
assertThat(response, startsWith("HTTP/1.1 404 "));
assertThat(response, not(containsString("Content-Length: 0")));
assertThat(response, containsString("Content-Type: text/json"));
HttpTester.Response response = HttpTester.parseResponse(rawResponse);
assertThat("Response status code", response.getStatus(), is(404));
assertThat("Response Content-Length", response.getField(HttpHeader.CONTENT_LENGTH).getIntValue(), greaterThan(0));
assertThat("Response Content-Type", response.get(HttpHeader.CONTENT_TYPE), containsString("text/json"));
assertContent(response);
}
@Test
@ -286,12 +373,14 @@ public class ErrorHandlerTest
"Host: Localhost\r\n" +
"Accept: text/plain\r\n" +
"\r\n");
HttpTester.Response response = HttpTester.parseResponse(rawResponse);
assertThat("Response status code", response.getStatus(), is(404));
HttpField contentType = response.getField(HttpHeader.CONTENT_TYPE);
assertThat("Response Content-Type", contentType, is(notNullValue()));
assertThat("Response Content-Type value", contentType.getValue(), not(containsString("null")));
assertThat("Response Content-Length", response.getField(HttpHeader.CONTENT_LENGTH).getIntValue(), greaterThan(0));
assertThat("Response Content-Type", response.get(HttpHeader.CONTENT_TYPE), containsString("text/plain"));
assertContent(response);
}
@Test
@ -301,9 +390,131 @@ public class ErrorHandlerTest
"GET /badmessage/444 HTTP/1.1\r\n" +
"Host: Localhost\r\n" +
"\r\n");
HttpTester.Response response = HttpTester.parseResponse(rawResponse);
assertThat("Response status code", response.getStatus(), is(444));
assertThat("Response Content-Length", response.getField(HttpHeader.CONTENT_LENGTH).getIntValue(), greaterThan(0));
assertThat("Response Content-Type", response.get(HttpHeader.CONTENT_TYPE), containsString("text/html;charset=ISO-8859-1"));
assertContent(response);
}
@ParameterizedTest
@ValueSource(strings = {
"/jsonmessage/",
"/xmlmessage/",
"/htmlmessage/",
"/utf8message/",
})
public void testComplexCauseMessageNoAcceptHeader(String path) throws Exception
{
String rawResponse = connector.getResponse(
"GET " + path + " HTTP/1.1\r\n" +
"Host: Localhost\r\n" +
"\r\n");
HttpTester.Response response = HttpTester.parseResponse(rawResponse);
assertThat("Response status code", response.getStatus(), is(500));
assertThat("Response Content-Length", response.getField(HttpHeader.CONTENT_LENGTH).getIntValue(), greaterThan(0));
assertThat("Response Content-Type", response.get(HttpHeader.CONTENT_TYPE), containsString("text/html;charset=ISO-8859-1"));
String content = assertContent(response);
if (path.startsWith("/utf8"))
{
// we are Not expecting UTF-8 output, look for mangled ISO-8859-1 version
assertThat("content", content, containsString("Euro is &amp;euro; and ? and %E2%82%AC"));
}
}
@ParameterizedTest
@ValueSource(strings = {
"/jsonmessage/",
"/xmlmessage/",
"/htmlmessage/",
"/utf8message/",
})
public void testComplexCauseMessageAcceptUtf8Header(String path) throws Exception
{
String rawResponse = connector.getResponse(
"GET " + path + " HTTP/1.1\r\n" +
"Host: Localhost\r\n" +
"Accept: text/html\r\n" +
"Accept-Charset: utf-8\r\n" +
"\r\n");
HttpTester.Response response = HttpTester.parseResponse(rawResponse);
assertThat("Response status code", response.getStatus(), is(500));
assertThat("Response Content-Length", response.getField(HttpHeader.CONTENT_LENGTH).getIntValue(), greaterThan(0));
assertThat("Response Content-Type", response.get(HttpHeader.CONTENT_TYPE), containsString("text/html;charset=UTF-8"));
String content = assertContent(response);
if (path.startsWith("/utf8"))
{
// we are Not expecting UTF-8 output, look for mangled ISO-8859-1 version
assertThat("content", content, containsString("Euro is &amp;euro; and \u20AC and %E2%82%AC"));
}
}
private String assertContent(HttpTester.Response response)
{
String contentType = response.get(HttpHeader.CONTENT_TYPE);
String content = response.getContent();
if (contentType.contains("text/html"))
{
assertThat(content, not(containsString("<script>")));
assertThat(content, not(containsString("<glossary>")));
assertThat(content, not(containsString("<!DOCTYPE>")));
assertThat(content, not(containsString("&euro;")));
}
else if (contentType.contains("text/json"))
{
Map jo = (Map)JSON.parse(response.getContent());
Set<String> acceptableKeyNames = new HashSet<>();
acceptableKeyNames.add("url");
acceptableKeyNames.add("status");
acceptableKeyNames.add("message");
acceptableKeyNames.add("servlet");
acceptableKeyNames.add("cause0");
acceptableKeyNames.add("cause1");
acceptableKeyNames.add("cause2");
for (Object key : jo.keySet())
{
String keyStr = (String)key;
assertTrue(acceptableKeyNames.contains(keyStr), "Unexpected Key [" + keyStr + "]");
Object value = jo.get(key);
assertThat("Unexpected value type (" + value.getClass().getName() + ")",
value, instanceOf(String.class));
}
assertThat("url field", jo.get("url"), is(notNullValue()));
String expectedStatus = String.valueOf(response.getStatus());
assertThat("status field", jo.get("status"), is(expectedStatus));
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"))
{
assertThat(content, containsString("STATUS: " + response.getStatus()));
}
else
{
System.out.println("Not checked Content-Type: " + contentType);
System.out.println(content);
}
return content;
}
@Test
@ -314,16 +525,38 @@ public class ErrorHandlerTest
"Host: Localhost\r\n" +
"Accept: text/json\r\n" +
"\r\n");
HttpTester.Response response = HttpTester.parseResponse(rawResponse);
assertThat("Response status code", response.getStatus(), is(444));
System.out.println("response:" + response.getContent());
assertContent(response);
}
Map<Object,Object> jo = (Map) JSON.parse(response.getContent());
@ParameterizedTest
@ValueSource(strings = {
"/jsonmessage/",
"/xmlmessage/",
"/htmlmessage/",
"/utf8message/",
})
public void testJsonResponseWorse(String path) throws Exception
{
String rawResponse = connector.getResponse(
"GET " + path + " HTTP/1.1\r\n" +
"Host: Localhost\r\n" +
"Accept: text/json\r\n" +
"\r\n");
assertThat("url field null", jo.get("url"), is(notNullValue()));
assertThat("status field null", jo.get("status"), is(notNullValue()));
assertThat("message field null", jo.get("message"), is(notNullValue()));
HttpTester.Response response = HttpTester.parseResponse(rawResponse);
assertThat("Response status code", response.getStatus(), is(500));
String content = assertContent(response);
if (path.startsWith("/utf8"))
{
// we are expecting UTF-8 output, look for it.
assertThat("content", content, containsString("Euro is &amp;euro; and \u20AC and %E2%82%AC"));
}
}
}

View File

@ -18,7 +18,7 @@ resources/
[ini-template]
## Logging directory (relative to $jetty.base)
# jetty.console-capture.dir=logs
# jetty.console-capture.dir=./logs
## Whether to append to existing file
# jetty.console-capture.append=true

View File

@ -945,7 +945,8 @@ public class UrlEncoded extends MultiMap<String> implements Cloneable
}
else if (b >= 'a' && b <= 'z' ||
b >= 'A' && b <= 'Z' ||
b >= '0' && b <= '9')
b >= '0' && b <= '9' ||
b == '-' || b == '.' || b == '_' || b == '~')
{
encoded[n++] = b;
}

View File

@ -66,7 +66,7 @@
<jackson-databind.version>2.9.9</jackson-databind.version>
<localRepoPath>${project.build.directory}/local-repo</localRepoPath>
<settingsPath>src/it/settings.xml</settingsPath>
<surefire.rerunFailingTestsCount>3</surefire.rerunFailingTestsCount>
<surefire.rerunFailingTestsCount>0</surefire.rerunFailingTestsCount>
</properties>
<licenses>
@ -1286,6 +1286,7 @@
<id>ci</id>
<properties>
<settingsPath>${env.GLOBAL_MVN_SETTINGS}</settingsPath>
<surefire.rerunFailingTestsCount>3</surefire.rerunFailingTestsCount>
</properties>
<modules>
<module>aggregates/jetty-all</module>