Merge branch 'jetty-9.4.x' of github.com:eclipse/jetty.project into jetty-9.4.x

This commit is contained in:
Joakim Erdfelt 2017-05-18 16:00:29 -07:00
commit d706a826bc
29 changed files with 434 additions and 372 deletions

View File

@ -1,11 +1,12 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
<version>9.4.6-SNAPSHOT</version>
<artifactId>jetty-parent</artifactId>
<version>25</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-bom</artifactId>
<version>9.4.6-SNAPSHOT</version>
<name>Jetty :: Bom</name>
<description>Jetty BOM artifact</description>
<packaging>pom</packaging>
@ -15,227 +16,227 @@
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-annotations</artifactId>
<version>${project.version}</version>
<version>9.4.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>cdi-core</artifactId>
<version>${project.version}</version>
<version>9.4.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>cdi-full-servlet</artifactId>
<version>${project.version}</version>
<version>9.4.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>cdi-servlet</artifactId>
<version>${project.version}</version>
<version>9.4.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>cdi-websocket</artifactId>
<version>${project.version}</version>
<version>9.4.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-client</artifactId>
<version>${project.version}</version>
<version>9.4.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-continuation</artifactId>
<version>${project.version}</version>
<version>9.4.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-gcloud-session-manager</artifactId>
<version>${project.version}</version>
<version>9.4.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-home</artifactId>
<version>${project.version}</version>
<version>9.4.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-http</artifactId>
<version>${project.version}</version>
<version>9.4.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-http-spi</artifactId>
<version>${project.version}</version>
<version>9.4.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-infinispan</artifactId>
<version>${project.version}</version>
<version>9.4.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-io</artifactId>
<version>${project.version}</version>
<version>9.4.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-jaas</artifactId>
<version>${project.version}</version>
<version>9.4.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-jaspi</artifactId>
<version>${project.version}</version>
<version>9.4.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-jmx</artifactId>
<version>${project.version}</version>
<version>9.4.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-jndi</artifactId>
<version>${project.version}</version>
<version>9.4.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-memcached-sessions</artifactId>
<version>${project.version}</version>
<version>9.4.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-monitor</artifactId>
<version>${project.version}</version>
<version>9.4.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-nosql</artifactId>
<version>${project.version}</version>
<version>9.4.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-osgi-boot</artifactId>
<version>${project.version}</version>
<version>9.4.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-osgi-boot-jsp</artifactId>
<version>${project.version}</version>
<version>9.4.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-osgi-boot-warurl</artifactId>
<version>${project.version}</version>
<version>9.4.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-plus</artifactId>
<version>${project.version}</version>
<version>9.4.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-proxy</artifactId>
<version>${project.version}</version>
<version>9.4.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-quickstart</artifactId>
<version>${project.version}</version>
<version>9.4.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-rewrite</artifactId>
<version>${project.version}</version>
<version>9.4.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-security</artifactId>
<version>${project.version}</version>
<version>9.4.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>${project.version}</version>
<version>9.4.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
<version>${project.version}</version>
<version>9.4.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlets</artifactId>
<version>${project.version}</version>
<version>9.4.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-spring</artifactId>
<version>${project.version}</version>
<version>9.4.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-start</artifactId>
<version>${project.version}</version>
<version>9.4.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-unixsocket</artifactId>
<version>${project.version}</version>
<version>9.4.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId>
<version>${project.version}</version>
<version>9.4.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util-ajax</artifactId>
<version>${project.version}</version>
<version>9.4.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId>
<version>${project.version}</version>
<version>9.4.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>javax-websocket-client-impl</artifactId>
<version>${project.version}</version>
<version>9.4.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>javax-websocket-server-impl</artifactId>
<version>${project.version}</version>
<version>9.4.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>javax-websocket-api</artifactId>
<version>${project.version}</version>
<version>9.4.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>javax-websocket-client</artifactId>
<version>${project.version}</version>
<version>9.4.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>javax-websocket-common</artifactId>
<version>${project.version}</version>
<version>9.4.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>javax-websocket-server</artifactId>
<version>${project.version}</version>
<version>9.4.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>javax-websocket-servlet</artifactId>
<version>${project.version}</version>
<version>9.4.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-xml</artifactId>
<version>${project.version}</version>
<version>9.4.6-SNAPSHOT</version>
</dependency>
</dependencies>
</dependencyManagement>

View File

