Merge branch 'jetty-9.4.x' of github.com:eclipse/jetty.project into jetty-9.4.x
This commit is contained in:
commit
d706a826bc
|
@ -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>
|
||||
|
|
|
@ -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<>();
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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 -->
|
||||
|
|
|
@ -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>
|
||||
----
|
||||
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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}"]
|
||||
----
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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 -->
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -335,6 +335,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
|
|||
catch (IOException e)
|
||||
{
|
||||
LOG.debug(e);
|
||||
_parser.atEOF();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"+
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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();
|
||||
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue