Merge branch 'jetty-10.0.x' into jetty-11.0.x

This commit is contained in:
olivier lamy 2021-03-24 16:44:23 +10:00
commit 36bef3ec6e
12 changed files with 83 additions and 77 deletions

View File

@ -138,5 +138,10 @@
<artifactId>testcontainers</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -46,11 +46,13 @@ import org.slf4j.LoggerFactory;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.output.Slf4jLogConsumer;
import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy;
import org.testcontainers.junit.jupiter.Testcontainers;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
@Testcontainers(disabledWithoutDocker = true)
public class RemoteQueryManagerTest
{
public static final String DEFAULT_CACHE_NAME = "remote-session-test";

View File

@ -76,6 +76,8 @@
<Set name="requestCookieCompliance"><Call class="org.eclipse.jetty.http.CookieCompliance" name="valueOf"><Arg><Property name="jetty.httpConfig.requestCookieCompliance" default="RFC6265"/></Arg></Call></Set>
<Set name="responseCookieCompliance"><Call class="org.eclipse.jetty.http.CookieCompliance" name="valueOf"><Arg><Property name="jetty.httpConfig.responseCookieCompliance" default="RFC6265"/></Arg></Call></Set>
<Set name="relativeRedirectAllowed"><Property name="jetty.httpConfig.relativeRedirectAllowed" default="false"/></Set>
<Set name="useInputDirectByteBuffers" property="jetty.httpConfig.useInputDirectByteBuffers"></Set>
<Set name="useOutputDirectByteBuffers" property="jetty.httpConfig.useOutputDirectByteBuffers"></Set>
</New>
<!-- =========================================================== -->

View File

@ -1,30 +0,0 @@
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
[description]
Enables logback request log.
[tags]
requestlog
logging
logback
[depend]
server
logback-impl
resources
[provide]
requestlog-impl
[xml]
etc/jetty-logback-access.xml
[files]
logs/
basehome:modules/logback-access/jetty-logback-access.xml|etc/jetty-logback-access.xml
basehome:modules/logback-access/logback-access.xml|resources/logback-access.xml
maven://ch.qos.logback/logback-access/${logback.version}|lib/logback/logback-access-${logback.version}.jar
[lib]
lib/logback/logback-access-${logback.version}.jar

View File

@ -1,15 +0,0 @@
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">
<!-- =============================================================== -->
<!-- Configure the Logback Request Log -->
<!-- =============================================================== -->
<Configure id="Server" class="org.eclipse.jetty.server.Server">
<Set name="RequestLog">
<New id="RequestLog" class="ch.qos.logback.access.jetty.RequestLogImpl">
<Set name="name">logback-access</Set>
<Set name="resource">/logback-access.xml</Set>
<Call name="start"/>
</New>
</Set>
</Configure>

View File

@ -1,17 +0,0 @@
<configuration>
<!-- always a good activate OnConsoleStatusListener -->
<statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/access.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/access.%d{yyyy-MM-dd}.log.zip</fileNamePattern>
</rollingPolicy>
<encoder>
<pattern>combined</pattern>
</encoder>
</appender>
<appender-ref ref="FILE" />
</configuration>

View File

@ -74,6 +74,10 @@ etc/jetty.xml
## Relative Redirect Locations allowed
# jetty.httpConfig.relativeRedirectAllowed=false
## Whether to use direct ByteBuffers for reading or writing
# jetty.httpConfig.useInputDirectByteBuffers=true
# jetty.httpConfig.useOutputDirectByteBuffers=true
### Server configuration
## Whether ctrl+c on the console gracefully stops the Jetty server
# jetty.server.stopAtShutdown=true
@ -91,3 +95,4 @@ etc/jetty.xml
# jetty.scheduler.name=
# jetty.scheduler.deamon=false
# jetty.scheduler.threads=-1

View File

@ -1692,7 +1692,8 @@ public class Request implements HttpServletRequest
_httpFields = request.getFields();
final HttpURI uri = request.getURI();
if (uri.isAmbiguous())
boolean ambiguous = uri.isAmbiguous();
if (ambiguous)
{
UriCompliance compliance = _channel == null || _channel.getHttpConfiguration() == null ? null : _channel.getHttpConfiguration().getUriCompliance();
if (uri.hasAmbiguousSegment() && (compliance == null || !compliance.allows(UriCompliance.Violation.AMBIGUOUS_PATH_SEGMENT)))
@ -1741,7 +1742,15 @@ public class Request implements HttpServletRequest
// TODO this is not really right for CONNECT
path = _uri.isAbsolute() ? "/" : null;
else if (encoded.startsWith("/"))
{
path = (encoded.length() == 1) ? "/" : _uri.getDecodedPath();
// Strictly speaking if a URI is legal and encodes ambiguous segments, then they should be
// reflected in the decoded string version. However, it can be ambiguous to provide a decoded path as
// a string, so we normalize again. If an application wishes to see ambiguous URIs, then they can look
// at the encoded form of the URI
if (ambiguous)
path = URIUtil.canonicalPath(path);
}
else if ("*".equals(encoded) || HttpMethod.CONNECT.is(getMethod()))
path = encoded;
else

View File

@ -13,6 +13,7 @@
package org.eclipse.jetty.server;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.HashSet;
import java.util.Set;
@ -48,10 +49,12 @@ import org.slf4j.LoggerFactory;
public class SecureRequestCustomizer implements HttpConfiguration.Customizer
{
private static final Logger LOG = LoggerFactory.getLogger(SecureRequestCustomizer.class);
public static final String JAKARTA_SERVLET_REQUEST_X_509_CERTIFICATE = "jakarta.servlet.request.X509Certificate";
public static final String JAKARTA_SERVLET_REQUEST_CIPHER_SUITE = "jakarta.servlet.request.cipher_suite";
public static final String JAKARTA_SERVLET_REQUEST_KEY_SIZE = "jakarta.servlet.request.key_size";
public static final String JAKARTA_SERVLET_REQUEST_SSL_SESSION_ID = "jakarta.servlet.request.ssl_session_id";
public static final String X509_CERT = "org.eclipse.jetty.server.x509_cert";
private String sslSessionAttribute = "org.eclipse.jetty.servlet.request.ssl_session";
@ -244,24 +247,24 @@ public class SecureRequestCustomizer implements HttpConfiguration.Customizer
if (isSniRequired() || isSniHostCheck())
{
String sniHost = (String)sslSession.getValue(SslContextFactory.Server.SNI_HOST);
X509 cert = new X509(null, (X509Certificate)sslSession.getLocalCertificates()[0]);
X509 x509 = (X509)sslSession.getValue(X509_CERT);
if (x509 == null)
{
Certificate[] certificates = sslSession.getLocalCertificates();
if (certificates == null || certificates.length == 0 || !(certificates[0] instanceof X509Certificate))
throw new BadMessageException(400, "Invalid SNI");
x509 = new X509(null, (X509Certificate)certificates[0]);
sslSession.putValue(X509_CERT, x509);
}
String serverName = request.getServerName();
if (LOG.isDebugEnabled())
LOG.debug("Host={}, SNI={}, SNI Certificate={}", serverName, sniHost, cert);
LOG.debug("Host={}, SNI={}, SNI Certificate={}", serverName, sniHost, x509);
if (isSniRequired())
{
if (sniHost == null)
throw new BadMessageException(400, "Invalid SNI");
if (!cert.matches(sniHost))
throw new BadMessageException(400, "Invalid SNI");
}
if (isSniRequired() && (sniHost == null || !x509.matches(sniHost)))
throw new BadMessageException(400, "Invalid SNI");
if (isSniHostCheck())
{
if (!cert.matches(serverName))
throw new BadMessageException(400, "Invalid SNI");
}
if (isSniHostCheck() && !x509.matches(serverName))
throw new BadMessageException(400, "Invalid SNI");
}
request.setAttributes(new SslAttributes(request, sslSession));

View File

@ -28,8 +28,11 @@ import jakarta.servlet.GenericServlet;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpTester;
import org.eclipse.jetty.http.UriCompliance;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.LocalConnector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
@ -284,6 +287,43 @@ public class WebAppContextTest
assertFalse(context.isProtectedTarget("/something-else/web-inf"));
}
@Test
public void testProtectedTarget() throws Exception
{
Server server = newServer();
HandlerList handlers = new HandlerList();
ContextHandlerCollection contexts = new ContextHandlerCollection();
WebAppContext context = new WebAppContext();
Path testWebapp = MavenTestingUtils.getProjectDirPath("src/test/webapp");
context.setBaseResource(new PathResource(testWebapp));
context.setContextPath("/");
server.setHandler(handlers);
handlers.addHandler(contexts);
contexts.addHandler(context);
LocalConnector connector = new LocalConnector(server);
server.addConnector(connector);
connector.getConnectionFactory(HttpConnectionFactory.class).getHttpConfiguration().setUriCompliance(UriCompliance.RFC3986);
server.start();
assertThat(HttpTester.parseResponse(connector.getResponse("GET /test.xml HTTP/1.1\r\nHost: localhost:8080\r\nConnection: close\r\n\r\n")).getStatus(), is(HttpStatus.OK_200));
assertThat(HttpTester.parseResponse(connector.getResponse("GET /%2e/%2e/test.xml HTTP/1.1\r\nHost: localhost:8080\r\nConnection: close\r\n\r\n")).getStatus(), is(HttpStatus.OK_200));
assertThat(HttpTester.parseResponse(connector.getResponse("GET /foo/%2e%2e/test.xml HTTP/1.1\r\nHost: localhost:8080\r\nConnection: close\r\n\r\n")).getStatus(), is(HttpStatus.OK_200));
assertThat(HttpTester.parseResponse(connector.getResponse("GET /WEB-INF HTTP/1.1\r\nHost: localhost:8080\r\nConnection: close\r\n\r\n")).getStatus(), is(HttpStatus.NOT_FOUND_404));
assertThat(HttpTester.parseResponse(connector.getResponse("GET /WEB-INF/ HTTP/1.1\r\nHost: localhost:8080\r\nConnection: close\r\n\r\n")).getStatus(), is(HttpStatus.NOT_FOUND_404));
assertThat(HttpTester.parseResponse(connector.getResponse("GET /WEB-INF/test.xml HTTP/1.1\r\nHost: localhost:8080\r\nConnection: close\r\n\r\n")).getStatus(), is(HttpStatus.NOT_FOUND_404));
assertThat(HttpTester.parseResponse(connector.getResponse("GET /web-inf/test.xml HTTP/1.1\r\nHost: localhost:8080\r\nConnection: close\r\n\r\n")).getStatus(), is(HttpStatus.NOT_FOUND_404));
assertThat(HttpTester.parseResponse(connector.getResponse("GET /%2e/WEB-INF/test.xml HTTP/1.1\r\nHost: localhost:8080\r\nConnection: close\r\n\r\n")).getStatus(), is(HttpStatus.NOT_FOUND_404));
assertThat(HttpTester.parseResponse(connector.getResponse("GET /%2e/%2e/WEB-INF/test.xml HTTP/1.1\r\nHost: localhost:8080\r\nConnection: close\r\n\r\n")).getStatus(), is(HttpStatus.NOT_FOUND_404));
assertThat(HttpTester.parseResponse(connector.getResponse("GET /foo/%2e%2e/WEB-INF/test.xml HTTP/1.1\r\nHost: localhost:8080\r\nConnection: close\r\n\r\n")).getStatus(), is(HttpStatus.NOT_FOUND_404));
assertThat(HttpTester.parseResponse(connector.getResponse("GET /%2E/WEB-INF/test.xml HTTP/1.1\r\nHost: localhost:8080\r\nConnection: close\r\n\r\n")).getStatus(), is(HttpStatus.NOT_FOUND_404));
assertThat(HttpTester.parseResponse(connector.getResponse("GET //WEB-INF/test.xml HTTP/1.1\r\nHost: localhost:8080\r\nConnection: close\r\n\r\n")).getStatus(), is(HttpStatus.NOT_FOUND_404));
assertThat(HttpTester.parseResponse(connector.getResponse("GET /WEB-INF%2ftest.xml HTTP/1.1\r\nHost: localhost:8080\r\nConnection: close\r\n\r\n")).getStatus(), is(HttpStatus.NOT_FOUND_404));
}
@Test
public void testNullPath() throws Exception
{

View File

@ -0,0 +1 @@
test

View File

@ -0,0 +1 @@
test