@ -59,6 +59,7 @@ import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.ClientConnectionFactory;
import org.eclipse.jetty.io.MappedByteBufferPool;
import org.eclipse.jetty.io.ssl.SslClientConnectionFactory;
import org.eclipse.jetty.util.Fields;
import org.eclipse.jetty.util.Jetty;
import org.eclipse.jetty.util.Promise;
@ -1074,6 +1075,11 @@ public class HttpClient extends ContainerLifeCycle
return HttpScheme.HTTPS.is(scheme) || HttpScheme.WSS.is(scheme);
}
protected ClientConnectionFactory newSslClientConnectionFactory(ClientConnectionFactory connectionFactory)
{
return new SslClientConnectionFactory(getSslContextFactory(), getByteBufferPool(), getExecutor(), connectionFactory);
}
private class ContentDecoderFactorySet implements Set<ContentDecoder.Factory>
{
private final Set<ContentDecoder.Factory> set = new HashSet<>();

View File

@ -34,7 +34,6 @@ import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.io.ClientConnectionFactory;
import org.eclipse.jetty.io.ssl.SslClientConnectionFactory;
import org.eclipse.jetty.util.BlockingArrayQueue;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.HostPort;
@ -124,7 +123,7 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
protected ClientConnectionFactory newSslClientConnectionFactory(ClientConnectionFactory connectionFactory)
{
return new SslClientConnectionFactory(client.getSslContextFactory(), client.getByteBufferPool(), client.getExecutor(), connectionFactory);
return client.newSslClientConnectionFactory(connectionFactory);
}
public boolean isSecure()

View File

@ -34,7 +34,6 @@ import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.io.ClientConnectionFactory;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.ssl.SslClientConnectionFactory;
import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@ -204,8 +203,7 @@ public class HttpProxy extends ProxyConfiguration.Proxy
context.put(HttpClientTransport.HTTP_CONNECTION_PROMISE_CONTEXT_KEY, promise);
HttpDestination destination = (HttpDestination)context.get(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY);
HttpClient client = destination.getHttpClient();
ClientConnectionFactory sslConnectionFactory =
new SslClientConnectionFactory(client.getSslContextFactory(), client.getByteBufferPool(), client.getExecutor(), connectionFactory);
ClientConnectionFactory sslConnectionFactory = client.newSslClientConnectionFactory(connectionFactory);
HttpConnectionOverHTTP oldConnection = (HttpConnectionOverHTTP)endPoint.getConnection();
org.eclipse.jetty.io.Connection newConnection = sslConnectionFactory.newConnection(endPoint, context);
// Creating the connection will link the new Connection the EndPoint,

View File

@ -30,7 +30,6 @@ import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.io.AbstractConnection;
import org.eclipse.jetty.io.ClientConnectionFactory;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.ssl.SslClientConnectionFactory;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.Promise;
@ -197,7 +196,7 @@ public class Socks4Proxy extends ProxyConfiguration.Proxy
HttpClient client = destination.getHttpClient();
ClientConnectionFactory connectionFactory = this.connectionFactory;
if (destination.isSecure())
connectionFactory = new SslClientConnectionFactory(client.getSslContextFactory(), client.getByteBufferPool(), client.getExecutor(), connectionFactory);
connectionFactory = client.newSslClientConnectionFactory(connectionFactory);
org.eclipse.jetty.io.Connection newConnection = connectionFactory.newConnection(getEndPoint(), context);
getEndPoint().upgrade(newConnection);
if (LOG.isDebugEnabled())

View File

@ -18,22 +18,34 @@
package org.eclipse.jetty.client;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSocket;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.io.ClientConnectionFactory;
import org.eclipse.jetty.io.ssl.SslClientConnectionFactory;
import org.eclipse.jetty.io.ssl.SslHandshakeListener;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Assert;
import org.junit.Test;
@ -390,4 +402,116 @@ public class HttpClientTLSTest
Assert.assertTrue(serverLatch.await(1, TimeUnit.SECONDS));
Assert.assertTrue(clientLatch.await(1, TimeUnit.SECONDS));
}
@Test
public void testClientRawCloseDoesNotInvalidateSession() throws Exception
{
SslContextFactory serverTLSFactory = createSslContextFactory();
startServer(serverTLSFactory, new EmptyServerHandler());
SslContextFactory clientTLSFactory = createSslContextFactory();
clientTLSFactory.start();
String host = "localhost";
int port = connector.getLocalPort();
Socket socket = new Socket(host, port);
SSLSocket sslSocket = (SSLSocket)clientTLSFactory.getSslContext().getSocketFactory().createSocket(socket, host, port, true);
CountDownLatch handshakeLatch1 = new CountDownLatch(1);
AtomicReference<byte[]> session1 = new AtomicReference<>();
sslSocket.addHandshakeCompletedListener(event ->
{
session1.set(event.getSession().getId());
handshakeLatch1.countDown();
});
sslSocket.startHandshake();
Assert.assertTrue(handshakeLatch1.await(5, TimeUnit.SECONDS));
// The client closes abruptly.
socket.close();
// Try again and compare the session ids.
socket = new Socket(host, port);
sslSocket = (SSLSocket)clientTLSFactory.getSslContext().getSocketFactory().createSocket(socket, host, port, true);
CountDownLatch handshakeLatch2 = new CountDownLatch(1);
AtomicReference<byte[]> session2 = new AtomicReference<>();
sslSocket.addHandshakeCompletedListener(event ->
{
session2.set(event.getSession().getId());
handshakeLatch2.countDown();
});
sslSocket.startHandshake();
Assert.assertTrue(handshakeLatch2.await(5, TimeUnit.SECONDS));
Assert.assertArrayEquals(session1.get(), session2.get());
sslSocket.close();
}
@Test
public void testServerRawCloseDetectedByClient() throws Exception
{
SslContextFactory serverTLSFactory = createSslContextFactory();
serverTLSFactory.start();
try (ServerSocket server = new ServerSocket(0))
{
QueuedThreadPool clientThreads = new QueuedThreadPool();
clientThreads.setName("client");
client = new HttpClient(createSslContextFactory())
{
@Override
protected ClientConnectionFactory newSslClientConnectionFactory(ClientConnectionFactory connectionFactory)
{
SslClientConnectionFactory ssl = (SslClientConnectionFactory)super.newSslClientConnectionFactory(connectionFactory);
ssl.setAllowMissingCloseMessage(false);
return ssl;
}
};
client.setExecutor(clientThreads);
client.start();
CountDownLatch latch = new CountDownLatch(1);
client.newRequest("localhost", server.getLocalPort())
.scheme(HttpScheme.HTTPS.asString())
.send(result ->
{
Assert.assertThat(result.getResponseFailure(), Matchers.instanceOf(SSLException.class));
latch.countDown();
});
Socket socket = server.accept();
SSLSocket sslSocket = (SSLSocket)serverTLSFactory.getSslContext().getSocketFactory().createSocket(socket, null, socket.getPort(), true);
sslSocket.setUseClientMode(false);
BufferedReader reader = new BufferedReader(new InputStreamReader(sslSocket.getInputStream(), StandardCharsets.UTF_8));
while (true)
{
String line = reader.readLine();
if (line == null || line.isEmpty())
break;
}
// If the response is Content-Length delimited, allowing the
// missing TLS Close Message is fine because the application
// will see a EOFException anyway.
// If the response is connection delimited, allowing the
// missing TLS Close Message is bad because the application
// will see a successful response with truncated content.
// Verify that by not allowing the missing
// TLS Close Message we get a response failure.
byte[] half = new byte[8];
String response = "HTTP/1.1 200 OK\r\n" +
// "Content-Length: " + (half.length * 2) + "\r\n" +
"Connection: close\r\n" +
"\r\n";
OutputStream output = sslSocket.getOutputStream();
output.write(response.getBytes(StandardCharsets.UTF_8));
output.write(half);
output.flush();
// Simulate a truncation attack by raw closing.
socket.close();
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
}
}
}

View File

@ -40,8 +40,6 @@ import org.eclipse.jetty.client.api.Destination;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
import org.eclipse.jetty.client.http.HttpDestinationOverHTTP;
import org.eclipse.jetty.client.util.BufferingResponseListener;
import org.eclipse.jetty.client.util.InputStreamContentProvider;
import org.eclipse.jetty.io.ByteBufferPool;
@ -252,37 +250,29 @@ public class HttpClientTimeoutTest extends AbstractHttpClientServerTest
start(new TimeoutHandler(2 * timeout));
client.stop();
final AtomicBoolean sslIdle = new AtomicBoolean();
client = new HttpClient(new HttpClientTransportOverHTTP()
client = new HttpClient(sslContextFactory)
{
@Override
public HttpDestination newHttpDestination(Origin origin)
public ClientConnectionFactory newSslClientConnectionFactory(ClientConnectionFactory connectionFactory)
{
return new HttpDestinationOverHTTP(getHttpClient(), origin)
return new SslClientConnectionFactory(getSslContextFactory(), getByteBufferPool(), getExecutor(), connectionFactory)
{
@Override
protected ClientConnectionFactory newSslClientConnectionFactory(ClientConnectionFactory connectionFactory)
protected SslConnection newSslConnection(ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, SSLEngine engine)
{
HttpClient client = getHttpClient();
return new SslClientConnectionFactory(client.getSslContextFactory(), client.getByteBufferPool(), client.getExecutor(), connectionFactory)
return new SslConnection(byteBufferPool, executor, endPoint, engine)
{
@Override
protected SslConnection newSslConnection(ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, SSLEngine engine)
protected boolean onReadTimeout()
{
return new SslConnection(byteBufferPool, executor, endPoint, engine)
{
@Override
protected boolean onReadTimeout()
{
sslIdle.set(true);
return super.onReadTimeout();
}
};
sslIdle.set(true);
return super.onReadTimeout();
}
};
}
};
}
}, sslContextFactory);
};
client.setIdleTimeout(timeout);
client.start();

View File

@ -3,7 +3,7 @@
<!-- =============================================================== -->
<!-- Documentation of this file format can be found at: -->
<!-- http://wiki.eclipse.org/Jetty/Reference/jetty.xml_syntax -->
<!-- https://www.eclipse.org/jetty/documentation/current/ -->
<!-- -->
<!-- Additional configuration files are available in $JETTY_HOME/etc -->
<!-- and can be mixed in. See start.ini file for the default -->

View File

@ -115,11 +115,11 @@ Maximum idle time for a connection.
soLinger;;
The socket linger time.
+
You could instead configure the connectors in a standard link:#jetty-xml-config[jetty xml config file] and put its location into the ` jettyXml` parameter.
You could instead configure the connectors in a standard link:#jetty-xml-config[jetty xml config file] and put its location into the `jettyXml` parameter.
Note that since jetty-9.0 it is no longer possible to configure a link:#maven-config-https[https connector] directly in the pom.xml: you need to link:#maven-config-https[use jetty xml config files to do it].
jettyXml::
Optional.
A comma separated list of locations of `jetty xml` files to apply in addition to any plugin configuration parameters.
A comma separated list of locations of Jetty xml files to apply in addition to any plugin configuration parameters.
You might use it if you have other webapps, handlers, specific types of connectors etc., to deploy, or if you have other Jetty objects that you cannot configure from the plugin.
scanIntervalSeconds::
The pause in seconds between sweeps of the webapp to check for changes and automatically hot redeploy if any are detected.
@ -152,7 +152,7 @@ There are three other ways to configure the RequestLog:
See link:#configuring-jetty-request-logs[Configuring Request Logs] for more information.
server::
Optional as of Jetty 9.3.1.
This would configure an instance of the link:{SRCDIR}/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java[`org.eclipse.jetty.server.Server`] for the plugin to use, however it is usually NOT necessary to configure this, as the plugin will automatically configure one for you.
This would configure an instance of the link:{GITBROWSEURL}/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java[`org.eclipse.jetty.server.Server`] for the plugin to use, however it is usually NOT necessary to configure this, as the plugin will automatically configure one for you.
In particular, if you use the jettyXml element, then you generally DON'T want to define this element, as you are probably using the jettyXml file to configure up a Server with a special constructor argument, such as a custom threadpool.
If you define both a server element AND use a jetty xml element which points to a config file that has a line like `<Configure id="Server" class="org.eclipse.jetty.server.Server">` then the the xml configuration will override what you configure for the server in the `pom.xml`.
stopPort::
@ -196,21 +196,29 @@ This example uses files copied directly from the jetty distribution etc/ directo
We will use the following files:
jetty.xml::
Sets up various characteristics of the link:{SRCDIR}/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java[`org.eclipse.jetty.server.Server`] instance for the plugin to use.
Importantly, it sets up the link:{SRCDIR}/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java[`org.eclipse.jetty.server.HttpConfiguration`] element that we can refer to in subsequent xml files that configure the connectors.
Here's the relevant section:
Sets up various characteristics of the link:{GITBROWSEURL}/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java[`org.eclipse.jetty.server.Server`] instance for the plugin to use.
Importantly, it sets up the link:{GITBROWSEURL}/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java[`org.eclipse.jetty.server.HttpConfiguration`] element that we can refer to in subsequent xml files that configure the connectors.
Below is the relevant section taken from link:{GITBROWSEURL}/jetty-server/src/main/config/etc/jetty.xml[jetty.xml].
+
[source, xml, subs="{sub-order}"]
----
<Configure id="Server" class="org.eclipse.jetty.server.Server">
...
<New id="httpConfig" class="org.eclipse.jetty.server.HttpConfiguration">
<Set name="secureScheme">https</Set>
<Set name="securePort"><Property name="jetty.secure.port" default="8443" /></Set>
<Set name="outputBufferSize">32768</Set>
<Set name="requestHeaderSize">8192</Set>
<Set name="responseHeaderSize">8192</Set>
<Set name="sendServerVersion">true</Set>
<Set name="sendDateHeader">false</Set>
<Set name="headerCacheSize">512</Set>
<Set name="secureScheme"><Property name="jetty.httpConfig.secureScheme" default="https" /></Set>
<Set name="securePort"><Property name="jetty.httpConfig.securePort" deprecated="jetty.secure.port" default="8443" /></Set>
<Set name="outputBufferSize"><Property name="jetty.httpConfig.outputBufferSize" deprecated="jetty.output.buffer.size" default="32768" /></Set>
<Set name="outputAggregationSize"><Property name="jetty.httpConfig.outputAggregationSize" deprecated="jetty.output.aggregation.size" default="8192" /></Set>
<Set name="requestHeaderSize"><Property name="jetty.httpConfig.requestHeaderSize" deprecated="jetty.request.header.size" default="8192" /></Set>
<Set name="responseHeaderSize"><Property name="jetty.httpConfig.responseHeaderSize" deprecated="jetty.response.header.size" default="8192" /></Set>
<Set name="sendServerVersion"><Property name="jetty.httpConfig.sendServerVersion" deprecated="jetty.send.server.version" default="true" /></Set>
<Set name="sendDateHeader"><Property name="jetty.httpConfig.sendDateHeader" deprecated="jetty.send.date.header" default="false" /></Set>
<Set name="headerCacheSize"><Property name="jetty.httpConfig.headerCacheSize" default="512" /></Set>
<Set name="delayDispatchUntilContent"><Property name="jetty.httpConfig.delayDispatchUntilContent" deprecated="jetty.delayDispatchUntilContent" default="true"/></Set>
<Set name="maxErrorDispatches"><Property name="jetty.httpConfig.maxErrorDispatches" default="10"/></Set>
<Set name="blockingTimeout"><Property name="jetty.httpConfig.blockingTimeout" default="-1"/></Set>
<Set name="persistentConnectionsEnabled"><Property name="jetty.httpConfig.persistentConnectionsEnabled" default="true"/></Set>
<Set name="cookieCompliance"><Call class="org.eclipse.jetty.http.CookieCompliance" name="valueOf"><Arg><Property name="jetty.httpConfig.cookieCompliance" default="RFC6265"/></Arg></Call></Set>
<!-- Uncomment to enable handling of X-Forwarded- style headers
<Call name="addCustomizer">
@ -218,9 +226,11 @@ Here's the relevant section:
</Call>
-->
</New>
...
</Configure>
----
jetty-ssl.xml::
Set up ssl which will be used by the https connector.
Sets up ssl which will be used by the https connector.
Here's the `jetty-ssl.xml` file from the jetty-distribution:
+
[source, xml, subs="{sub-order}"]
@ -251,7 +261,7 @@ Now you need to let the plugin know to apply the files above:
____
[CAUTION]
Just like with an installed distribution of Jetty, the ordering of the xml files is significant.
Just as with an installed distribution of Jetty, the ordering of the xml files is significant.
____
You can also use Jetty xml files to configure a http connector for the plugin to use.
@ -445,6 +455,7 @@ Here's an example:
</configuration>
</plugin>
</plugins>
...
</project>
----

View File

@ -17,7 +17,7 @@
[[jetty-xml-config]]
=== jetty.xml
`jetty.xml` is the default configuration file for Jetty, typically located at ` $JETTY_HOME/etc/jetty.xml`. Usually the `jetty.xml` configures:
`jetty.xml` is the default configuration file for Jetty, typically located at `$JETTY_HOME/etc/jetty.xml`. Usually the `jetty.xml` configures:
* The Server class (or subclass if extended) and global options.
* A ThreadPool (min and max thread).

View File

@ -29,7 +29,7 @@ The `webdefault.xml` file is located in `$(jetty.home)/etc/webdefault.xml`.
[[using-webdefault-xml]]
==== Using webdefault.xml
You can specify a custom configuration file to use for specific webapps, or for all webapps. If you do not specify an alternate defaults descriptor, the $JETTY-HOME/etc/jetty-deploy.xml file will configure jetty to automatically use $JETTY_HOME/etc/`webdefault.xml`.
You can specify a custom configuration file to use for specific webapps, or for all webapps. If you do not specify an alternate defaults descriptor, the `$JETTY-HOME/etc/jetty-deploy.xml` file will configure jetty to automatically use `$JETTY_HOME/etc/webdefault.xml`.
____
[NOTE]
@ -73,9 +73,9 @@ import org.eclipse.jetty.webapp.WebAppContext;
Alternatively, you can use a xref:jetty-classloading[] to find the resource representing your custom `webdefault.xml`.
[[creating-custom-webdefault-xml-multiple-webapps]]
===== Creating a Custom `webdefault.xml` for Multiple WebApps
===== Creating a Custom webdefault.xml for Multiple WebApps
If you want to apply the same custom `webdefault.xml` to a number of webapps, provide the path to the file in xref:jetty-xml-config[] in the $JETTY_HOME/etc/jetty-deploy.xml file:
If you want to apply the same custom `webdefault.xml` to a number of webapps, provide the path to the file in xref:jetty-xml-config[] in the `$JETTY_HOME/etc/jetty-deploy.xml` file:
[source, xml, subs="{sub-order}"]
----

View File

@ -150,7 +150,7 @@ public class HTTP2Client extends ContainerLifeCycle
if (sslContextFactory != null)
{
ALPNClientConnectionFactory alpn = new ALPNClientConnectionFactory(getExecutor(), h2, getProtocols());
factory = new SslClientConnectionFactory(sslContextFactory, getByteBufferPool(), getExecutor(), alpn);
factory = newSslClientConnectionFactory(sslContextFactory, alpn);
}
return factory.newConnection(endPoint, context);
});
@ -171,6 +171,11 @@ public class HTTP2Client extends ContainerLifeCycle
return new ClientSelectorManager(getExecutor(), getScheduler(), getSelectors());
}
protected ClientConnectionFactory newSslClientConnectionFactory(SslContextFactory sslContextFactory, ClientConnectionFactory connectionFactory)
{
return new SslClientConnectionFactory(sslContextFactory, getByteBufferPool(), getExecutor(), connectionFactory);
}
public Executor getExecutor()
{
return executor;

View File

@ -42,6 +42,7 @@ public class SslClientConnectionFactory implements ClientConnectionFactory
private final ByteBufferPool byteBufferPool;
private final Executor executor;
private final ClientConnectionFactory connectionFactory;
private boolean allowMissingCloseMessage = true;
public SslClientConnectionFactory(SslContextFactory sslContextFactory, ByteBufferPool byteBufferPool, Executor executor, ClientConnectionFactory connectionFactory)
{
@ -51,6 +52,16 @@ public class SslClientConnectionFactory implements ClientConnectionFactory
this.connectionFactory = connectionFactory;
}
public boolean isAllowMissingCloseMessage()
{
return allowMissingCloseMessage;
}
public void setAllowMissingCloseMessage(boolean allowMissingCloseMessage)
{
this.allowMissingCloseMessage = allowMissingCloseMessage;
}
@Override
public org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, Map<String, Object> context) throws IOException
{
@ -84,6 +95,7 @@ public class SslClientConnectionFactory implements ClientConnectionFactory
SslConnection sslConnection = (SslConnection)connection;
sslConnection.setRenegotiationAllowed(sslContextFactory.isRenegotiationAllowed());
sslConnection.setRenegotiationLimit(sslContextFactory.getRenegotiationLimit());
sslConnection.setAllowMissingCloseMessage(isAllowMissingCloseMessage());
ContainerLifeCycle connector = (ContainerLifeCycle)context.get(ClientConnectionFactory.CONNECTOR_CONTEXT_KEY);
connector.getBeans(SslHandshakeListener.class).forEach(sslConnection::addHandshakeListener);
}

View File

@ -93,6 +93,7 @@ public class SslConnection extends AbstractConnection
private boolean _renegotiationAllowed;
private int _renegotiationLimit = -1;
private boolean _closedOutbound;
private boolean _allowMissingCloseMessage = true;
private abstract class RunnableTask implements Runnable, Invocable
{
@ -231,6 +232,16 @@ public class SslConnection extends AbstractConnection
_renegotiationLimit = renegotiationLimit;
}
public boolean isAllowMissingCloseMessage()
{
return _allowMissingCloseMessage;
}
public void setAllowMissingCloseMessage(boolean allowMissingCloseMessage)
{
this._allowMissingCloseMessage = allowMissingCloseMessage;
}
@Override
public void onOpen()
{
@ -662,7 +673,7 @@ public class SslConnection extends AbstractConnection
if (_underFlown)
{
if (net_filled < 0)
if (net_filled < 0 && _sslEngine.getUseClientMode())
closeInbound();
if (net_filled <= 0)
return net_filled;
@ -860,30 +871,46 @@ public class SslConnection extends AbstractConnection
{
if (LOG.isDebugEnabled())
LOG.debug("Renegotiation denied {}", SslConnection.this);
closeInbound();
terminateInput();
return false;
}
if (_renegotiationLimit==0)
if (getRenegotiationLimit()==0)
{
if (LOG.isDebugEnabled())
LOG.debug("Renegotiation limit exceeded {}", SslConnection.this);
closeInbound();
terminateInput();
return false;
}
return true;
}
private void closeInbound()
private void terminateInput()
{
try
{
_sslEngine.closeInbound();
}
catch (Throwable x)
{
LOG.ignore(x);
}
}
private void closeInbound() throws SSLException
{
HandshakeStatus handshakeStatus = _sslEngine.getHandshakeStatus();
try
{
_sslEngine.closeInbound();
}
catch (SSLException x)
{
LOG.ignore(x);
if (handshakeStatus == HandshakeStatus.NOT_HANDSHAKING && !isAllowMissingCloseMessage())
throw x;
else
LOG.ignore(x);
}
}

View File

@ -24,12 +24,10 @@ import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLEngineResult;
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSocket;
import org.eclipse.jetty.io.ssl.SslConnection;
@ -93,146 +91,12 @@ public class SelectChannelEndPointSslTest extends SelectChannelEndPointTest
super.testEcho();
}
@Ignore // SSL does not do half closes
@Override
public void testShutdown() throws Exception
{
}
@Test
public void testTcpClose() throws Exception
{
// This test replaces SSLSocket() with a very manual SSL client
// so we can close TCP underneath SSL.
SocketChannel client = SocketChannel.open(_connector.socket().getLocalSocketAddress());
client.socket().setSoTimeout(500);
SocketChannel server = _connector.accept();
server.configureBlocking(false);
_manager.accept(server);
SSLEngine engine = __sslCtxFactory.newSSLEngine();
engine.setUseClientMode(true);
engine.beginHandshake();
ByteBuffer appOut = ByteBuffer.allocate(engine.getSession().getApplicationBufferSize());
ByteBuffer sslOut = ByteBuffer.allocate(engine.getSession().getPacketBufferSize()*2);
ByteBuffer appIn = ByteBuffer.allocate(engine.getSession().getApplicationBufferSize());
ByteBuffer sslIn = ByteBuffer.allocate(engine.getSession().getPacketBufferSize()*2);
boolean debug=false;
if (debug) System.err.println(engine.getHandshakeStatus());
int loop=20;
while (engine.getHandshakeStatus()!=HandshakeStatus.NOT_HANDSHAKING)
{
if (--loop==0)
throw new IllegalStateException();
if (engine.getHandshakeStatus()==HandshakeStatus.NEED_WRAP)
{
if (debug) System.err.printf("sslOut %d-%d-%d%n",sslOut.position(),sslOut.limit(),sslOut.capacity());
if (debug) System.err.printf("appOut %d-%d-%d%n",appOut.position(),appOut.limit(),appOut.capacity());
SSLEngineResult result =engine.wrap(appOut,sslOut);
if (debug) System.err.println(result);
sslOut.flip();
int flushed=client.write(sslOut);
if (debug) System.err.println("out="+flushed);
sslOut.clear();
}
if (engine.getHandshakeStatus()==HandshakeStatus.NEED_UNWRAP)
{
if (debug) System.err.printf("sslIn %d-%d-%d%n",sslIn.position(),sslIn.limit(),sslIn.capacity());
if (sslIn.position()==0)
{
int filled=client.read(sslIn);
if (debug) System.err.println("in="+filled);
}
sslIn.flip();
if (debug) System.err.printf("sslIn %d-%d-%d%n",sslIn.position(),sslIn.limit(),sslIn.capacity());
SSLEngineResult result =engine.unwrap(sslIn,appIn);
if (debug) System.err.println(result);
if (debug) System.err.printf("sslIn %d-%d-%d%n",sslIn.position(),sslIn.limit(),sslIn.capacity());
if (sslIn.hasRemaining())
sslIn.compact();
else
sslIn.clear();
if (debug) System.err.printf("sslIn %d-%d-%d%n",sslIn.position(),sslIn.limit(),sslIn.capacity());
}
if (engine.getHandshakeStatus()==HandshakeStatus.NEED_TASK)
{
Runnable task;
while ((task=engine.getDelegatedTask())!=null)
task.run();
if (debug) System.err.println(engine.getHandshakeStatus());
}
}
if (debug) System.err.println("\nSay Hello");
// write a message
appOut.put("HelloWorld".getBytes(StandardCharsets.UTF_8));
appOut.flip();
SSLEngineResult result =engine.wrap(appOut,sslOut);
if (debug) System.err.println(result);
sslOut.flip();
int flushed=client.write(sslOut);
if (debug) System.err.println("out="+flushed);
sslOut.clear();
appOut.clear();
// read the response
int filled=client.read(sslIn);
if (debug) System.err.println("in="+filled);
sslIn.flip();
result =engine.unwrap(sslIn,appIn);
if (debug) System.err.println(result);
if (sslIn.hasRemaining())
sslIn.compact();
else
sslIn.clear();
appIn.flip();
String reply= new String(appIn.array(),appIn.arrayOffset(),appIn.remaining());
appIn.clear();
Assert.assertEquals("HelloWorld",reply);
if (debug) System.err.println("Shutting down output");
client.socket().shutdownOutput();
filled=client.read(sslIn);
if (debug) System.err.println("in="+filled);
if (filled>=0)
{
// this is the old behaviour.
sslIn.flip();
try
{
// Since the client closed abruptly, the server is sending a close alert with a failure
engine.unwrap(sslIn, appIn);
Assert.fail();
}
catch (SSLException x)
{
// Expected
}
}
sslIn.clear();
filled=client.read(sslIn);
Assert.assertEquals(-1,filled);
Thread.sleep(100); // TODO This should not be needed
Assert.assertFalse(server.isOpen());
}
@Test
@Override
public void testWriteBlocked() throws Exception

View File

@ -336,7 +336,7 @@ public class DigestAuthModule extends BaseAuthModule
byte[] digest = md.digest();
// check digest
return (TypeUtil.toString(digest, 16).equalsIgnoreCase(response));
return stringEquals(TypeUtil.toString(digest, 16).toLowerCase(), response == null ? null : response.toLowerCase());
}
catch (Exception e)
{
@ -351,6 +351,5 @@ public class DigestAuthModule extends BaseAuthModule
{
return username + "," + response;
}
}
}

View File

@ -6,7 +6,7 @@
<!-- Configure the Jetty Server -->
<!-- -->
<!-- Documentation of this file format can be found at: -->
<!-- http://wiki.eclipse.org/Jetty/Reference/jetty.xml_syntax -->
<!-- https://www.eclipse.org/jetty/documentation/current/ -->
<!-- =============================================================== -->
<Configure id="Server" class="org.eclipse.jetty.server.Server">

View File

@ -6,7 +6,7 @@
<!-- Configure the Jetty Server -->
<!-- -->
<!-- Documentation of this file format can be found at: -->
<!-- http://wiki.eclipse.org/Jetty/Reference/jetty.xml_syntax -->
<!-- https://www.eclipse.org/jetty/documentation/current/ -->
<!-- =============================================================== -->
<Configure id="Server" class="org.eclipse.jetty.server.Server">

View File

@ -383,7 +383,7 @@ public class DigestAuthenticator extends LoginAuthenticator
byte[] digest = md.digest();
// check digest
return (TypeUtil.toString(digest, 16).equalsIgnoreCase(response));
return stringEquals(TypeUtil.toString(digest, 16).toLowerCase(), response == null ? null : response.toLowerCase());
}
catch (Exception e)
{

View File

@ -3,7 +3,7 @@
<!-- =============================================================== -->
<!-- Documentation of this file format can be found at: -->
<!-- http://wiki.eclipse.org/Jetty/Reference/jetty.xml_syntax -->
<!-- https://www.eclipse.org/jetty/documentation/current/ -->
<!-- -->
<!-- Additional configuration files are available in $JETTY_HOME/etc -->
<!-- and can be mixed in. See start.ini file for the default -->

View File

@ -23,6 +23,7 @@ import java.util.Locale;
import javax.servlet.http.Cookie;
import org.eclipse.jetty.http.CookieCompliance;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@ -40,6 +41,7 @@ public class CookieCutter
{
private static final Logger LOG = Log.getLogger(CookieCutter.class);
private final CookieCompliance _compliance;
private Cookie[] _cookies;
private Cookie[] _lastCookies;
private final List<String> _fieldList = new ArrayList<>();
@ -47,6 +49,12 @@ public class CookieCutter
public CookieCutter()
{
this(CookieCompliance.RFC6265);
}
public CookieCutter(CookieCompliance compliance)
{
_compliance = compliance;
}
public Cookie[] getCookies()
@ -154,6 +162,7 @@ public class CookieCutter
if (i==last)
{
value = unquoted.toString();
unquoted.setLength(0);
}
else
{
@ -179,6 +188,7 @@ public class CookieCutter
default:
if (i==last)
{
// unterminated quote, let's ignore quotes
unquoted.setLength(0);
inQuoted = false;
i--;
@ -200,7 +210,7 @@ public class CookieCutter
{
case ' ':
case '\t':
continue;
break;
case ';':
if (quoted)
@ -211,6 +221,8 @@ public class CookieCutter
}
else if(tokenstart>=0 && tokenend>=0)
value = hdr.substring(tokenstart, tokenend+1);
else
value = "";
tokenstart = -1;
invalue=false;
@ -223,7 +235,7 @@ public class CookieCutter
inQuoted=true;
if (unquoted==null)
unquoted=new StringBuilder();
continue;
break;
}
// fall through to default case
@ -283,10 +295,9 @@ public class CookieCutter
{
name = hdr.substring(tokenstart, tokenend+1);
}
tokenstart = -1;
invalue=true;
continue;
break;
default:
if (quoted)
@ -302,24 +313,41 @@ public class CookieCutter
tokenstart=i;
tokenend=i;
if (i==last)
{
name = hdr.substring(tokenstart, tokenend+1);
break;
}
continue;
}
}
}
if (invalue && i==last && value==null)
{
if (quoted)
{
value = unquoted.toString();
unquoted.setLength(0);
quoted = false;
}
else if(tokenstart>=0 && tokenend>=0)
{
value = hdr.substring(tokenstart, tokenend+1);
}
else
value = "";
}
// If after processing the current character we have a value and a name, then it is a cookie
if (value!=null && name!=null)
if (name!=null && value!=null)
{
try
{
if (name.startsWith("$"))
{
String lowercaseName = name.toLowerCase(Locale.ENGLISH);
if ("$path".equals(lowercaseName))
if (_compliance==CookieCompliance.RFC6265)
{
// Ignore
}
else if ("$path".equals(lowercaseName))
{
if (cookie!=null)
cookie.setPath(value);
@ -338,13 +366,6 @@ public class CookieCutter
{
version = Integer.parseInt(value);
}
else
{
cookie = new Cookie(name, value);
if (version > 0)
cookie.setVersion(version);
cookies.add(cookie);
}
}
else
{

View File

@ -335,6 +335,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
catch (IOException e)
{
LOG.debug(e);
_parser.atEOF();
return -1;
}
}

View File

@ -756,7 +756,7 @@ public class Request implements HttpServletRequest
for (String c : metadata.getFields().getValuesList(HttpHeader.COOKIE))
{
if (_cookies == null)
_cookies = new CookieCutter();
_cookies = new CookieCutter(getHttpChannel().getHttpConfiguration().getCookieCompliance());
_cookies.addCookieField(c);
}
@ -2043,7 +2043,7 @@ public class Request implements HttpServletRequest
public void setCookies(Cookie[] cookies)
{
if (_cookies == null)
_cookies = new CookieCutter();
_cookies = new CookieCutter(getHttpChannel().getHttpConfiguration().getCookieCompliance());
_cookies.setCookies(cookies);
}

View File

@ -23,14 +23,15 @@ import static org.junit.Assert.assertThat;
import javax.servlet.http.Cookie;
import org.eclipse.jetty.http.CookieCompliance;
import org.junit.Ignore;
import org.junit.Test;
public class CookieCutterTest
{
private Cookie[] parseCookieHeaders(String... headers)
private Cookie[] parseCookieHeaders(CookieCompliance compliance,String... headers)
{
CookieCutter cutter = new CookieCutter();
CookieCutter cutter = new CookieCutter(compliance);
for (String header : headers)
{
cutter.addCookieField(header);
@ -58,7 +59,7 @@ public class CookieCutterTest
{
String rawCookie = "$Version=\"1\"; Customer=\"WILE_E_COYOTE\"; $Path=\"/acme\"";
Cookie cookies[] = parseCookieHeaders(rawCookie);
Cookie cookies[] = parseCookieHeaders(CookieCompliance.RFC2965,rawCookie);
assertThat("Cookies.length", cookies.length, is(1));
assertCookie("Cookies[0]", cookies[0], "Customer", "WILE_E_COYOTE", 1, "/acme");
@ -74,7 +75,7 @@ public class CookieCutterTest
"Customer=\"WILE_E_COYOTE\"; $Path=\"/acme\"; " +
"Part_Number=\"Rocket_Launcher_0001\"; $Path=\"/acme\"";
Cookie cookies[] = parseCookieHeaders(rawCookie);
Cookie cookies[] = parseCookieHeaders(CookieCompliance.RFC2965,rawCookie);
assertThat("Cookies.length", cookies.length, is(2));
assertCookie("Cookies[0]", cookies[0], "Customer", "WILE_E_COYOTE", 1, "/acme");
@ -92,7 +93,7 @@ public class CookieCutterTest
"Part_Number=\"Rocket_Launcher_0001\"; $Path=\"/acme\"; " +
"Shipping=\"FedEx\"; $Path=\"/acme\"";
Cookie cookies[] = parseCookieHeaders(rawCookie);
Cookie cookies[] = parseCookieHeaders(CookieCompliance.RFC2965,rawCookie);
assertThat("Cookies.length", cookies.length, is(3));
assertCookie("Cookies[0]", cookies[0], "Customer", "WILE_E_COYOTE", 1, "/acme");
@ -110,7 +111,7 @@ public class CookieCutterTest
"Part_Number=\"Riding_Rocket_0023\"; $Path=\"/acme/ammo\"; " +
"Part_Number=\"Rocket_Launcher_0001\"; $Path=\"/acme\"";
Cookie cookies[] = parseCookieHeaders(rawCookie);
Cookie cookies[] = parseCookieHeaders(CookieCompliance.RFC2965,rawCookie);
assertThat("Cookies.length", cookies.length, is(2));
assertCookie("Cookies[0]", cookies[0], "Part_Number", "Riding_Rocket_0023", 1, "/acme/ammo");
@ -127,7 +128,7 @@ public class CookieCutterTest
"session_id=\"1234\"; " +
"session_id=\"1111\"; $Domain=\".cracker.edu\"";
Cookie cookies[] = parseCookieHeaders(rawCookie);
Cookie cookies[] = parseCookieHeaders(CookieCompliance.RFC2965,rawCookie);
assertThat("Cookies.length", cookies.length, is(2));
assertCookie("Cookies[0]", cookies[0], "session_id", "1234", 1, null);
@ -144,7 +145,7 @@ public class CookieCutterTest
String rawCookie = "$Version=\"1\"; session_id=\"1234\", " +
"$Version=\"1\"; session_id=\"1111\"; $Domain=\".cracker.edu\"";
Cookie cookies[] = parseCookieHeaders(rawCookie);
Cookie cookies[] = parseCookieHeaders(CookieCompliance.RFC6265,rawCookie);
assertThat("Cookies.length", cookies.length, is(2));
assertCookie("Cookies[0]", cookies[0], "session_id", "1234", 1, null);
@ -159,7 +160,7 @@ public class CookieCutterTest
{
String rawCookie = "SID=31d4d96e407aad42";
Cookie cookies[] = parseCookieHeaders(rawCookie);
Cookie cookies[] = parseCookieHeaders(CookieCompliance.RFC6265,rawCookie);
assertThat("Cookies.length", cookies.length, is(1));
assertCookie("Cookies[0]", cookies[0], "SID", "31d4d96e407aad42", 0, null);
@ -173,7 +174,7 @@ public class CookieCutterTest
{
String rawCookie = "SID=31d4d96e407aad42; lang=en-US";
Cookie cookies[] = parseCookieHeaders(rawCookie);
Cookie cookies[] = parseCookieHeaders(CookieCompliance.RFC6265,rawCookie);
assertThat("Cookies.length", cookies.length, is(2));
assertCookie("Cookies[0]", cookies[0], "SID", "31d4d96e407aad42", 0, null);
@ -188,9 +189,22 @@ public class CookieCutterTest
{
String rawCookie = "key=value";
Cookie cookies[] = parseCookieHeaders(rawCookie);
Cookie cookies[] = parseCookieHeaders(CookieCompliance.RFC6265,rawCookie);
assertThat("Cookies.length", cookies.length, is(1));
assertCookie("Cookies[0]", cookies[0], "key", "value", 0, null);
}
/**
* Basic name=value, following RFC6265 rules
*/
@Test
public void testDollarName()
{
String rawCookie = "$key=value";
Cookie cookies[] = parseCookieHeaders(CookieCompliance.RFC6265,rawCookie);
assertThat("Cookies.length", cookies.length, is(0));
}
}

View File

@ -65,7 +65,18 @@ public class CookieCutter_LenientTest
// quoted-string = ( <"> *(qdtext) <"> )
// qdtext = <any TEXT except <">>
// lenient with spaces and EOF
ret.add(new String[]{"abc=", "abc", ""});
ret.add(new String[]{"abc = ", "abc", ""});
ret.add(new String[]{"abc = ;", "abc", ""});
ret.add(new String[]{"abc = ; ", "abc", ""});
ret.add(new String[]{"abc = x ", "abc", "x"});
ret.add(new String[]{"abc=\"\"", "abc", ""});
ret.add(new String[]{"abc= \"\" ", "abc", ""});
ret.add(new String[]{"abc= \"x\" ", "abc", "x"});
ret.add(new String[]{"abc= \"x\" ;", "abc", "x"});
ret.add(new String[]{"abc= \"x\" ; ", "abc", "x"});
// The backslash character ("\") may be used as a single-character quoting
// mechanism only within quoted-string and comment constructs.
// quoted-pair = "\" CHAR

View File

@ -58,6 +58,7 @@ import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import org.eclipse.jetty.http.BadMessageException;
import org.eclipse.jetty.http.CookieCompliance;
import org.eclipse.jetty.http.HttpTester;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.server.LocalConnector.LocalEndPoint;
@ -1319,39 +1320,6 @@ public class RequestTest
assertNotSame(cookies.get(0), cookies.get(2));
assertNotSame(cookies.get(1), cookies.get(3));
cookies.clear();
//NOTE: the javax.servlet.http.Cookie class sets the system property org.glassfish.web.rfc2109_cookie_names_enforced
//to TRUE by default, and rejects all cookie names containing punctuation.Therefore this test cannot use "name2".
response=_connector.getResponse(
"POST / HTTP/1.1\r\n"+
"Host: whatever\r\n"+
"Cookie: name0=value0; name1 = value1 ; name2 = \"\\\"value2\\\"\" \n" +
"Cookie: $Version=2; name3=value3=value3;$path=/path;$domain=acme.com;$port=8080; name4=; name5 = ; name6\n" +
"Cookie: name7=value7;\n" +
"Connection: close\r\n"+
"\r\n");
assertEquals("name0", cookies.get(0).getName());
assertEquals("value0", cookies.get(0).getValue());
assertEquals("name1", cookies.get(1).getName());
assertEquals("value1", cookies.get(1).getValue());
assertEquals("name2", cookies.get(2).getName());
assertEquals("\"value2\"", cookies.get(2).getValue());
assertEquals("name3", cookies.get(3).getName());
assertEquals("value3=value3", cookies.get(3).getValue());
assertEquals(2, cookies.get(3).getVersion());
assertEquals("/path", cookies.get(3).getPath());
assertEquals("acme.com", cookies.get(3).getDomain());
assertEquals("$port=8080", cookies.get(3).getComment());
// assertEquals("name4", cookies.get(4).getName());
// assertEquals("", cookies.get(4).getValue());
// assertEquals("name5", cookies.get(5).getName());
// assertEquals("", cookies.get(5).getValue());
// assertEquals("name6", cookies.get(6).getName());
// assertEquals("", cookies.get(6).getValue());
assertEquals("name7", cookies.get(4).getName());
assertEquals("value7", cookies.get(4).getValue());
cookies.clear();
response=_connector.getResponse(
"GET /other HTTP/1.1\n"+

View File

@ -26,10 +26,7 @@ import java.util.ServiceLoader;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.security.Credential.Crypt;
import org.eclipse.jetty.util.security.Credential.MD5;
/* ------------------------------------------------------------ */
/**
* Credentials. The Credential class represents an abstract mechanism for checking authentication credentials. A credential instance either represents a secret,
* or some data that could only be derived from knowing the secret.
@ -40,18 +37,13 @@ import org.eclipse.jetty.util.security.Credential.MD5;
* This class includes an implementation for unix Crypt an MD5 digest.
*
* @see Password
*
*/
public abstract class Credential implements Serializable
{
private static final long serialVersionUID = -7760551052768181572L;
private static final Logger LOG = Log.getLogger(Credential.class);
private static final ServiceLoader<CredentialProvider> CREDENTIAL_PROVIDER_LOADER = ServiceLoader.load(CredentialProvider.class);
private static final Logger LOG = Log.getLogger(Credential.class);
private static final long serialVersionUID = -7760551052768181572L;
/* ------------------------------------------------------------ */
/**
* Check a credential
*
@ -62,7 +54,6 @@ public abstract class Credential implements Serializable
*/
public abstract boolean check(Object credentials);
/* ------------------------------------------------------------ */
/**
* Get a credential from a String. If the credential String starts with a known Credential type (eg "CRYPT:" or "MD5:" ) then a Credential of that type is
* returned. Otherwise, it tries to find a credential provider whose prefix matches with the start of the credential String. Else the credential is assumed
@ -94,15 +85,61 @@ public abstract class Credential implements Serializable
return new Password(credential);
}
/* ------------------------------------------------------------ */
/**
* <p>Utility method that replaces String.equals() to avoid timing attacks.</p>
*
* @param s1 the first string to compare
* @param s2 the second string to compare
* @return whether the two strings are equal
*/
protected static boolean stringEquals(String s1, String s2)
{
if (s1 == s2)
return true;
if (s1 == null || s2 == null)
return false;
boolean result = true;
int l1 = s1.length();
int l2 = s2.length();
if (l1 != l2)
result = false;
int l = Math.min(l1, l2);
for (int i = 0; i < l; ++i)
result &= s1.charAt(i) == s2.charAt(i);
return result;
}
/**
* <p>Utility method that replaces Arrays.equals() to avoid timing attacks.</p>
*
* @param b1 the first byte array to compare
* @param b2 the second byte array to compare
* @return whether the two byte arrays are equal
*/
protected static boolean byteEquals(byte[] b1, byte[] b2)
{
if (b1 == b2)
return true;
if (b1 == null || b2 == null)
return false;
boolean result = true;
int l1 = b1.length;
int l2 = b2.length;
if (l1 != l2)
result = false;
int l = Math.min(l1, l2);
for (int i = 0; i < l; ++i)
result &= b1[i] == b2[i];
return result;
}
/**
* Unix Crypt Credentials
*/
public static class Crypt extends Credential
{
private static final long serialVersionUID = -2027792997664744210L;
public static final String __TYPE = "CRYPT:";
private static final String __TYPE = "CRYPT:";
private final String _cooked;
@ -118,9 +155,7 @@ public abstract class Credential implements Serializable
credentials = new String((char[])credentials);
if (!(credentials instanceof String) && !(credentials instanceof Password))
LOG.warn("Can't check " + credentials.getClass() + " against CRYPT");
String passwd = credentials.toString();
return _cooked.equals(UnixCrypt.crypt(passwd,_cooked));
return stringEquals(_cooked, UnixCrypt.crypt(credentials.toString(),_cooked));
}
@Override
@ -128,59 +163,49 @@ public abstract class Credential implements Serializable
{
if (!(credential instanceof Crypt))
return false;
Crypt c = (Crypt)credential;
return _cooked.equals(c._cooked);
return stringEquals(_cooked, c._cooked);
}
public static String crypt(String user, String pw)
{
return "CRYPT:" + UnixCrypt.crypt(pw,user);
return __TYPE + UnixCrypt.crypt(pw, user);
}
}
/* ------------------------------------------------------------ */
/**
* MD5 Credentials
*/
public static class MD5 extends Credential
{
private static final long serialVersionUID = 5533846540822684240L;
public static final String __TYPE = "MD5:";
public static final Object __md5Lock = new Object();
private static final String __TYPE = "MD5:";
private static final Object __md5Lock = new Object();
private static MessageDigest __md;
private final byte[] _digest;
/* ------------------------------------------------------------ */
MD5(String digest)
{
digest = digest.startsWith(__TYPE)?digest.substring(__TYPE.length()):digest;
_digest = TypeUtil.parseBytes(digest,16);
digest = digest.startsWith(__TYPE) ? digest.substring(__TYPE.length()) : digest;
_digest = TypeUtil.parseBytes(digest, 16);
}
/* ------------------------------------------------------------ */
public byte[] getDigest()
{
return _digest;
}
/* ------------------------------------------------------------ */
@Override
public boolean check(Object credentials)
{
try
{
byte[] digest = null;
if (credentials instanceof char[])
credentials = new String((char[])credentials);
if (credentials instanceof Password || credentials instanceof String)
{
byte[] digest;
synchronized (__md5Lock)
{
if (__md == null)
@ -189,16 +214,11 @@ public abstract class Credential implements Serializable
__md.update(credentials.toString().getBytes(StandardCharsets.ISO_8859_1));
digest = __md.digest();
}
if (digest == null || digest.length != _digest.length)
return false;
boolean digestMismatch = false;
for (int i = 0; i < digest.length; i++)
digestMismatch |= (digest[i] != _digest[i]);
return !digestMismatch;
return byteEquals(_digest, digest);
}
else if (credentials instanceof MD5)
{
return equals((MD5)credentials);
return equals(credentials);
}
else if (credentials instanceof Credential)
{
@ -223,20 +243,10 @@ public abstract class Credential implements Serializable
public boolean equals(Object obj)
{
if (obj instanceof MD5)
{
MD5 md5 = (MD5)obj;
if (_digest.length != md5._digest.length)
return false;
boolean digestMismatch = false;
for (int i = 0; i < _digest.length; i++)
digestMismatch |= (_digest[i] != md5._digest[i]);
return !digestMismatch;
}
return byteEquals(_digest, ((MD5)obj)._digest);
return false;
}
/* ------------------------------------------------------------ */
public static String digest(String password)
{
try
@ -262,7 +272,7 @@ public abstract class Credential implements Serializable
digest = __md.digest();
}
return __TYPE + TypeUtil.toString(digest,16);
return __TYPE + TypeUtil.toString(digest, 16);
}
catch (Exception e)
{

View File

@ -20,7 +20,6 @@ package org.eclipse.jetty.util.security;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Locale;
import org.eclipse.jetty.util.log.Log;
@ -96,15 +95,20 @@ public class Password extends Credential
@Override
public boolean check(Object credentials)
{
if (this == credentials) return true;
if (this == credentials)
return true;
if (credentials instanceof Password) return credentials.equals(_pw);
if (credentials instanceof Password)
return credentials.equals(_pw);
if (credentials instanceof String) return credentials.equals(_pw);
if (credentials instanceof String)
return stringEquals(_pw, (String)credentials);
if (credentials instanceof char[]) return Arrays.equals(_pw.toCharArray(), (char[]) credentials);
if (credentials instanceof char[])
return stringEquals(_pw, new String((char[])credentials));
if (credentials instanceof Credential) return ((Credential) credentials).check(_pw);
if (credentials instanceof Credential)
return ((Credential)credentials).check(_pw);
return false;
}
@ -120,14 +124,10 @@ public class Password extends Credential
return false;
if (o instanceof Password)
{
Password p = (Password) o;
//noinspection StringEquality
return p._pw == _pw || (null != _pw && _pw.equals(p._pw));
}
return stringEquals(_pw, ((Password)o)._pw);
if (o instanceof String)
return o.equals(_pw);
return stringEquals(_pw, (String)o);
return false;
}
@ -175,7 +175,6 @@ public class Password extends Credential
}
return buf.toString();
}
/* ------------------------------------------------------------ */

View File

@ -648,6 +648,9 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor
public void visitSessionConfig(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
{
if (context.getSessionHandler() == null)
return; //no session handler, ignore session setup
XmlParser.Node tNode = node.get("session-timeout");
if (tNode != null)
{