diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpAuthenticationStore.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpAuthenticationStore.java index fd58166003a..4b73c941ec6 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpAuthenticationStore.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpAuthenticationStore.java @@ -26,6 +26,7 @@ import java.util.concurrent.CopyOnWriteArrayList; import org.eclipse.jetty.client.api.Authentication; import org.eclipse.jetty.client.api.AuthenticationStore; +import org.eclipse.jetty.client.util.AbstractAuthentication; public class HttpAuthenticationStore implements AuthenticationStore { @@ -85,7 +86,7 @@ public class HttpAuthenticationStore implements AuthenticationStore // TODO: I should match the longest URI for (Map.Entry entry : results.entrySet()) { - if (uri.toString().startsWith(entry.getKey().toString())) + if (AbstractAuthentication.matchesURI(entry.getKey(), uri)) return entry.getValue(); } return null; diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java index ba4e6585d8f..5af0cd1c391 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java @@ -1035,7 +1035,7 @@ public class HttpClient extends ContainerLifeCycle return host; } - protected int normalizePort(String scheme, int port) + public static int normalizePort(String scheme, int port) { if (port > 0) return port; @@ -1053,7 +1053,7 @@ public class HttpClient extends ContainerLifeCycle return port == 80; } - public boolean isSchemeSecure(String scheme) + static boolean isSchemeSecure(String scheme) { return HttpScheme.HTTPS.is(scheme) || HttpScheme.WSS.is(scheme); } diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java index 54e2d438141..8a1ea7cc1f7 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java @@ -126,7 +126,7 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest public boolean isSecure() { - return client.isSchemeSecure(getScheme()); + return HttpClient.isSchemeSecure(getScheme()); } public HttpClient getHttpClient() diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java index 7270abbf3db..746763a1ab7 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java @@ -88,7 +88,7 @@ public class HttpRequest implements Request this.conversation = conversation; scheme = uri.getScheme(); host = client.normalizeHost(uri.getHost()); - port = client.normalizePort(scheme, uri.getPort()); + port = HttpClient.normalizePort(scheme, uri.getPort()); path = uri.getRawPath(); query = uri.getRawQuery(); extractParams(query); diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/AbstractAuthentication.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/AbstractAuthentication.java new file mode 100644 index 00000000000..7d5f9738b86 --- /dev/null +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/AbstractAuthentication.java @@ -0,0 +1,80 @@ +// +// ======================================================================== +// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.client.util; + +import java.net.URI; + +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.api.Authentication; + +public abstract class AbstractAuthentication implements Authentication +{ + private final URI uri; + private final String realm; + + public AbstractAuthentication(URI uri, String realm) + { + this.uri = uri; + this.realm = realm; + } + + public abstract String getType(); + + public URI getURI() + { + return uri; + } + + public String getRealm() + { + return realm; + } + + @Override + public boolean matches(String type, URI uri, String realm) + { + if (!getType().equalsIgnoreCase(type)) + return false; + + if (!this.realm.equals(realm)) + return false; + + return matchesURI(this.uri, uri); + } + + public static boolean matchesURI(URI uri1, URI uri2) + { + String scheme = uri1.getScheme(); + if (uri1.getScheme().equalsIgnoreCase(scheme)) + { + if (uri1.getHost().equalsIgnoreCase(uri2.getHost())) + { + // Handle default HTTP ports. + int thisPort = HttpClient.normalizePort(scheme, uri1.getPort()); + int thatPort = HttpClient.normalizePort(scheme, uri2.getPort()); + if (thisPort == thatPort) + { + // Use decoded URI paths. + return uri2.getPath().startsWith(uri1.getPath()); + } + } + } + return false; + } +} diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/BasicAuthentication.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/BasicAuthentication.java index 83b449dccd0..b7e0d0bfdfb 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/util/BasicAuthentication.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/BasicAuthentication.java @@ -22,7 +22,6 @@ import java.net.URI; import java.nio.charset.StandardCharsets; import org.eclipse.jetty.client.HttpClient; -import org.eclipse.jetty.client.api.Authentication; import org.eclipse.jetty.client.api.AuthenticationStore; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.Request; @@ -37,10 +36,8 @@ import org.eclipse.jetty.util.B64Code; * {@link AuthenticationStore} retrieved from the {@link HttpClient} * via {@link HttpClient#getAuthenticationStore()}. */ -public class BasicAuthentication implements Authentication +public class BasicAuthentication extends AbstractAuthentication { - private final URI uri; - private final String realm; private final String user; private final String password; @@ -52,48 +49,39 @@ public class BasicAuthentication implements Authentication */ public BasicAuthentication(URI uri, String realm, String user, String password) { - this.uri = uri; - this.realm = realm; + super(uri, realm); this.user = user; this.password = password; } @Override - public boolean matches(String type, URI uri, String realm) + public String getType() { - if (!"basic".equalsIgnoreCase(type)) - return false; - - if (!uri.toString().startsWith(this.uri.toString())) - return false; - - return this.realm.equals(realm); + return "Basic"; } @Override public Result authenticate(Request request, ContentResponse response, HeaderInfo headerInfo, Attributes context) { String value = "Basic " + B64Code.encode(user + ":" + password, StandardCharsets.ISO_8859_1); - return new BasicResult(headerInfo.getHeader(), uri, value); + return new BasicResult(headerInfo.getHeader(), value); } - private static class BasicResult implements Result + private class BasicResult implements Result { private final HttpHeader header; - private final URI uri; private final String value; - public BasicResult(HttpHeader header, URI uri, String value) + public BasicResult(HttpHeader header, String value) { this.header = header; - this.uri = uri; this.value = value; } @Override public URI getURI() { - return uri; + return BasicAuthentication.this.getURI(); } @Override @@ -105,7 +93,7 @@ public class BasicAuthentication implements Authentication @Override public String toString() { - return String.format("Basic authentication result for %s", uri); + return String.format("Basic authentication result for %s", getURI()); } } } diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/DigestAuthentication.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/DigestAuthentication.java index 2af088cbc4f..6e4d9871289 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/util/DigestAuthentication.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/DigestAuthentication.java @@ -23,7 +23,6 @@ import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Locale; @@ -34,7 +33,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import org.eclipse.jetty.client.HttpClient; -import org.eclipse.jetty.client.api.Authentication; import org.eclipse.jetty.client.api.AuthenticationStore; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.Request; @@ -50,12 +48,10 @@ import org.eclipse.jetty.util.TypeUtil; * {@link AuthenticationStore} retrieved from the {@link HttpClient} * via {@link HttpClient#getAuthenticationStore()}. */ -public class DigestAuthentication implements Authentication +public class DigestAuthentication extends AbstractAuthentication { private static final Pattern PARAM_PATTERN = Pattern.compile("([^=]+)=(.*)"); - private final URI uri; - private final String realm; private final String user; private final String password; @@ -67,22 +63,15 @@ public class DigestAuthentication implements Authentication */ public DigestAuthentication(URI uri, String realm, String user, String password) { - this.uri = uri; - this.realm = realm; + super(uri, realm); this.user = user; this.password = password; } @Override - public boolean matches(String type, URI uri, String realm) + public String getType() { - if (!"digest".equalsIgnoreCase(type)) - return false; - - if (!uri.toString().startsWith(this.uri.toString())) - return false; - - return this.realm.equals(realm); + return "Digest"; } @Override @@ -110,7 +99,7 @@ public class DigestAuthentication implements Authentication clientQOP = "auth-int"; } - return new DigestResult(headerInfo.getHeader(), uri, response.getContent(), realm, user, password, algorithm, nonce, clientQOP, opaque); + return new DigestResult(headerInfo.getHeader(), response.getContent(), getRealm(), user, password, algorithm, nonce, clientQOP, opaque); } private Map parseParameters(String wwwAuthenticate) @@ -181,7 +170,6 @@ public class DigestAuthentication implements Authentication { private final AtomicInteger nonceCount = new AtomicInteger(); private final HttpHeader header; - private final URI uri; private final byte[] content; private final String realm; private final String user; @@ -191,10 +179,9 @@ public class DigestAuthentication implements Authentication private final String qop; private final String opaque; - public DigestResult(HttpHeader header, URI uri, byte[] content, String realm, String user, String password, String algorithm, String nonce, String qop, String opaque) + public DigestResult(HttpHeader header, byte[] content, String realm, String user, String password, String algorithm, String nonce, String qop, String opaque) { this.header = header; - this.uri = uri; this.content = content; this.realm = realm; this.user = user; @@ -208,7 +195,7 @@ public class DigestAuthentication implements Authentication @Override public URI getURI() { - return uri; + return DigestAuthentication.this.getURI(); } @Override diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HostnameVerificationTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HostnameVerificationTest.java index d066aec9527..8736482cf59 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/HostnameVerificationTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HostnameVerificationTest.java @@ -125,7 +125,7 @@ public class HostnameVerificationTest if (cause instanceof SSLHandshakeException) Assert.assertThat(cause.getCause().getCause(), Matchers.instanceOf(CertificateException.class)); else - Assert.assertThat(cause.getCause(), Matchers.instanceOf(ClosedChannelException.class)); + Assert.assertThat(cause, Matchers.instanceOf(ClosedChannelException.class)); } } diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpAuthenticationStoreTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpAuthenticationStoreTest.java new file mode 100644 index 00000000000..062986f1c5a --- /dev/null +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpAuthenticationStoreTest.java @@ -0,0 +1,100 @@ +// +// ======================================================================== +// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.client; + +import java.net.URI; + +import org.eclipse.jetty.client.api.Authentication; +import org.eclipse.jetty.client.api.AuthenticationStore; +import org.eclipse.jetty.client.api.Request; +import org.eclipse.jetty.client.util.BasicAuthentication; +import org.eclipse.jetty.client.util.DigestAuthentication; +import org.junit.Assert; +import org.junit.Test; + +public class HttpAuthenticationStoreTest +{ + @Test + public void testFindAuthenticationWithDefaultHTTPPort() throws Exception + { + AuthenticationStore store = new HttpAuthenticationStore(); + + URI uri1 = URI.create("http://host:80"); + URI uri2 = URI.create("http://host"); + String realm = "realm"; + store.addAuthentication(new BasicAuthentication(uri1, realm, "user", "password")); + + Authentication result = store.findAuthentication("Basic", uri2, realm); + Assert.assertNotNull(result); + + store.clearAuthentications(); + + // Flip the URIs. + uri1 = URI.create("https://server/"); + uri2 = URI.create("https://server:443/path"); + store.addAuthentication(new DigestAuthentication(uri1, realm, "user", "password")); + result = store.findAuthentication("Digest", uri2, realm); + Assert.assertNotNull(result); + } + + @Test + public void testFindAuthenticationResultWithDefaultHTTPPort() throws Exception + { + AuthenticationStore store = new HttpAuthenticationStore(); + + store.addAuthenticationResult(new Authentication.Result() + { + @Override + public URI getURI() + { + return URI.create("http://host:80"); + } + + @Override + public void apply(Request request) + { + } + }); + + URI uri2 = URI.create("http://host"); + Authentication.Result result = store.findAuthenticationResult(uri2); + Assert.assertNotNull(result); + + store.clearAuthenticationResults(); + + // Flip the URIs. + store.addAuthenticationResult(new Authentication.Result() + { + @Override + public URI getURI() + { + return URI.create("https://server/"); + } + + @Override + public void apply(Request request) + { + } + }); + + uri2 = URI.create("https://server:443/path"); + result = store.findAuthenticationResult(uri2); + Assert.assertNotNull(result); + } +} diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTLSTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTLSTest.java new file mode 100644 index 00000000000..43559aa2184 --- /dev/null +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTLSTest.java @@ -0,0 +1,183 @@ +// +// ======================================================================== +// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.client; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jetty.http.HttpScheme; +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.junit.After; +import org.junit.Assert; +import org.junit.Test; + +public class HttpClientTLSTest +{ + private Server server; + private ServerConnector connector; + private HttpClient client; + + private void startServer(SslContextFactory sslContextFactory, Handler handler) throws Exception + { + QueuedThreadPool serverThreads = new QueuedThreadPool(); + serverThreads.setName("server"); + server = new Server(serverThreads); + + connector = new ServerConnector(server, sslContextFactory); + server.addConnector(connector); + + server.setHandler(handler); + server.start(); + } + + private void startClient(SslContextFactory sslContextFactory) throws Exception + { + QueuedThreadPool clientThreads = new QueuedThreadPool(); + clientThreads.setName("client"); + client = new HttpClient(sslContextFactory); + client.setExecutor(clientThreads); + client.start(); + } + + private SslContextFactory createSslContextFactory() + { + SslContextFactory sslContextFactory = new SslContextFactory(); + sslContextFactory.setEndpointIdentificationAlgorithm(""); + sslContextFactory.setKeyStorePath("src/test/resources/keystore.jks"); + sslContextFactory.setKeyStorePassword("storepwd"); + sslContextFactory.setTrustStorePath("src/test/resources/truststore.jks"); + sslContextFactory.setTrustStorePassword("storepwd"); + return sslContextFactory; + } + + @After + public void dispose() throws Exception + { + if (client != null) + client.stop(); + if (server != null) + server.stop(); + } + + @Test + public void testNoCommonTLSProtocol() throws Exception + { + SslContextFactory serverTLSFactory = createSslContextFactory(); + serverTLSFactory.setIncludeProtocols("TLSv1.2"); + startServer(serverTLSFactory, new EmptyServerHandler()); + + SslContextFactory clientTLSFactory = createSslContextFactory(); + clientTLSFactory.setIncludeProtocols("TLSv1.1"); + startClient(clientTLSFactory); + + try + { + client.newRequest("localhost", connector.getLocalPort()) + .scheme(HttpScheme.HTTPS.asString()) + .timeout(5, TimeUnit.SECONDS) + .send(); + Assert.fail(); + } + catch (ExecutionException x) + { + // Expected. + } + } + + @Test + public void testNoCommonTLSCiphers() throws Exception + { + SslContextFactory serverTLSFactory = createSslContextFactory(); + serverTLSFactory.setIncludeCipherSuites("TLS_RSA_WITH_AES_128_CBC_SHA"); + startServer(serverTLSFactory, new EmptyServerHandler()); + + SslContextFactory clientTLSFactory = createSslContextFactory(); + clientTLSFactory.setExcludeCipherSuites(".*_SHA$"); + startClient(clientTLSFactory); + + try + { + client.newRequest("localhost", connector.getLocalPort()) + .scheme(HttpScheme.HTTPS.asString()) + .timeout(5, TimeUnit.SECONDS) + .send(); + Assert.fail(); + } + catch (ExecutionException x) + { + // Expected. + } + } + + @Test + public void testMismatchBetweenTLSProtocolAndTLSCiphersOnServer() throws Exception + { + SslContextFactory serverTLSFactory = createSslContextFactory(); + // TLS 1.1 protocol, but only TLS 1.2 ciphers. + serverTLSFactory.setIncludeProtocols("TLSv1.1"); + serverTLSFactory.setIncludeCipherSuites("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"); + startServer(serverTLSFactory, new EmptyServerHandler()); + + SslContextFactory clientTLSFactory = createSslContextFactory(); + startClient(clientTLSFactory); + + try + { + client.newRequest("localhost", connector.getLocalPort()) + .scheme(HttpScheme.HTTPS.asString()) + .timeout(5, TimeUnit.SECONDS) + .send(); + Assert.fail(); + } + catch (ExecutionException x) + { + // Expected. + } + } + + @Test + public void testMismatchBetweenTLSProtocolAndTLSCiphersOnClient() throws Exception + { + SslContextFactory serverTLSFactory = createSslContextFactory(); + startServer(serverTLSFactory, new EmptyServerHandler()); + + SslContextFactory clientTLSFactory = createSslContextFactory(); + // TLS 1.1 protocol, but only TLS 1.2 ciphers. + clientTLSFactory.setIncludeProtocols("TLSv1.1"); + clientTLSFactory.setIncludeCipherSuites("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"); + startClient(clientTLSFactory); + + try + { + client.newRequest("localhost", connector.getLocalPort()) + .scheme(HttpScheme.HTTPS.asString()) + .timeout(5, TimeUnit.SECONDS) + .send(); + Assert.fail(); + } + catch (ExecutionException x) + { + // Expected. + } + } +} diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslBytesServerTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslBytesServerTest.java index 0e0f48b714b..9c10f7bc1ac 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslBytesServerTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslBytesServerTest.java @@ -18,8 +18,6 @@ package org.eclipse.jetty.client.ssl; -import static org.hamcrest.Matchers.nullValue; - import java.io.BufferedReader; import java.io.EOFException; import java.io.File; @@ -368,7 +366,7 @@ public class SslBytesServerTest extends SslBytesTest System.arraycopy(doneBytes, 0, chunk, recordBytes.length, doneBytes.length); System.arraycopy(closeRecordBytes, 0, chunk, recordBytes.length + doneBytes.length, closeRecordBytes.length); proxy.flushToServer(0, chunk); - + // Close the raw socket proxy.flushToServer(null); @@ -380,7 +378,7 @@ public class SslBytesServerTest extends SslBytesTest Assert.assertEquals(Type.ALERT,record.getType()); record = proxy.readFromServer(); } - + Assert.assertNull(record); // Check that we did not spin @@ -488,7 +486,7 @@ public class SslBytesServerTest extends SslBytesTest if (record!=null) { Assert.assertEquals(record.getType(),Type.ALERT); - + // Now should be a raw close record = proxy.readFromServer(); Assert.assertNull(String.valueOf(record), record); @@ -784,7 +782,7 @@ public class SslBytesServerTest extends SslBytesTest if (record!=null) { Assert.assertEquals(record.getType(),Type.ALERT); - + // Now should be a raw close record = proxy.readFromServer(); Assert.assertNull(String.valueOf(record), record); @@ -846,7 +844,7 @@ public class SslBytesServerTest extends SslBytesTest if (record!=null) { Assert.assertEquals(record.getType(),Type.ALERT); - + // Now should be a raw close record = proxy.readFromServer(); Assert.assertNull(String.valueOf(record), record); @@ -921,7 +919,7 @@ public class SslBytesServerTest extends SslBytesTest if (record!=null) { Assert.assertEquals(record.getType(),Type.ALERT); - + // Now should be a raw close record = proxy.readFromServer(); Assert.assertNull(String.valueOf(record), record); @@ -983,7 +981,7 @@ public class SslBytesServerTest extends SslBytesTest if (record!=null) { Assert.assertEquals(record.getType(),Type.ALERT); - + // Now should be a raw close record = proxy.readFromServer(); Assert.assertNull(String.valueOf(record), record); @@ -1040,7 +1038,7 @@ public class SslBytesServerTest extends SslBytesTest if (record!=null) { Assert.assertEquals(record.getType(),Type.ALERT); - + // Now should be a raw close record = proxy.readFromServer(); Assert.assertNull(String.valueOf(record), record); @@ -1060,7 +1058,7 @@ public class SslBytesServerTest extends SslBytesTest { // Don't run on Windows (buggy JVM) Assume.assumeTrue(!OS.IS_WINDOWS); - + final SSLSocket client = newClient(); SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow(); @@ -1121,7 +1119,7 @@ public class SslBytesServerTest extends SslBytesTest { // Don't run on Windows (buggy JVM) Assume.assumeTrue(!OS.IS_WINDOWS); - + final SSLSocket client = newClient(); SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow(); @@ -1247,7 +1245,7 @@ public class SslBytesServerTest extends SslBytesTest if (record!=null) { Assert.assertEquals(record.getType(),Type.ALERT); - + // Now should be a raw close record = proxy.readFromServer(); Assert.assertNull(String.valueOf(record), record); @@ -1862,8 +1860,11 @@ public class SslBytesServerTest extends SslBytesTest // Instead of passing the Client Hello, we simulate plain text was passed in proxy.flushToServer(0, "GET / HTTP/1.1\r\n".getBytes(StandardCharsets.UTF_8)); - // We expect that the server closes the connection immediately + // We expect that the server sends an alert message and closes. TLSRecord record = proxy.readFromServer(); + Assert.assertNotNull(record); + Assert.assertEquals(TLSRecord.Type.ALERT, record.getType()); + record = proxy.readFromServer(); Assert.assertNull(String.valueOf(record), record); // Check that we did not spin @@ -1982,6 +1983,6 @@ public class SslBytesServerTest extends SslBytesTest Assert.assertEquals(record.getType(),Type.ALERT); record = proxy.readFromServer(); } - Assert.assertThat(record,nullValue()); + Assert.assertThat(record, Matchers.nullValue()); } } diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslBytesTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslBytesTest.java index e217d4f8978..7e5490eb6bd 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslBytesTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslBytesTest.java @@ -28,7 +28,6 @@ import java.net.Socket; import java.net.SocketTimeoutException; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; @@ -49,16 +48,16 @@ public abstract class SslBytesTest public static class TLSRecord { - private final SslBytesServerTest.TLSRecord.Type type; + private final Type type; private final byte[] bytes; - public TLSRecord(SslBytesServerTest.TLSRecord.Type type, byte[] bytes) + public TLSRecord(Type type, byte[] bytes) { this.type = type; this.bytes = bytes; } - public SslBytesServerTest.TLSRecord.Type getType() + public Type getType() { return type; } @@ -80,15 +79,15 @@ public abstract class SslBytesTest private int code; - private Type(int code) + Type(int code) { this.code = code; - SslBytesServerTest.TLSRecord.Type.Mapper.codes.put(this.code, this); + Mapper.codes.put(this.code, this); } - public static SslBytesServerTest.TLSRecord.Type from(int code) + public static Type from(int code) { - SslBytesServerTest.TLSRecord.Type result = SslBytesServerTest.TLSRecord.Type.Mapper.codes.get(code); + Type result = Mapper.codes.get(code); if (result == null) throw new IllegalArgumentException("Invalid TLSRecord.Type " + code); return result; @@ -96,7 +95,7 @@ public abstract class SslBytesTest private static class Mapper { - private static final Map codes = new HashMap<>(); + private static final Map codes = new HashMap<>(); } } } @@ -218,7 +217,7 @@ public abstract class SslBytesTest } } - private TLSRecord read(SslBytesServerTest.TLSRecord.Type type, InputStream input, byte[] bytes, int offset, int length) throws IOException + private TLSRecord read(TLSRecord.Type type, InputStream input, byte[] bytes, int offset, int length) throws IOException { while (length > 0) { @@ -291,57 +290,51 @@ public abstract class SslBytesTest } } - public SslBytesServerTest.SimpleProxy.AutomaticFlow startAutomaticFlow() throws InterruptedException + public SslBytesTest.SimpleProxy.AutomaticFlow startAutomaticFlow() throws InterruptedException { final CountDownLatch startLatch = new CountDownLatch(2); final CountDownLatch stopLatch = new CountDownLatch(2); - Future clientToServer = threadPool.submit(new Callable() + Future clientToServer = threadPool.submit(() -> { - public Object call() throws Exception + startLatch.countDown(); + logger.debug("Automatic flow C --> S started"); + try { - startLatch.countDown(); - logger.debug("Automatic flow C --> S started"); - try + while (true) { - while (true) - { - flushToServer(readFromClient(), 0); - } - } - catch (InterruptedIOException x) - { - return null; - } - finally - { - stopLatch.countDown(); - logger.debug("Automatic flow C --> S finished"); + flushToServer(readFromClient(), 0); } } - }); - Future serverToClient = threadPool.submit(new Callable() - { - public Object call() throws Exception + catch (InterruptedIOException x) { - startLatch.countDown(); - logger.debug("Automatic flow C <-- S started"); - try + return null; + } + finally + { + stopLatch.countDown(); + logger.debug("Automatic flow C --> S finished"); + } + }); + Future serverToClient = threadPool.submit(() -> + { + startLatch.countDown(); + logger.debug("Automatic flow C <-- S started"); + try + { + while (true) { - while (true) - { - flushToClient(readFromServer()); - } - } - catch (InterruptedIOException x) - { - return null; - } - finally - { - stopLatch.countDown(); - logger.debug("Automatic flow C <-- S finished"); + flushToClient(readFromServer()); } } + catch (InterruptedIOException x) + { + return null; + } + finally + { + stopLatch.countDown(); + logger.debug("Automatic flow C <-- S finished"); + } }); Assert.assertTrue(startLatch.await(5, TimeUnit.SECONDS)); return new SslBytesServerTest.SimpleProxy.AutomaticFlow(stopLatch, clientToServer, serverToClient); diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/util/MultiPartContentProviderTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/util/MultiPartContentProviderTest.java index ad59b41cdac..0f9d8280f76 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/util/MultiPartContentProviderTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/util/MultiPartContentProviderTest.java @@ -185,11 +185,9 @@ public class MultiPartContentProviderTest extends AbstractHttpClientServerTest .content(multiPart) .send(result -> { - if (result.isSucceeded()) - { - Assert.assertEquals(200, result.getResponse().getStatus()); - responseLatch.countDown(); - } + Assert.assertTrue(String.valueOf(result.getFailure()), result.isSucceeded()); + Assert.assertEquals(200, result.getResponse().getStatus()); + responseLatch.countDown(); }); // Wait until the request has been sent. @@ -408,11 +406,9 @@ public class MultiPartContentProviderTest extends AbstractHttpClientServerTest .content(multiPart) .send(result -> { - if (result.isSucceeded()) - { - Assert.assertEquals(200, result.getResponse().getStatus()); - responseLatch.countDown(); - } + Assert.assertTrue(String.valueOf(result.getFailure()), result.isSucceeded()); + Assert.assertEquals(200, result.getResponse().getStatus()); + responseLatch.countDown(); }); // Wait until the request has been sent. diff --git a/jetty-gcloud/jetty-gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudSessionDataStore.java b/jetty-gcloud/jetty-gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudSessionDataStore.java index 74454132f3a..eec6f303a64 100644 --- a/jetty-gcloud/jetty-gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudSessionDataStore.java +++ b/jetty-gcloud/jetty-gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudSessionDataStore.java @@ -19,20 +19,6 @@ package org.eclipse.jetty.gcloud.session; -import java.io.ByteArrayOutputStream; -import java.io.ObjectOutputStream; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.atomic.AtomicReference; - -import org.eclipse.jetty.server.session.AbstractSessionDataStore; -import org.eclipse.jetty.server.session.SessionContext; -import org.eclipse.jetty.server.session.SessionData; -import org.eclipse.jetty.util.ClassLoadingObjectInputStream; -import org.eclipse.jetty.util.log.Log; -import org.eclipse.jetty.util.log.Logger; - import com.google.gcloud.datastore.Blob; import com.google.gcloud.datastore.Datastore; import com.google.gcloud.datastore.DatastoreFactory; @@ -49,6 +35,21 @@ import com.google.gcloud.datastore.StructuredQuery.Projection; import com.google.gcloud.datastore.StructuredQuery.ProjectionEntityQueryBuilder; import com.google.gcloud.datastore.StructuredQuery.PropertyFilter; +import java.io.ByteArrayOutputStream; +import java.io.ObjectOutputStream; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; + +import org.eclipse.jetty.server.session.AbstractSessionDataStore; +import org.eclipse.jetty.server.session.SessionContext; +import org.eclipse.jetty.server.session.SessionData; +import org.eclipse.jetty.util.ClassLoadingObjectInputStream; +import org.eclipse.jetty.util.StringUtil; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; + /** * GCloudSessionDataStore * @@ -78,11 +79,6 @@ public class GCloudSessionDataStore extends AbstractSessionDataStore private KeyFactory _keyFactory; private int _maxResults = DEFAULT_MAX_QUERY_RESULTS; - - - - - @@ -186,60 +182,93 @@ public class GCloudSessionDataStore extends AbstractSessionDataStore } /** - * @see org.eclipse.jetty.server.session.SessionDataStore#getExpired(java.util.Set) + * @see org.eclipse.jetty.server.session.SessionDataStore#getExpired(Set, int) */ @Override - public Set getExpired(Set candidates) + public Set doGetExpired(Set candidates, int expiryTimeoutSec) { - long now = System.currentTimeMillis(); - Set expired = new HashSet(); - - //get up to maxResult number of sessions that have expired - ProjectionEntityQueryBuilder pbuilder = Query.projectionEntityQueryBuilder(); - pbuilder.addProjection(Projection.property(ID)); - pbuilder.filter(CompositeFilter.and(PropertyFilter.gt(EXPIRY, 0), PropertyFilter.le(EXPIRY, now))); - pbuilder.limit(_maxResults); - pbuilder.kind(KIND); - StructuredQuery pquery = pbuilder.build(); - QueryResults presults = _datastore.run(pquery); - - while (presults.hasNext()) - { - ProjectionEntity pe = presults.next(); - String id = pe.getString(ID); - expired.add(id); - } - - //reconcile against ids that the SessionStore thinks are expired - Set tmp = new HashSet(candidates); - tmp.removeAll(expired); - if (!tmp.isEmpty()) - { - //sessionstore thinks these are expired, but they are either no - //longer in the db or not expired in the db, or we exceeded the - //number of records retrieved by the expiry query, so check them - //individually - for (String s:tmp) - { - try - { - KeyQueryBuilder kbuilder = Query.keyQueryBuilder(); - kbuilder.filter(PropertyFilter.eq(ID, s)); - kbuilder.kind(KIND); - StructuredQuery kq = kbuilder.build(); - QueryResults kresults = _datastore.run(kq); - if (!kresults.hasNext()) - expired.add(s); //not in db, can be expired - } - catch (Exception e) - { - LOG.warn(e); - } - } - } - - return expired; - + long now = System.currentTimeMillis(); + Set expired = new HashSet(); + + try + { + //get up to maxResult number of sessions that have expired + ProjectionEntityQueryBuilder pbuilder = Query.projectionEntityQueryBuilder(); + pbuilder.addProjection(Projection.property(ID), Projection.property(LASTNODE), Projection.property(EXPIRY)); + pbuilder.filter(CompositeFilter.and(PropertyFilter.gt(EXPIRY, 0), PropertyFilter.le(EXPIRY, now))); + pbuilder.limit(_maxResults); + pbuilder.kind(KIND); + StructuredQuery pquery = pbuilder.build(); + QueryResults presults = _datastore.run(pquery); + + while (presults.hasNext()) + { + ProjectionEntity pe = presults.next(); + String id = pe.getString(ID); + String lastNode = pe.getString(LASTNODE); + long expiry = pe.getLong(EXPIRY); + + if (StringUtil.isBlank(lastNode)) + expired.add(id); //nobody managing it + else + { + if (_context.getWorkerName().equals(lastNode)) + expired.add(id); //we're managing it, we can expire it + else + { + if (_lastExpiryCheckTime <= 0) + { + //our first check, just look for sessions that we managed by another node that + //expired at least 3 graceperiods ago + if (expiry < (now - (1000L * (3 * _gracePeriodSec)))) + expired.add(id); + } + else + { + //another node was last managing it, only expire it if it expired a graceperiod ago + if (expiry < (now - (1000L * _gracePeriodSec))) + expired.add(id); + } + } + } + } + + //reconcile against ids that the SessionStore thinks are expired + Set tmp = new HashSet(candidates); + tmp.removeAll(expired); + if (!tmp.isEmpty()) + { + //sessionstore thinks these are expired, but they are either no + //longer in the db or not expired in the db, or we exceeded the + //number of records retrieved by the expiry query, so check them + //individually + for (String s:tmp) + { + try + { + KeyQueryBuilder kbuilder = Query.keyQueryBuilder(); + kbuilder.filter(PropertyFilter.eq(ID, s)); + kbuilder.kind(KIND); + StructuredQuery kq = kbuilder.build(); + QueryResults kresults = _datastore.run(kq); + if (!kresults.hasNext()) + expired.add(s); //not in db, can be expired + } + catch (Exception e) + { + LOG.warn(e); + } + } + } + + return expired; + } + catch (Exception e) + { + LOG.warn(e); + return expired; //return what we got + } + } @@ -267,8 +296,9 @@ public class GCloudSessionDataStore extends AbstractSessionDataStore * * * - * @param session - * @return + * @param id the id + * @param context the session context + * @return the key */ private Key makeKey (String id, SessionContext context) { @@ -279,9 +309,9 @@ public class GCloudSessionDataStore extends AbstractSessionDataStore /** * Generate a gcloud datastore Entity from SessionData - * @param session - * @param key - * @return + * @param session the session data + * @param key the key + * @return the entity * @throws Exception */ private Entity entityFromSession (SessionData session, Key key) throws Exception @@ -297,6 +327,8 @@ public class GCloudSessionDataStore extends AbstractSessionDataStore oos.writeObject(session.getAllAttributes()); oos.flush(); + try + { //turn a session into an entity entity = Entity.builder(key) .set(ID, session.getId()) @@ -310,6 +342,12 @@ public class GCloudSessionDataStore extends AbstractSessionDataStore .set(EXPIRY, session.getExpiry()) .set(MAXINACTIVE, session.getMaxInactiveMs()) .set(ATTRIBUTES, Blob.copyFrom(baos.toByteArray())).build(); + } + catch (Exception e) + { + e.printStackTrace(); + throw e; + } return entity; } diff --git a/jetty-gcloud/jetty-gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudSessionManager.java b/jetty-gcloud/jetty-gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudSessionManager.java index dd50b9412eb..03c778f3672 100644 --- a/jetty-gcloud/jetty-gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudSessionManager.java +++ b/jetty-gcloud/jetty-gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudSessionManager.java @@ -18,11 +18,6 @@ package org.eclipse.jetty.gcloud.session; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.locks.ReentrantLock; - -import javax.servlet.http.HttpServletRequest; - import org.eclipse.jetty.server.session.AbstractSessionStore; import org.eclipse.jetty.server.session.MemorySessionStore; import org.eclipse.jetty.server.session.SessionManager; @@ -49,9 +44,7 @@ public class GCloudSessionManager extends SessionManager -/* - - *//** + /* * * Session * * Representation of a session in local memory. @@ -75,7 +68,8 @@ public class GCloudSessionManager extends SessionManager - *//** + */ + /* * * Called on entry to the session. * * @see org.eclipse.jetty.server.session.AbstractSession#access(long) @@ -122,7 +116,7 @@ public class GCloudSessionManager extends SessionManager } - *//** + *//* * * Exit from session * @see org.eclipse.jetty.server.session.AbstractSession#complete() *//* @@ -174,7 +168,7 @@ public class GCloudSessionManager extends SessionManager } } - *//** Test if the session is stale + *//* * Test if the session is stale * @param atTime * @return *//* @@ -184,7 +178,7 @@ public class GCloudSessionManager extends SessionManager } - *//** + *//* * * Reload the session from the cluster. If the node that * last managed the session from the cluster is ourself, * then the session does not need refreshing. @@ -295,7 +289,7 @@ public class GCloudSessionManager extends SessionManager /** * Start the session manager. * - * @see org.eclipse.jetty.server.session.AbstractSessionManager#doStart() + * @see org.eclipse.jetty.server.session.SessionManager#doStart() */ @Override public void doStart() throws Exception @@ -308,7 +302,7 @@ public class GCloudSessionManager extends SessionManager /** * Stop the session manager. * - * @see org.eclipse.jetty.server.session.AbstractSessionManager#doStop() + * @see org.eclipse.jetty.server.session.SessionManager#doStop() */ @Override public void doStop() throws Exception diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpContent.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpContent.java index 50871afede3..e5a059c39a3 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpContent.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpContent.java @@ -73,7 +73,7 @@ public interface HttpContent * @param maxBuffer The maximum buffer to allocated for this request. For cached content, a larger buffer may have * previously been allocated and returned by the {@link HttpContent#getDirectBuffer()} or {@link HttpContent#getIndirectBuffer()} calls. * @return A {@link HttpContent} - * @throws IOException + * @throws IOException if unable to get content */ HttpContent getContent(String path,int maxBuffer) throws IOException; } diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java index 8093eb61dd6..3b56c18e392 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java @@ -1174,7 +1174,7 @@ public class HttpParser { _value=null; _string.setLength(0); - _valueString=_compliance.ordinal()<=HttpCompliance.RFC2616.ordinal()?"":null; + _valueString=""; _length=-1; setState(State.HEADER); diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java index 411ca29d101..ec54cc5f19c 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java @@ -177,9 +177,10 @@ public class HttpURI } /* ------------------------------------------------------------ */ - /** Parse according to https://tools.ietf.org/html/rfc7230#section-5.3 - * @param method - * @param uri + /** + * Parse according to https://tools.ietf.org/html/rfc7230#section-5.3 + * @param method the request method + * @param uri the request uri */ public void parseRequestTarget(String method,String uri) { diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/PathSpecGroup.java b/jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/PathSpecGroup.java index e03a035de10..d45ffc28458 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/PathSpecGroup.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/PathSpecGroup.java @@ -62,8 +62,7 @@ public enum PathSpecGroup MIDDLE_GLOB, /** * For path specs that have a hardcoded prefix and a trailing wildcard glob. - *

- * + * *

      *   "/downloads/*"          - servlet spec
      *   "/api/*"                - servlet spec
diff --git a/jetty-http/src/main/resources/org/eclipse/jetty/http/mime.properties b/jetty-http/src/main/resources/org/eclipse/jetty/http/mime.properties
index e575db19ec4..0112b88a256 100644
--- a/jetty-http/src/main/resources/org/eclipse/jetty/http/mime.properties
+++ b/jetty-http/src/main/resources/org/eclipse/jetty/http/mime.properties
@@ -13,6 +13,7 @@ bin=application/octet-stream
 bmp=image/bmp
 cab=application/x-cabinet
 cdf=application/x-netcdf
+chm=application/vnd.ms-htmlhelp
 class=application/java-vm
 cpio=application/x-cpio
 cpt=application/mac-compactpro
diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java
index de60c40b742..62d7723f3a3 100644
--- a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java
+++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java
@@ -334,9 +334,8 @@ public class HttpParserTest
         
     }
     
-
     @Test
-    public void test2616NoValue() throws Exception
+    public void testNoValue() throws Exception
     {
         ByteBuffer buffer= BufferUtil.toBuffer(
                 "GET / HTTP/1.0\015\012" +
@@ -366,37 +365,6 @@ public class HttpParserTest
         assertEquals(3, _headers);
     }
 
-    @Test
-    public void test7230NoValue() throws Exception
-    {
-        ByteBuffer buffer= BufferUtil.toBuffer(
-                "GET / HTTP/1.0\015\012" +
-                "Host: localhost\015\012" +
-                "Name0: \015\012"+
-                "Name1: \015\012"+
-                "Connection: close\015\012" +
-                "\015\012");
-        
-        HttpParser.RequestHandler handler  = new Handler();
-        HttpParser parser= new HttpParser(handler);
-        parseAll(parser,buffer);
-
-        assertTrue(_headerCompleted);
-        assertTrue(_messageCompleted);
-        assertEquals("GET", _methodOrVersion);
-        assertEquals("/", _uriOrStatus);
-        assertEquals("HTTP/1.0", _versionOrReason);
-        assertEquals("Host", _hdr[0]);
-        assertEquals("localhost", _val[0]);
-        assertEquals("Name0", _hdr[1]);
-        assertEquals(null, _val[1]);
-        assertEquals("Name1", _hdr[2]);
-        assertEquals(null, _val[2]);
-        assertEquals("Connection", _hdr[3]);
-        assertEquals("close", _val[3]);
-        assertEquals(3, _headers);
-    }
-
     @Test
     public void testHeaderParseDirect() throws Exception
     {
diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpURIParseTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpURIParseTest.java
index d67a3b70d10..189e09fe8ea 100644
--- a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpURIParseTest.java
+++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpURIParseTest.java
@@ -19,22 +19,24 @@
 
 package org.eclipse.jetty.http;
 
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
-import static org.junit.Assume.*;
-
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.Arrays;
 import java.util.List;
 
-import org.eclipse.jetty.util.URIUtil;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameter;
 import org.junit.runners.Parameterized.Parameters;
 
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assume.assumeNoException;
+import static org.junit.Assume.assumeNotNull;
+
 
 @RunWith(Parameterized.class)
 public class HttpURIParseTest
@@ -69,7 +71,8 @@ public class HttpURIParseTest
         {"/path/info;param#fragment",null,null,null,"/path/info;param","param",null,"fragment"},
         {"/path/info;param?query",null,null,null,"/path/info;param","param","query",null},
         {"/path/info;param?query#fragment",null,null,null,"/path/info;param","param","query","fragment"},
-        
+        // FIXME: {"/path/info;a=b/foo;c=d",null,null,null,"/data/info;a=b/foo;c=d","foo=bar1",null,null},
+
         // Protocol Less (aka scheme-less) URIs
         {"//host/path/info",null,"host",null,"/path/info",null,null,null},
         {"//user@host/path/info",null,"host",null,"/path/info",null,null,null},
@@ -151,14 +154,14 @@ public class HttpURIParseTest
         // Interpreted as relative path of "*" (no host/port/scheme/query/fragment)
         {"*",null,null,null,"*",null, null,null},
 
-        // Path detection Tests (seen from JSP/JSTL and  use
+        // Path detection Tests (seen from JSP/JSTL and  use)
         {"http://host:8080/path/info?q1=v1&q2=v2","http","host","8080","/path/info",null,"q1=v1&q2=v2",null},
         {"/path/info?q1=v1&q2=v2",null,null,null,"/path/info",null,"q1=v1&q2=v2",null},
         {"/info?q1=v1&q2=v2",null,null,null,"/info",null,"q1=v1&q2=v2",null},
         {"info?q1=v1&q2=v2",null,null,null,"info",null,"q1=v1&q2=v2",null},
         {"info;q1=v1?q2=v2",null,null,null,"info;q1=v1","q1=v1","q2=v2",null},
         
-        // Path-less, query only (seen from JSP/JSTL and  use
+        // Path-less, query only (seen from JSP/JSTL and  use)
         {"?q1=v1&q2=v2",null,null,null,"",null,"q1=v1&q2=v2",null}
         };
         
diff --git a/jetty-http2/http2-alpn-tests/src/test/java/org/eclipse/jetty/http2/alpn/tests/ALPNNegotiationTest.java b/jetty-http2/http2-alpn-tests/src/test/java/org/eclipse/jetty/http2/alpn/tests/ALPNNegotiationTest.java
index baac1bf3ebf..752471a2559 100644
--- a/jetty-http2/http2-alpn-tests/src/test/java/org/eclipse/jetty/http2/alpn/tests/ALPNNegotiationTest.java
+++ b/jetty-http2/http2-alpn-tests/src/test/java/org/eclipse/jetty/http2/alpn/tests/ALPNNegotiationTest.java
@@ -91,12 +91,17 @@ public class ALPNNegotiationTest extends AbstractALPNTest
             Assert.assertTrue(read > 0);
             // Cannot decrypt, as the SSLEngine has been already closed
 
-            // Now if we read more, we should either read the TLS Close Alert, or directly -1
+            // Now if we read more, we should read a TLS Alert.
             encrypted.clear();
             read = channel.read(encrypted);
-            // Sending a TLS Close Alert during handshake results in an exception when
-            // unwrapping that the server react to by closing the connection abruptly.
-            Assert.assertTrue(read < 0);
+            if (read > 0)
+            {
+                encrypted.flip();
+                // TLS Alert message type == 21.
+                Assert.assertEquals(21, encrypted.get() & 0xFF);
+                encrypted.clear();
+                Assert.assertEquals(-1, channel.read(encrypted));
+            }
         }
     }
 
diff --git a/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpClientTransportOverHTTP2.java b/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpClientTransportOverHTTP2.java
index a5b305c1297..844be1a5e37 100644
--- a/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpClientTransportOverHTTP2.java
+++ b/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpClientTransportOverHTTP2.java
@@ -138,6 +138,11 @@ public class HttpClientTransportOverHTTP2 extends ContainerLifeCycle implements
         return new HttpConnectionOverHTTP2(destination, session);
     }
 
+    protected void onClose(HttpConnectionOverHTTP2 connection, GoAwayFrame frame)
+    {
+        connection.close();
+    }
+
     private class SessionListenerPromise extends Session.Listener.Adapter implements Promise
     {
         private final HttpDestinationOverHTTP2 destination;
@@ -182,7 +187,7 @@ public class HttpClientTransportOverHTTP2 extends ContainerLifeCycle implements
         @Override
         public void onClose(Session session, GoAwayFrame frame)
         {
-            connection.close();
+            HttpClientTransportOverHTTP2.this.onClose(connection, frame);
         }
 
         @Override
@@ -194,7 +199,9 @@ public class HttpClientTransportOverHTTP2 extends ContainerLifeCycle implements
         @Override
         public void onFailure(Session session, Throwable failure)
         {
-            connection.close(failure);
+            HttpConnectionOverHTTP2 c = connection;
+            if (c != null)
+                c.close(failure);
         }
     }
 }
diff --git a/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionDataStore.java b/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionDataStore.java
index e7611aab8d7..62e98eb79bf 100644
--- a/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionDataStore.java
+++ b/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionDataStore.java
@@ -82,7 +82,7 @@ public class InfinispanSessionDataStore extends AbstractSessionDataStore
     
     
     /** 
-     * @see org.eclipse.jetty.server.session.SessionDataStore#load(org.eclipse.jetty.server.session.SessionKey)
+     * @see org.eclipse.jetty.server.session.SessionDataStore#load(String)
      */
     @Override
     public SessionData load(String id) throws Exception
@@ -120,7 +120,7 @@ public class InfinispanSessionDataStore extends AbstractSessionDataStore
     }
 
     /** 
-     * @see org.eclipse.jetty.server.session.SessionDataStore#delete(org.eclipse.jetty.server.session.SessionKey)
+     * @see org.eclipse.jetty.server.session.SessionDataStore#delete(String)
      */
     @Override
     public boolean delete(String id) throws Exception
@@ -131,10 +131,10 @@ public class InfinispanSessionDataStore extends AbstractSessionDataStore
     }
 
     /** 
-     * @see org.eclipse.jetty.server.session.SessionDataStore#getExpired(java.util.Set)
+     * @see org.eclipse.jetty.server.session.SessionDataStore#getExpired(Set, int)
      */
     @Override
-    public Set getExpired(Set candidates)
+    public Set doGetExpired(Set candidates, int expiryTimeoutSec)
     {
        if (candidates == null  || candidates.isEmpty())
            return candidates;
@@ -143,6 +143,9 @@ public class InfinispanSessionDataStore extends AbstractSessionDataStore
        
        Set expired = new HashSet();
        
+       //TODO if there is NOT an idle timeout set, need to check other sessions that
+       //might have expired
+       
        for (String candidate:candidates)
        {
            if (LOG.isDebugEnabled())
@@ -151,12 +154,43 @@ public class InfinispanSessionDataStore extends AbstractSessionDataStore
            {
                SessionData sd = load(candidate);
 
-               if (sd == null || sd.isExpiredAt(now)) 
+               //if the session no longer exists
+               if (sd == null)
                {
                    expired.add(candidate);
                    if (LOG.isDebugEnabled())
-                       LOG.debug("Is null {} is expired {}", (sd==null), (sd !=null));
-               }    
+                       LOG.debug("Session {} does not exist in infinispan", candidate);
+               }
+               else
+               {
+                   if (_context.getWorkerName().equals(sd.getLastNode()))
+                   {
+                       //we are its manager, add it to the expired set if it is expired now
+                       if ((sd.getExpiry() > 0 ) && sd.getExpiry() <= now)
+                       {
+                           expired.add(candidate);
+                           if (LOG.isDebugEnabled())
+                               LOG.debug("Session {} managed by {} is expired", candidate, _context.getWorkerName());
+                       }
+                   }
+                   else
+                   {
+                       //if we are not the session's manager, only expire it iff:
+                       // this is our first expiryCheck and the session expired a long time ago
+                       //or
+                       //the session expired at least one graceperiod ago
+                       if (_lastExpiryCheckTime <=0)
+                       {
+                           if ((sd.getExpiry() > 0 ) && sd.getExpiry() < (now - (1000L * (3 * _gracePeriodSec))))
+                               expired.add(candidate);
+                       }
+                       else
+                       {
+                           if ((sd.getExpiry() > 0 ) && sd.getExpiry() < (now - (1000L * _gracePeriodSec)))
+                               expired.add(candidate);
+                       }
+                   }
+               }
            }
            catch (Exception e)
            {
@@ -168,7 +202,7 @@ public class InfinispanSessionDataStore extends AbstractSessionDataStore
     }
 
     /** 
-     * @see org.eclipse.jetty.server.session.AbstractSessionDataStore#doStore(org.eclipse.jetty.server.session.SessionKey, org.eclipse.jetty.server.session.SessionData, long)
+     * @see org.eclipse.jetty.server.session.AbstractSessionDataStore#doStore(String, SessionData, long)
      */
     @Override
     public void doStore(String id, SessionData data, long lastSaveTime) throws Exception
@@ -193,6 +227,11 @@ public class InfinispanSessionDataStore extends AbstractSessionDataStore
     }
     
     
+    /**
+     * @param id
+     * @param context
+     * @return
+     */
     public static String getCacheKey (String id, SessionContext context)
     {
         return context.getCanonicalContextPath()+"_"+context.getVhost()+"_"+id;
@@ -224,11 +263,17 @@ public class InfinispanSessionDataStore extends AbstractSessionDataStore
     
     
     
+    /**
+     * @param sec the infinispan-specific idle timeout in sec or 0 if not set
+     */
     public void setInfinispanIdleTimeoutSec (int sec)
     {
         _infinispanIdleTimeoutSec = sec;
     }
     
+    /**
+     * @return
+     */
     public int getInfinispanIdleTimeoutSec ()
     {
         return _infinispanIdleTimeoutSec;
diff --git a/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionIdManager.java b/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionIdManager.java
index 22efa61748a..7ba9c75d63d 100644
--- a/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionIdManager.java
+++ b/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionIdManager.java
@@ -21,17 +21,9 @@ package org.eclipse.jetty.session.infinispan;
 import java.util.Random;
 import java.util.concurrent.TimeUnit;
 
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpSession;
-
-import org.eclipse.jetty.server.Handler;
 import org.eclipse.jetty.server.Server;
-
-import org.eclipse.jetty.server.handler.ContextHandler;
 import org.eclipse.jetty.server.session.AbstractSessionIdManager;
 import org.eclipse.jetty.server.session.Session;
-import org.eclipse.jetty.server.session.SessionHandler;
-import org.eclipse.jetty.server.session.SessionManager;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 import org.infinispan.commons.api.BasicCache;
@@ -270,7 +262,7 @@ public class InfinispanSessionIdManager extends AbstractSessionIdManager
     }
 
     /** 
-     * @see org.eclipse.jetty.server.SessionIdManager#useId(java.lang.String)
+     * @see org.eclipse.jetty.server.SessionIdManager#useId(Session)
      */
     @Override
     public void useId(Session session)
diff --git a/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionManager.java b/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionManager.java
index 47900962e9c..25bec8dfab8 100644
--- a/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionManager.java
+++ b/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionManager.java
@@ -51,7 +51,7 @@ public class InfinispanSessionManager extends SessionManager
     /**
      * Start the session manager.
      *
-     * @see org.eclipse.jetty.server.session.AbstractSessionManager#doStart()
+     * @see org.eclipse.jetty.server.session.SessionManager#doStart()
      */
     @Override
     public void doStart() throws Exception
@@ -69,7 +69,7 @@ public class InfinispanSessionManager extends SessionManager
     /**
      * Stop the session manager.
      *
-     * @see org.eclipse.jetty.server.session.AbstractSessionManager#doStop()
+     * @see org.eclipse.jetty.server.session.SessionManager#doStop()
      */
     @Override
     public void doStop() throws Exception
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/SelectorManager.java b/jetty-io/src/main/java/org/eclipse/jetty/io/SelectorManager.java
index 53631ee31c9..b1487b343f2 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/SelectorManager.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/SelectorManager.java
@@ -138,7 +138,7 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
     private ManagedSelector chooseSelector(SelectableChannel channel)
     {
         // Ideally we would like to have all connections from the same client end
-        // up on the same selector (to try to avoid smearing the data from a single 
+        // up on the same selector (to try to avoid smearing the data from a single
         // client over all cores), but because of proxies, the remote address may not
         // really be the client - so we have to hedge our bets to ensure that all
         // channels don't end up on the one selector for a proxy.
@@ -316,6 +316,7 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
                 LOG.warn("Exception while notifying connection " + connection, x);
             else
                 LOG.debug("Exception while notifying connection " + connection, x);
+            throw x;
         }
     }
 
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java
index 3ba5b463862..b8aace75db4 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java
@@ -678,12 +678,12 @@ public class SslConnection extends AbstractConnection
             {
                 // Some internal error in SSLEngine
                 LOG.debug(e);
-                getEndPoint().close();
+                close();
                 throw new EofException(e);
             }
             catch (Exception e)
             {
-                getEndPoint().close();
+                close();
                 throw e;
             }
             finally
diff --git a/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/NoSqlSessionDataStore.java b/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/NoSqlSessionDataStore.java
index acea527b6d4..7bdb6281c17 100644
--- a/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/NoSqlSessionDataStore.java
+++ b/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/NoSqlSessionDataStore.java
@@ -40,15 +40,6 @@ public abstract class NoSqlSessionDataStore extends AbstractSessionDataStore
         private Set _dirtyAttributes = new HashSet();
         
 
-        /**
-         * @param id
-         * @param cpath
-         * @param vhost
-         * @param created
-         * @param accessed
-         * @param lastAccessed
-         * @param maxInactiveMs
-         */
         public NoSqlSessionData(String id, String cpath, String vhost, long created, long accessed, long lastAccessed, long maxInactiveMs)
         {
             super(id, cpath, vhost, created, accessed, lastAccessed, maxInactiveMs);
diff --git a/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionDataStore.java b/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionDataStore.java
index 329c72c5162..f43e4f463a5 100644
--- a/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionDataStore.java
+++ b/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionDataStore.java
@@ -19,6 +19,12 @@
 
 package org.eclipse.jetty.nosql.mongodb;
 
+import com.mongodb.BasicDBObject;
+import com.mongodb.DBCollection;
+import com.mongodb.DBCursor;
+import com.mongodb.DBObject;
+import com.mongodb.WriteConcern;
+
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
@@ -36,13 +42,6 @@ import org.eclipse.jetty.util.ClassLoadingObjectInputStream;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 
-import com.mongodb.BasicDBObject;
-import com.mongodb.DBCollection;
-import com.mongodb.DBCursor;
-import com.mongodb.DBObject;
-import com.mongodb.WriteConcern;
-import com.mongodb.WriteResult;
-
 /**
  * MongoSessionDataStore
  *
@@ -153,43 +152,21 @@ public class MongoSessionDataStore extends NoSqlSessionDataStore
     private DBCollection _dbSessions;
     
     
-    private long _gracePeriodMs = 1000L * 60 * 60; //default grace period is 1hr
-    
     public void setDBCollection (DBCollection collection)
     {
         _dbSessions = collection;
     }
     
     
-    /**
-     * @return
-     */
     public DBCollection getDBCollection ()
     {
         return _dbSessions;
     }
     
-    /**
-     * @return
-     */
-    public int getGracePeriodSec ()
-    {
-        return (int)(_gracePeriodMs == 0L? 0 : _gracePeriodMs/1000L);
-    }
-    
-    /**
-     * @param sec
-     */
-    public void setGracePeriodSec (int sec)
-    {
-        if (sec < 0)
-            _gracePeriodMs = 0;
-        else
-            _gracePeriodMs = sec * 1000L;
-    }
+  
     
     /** 
-     * @see org.eclipse.jetty.server.session.SessionDataStore#load(org.eclipse.jetty.server.session.SessionKey)
+     * @see org.eclipse.jetty.server.session.SessionDataStore#load(String)
      */
     @Override
     public SessionData load(String id) throws Exception
@@ -281,7 +258,7 @@ public class MongoSessionDataStore extends NoSqlSessionDataStore
     }
 
     /** 
-     * @see org.eclipse.jetty.server.session.SessionDataStore#delete(org.eclipse.jetty.server.session.SessionKey)
+     * @see org.eclipse.jetty.server.session.SessionDataStore#delete(String)
      */
     @Override
     public boolean delete(String id) throws Exception
@@ -339,15 +316,17 @@ public class MongoSessionDataStore extends NoSqlSessionDataStore
     }
 
     /** 
-     * @see org.eclipse.jetty.server.session.SessionDataStore#getExpired(java.util.Set)
+     * @see org.eclipse.jetty.server.session.SessionDataStore#getExpired(Set, int)
      */
     @Override
-    public Set getExpired(Set candidates)
+    public Set doGetExpired(Set candidates, int expiryTimeoutSec)
     {
-        long upperBound = System.currentTimeMillis();
+        long now = System.currentTimeMillis();
+        long upperBound = now;
         Set expiredSessions = new HashSet<>();
         
-        //firstly ask mongo to verify if these candidate ids have expired
+        //firstly ask mongo to verify if these candidate ids have expired - all of
+        //these candidates will be for our node
         BasicDBObject query = new BasicDBObject();     
         query.put(__ID,new BasicDBObject("$in", candidates));
         query.put(__EXPIRY, new BasicDBObject("$gt", 0));
@@ -369,9 +348,13 @@ public class MongoSessionDataStore extends NoSqlSessionDataStore
             if (verifiedExpiredSessions != null) verifiedExpiredSessions.close();
         }
 
-
-        //now ask mongo to find sessions that expired a while ago    
-        upperBound = upperBound - (3 * _gracePeriodMs);
+        //now ask mongo to find sessions last managed by other nodes that expired a while ago 
+        //if this is our first expiry check, make sure that we only grab really old sessions
+        if (_lastExpiryCheckTime <= 0)
+            upperBound = (now - (3*(1000L * _gracePeriodSec)));
+        else
+            upperBound =  _lastExpiryCheckTime - (1000L * _gracePeriodSec);
+        
         query.clear();
         query.put(__EXPIRY, new BasicDBObject("$gt", 0));
         query.put(__EXPIRY, new BasicDBObject("$lt", upperBound));
@@ -397,7 +380,7 @@ public class MongoSessionDataStore extends NoSqlSessionDataStore
     }
 
     /** 
-     * @see org.eclipse.jetty.server.session.AbstractSessionDataStore#doStore(org.eclipse.jetty.server.session.SessionKey, org.eclipse.jetty.server.session.SessionData, long)
+     * @see org.eclipse.jetty.server.session.AbstractSessionDataStore#doStore(String, SessionData, long) 
      */
     @Override
     public void doStore(String id, SessionData data, long lastSaveTime) throws Exception
diff --git a/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionManager.java b/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionManager.java
index 4e813b1d007..5fc15d0ff03 100644
--- a/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionManager.java
+++ b/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionManager.java
@@ -18,21 +18,20 @@
 
 package org.eclipse.jetty.nosql.mongodb;
 
+import com.mongodb.DBCollection;
+import com.mongodb.MongoException;
+
 import java.net.UnknownHostException;
 
 import org.eclipse.jetty.server.SessionIdManager;
 import org.eclipse.jetty.server.session.AbstractSessionStore;
 import org.eclipse.jetty.server.session.MemorySessionStore;
-import org.eclipse.jetty.server.session.SessionDataStore;
 import org.eclipse.jetty.server.session.SessionManager;
 import org.eclipse.jetty.util.annotation.ManagedAttribute;
 import org.eclipse.jetty.util.annotation.ManagedObject;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 
-import com.mongodb.DBCollection;
-import com.mongodb.MongoException;
-
 
 /**
  * MongoSessionManager
@@ -116,9 +115,6 @@ public class MongoSessionManager extends SessionManager
     }
 
     /* ------------------------------------------------------------ */
-    /**
-     * @see org.eclipse.jetty.server.session.AbstractSessionManager#setSessionIdManager(org.eclipse.jetty.server.SessionIdManager)
-     */
     @Override
     public void setSessionIdManager(SessionIdManager metaManager)
     {
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractNCSARequestLog.java b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractNCSARequestLog.java
index 0df61d8f739..9b0f8672e71 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractNCSARequestLog.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractNCSARequestLog.java
@@ -222,6 +222,9 @@ public abstract class AbstractNCSARequestLog extends AbstractLifeCycle implement
     }
 
     /**
+     * @param request request object
+     * @param b StringBuilder to write to
+     * @throws IOException if unable to append extended log
      * @deprecated override {@link #logExtended(StringBuilder, Request, Response)} instead
      */
     @Deprecated
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/PushBuilder.java b/jetty-server/src/main/java/org/eclipse/jetty/server/PushBuilder.java
index 6300a09f2db..8771f3b29e5 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/PushBuilder.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/PushBuilder.java
@@ -179,7 +179,7 @@ public interface PushBuilder
      * Set the URI path to be used for the push.  The path may start
      * with "/" in which case it is treated as an absolute path,
      * otherwise it is relative to the context path of the associated
-     * request.  There is no path default and {@link #path(String)} must
+     * request.  There is no path default and path(String) must
      * be called before every call to {@link #push()}.  If a query
      * string is present in the argument {@code path}, its contents must
      * be merged with the contents previously passed to {@link
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java
index 55ec1899cc9..e8eb91c6f6c 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java
@@ -2036,7 +2036,7 @@ public class Request implements HttpServletRequest
 
     /* ------------------------------------------------------------ */
     /**
-     * @return True if this is the first call of {@link #takeNewContext()} since the last
+     * @return True if this is the first call of takeNewContext() since the last
      *         {@link #setContext(org.eclipse.jetty.server.handler.ContextHandler.Context)} call.
      */
     public boolean takeNewContext()
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/SecureRequestCustomizer.java b/jetty-server/src/main/java/org/eclipse/jetty/server/SecureRequestCustomizer.java
index cc7eb05f58a..16b618552f2 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/SecureRequestCustomizer.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/SecureRequestCustomizer.java
@@ -56,6 +56,8 @@ public class SecureRequestCustomizer implements HttpConfiguration.Customizer
      */
     public static final String CACHED_INFO_ATTR = CachedInfo.class.getName();
 
+    private String sslSessionAttribute = "org.eclipse.jetty.servlet.request.ssl_session";
+
     private boolean _sniHostCheck;
     private long _stsMaxAge=-1;
     private boolean _stsIncludeSubDomains;
@@ -270,12 +272,23 @@ public class SecureRequestCustomizer implements HttpConfiguration.Customizer
             request.setAttribute("javax.servlet.request.cipher_suite",cipherSuite);
             request.setAttribute("javax.servlet.request.key_size",keySize);
             request.setAttribute("javax.servlet.request.ssl_session_id", idStr);
+            request.setAttribute(getSslSessionAttribute(), sslSession);
         }
         catch (Exception e)
         {
             LOG.warn(Log.EXCEPTION,e);
         }
     }
+    
+    public void setSslSessionAttribute(String attribute)
+    {
+        this.sslSessionAttribute = attribute;
+    }
+
+    public String getSslSessionAttribute()
+    {
+        return sslSessionAttribute;
+    }
 
     @Override
     public String toString()
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/SessionManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/SessionManager.java
index 1dd7bbc6853..071ed64340a 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/SessionManager.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/SessionManager.java
@@ -32,15 +32,9 @@ import org.eclipse.jetty.server.session.SessionHandler;
 import org.eclipse.jetty.server.session.SessionStore;
 import org.eclipse.jetty.util.component.LifeCycle;
 
-/* --------------------------------------------------------------------- */
 /**
  * Session Manager.
  * The API required to manage sessions for a servlet context.
- *
- */
-
-/* ------------------------------------------------------------ */
-/**
  */
 public interface SessionManager extends LifeCycle
 {
@@ -313,7 +307,7 @@ public interface SessionManager extends LifeCycle
     
     /**
      * Get the session store for this manager
-     * @return
+     * @return the session store
      */
     public SessionStore getSessionStore();
     
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ShutdownMonitor.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ShutdownMonitor.java
index 7fbd20a4e01..f6f3f1c526b 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/ShutdownMonitor.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ShutdownMonitor.java
@@ -27,10 +27,12 @@ import java.net.InetSocketAddress;
 import java.net.ServerSocket;
 import java.net.Socket;
 import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Properties;
+import java.util.LinkedHashSet;
+import java.util.List;
 import java.util.Set;
-import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.function.Predicate;
 
 import org.eclipse.jetty.util.component.Destroyable;
 import org.eclipse.jetty.util.component.LifeCycle;
@@ -39,19 +41,20 @@ import org.eclipse.jetty.util.thread.ShutdownThread;
 /**
  * Shutdown/Stop Monitor thread.
  * 

- * This thread listens on the host/port specified by the STOP.HOST/STOP.PORT system parameter (defaults to 127.0.0.1/-1 for not listening) for - * request authenticated with the key given by the STOP.KEY system parameter (defaults to "eclipse") for admin requests. + * This thread listens on the host/port specified by the STOP.HOST/STOP.PORT + * system parameter (defaults to 127.0.0.1/-1 for not listening) for request + * authenticated with the key given by the STOP.KEY system parameter + * (defaults to "eclipse") for admin requests. *

- * If the stop port is set to zero, then a random port is assigned and the port number is printed to stdout. + * If the stop port is set to zero, then a random port is assigned and the + * port number is printed to stdout. *

* Commands "stop" and "status" are currently supported. */ -public class ShutdownMonitor +public class ShutdownMonitor { - private final Set _lifeCycles = new CopyOnWriteArraySet(); - // Implementation of safe lazy init, using Initialization on Demand Holder technique. - static class Holder + private static class Holder { static ShutdownMonitor instance = new ShutdownMonitor(); } @@ -60,283 +63,32 @@ public class ShutdownMonitor { return Holder.instance; } - - /* ------------------------------------------------------------ */ - public static synchronized void register(LifeCycle... lifeCycles) + + public static void register(LifeCycle... lifeCycles) { - getInstance()._lifeCycles.addAll(Arrays.asList(lifeCycles)); + getInstance().addLifeCycles(lifeCycles); } - - /* ------------------------------------------------------------ */ - public static synchronized void deregister(LifeCycle lifeCycle) + public static void deregister(LifeCycle lifeCycle) { - getInstance()._lifeCycles.remove(lifeCycle); + getInstance().removeLifeCycle(lifeCycle); } - - /* ------------------------------------------------------------ */ - public static synchronized boolean isRegistered(LifeCycle lifeCycle) + + public static boolean isRegistered(LifeCycle lifeCycle) { - return getInstance()._lifeCycles.contains(lifeCycle); + return getInstance().containsLifeCycle(lifeCycle); } - - /* ------------------------------------------------------------ */ - /** - * ShutdownMonitorRunnable - * - * Thread for listening to STOP.PORT for command to stop Jetty. - * If ShowndownMonitor.exitVm is true, then Sytem.exit will also be - * called after the stop. - * - */ - private class ShutdownMonitorRunnable implements Runnable - { - public ShutdownMonitorRunnable() - { - startListenSocket(); - } - - @Override - public void run() - { - if (serverSocket == null) - { - return; - } - while (serverSocket != null) - { - Socket socket = null; - try - { - socket = serverSocket.accept(); - - LineNumberReader lin = new LineNumberReader(new InputStreamReader(socket.getInputStream())); - String receivedKey = lin.readLine(); - if (!key.equals(receivedKey)) - { - System.err.println("Ignoring command with incorrect key"); - continue; - } - - OutputStream out = socket.getOutputStream(); - - String cmd = lin.readLine(); - debug("command=%s",cmd); - if ("stop".equalsIgnoreCase(cmd)) //historic, for backward compatibility - { - //Stop the lifecycles, only if they are registered with the ShutdownThread, only destroying if vm is exiting - debug("Issuing stop..."); - - for (LifeCycle l:_lifeCycles) - { - try - { - if (l.isStarted() && ShutdownThread.isRegistered(l)) - { - l.stop(); - } - - if ((l instanceof Destroyable) && exitVm) - ((Destroyable)l).destroy(); - } - catch (Exception e) - { - debug(e); - } - } - - //Stop accepting any more commands - stopInput(socket); - - // Reply to client - debug("Informing client that we are stopped."); - informClient(out, "Stopped\r\n"); - - //Stop the output and close the monitor socket - stopOutput(socket); - - if (exitVm) - { - // Kill JVM - debug("Killing JVM"); - System.exit(0); - } - } - else if ("forcestop".equalsIgnoreCase(cmd)) - { - debug("Issuing force stop..."); - - //Ensure that objects are stopped, destroyed only if vm is forcibly exiting - stopLifeCycles(exitVm); - - //Stop accepting any more commands - stopInput(socket); - - // Reply to client - debug("Informing client that we are stopped."); - informClient(out, "Stopped\r\n"); - - //Stop the output and close the monitor socket - stopOutput(socket); - - //Honour any pre-setup config to stop the jvm when this command is given - if (exitVm) - { - // Kill JVM - debug("Killing JVM"); - System.exit(0); - } - } - else if ("stopexit".equalsIgnoreCase(cmd)) - { - debug("Issuing stop and exit..."); - //Make sure that objects registered with the shutdown thread will be stopped - stopLifeCycles(true); - - //Stop accepting any more input - stopInput(socket); - - // Reply to client - debug("Informing client that we are stopped."); - informClient(out, "Stopped\r\n"); - - //Stop the output and close the monitor socket - stopOutput(socket); - - debug("Killing JVM"); - System.exit(0); - } - else if ("exit".equalsIgnoreCase(cmd)) - { - debug("Killing JVM"); - System.exit(0); - } - else if ("status".equalsIgnoreCase(cmd)) - { - // Reply to client - informClient(out, "OK\r\n"); - } - } - catch (Exception e) - { - debug(e); - System.err.println(e.toString()); - } - finally - { - close(socket); - socket = null; - } - } - } - - public void stopInput (Socket socket) - { - //Stop accepting any more input - close(serverSocket); - serverSocket = null; - //Shutdown input from client - shutdownInput(socket); - } - - public void stopOutput (Socket socket) throws IOException - { - socket.shutdownOutput(); - close(socket); - socket = null; - debug("Shutting down monitor"); - serverSocket = null; - } - - public void informClient (OutputStream out, String message) throws IOException - { - out.write(message.getBytes(StandardCharsets.UTF_8)); - out.flush(); - } - - /** - * Stop the registered lifecycles, optionally - * calling destroy on them. - * - * @param destroy true if {@link Destroyable}'s should also be destroyed. - */ - public void stopLifeCycles (boolean destroy) - { - for (LifeCycle l:_lifeCycles) - { - try - { - if (l.isStarted()) - { - l.stop(); - } - - if ((l instanceof Destroyable) && destroy) - ((Destroyable)l).destroy(); - } - catch (Exception e) - { - debug(e); - } - } - } - - public void startListenSocket() - { - if (port < 0) - { - if (DEBUG) - System.err.println("ShutdownMonitor not in use (port < 0): " + port); - return; - } - - try - { - serverSocket = new ServerSocket(); - serverSocket.setReuseAddress(true); - serverSocket.bind(new InetSocketAddress(InetAddress.getByName(host), port), 1); - if (port == 0) - { - // server assigned port in use - port = serverSocket.getLocalPort(); - System.out.printf("STOP.PORT=%d%n",port); - } - - if (key == null) - { - // create random key - key = Long.toString((long)(Long.MAX_VALUE * Math.random() + this.hashCode() + System.currentTimeMillis()),36); - System.out.printf("STOP.KEY=%s%n",key); - } - } - catch (Exception e) - { - debug(e); - System.err.println("Error binding monitor port " + port + ": " + e.toString()); - serverSocket = null; - } - finally - { - // establish the port and key that are in use - debug("STOP.PORT=%d",port); - debug("STOP.KEY=%s",key); - debug("%s",serverSocket); - } - } - - } - - private boolean DEBUG; - private String host; + private final Set _lifeCycles = new LinkedHashSet<>(); + private boolean debug; + private final String host; private int port; private String key; private boolean exitVm; - private ServerSocket serverSocket; - private Thread thread; + private boolean alive; /** - * Create a ShutdownMonitor using configuration from the System properties. + * Creates a ShutdownMonitor using configuration from the System properties. *

* STOP.PORT = the port to listen on (empty, null, or values less than 0 disable the stop ability)
* STOP.KEY = the magic key/passphrase to allow the stop (defaults to "eclipse")
@@ -345,106 +97,76 @@ public class ShutdownMonitor */ private ShutdownMonitor() { - this.DEBUG = System.getProperty("DEBUG") != null; - - // Use values passed thru via /jetty-start/ - this.host = System.getProperty("STOP.HOST","127.0.0.1"); - this.port = Integer.parseInt(System.getProperty("STOP.PORT","-1")); - this.key = System.getProperty("STOP.KEY",null); + this.debug = System.getProperty("DEBUG") != null; + this.host = System.getProperty("STOP.HOST", "127.0.0.1"); + this.port = Integer.parseInt(System.getProperty("STOP.PORT", "-1")); + this.key = System.getProperty("STOP.KEY", null); this.exitVm = true; } - private void close(ServerSocket server) + private void addLifeCycles(LifeCycle... lifeCycles) { - if (server == null) + synchronized (this) { - return; - } - - try - { - server.close(); - } - catch (IOException ignore) - { - debug(ignore); + _lifeCycles.addAll(Arrays.asList(lifeCycles)); } } - private void close(Socket socket) + private void removeLifeCycle(LifeCycle lifeCycle) { - if (socket == null) + synchronized (this) { - return; - } - - try - { - socket.close(); - } - catch (IOException ignore) - { - debug(ignore); + _lifeCycles.remove(lifeCycle); } } - - private void shutdownInput(Socket socket) + private boolean containsLifeCycle(LifeCycle lifeCycle) { - if (socket == null) - return; - - try + synchronized (this) { - socket.shutdownInput(); - } - catch (IOException ignore) - { - debug(ignore); + return _lifeCycles.contains(lifeCycle); } } - - + private void debug(String format, Object... args) { - if (DEBUG) - { - System.err.printf("[ShutdownMonitor] " + format + "%n",args); - } + if (debug) + System.err.printf("[ShutdownMonitor] " + format + "%n", args); } private void debug(Throwable t) { - if (DEBUG) - { + if (debug) t.printStackTrace(System.err); - } } public String getKey() { - return key; + synchronized (this) + { + return key; + } } public int getPort() { - return port; - } - - public ServerSocket getServerSocket() - { - return serverSocket; + synchronized (this) + { + return port; + } } public boolean isExitVm() { - return exitVm; + synchronized (this) + { + return exitVm; + } } - public void setDebug(boolean flag) { - this.DEBUG = flag; + this.debug = flag; } /** @@ -454,10 +176,8 @@ public class ShutdownMonitor { synchronized (this) { - if (thread != null && thread.isAlive()) - { - throw new IllegalStateException("ShutdownMonitorThread already started"); - } + if (alive) + throw new IllegalStateException("ShutdownMonitor already started"); this.exitVm = exitVm; } } @@ -466,10 +186,8 @@ public class ShutdownMonitor { synchronized (this) { - if (thread != null && thread.isAlive()) - { - throw new IllegalStateException("ShutdownMonitorThread already started"); - } + if (alive) + throw new IllegalStateException("ShutdownMonitor already started"); this.key = key; } } @@ -478,52 +196,254 @@ public class ShutdownMonitor { synchronized (this) { - if (thread != null && thread.isAlive()) - { - throw new IllegalStateException("ShutdownMonitorThread already started"); - } + if (alive) + throw new IllegalStateException("ShutdownMonitor already started"); this.port = port; } } protected void start() throws Exception { - Thread t = null; - synchronized (this) { - if (thread != null && thread.isAlive()) + if (alive) { - if (DEBUG) - System.err.printf("ShutdownMonitorThread already started"); + debug("Already started"); return; // cannot start it again } - - thread = new Thread(new ShutdownMonitorRunnable()); - thread.setDaemon(true); - thread.setName("ShutdownMonitor"); - t = thread; + ServerSocket serverSocket = listen(); + if (serverSocket != null) + { + alive = true; + Thread thread = new Thread(new ShutdownMonitorRunnable(serverSocket)); + thread.setDaemon(true); + thread.setName("ShutdownMonitor"); + thread.start(); + } } - - if (t != null) - t.start(); } - - protected boolean isAlive () + private void stop() { - boolean result = false; synchronized (this) { - result = (thread != null && thread.isAlive()); + alive = false; + notifyAll(); } - return result; } - - + + // For test purposes only. + void await() throws InterruptedException + { + synchronized (this) + { + while (alive) + { + wait(); + } + } + } + + protected boolean isAlive() + { + synchronized (this) + { + return alive; + } + } + + + private ServerSocket listen() + { + int port = getPort(); + if (port < 0) + { + debug("Not enabled (port < 0): %d", port); + return null; + } + + String key = getKey(); + try + { + ServerSocket serverSocket = new ServerSocket(); + serverSocket.setReuseAddress(true); + serverSocket.bind(new InetSocketAddress(InetAddress.getByName(host), port)); + if (port == 0) + { + port = serverSocket.getLocalPort(); + System.out.printf("STOP.PORT=%d%n", port); + setPort(port); + } + + if (key == null) + { + key = Long.toString((long)(Long.MAX_VALUE * Math.random() + this.hashCode() + System.currentTimeMillis()), 36); + System.out.printf("STOP.KEY=%s%n", key); + setKey(key); + } + + return serverSocket; + } + catch (Throwable x) + { + debug(x); + System.err.println("Error binding ShutdownMonitor to port " + port + ": " + x.toString()); + return null; + } + finally + { + // establish the port and key that are in use + debug("STOP.PORT=%d", port); + debug("STOP.KEY=%s", key); + } + } + @Override public String toString() { - return String.format("%s[port=%d]",this.getClass().getName(),port); + return String.format("%s[port=%d,alive=%b]", this.getClass().getName(), getPort(), isAlive()); + } + + /** + * Thread for listening to STOP.PORT for command to stop Jetty. + * If ShutdownMonitor.exitVm is true, then System.exit will also be + * called after the stop. + */ + private class ShutdownMonitorRunnable implements Runnable + { + private final ServerSocket serverSocket; + + private ShutdownMonitorRunnable(ServerSocket serverSocket) + { + this.serverSocket = serverSocket; + } + + @Override + public void run() + { + debug("Started"); + try + { + String key = getKey(); + while (true) + { + try (Socket socket = serverSocket.accept()) + { + LineNumberReader reader = new LineNumberReader(new InputStreamReader(socket.getInputStream())); + String receivedKey = reader.readLine(); + if (!key.equals(receivedKey)) + { + debug("Ignoring command with incorrect key: %s", receivedKey); + continue; + } + + String cmd = reader.readLine(); + debug("command=%s", cmd); + OutputStream out = socket.getOutputStream(); + boolean exitVm = isExitVm(); + + if ("stop".equalsIgnoreCase(cmd)) //historic, for backward compatibility + { + //Stop the lifecycles, only if they are registered with the ShutdownThread, only destroying if vm is exiting + debug("Performing stop command"); + stopLifeCycles(ShutdownThread::isRegistered, exitVm); + + // Reply to client + debug("Informing client that we are stopped"); + informClient(out, "Stopped\r\n"); + + if (!exitVm) + break; + + // Kill JVM + debug("Killing JVM"); + System.exit(0); + } + else if ("forcestop".equalsIgnoreCase(cmd)) + { + debug("Performing forced stop command"); + stopLifeCycles(l -> true, exitVm); + + // Reply to client + debug("Informing client that we are stopped"); + informClient(out, "Stopped\r\n"); + + if (!exitVm) + break; + + // Kill JVM + debug("Killing JVM"); + System.exit(0); + } + else if ("stopexit".equalsIgnoreCase(cmd)) + { + debug("Performing stop and exit commands"); + stopLifeCycles(ShutdownThread::isRegistered, true); + + // Reply to client + debug("Informing client that we are stopped"); + informClient(out, "Stopped\r\n"); + + debug("Killing JVM"); + System.exit(0); + } + else if ("exit".equalsIgnoreCase(cmd)) + { + debug("Killing JVM"); + System.exit(0); + } + else if ("status".equalsIgnoreCase(cmd)) + { + // Reply to client + informClient(out, "OK\r\n"); + } + } + catch (Throwable x) + { + debug(x); + } + } + } + catch (Throwable x) + { + debug(x); + } + finally + { + stop(); + debug("Stopped"); + } + } + + private void informClient(OutputStream out, String message) throws IOException + { + out.write(message.getBytes(StandardCharsets.UTF_8)); + out.flush(); + } + + private void stopLifeCycles(Predicate predicate, boolean destroy) + { + List lifeCycles = new ArrayList<>(); + synchronized (this) + { + lifeCycles.addAll(_lifeCycles); + } + + for (LifeCycle l : lifeCycles) + { + try + { + if (l.isStarted() && predicate.test(l)) + l.stop(); + + if ((l instanceof Destroyable) && destroy) + ((Destroyable)l).destroy(); + } + catch (Throwable x) + { + debug(x); + } + } + } } } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java index 08115723811..85532a004ac 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java @@ -754,10 +754,10 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu if (_mimeTypes == null) _mimeTypes = new MimeTypes(); - + try { - // Set the classloader + // Set the classloader, context and enter scope if (_classLoader != null) { current_thread = Thread.currentThread(); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ErrorHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ErrorHandler.java index 9d64b769c26..018c5f201db 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ErrorHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ErrorHandler.java @@ -49,7 +49,7 @@ import org.eclipse.jetty.util.log.Logger; /* ------------------------------------------------------------ */ /** Handler for Error pages * An ErrorHandler is registered with {@link ContextHandler#setErrorHandler(ErrorHandler)} or - * {@link Server#setErrorHandler(ErrorHandler). + * {@link Server#setErrorHandler(ErrorHandler)}. * It is called by the HttpResponse.sendError method to write a error page via {@link #handle(String, Request, HttpServletRequest, HttpServletResponse)} * or via {@link #badMessageError(int, String, HttpFields)} for bad requests for which a dispatch cannot be done. * diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHandler.java index ec1d4774534..f09c5c54047 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHandler.java @@ -18,8 +18,6 @@ package org.eclipse.jetty.server.handler.gzip; -import static org.eclipse.jetty.http.GzipHttpContent.ETAG_GZIP_QUOTE; - import java.io.File; import java.io.IOException; import java.util.Set; @@ -47,7 +45,6 @@ import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; - /** * A Handler that can dynamically GZIP compress responses. Unlike * previous and 3rd party GzipFilters, this mechanism works with asynchronously @@ -71,7 +68,7 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory private boolean _syncFlush = false; // non-static, as other GzipHandler instances may have different configurations - private final ThreadLocal _deflater = new ThreadLocal(); + private final ThreadLocal _deflater = new ThreadLocal<>(); private final IncludeExclude _agentPatterns=new IncludeExclude<>(RegexSet.class); private final IncludeExclude _methods = new IncludeExclude<>(); @@ -398,6 +395,11 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory return _minGzipSize; } + protected HttpField getVaryField() + { + return _vary; + } + /* ------------------------------------------------------------ */ /** * @see org.eclipse.jetty.server.handler.HandlerWrapper#handle(java.lang.String, org.eclipse.jetty.server.Request, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) @@ -474,20 +476,21 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory String etag = baseRequest.getHttpFields().get(HttpHeader.IF_NONE_MATCH); if (etag!=null) { - int i=etag.indexOf(ETAG_GZIP_QUOTE); + int i=etag.indexOf(GzipHttpContent.ETAG_GZIP_QUOTE); if (i>0) { while (i>=0) { etag=etag.substring(0,i)+etag.substring(i+GzipHttpContent.ETAG_GZIP.length()); - i=etag.indexOf(ETAG_GZIP_QUOTE,i); + i=etag.indexOf(GzipHttpContent.ETAG_GZIP_QUOTE,i); } baseRequest.getHttpFields().put(new HttpField(HttpHeader.IF_NONE_MATCH,etag)); } } // install interceptor and handle - out.setInterceptor(new GzipHttpOutputInterceptor(this,_vary,baseRequest.getHttpChannel(),out.getInterceptor(),_syncFlush)); + out.setInterceptor(new GzipHttpOutputInterceptor(this,getVaryField(),baseRequest.getHttpChannel(),out.getInterceptor(),isSyncFlush())); + if (_handler!=null) _handler.handle(target,baseRequest, request, response); } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHttpOutputInterceptor.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHttpOutputInterceptor.java index f717c2656af..17a1e25fb46 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHttpOutputInterceptor.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHttpOutputInterceptor.java @@ -32,6 +32,7 @@ import org.eclipse.jetty.http.MimeTypes; import org.eclipse.jetty.http.PreEncodedHttpField; import org.eclipse.jetty.server.HttpChannel; import org.eclipse.jetty.server.HttpOutput; +import org.eclipse.jetty.server.Response; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.IteratingNestedCallback; @@ -46,7 +47,7 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor public final static HttpField VARY_ACCEPT_ENCODING_USER_AGENT=new PreEncodedHttpField(HttpHeader.VARY,HttpHeader.ACCEPT_ENCODING+", "+HttpHeader.USER_AGENT); public final static HttpField VARY_ACCEPT_ENCODING=new PreEncodedHttpField(HttpHeader.VARY,HttpHeader.ACCEPT_ENCODING.asString()); - + private enum GZState { MIGHT_COMPRESS, NOT_COMPRESSING, COMMITTING, COMPRESSING, FINISHED}; private final AtomicReference _state = new AtomicReference<>(GZState.MIGHT_COMPRESS); private final CRC32 _crc = new CRC32(); @@ -57,7 +58,7 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor private final HttpField _vary; private final int _bufferSize; private final boolean _syncFlush; - + private Deflater _deflater; private ByteBuffer _buffer; @@ -65,12 +66,12 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor { this(factory,VARY_ACCEPT_ENCODING_USER_AGENT,channel.getHttpConfiguration().getOutputBufferSize(),channel,next,syncFlush); } - + public GzipHttpOutputInterceptor(GzipFactory factory, HttpField vary, HttpChannel channel, HttpOutput.Interceptor next,boolean syncFlush) { this(factory,vary,channel.getHttpConfiguration().getOutputBufferSize(),channel,next,syncFlush); } - + public GzipHttpOutputInterceptor(GzipFactory factory, HttpField vary, int bufferSize, HttpChannel channel, HttpOutput.Interceptor next,boolean syncFlush) { _factory=factory; @@ -85,14 +86,14 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor { return _interceptor; } - + @Override public boolean isOptimizedForDirectBuffers() { return false; // No point as deflator is in user space. } - - + + @Override public void write(ByteBuffer content, boolean complete, Callback callback) { @@ -101,11 +102,11 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor case MIGHT_COMPRESS: commit(content,complete,callback); break; - + case NOT_COMPRESSING: _interceptor.write(content, complete, callback); return; - + case COMMITTING: callback.failed(new WritePendingException()); break; @@ -124,21 +125,21 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor { int i=_buffer.limit(); _buffer.limit(i+8); - + int v=(int)_crc.getValue(); _buffer.put(i++,(byte)(v & 0xFF)); _buffer.put(i++,(byte)((v>>>8) & 0xFF)); _buffer.put(i++,(byte)((v>>>16) & 0xFF)); _buffer.put(i++,(byte)((v>>>24) & 0xFF)); - + v=_deflater.getTotalIn(); _buffer.put(i++,(byte)(v & 0xFF)); _buffer.put(i++,(byte)((v>>>8) & 0xFF)); _buffer.put(i++,(byte)((v>>>16) & 0xFF)); _buffer.put(i++,(byte)((v>>>24) & 0xFF)); } - - + + private void gzip(ByteBuffer content, boolean complete, final Callback callback) { if (content.hasRemaining() || complete) @@ -150,7 +151,8 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor protected void commit(ByteBuffer content, boolean complete, Callback callback) { // Are we excluding because of status? - int sc = _channel.getResponse().getStatus(); + Response response = _channel.getResponse(); + int sc = response.getStatus(); if (sc>0 && (sc<200 || sc==204 || sc==205 || sc>=300)) { LOG.debug("{} exclude by status {}",this,sc); @@ -158,9 +160,9 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor _interceptor.write(content, complete, callback); return; } - + // Are we excluding because of mime-type? - String ct = _channel.getResponse().getContentType(); + String ct = response.getContentType(); if (ct!=null) { ct=MimeTypes.getContentTypeWithoutCharset(ct); @@ -172,9 +174,9 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor return; } } - + // Has the Content-Encoding header already been set? - String ce=_channel.getResponse().getHeader("Content-Encoding"); + String ce=response.getHeader("Content-Encoding"); if (ce != null) { LOG.debug("{} exclude by content-encoding {}",this,ce); @@ -182,20 +184,21 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor _interceptor.write(content, complete, callback); return; } - + // Are we the thread that commits? if (_state.compareAndSet(GZState.MIGHT_COMPRESS,GZState.COMMITTING)) { // We are varying the response due to accept encoding header. - HttpFields fields = _channel.getResponse().getHttpFields(); - fields.add(_vary); + HttpFields fields = response.getHttpFields(); + if (_vary != null) + fields.add(_vary); - long content_length = _channel.getResponse().getContentLength(); + long content_length = response.getContentLength(); if (content_length<0 && complete) content_length=content.remaining(); - + _deflater = _factory.getDeflater(_channel.getRequest(),content_length); - + if (_deflater==null) { LOG.debug("{} exclude no deflater",this); @@ -210,7 +213,7 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor BufferUtil.fill(_buffer,GZIP_HEADER,0,GZIP_HEADER.length); // Adjust headers - _channel.getResponse().setContentLength(-1); + response.setContentLength(-1); String etag=fields.get(HttpHeader.ETAG); if (etag!=null) { @@ -218,10 +221,10 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor etag=(etag.charAt(end)=='"')?etag.substring(0,end)+GzipHttpContent.ETAG_GZIP+'"':etag+GzipHttpContent.ETAG_GZIP; fields.put(HttpHeader.ETAG,etag); } - + LOG.debug("{} compressing {}",this,_deflater); _state.set(GZState.COMPRESSING); - + gzip(content,complete,callback); } else @@ -268,14 +271,14 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor } } } - + public boolean mightCompress() { return _state.get()==GZState.MIGHT_COMPRESS; } - + private class GzipBufferCB extends IteratingNestedCallback - { + { private ByteBuffer _copy; private final ByteBuffer _content; private final boolean _last; @@ -291,11 +294,11 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor { if (_deflater==null) return Action.SUCCEEDED; - + if (_deflater.needsInput()) - { + { if (BufferUtil.isEmpty(_content)) - { + { if (_deflater.finished()) { _factory.recycle(_deflater); @@ -309,12 +312,12 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor } return Action.SUCCEEDED; } - + if (!_last) { return Action.SUCCEEDED; } - + _deflater.finish(); } else if (_content.hasArray()) @@ -323,9 +326,9 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor int off=_content.arrayOffset()+_content.position(); int len=_content.remaining(); BufferUtil.clear(_content); - + _crc.update(array,off,len); - _deflater.setInput(array,off,len); + _deflater.setInput(array,off,len); if (_last) _deflater.finish(); } @@ -338,13 +341,13 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor BufferUtil.flipToFlush(_copy,0); if (took==0) throw new IllegalStateException(); - + byte[] array=_copy.array(); int off=_copy.arrayOffset()+_copy.position(); int len=_copy.remaining(); _crc.update(array,off,len); - _deflater.setInput(array,off,len); + _deflater.setInput(array,off,len); if (_last && BufferUtil.isEmpty(_content)) _deflater.finish(); } @@ -359,10 +362,10 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor _buffer.limit(_buffer.limit()+produced); } boolean finished=_deflater.finished(); - + if (finished) addTrailer(); - + _interceptor.write(_buffer,finished,this); return Action.SCHEDULED; } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionDataStore.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionDataStore.java index ec9e90f3a59..afa542f0fed 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionDataStore.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionDataStore.java @@ -20,6 +20,8 @@ package org.eclipse.jetty.server.session; +import java.util.Set; + import org.eclipse.jetty.util.component.AbstractLifeCycle; /** @@ -30,6 +32,8 @@ import org.eclipse.jetty.util.component.AbstractLifeCycle; public abstract class AbstractSessionDataStore extends AbstractLifeCycle implements SessionDataStore { protected SessionContext _context; //context associated with this session data store + protected int _gracePeriodSec = 60 * 60; //default of 1hr + protected long _lastExpiryCheckTime = 0; //last time in ms that getExpired was called /** @@ -38,11 +42,20 @@ public abstract class AbstractSessionDataStore extends AbstractLifeCycle impleme * @param id identity of session to store * @param data info of the session * @param lastSaveTime time of previous save or 0 if never saved - * @throws Exception + * @throws Exception if unable to store data */ public abstract void doStore(String id, SessionData data, long lastSaveTime) throws Exception; + /** + * Implemented by subclasses to resolve which sessions this node + * should attempt to expire. + * + * @param candidates the ids of sessions the SessionStore thinks has expired + * @param scavengePeriodSec the period in sec of the scavenge cycle checks + * @return the reconciled set of session ids that this node should attempt to expire + */ + public abstract Set doGetExpired (Set candidates, int scavengePeriodSec); /** @@ -81,6 +94,25 @@ public abstract class AbstractSessionDataStore extends AbstractLifeCycle impleme + /** + * @see org.eclipse.jetty.server.session.SessionDataStore#getExpired(java.util.Set, int) + */ + @Override + public Set getExpired(Set candidates, int scavengePeriodSec) + { + try + { + return doGetExpired (candidates, scavengePeriodSec); + } + finally + { + _lastExpiryCheckTime = System.currentTimeMillis(); + } + } + + + + /** * @see org.eclipse.jetty.server.session.SessionDataStore#newSessionData(java.lang.String, long, long, long, long) */ @@ -90,18 +122,12 @@ public abstract class AbstractSessionDataStore extends AbstractLifeCycle impleme return new SessionData(id, _context.getCanonicalContextPath(), _context.getVhost(), created, accessed, lastAccessed, maxInactiveMs); } - /** - * @throws IllegalStateException - */ protected void checkStarted () throws IllegalStateException { if (isStarted()) throw new IllegalStateException("Already started"); } - - - @Override protected void doStart() throws Exception { @@ -110,7 +136,14 @@ public abstract class AbstractSessionDataStore extends AbstractLifeCycle impleme super.doStart(); } - - - + + public int getGracePeriodSec() + { + return _gracePeriodSec; + } + + public void setGracePeriodSec(int sec) + { + _gracePeriodSec = sec; + } } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionIdManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionIdManager.java index 39b477cb1a4..5710059861f 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionIdManager.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionIdManager.java @@ -100,7 +100,7 @@ public abstract class AbstractSessionIdManager extends AbstractLifeCycle impleme /* ------------------------------------------------------------ */ /** - * @param period inspector of sessions + * @param inspector inspector of sessions */ public void setSessionInspector (PeriodicSessionInspector inspector) { @@ -219,7 +219,7 @@ public abstract class AbstractSessionIdManager extends AbstractLifeCycle impleme /* ------------------------------------------------------------ */ /** - * @param seedTerm + * @param seedTerm the seed for RNG * @return a new unique session id */ public String newSessionId(long seedTerm) @@ -399,9 +399,6 @@ public abstract class AbstractSessionIdManager extends AbstractLifeCycle impleme } /* ------------------------------------------------------------ */ - /** - * @param id - */ public void invalidateAll (String id) { //take the id out of the list of known sessionids for this node diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionInspector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionInspector.java index 45aceaef47c..b9163da897f 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionInspector.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionInspector.java @@ -21,15 +21,15 @@ package org.eclipse.jetty.server.session; /** * AbstractInspector - * - * */ public abstract class AbstractSessionInspector implements SessionInspector { /** - * <0 means never inspect - * 0 means always inspect - * >0 means inspect at that interval + *

    + *
  • <0 means never inspect
  • + *
  • 0 means always inspect
  • + *
  • >0 means inspect at that interval
  • + *
*/ protected int _timeoutSec = -1; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionStore.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionStore.java index 4eece0bd65a..8139a36f92c 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionStore.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionStore.java @@ -21,7 +21,6 @@ package org.eclipse.jetty.server.session; import java.util.ArrayList; import java.util.Collections; -import java.util.List; import java.util.Set; import java.util.stream.Stream; @@ -56,7 +55,7 @@ public abstract class AbstractSessionStore extends AbstractLifeCycle implements /** * Create a new Session object from session data - * @param data + * @param data the session data * @return a new Session object */ public abstract Session newSession (SessionData data); @@ -84,9 +83,9 @@ public abstract class AbstractSessionStore extends AbstractLifeCycle implements /** * Replace the mapping from id to oldValue with newValue - * @param id - * @param oldValue - * @param newValue + * @param id the id + * @param oldValue the old value + * @param newValue the new value * @return true if replacement was done */ public abstract boolean doReplace (String id, Session oldValue, Session newValue); @@ -95,7 +94,7 @@ public abstract class AbstractSessionStore extends AbstractLifeCycle implements /** * Check to see if the session exists in the store - * @param id + * @param id the id * @return true if the Session object exists in the session store */ public abstract boolean doExists (String id); @@ -104,7 +103,7 @@ public abstract class AbstractSessionStore extends AbstractLifeCycle implements /** * Remove the session with this identity from the store - * @param id + * @param id the id * @return true if removed false otherwise */ public abstract Session doDelete (String id); @@ -113,14 +112,12 @@ public abstract class AbstractSessionStore extends AbstractLifeCycle implements /** * PlaceHolder - * - * */ protected class PlaceHolderSession extends Session { /** - * @param data + * @param data the session data */ public PlaceHolderSession(SessionData data) { @@ -139,7 +136,7 @@ public abstract class AbstractSessionStore extends AbstractLifeCycle implements /** - * @param manager + * @param manager the SessionManager */ public void setSessionManager (SessionManager manager) { @@ -216,7 +213,7 @@ public abstract class AbstractSessionStore extends AbstractLifeCycle implements } /** - * @param sessionDataStore + * @param sessionDataStore the session datastore */ public void setSessionDataStore(SessionDataStore sessionDataStore) { @@ -402,7 +399,7 @@ public abstract class AbstractSessionStore extends AbstractLifeCycle implements /** * Load the info for the session from the session data store * - * @param id + * @param id the id * @return a Session object filled with data or null if the session doesn't exist * @throws Exception */ @@ -555,7 +552,7 @@ public abstract class AbstractSessionStore extends AbstractLifeCycle implements /** - * @see org.eclipse.jetty.server.session.SessionStore#checkExpiry(java.util.Set) + * @see org.eclipse.jetty.server.session.SessionStore#checkExpiration(Set) */ @Override public Set checkExpiration(Set candidates) @@ -565,7 +562,7 @@ public abstract class AbstractSessionStore extends AbstractLifeCycle implements if (LOG.isDebugEnabled()) LOG.debug("SessionStore checking expiration on {}", candidates); - return _sessionDataStore.getExpired(candidates); + return _sessionDataStore.getExpired(candidates, _expiryTimeoutSec); } @@ -695,23 +692,16 @@ public abstract class AbstractSessionStore extends AbstractLifeCycle implements } - /** - * @param passivateOnComplete - */ public void setPassivateOnComplete (boolean passivateOnComplete) { _passivateOnComplete = passivateOnComplete; } - /** - * @return - */ public boolean isPassivateOnComplete () { return _passivateOnComplete; } - /** * @see org.eclipse.jetty.server.session.SessionStore#newSession(javax.servlet.http.HttpServletRequest, java.lang.String, long, long) diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/CachingSessionDataStore.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/CachingSessionDataStore.java index fe16f6aa966..5248fd3be9a 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/CachingSessionDataStore.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/CachingSessionDataStore.java @@ -116,10 +116,10 @@ public class CachingSessionDataStore extends AbstractSessionDataStore } /** - * @see org.eclipse.jetty.server.session.SessionDataStore#getExpired(java.util.Set) + * @see org.eclipse.jetty.server.session.SessionDataStore#getExpired(Set, int) */ @Override - public Set getExpired(Set candidates) + public Set doGetExpired(Set candidates, int expiryTimeoutSec) { // TODO Auto-generated method stub return null; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/ExpiryInspector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/ExpiryInspector.java index 6ed53f7eae5..3d3614b59b0 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/ExpiryInspector.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/ExpiryInspector.java @@ -42,8 +42,8 @@ public class ExpiryInspector extends AbstractSessionInspector /** - * @param sessionStore - * @param idManager + * @param sessionStore the session store + * @param idManager the session id manager */ public ExpiryInspector (AbstractSessionStore sessionStore, SessionIdManager idManager) { @@ -52,7 +52,7 @@ public class ExpiryInspector extends AbstractSessionInspector } /** - * @see org.eclipse.jetty.server.session.SessionInspector#inspect(org.eclipse.jetty.server.session.SessionStore, org.eclipse.jetty.server.session.Session) + * @see org.eclipse.jetty.server.session.SessionInspector#inspect(Session) */ @Override public void inspect(Session s) diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/FileSessionDataStore.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/FileSessionDataStore.java index 6c26b6fe640..1b58d645ad9 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/FileSessionDataStore.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/FileSessionDataStore.java @@ -111,10 +111,10 @@ public class FileSessionDataStore extends AbstractSessionDataStore /** - * @see org.eclipse.jetty.server.session.SessionDataStore#getExpired(java.util.Set) + * @see org.eclipse.jetty.server.session.SessionDataStore#getExpired(Set, int) */ @Override - public Set getExpired(Set candidates) + public Set doGetExpired(Set candidates, int expiryTimeoutSec) { //we don't want to open up each file and check, so just leave it up to the SessionStore //TODO as the session manager is likely to be a lazy loader, if a session is never requested, its @@ -258,7 +258,7 @@ public class FileSessionDataStore extends AbstractSessionDataStore /** * @param id identity of session - * @return + * @return the filename of the session data store */ private String getFileName (String id) { @@ -268,7 +268,7 @@ public class FileSessionDataStore extends AbstractSessionDataStore /** * @param is inputstream containing session data - * @return + * @return the session data * @throws Exception */ private SessionData load (InputStream is) diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/FileSessionManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/FileSessionManager.java index 090841b5c18..1f79b33baa4 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/FileSessionManager.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/FileSessionManager.java @@ -54,7 +54,7 @@ public class FileSessionManager extends SessionManager /** * Get the SessionDataStore to configure it - * @return + * @return the session datastore */ public FileSessionDataStore getSessionDataStore() { diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionIdManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionIdManager.java index 6bb37a2273a..a6948dc5e7f 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionIdManager.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionIdManager.java @@ -32,7 +32,7 @@ import org.eclipse.jetty.util.ConcurrentHashSet; public class HashSessionIdManager extends AbstractSessionIdManager { /** - * @param server + * @param server the server */ public HashSessionIdManager(Server server) { diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/IdleInspector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/IdleInspector.java index dc3915ade69..bac6787f716 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/IdleInspector.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/IdleInspector.java @@ -39,7 +39,7 @@ public class IdleInspector extends AbstractSessionInspector protected AbstractSessionStore _sessionStore; /** - * @param sessionStore + * @param sessionStore the session store */ public IdleInspector (AbstractSessionStore sessionStore) { diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionDataStore.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionDataStore.java index a60f74d2367..cb67d5de33d 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionDataStore.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionDataStore.java @@ -58,8 +58,7 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore private int _attempts = -1; // <= 0 means unlimited attempts to load a session private boolean _deleteUnloadables = false; //true means if attempts exhausted delete the session - private long _gracePeriodMs = 1000L * 60 * 60; //default grace period is 1hr - + /** * SessionTableSchema * @@ -285,7 +284,7 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore } - public PreparedStatement getMyExpiredSessionsStatement (Connection connection, String canonicalContextPath, String vhost, long expiry) + public PreparedStatement getExpiredSessionsStatement (Connection connection, String canonicalContextPath, String vhost, long expiry) throws SQLException { if (_dbAdaptor == null) @@ -317,6 +316,42 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore return statement; } + + public PreparedStatement getMyExpiredSessionsStatement (Connection connection, SessionContext sessionContext, long expiry) + throws SQLException + { + if (_dbAdaptor == null) + throw new IllegalStateException("No DB adaptor"); + + if (sessionContext.getCanonicalContextPath() == null || "".equals(sessionContext.getCanonicalContextPath())) + { + if (_dbAdaptor.isEmptyStringNull()) + { + PreparedStatement statement = connection.prepareStatement("select "+getIdColumn()+", "+getExpiryTimeColumn()+ + " from "+getTableName()+" where "+ + getLastNodeColumn() + " = ? and "+ + getContextPathColumn()+" is null and "+ + getVirtualHostColumn()+" = ? and "+getExpiryTimeColumn()+" >0 and "+getExpiryTimeColumn()+" <= ?"); + statement.setString(1, sessionContext.getWorkerName()); + statement.setString(2, sessionContext.getVhost()); + statement.setLong(3, expiry); + return statement; + } + } + + PreparedStatement statement = connection.prepareStatement("select "+getIdColumn()+", "+getExpiryTimeColumn()+ + " from "+getTableName()+" where "+ + getLastNodeColumn()+" = ? and "+ + getContextPathColumn()+" = ? and "+ + getVirtualHostColumn()+" = ? and "+ + getExpiryTimeColumn()+" >0 and "+getExpiryTimeColumn()+" <= ?"); + + statement.setString(1, sessionContext.getWorkerName()); + statement.setString(2, sessionContext.getCanonicalContextPath()); + statement.setString(3, sessionContext.getVhost()); + statement.setLong(4, expiry); + return statement; + } public PreparedStatement getAllAncientExpiredSessionsStatement (Connection connection) @@ -483,10 +518,7 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore /** * Set up the tables in the database - * @throws SQLException - */ - /** - * @throws SQLException + * @throws SQLException if unable to prepare tables */ public void prepareTables() throws SQLException @@ -747,7 +779,7 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore /** - * @see org.eclipse.jetty.server.session.AbstractSessionDataStore#doStore(java.lang.String, org.eclipse.jetty.server.session.SessionData, boolean) + * @see org.eclipse.jetty.server.session.AbstractSessionDataStore#doStore(String, SessionData, long) */ @Override public void doStore(String id, SessionData data, long lastSaveTime) throws Exception @@ -804,6 +836,7 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore } } + private void doUpdate (String id, SessionData data) throws Exception { @@ -851,30 +884,29 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore /** - * @see org.eclipse.jetty.server.session.SessionDataStore#getExpired(java.util.Set) + * @see org.eclipse.jetty.server.session.SessionDataStore#getExpired(Set, int) */ @Override - public Set getExpired(Set candidates) + public Set doGetExpired(Set candidates, int scavengeIntervalSec) { if (LOG.isDebugEnabled()) LOG.debug("Getting expired sessions "+System.currentTimeMillis()); long now = System.currentTimeMillis(); - Set expiredSessionKeys = new HashSet<>(); try (Connection connection = _dbAdaptor.getConnection()) { connection.setAutoCommit(true); /* - * 1. Select sessions for our context that have expired + * 1. Select sessions managed by this node for our context that have expired */ long upperBound = now; if (LOG.isDebugEnabled()) - LOG.debug ("{}- Pass 1: Searching for sessions for context {} expired before {}", _context.getWorkerName(), _context.getCanonicalContextPath(), upperBound); + LOG.debug ("{}- Pass 1: Searching for sessions for context {} managed by me {} and expired before {}", _context.getCanonicalContextPath(), _context.getWorkerName(), upperBound); - try (PreparedStatement statement = _sessionTableSchema.getMyExpiredSessionsStatement(connection, _context.getCanonicalContextPath(), _context.getVhost(), upperBound)) + try (PreparedStatement statement = _sessionTableSchema.getExpiredSessionsStatement(connection, _context.getCanonicalContextPath(), _context.getVhost(), upperBound)) { try (ResultSet result = statement.executeQuery()) { @@ -889,30 +921,33 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore } /* - * 2. Select sessions for any node or context that have expired a long time ago (ie at least 3 grace periods ago) + * 2. Select sessions for any node or context that have expired + * at least 1 graceperiod since the last expiry check. If we haven't done previous expiry checks, then check + * those that have expired at least 3 graceperiod ago. */ try (PreparedStatement selectExpiredSessions = _sessionTableSchema.getAllAncientExpiredSessionsStatement(connection)) { - upperBound = now - (3 * _gracePeriodMs); - if (upperBound > 0) - { - if (LOG.isDebugEnabled()) LOG.debug("{}- Pass 2: Searching for sessions expired before {}",_context.getWorkerName(), upperBound); + if (_lastExpiryCheckTime <= 0) + upperBound = (now - (3*(1000L * _gracePeriodSec))); + else + upperBound = _lastExpiryCheckTime - (1000L * _gracePeriodSec); - selectExpiredSessions.setLong(1, upperBound); - try (ResultSet result = selectExpiredSessions.executeQuery()) + if (LOG.isDebugEnabled()) LOG.debug("{}- Pass 2: Searching for sessions expired before {}",_context.getWorkerName(), upperBound); + + selectExpiredSessions.setLong(1, upperBound); + try (ResultSet result = selectExpiredSessions.executeQuery()) + { + while (result.next()) { - while (result.next()) - { - String sessionId = result.getString(_sessionTableSchema.getIdColumn()); - String ctxtpth = result.getString(_sessionTableSchema.getContextPathColumn()); - String vh = result.getString(_sessionTableSchema.getVirtualHostColumn()); - expiredSessionKeys.add(sessionId); - if (LOG.isDebugEnabled()) LOG.debug ("{}- Found expired sessionId=",_context.getWorkerName(), sessionId); - } + String sessionId = result.getString(_sessionTableSchema.getIdColumn()); + String ctxtpth = result.getString(_sessionTableSchema.getContextPathColumn()); + String vh = result.getString(_sessionTableSchema.getVirtualHostColumn()); + expiredSessionKeys.add(sessionId); + if (LOG.isDebugEnabled()) LOG.debug ("{}- Found expired sessionId=",_context.getWorkerName(), sessionId); } } } - + Set notExpiredInDB = new HashSet<>(); for (String k: candidates) @@ -958,20 +993,8 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore LOG.warn(e); return expiredSessionKeys; //return whatever we got } - - } - public int getGracePeriodSec () - { - return (int)(_gracePeriodMs == 0L? 0 : _gracePeriodMs/1000L); } - public void setGracePeriodSec (int sec) - { - if (sec < 0) - _gracePeriodMs = 0; - else - _gracePeriodMs = sec * 1000L; - } public void setDatabaseAdaptor (DatabaseAdaptor dbAdaptor) { @@ -1010,6 +1033,9 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore _deleteUnloadables = delete; } + /** + * @return true if we should delete data for sessions that we cant reconstitute + */ public boolean isDeleteUnloadableSessions () { return _deleteUnloadables; @@ -1027,6 +1053,10 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore + /** + * @param id the id + * @return number of attempts to load the given id + */ public int getLoadAttempts (String id) { AtomicInteger i = _unloadables.get(id); @@ -1035,18 +1065,21 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore return i.get(); } + /** + * @return how many sessions we've failed to load + */ public Set getUnloadableSessions () { return new HashSet(_unloadables.keySet()); } - - public void clearUnloadableSessions() - { - _unloadables.clear(); - } - - + /** + * + */ + public void clearUnloadableSessions() + { + _unloadables.clear(); + } /** diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionManager.java index 43018a7a2bf..83b6c9ee600 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionManager.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionManager.java @@ -55,7 +55,7 @@ public class JDBCSessionManager extends SessionManager /** * Get the db adaptor to configure jdbc settings - * @return + * @return the database adaptor */ public DatabaseAdaptor getDatabaseAdaptor() { @@ -64,7 +64,7 @@ public class JDBCSessionManager extends SessionManager /** * Get the SessionDataStore to configure it - * @return + * @return the session data store */ public JDBCSessionDataStore getSessionDataStore () { diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/NullSessionDataStore.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/NullSessionDataStore.java index 78f4004102e..9b0e1144cfd 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/NullSessionDataStore.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/NullSessionDataStore.java @@ -69,10 +69,10 @@ public class NullSessionDataStore extends AbstractSessionDataStore /** - * @see org.eclipse.jetty.server.session.SessionDataStore#getExpired(java.util.Set) + * @see org.eclipse.jetty.server.session.SessionDataStore#getExpired(Set, int) */ @Override - public Set getExpired(Set candidates) + public Set doGetExpired(Set candidates, int expiryTimeoutSec) { return candidates; //whatever is suggested we accept } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/PeriodicSessionInspector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/PeriodicSessionInspector.java index 63807a62bf6..afc40674d30 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/PeriodicSessionInspector.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/PeriodicSessionInspector.java @@ -19,7 +19,6 @@ package org.eclipse.jetty.server.session; -import java.util.Set; import java.util.concurrent.TimeUnit; import org.eclipse.jetty.server.SessionIdManager; @@ -77,7 +76,7 @@ public class PeriodicSessionInspector extends AbstractLifeCycle /** * SessionIdManager associated with this scavenger - * @param sessionIdManager + * @param sessionIdManager the session id manager */ public void setSessionIdManager (SessionIdManager sessionIdManager) { @@ -137,7 +136,7 @@ public class PeriodicSessionInspector extends AbstractLifeCycle /** * Set the period between scavenge cycles - * @param sec + * @param sec the interval (in seconds) */ public void setIntervalSec (long sec) { @@ -176,7 +175,7 @@ public class PeriodicSessionInspector extends AbstractLifeCycle /** * Get the period between inspection cycles. * - * @return + * @return the interval (in seconds) */ public long getIntervalSec () { diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/Session.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/Session.java index add98ac4852..b46df8efb9f 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/Session.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/Session.java @@ -90,8 +90,8 @@ public class Session implements SessionManager.SessionIf /** * Create a new session * - * @param request - * @param data + * @param request the request the session should be based on + * @param data the session data */ public Session (HttpServletRequest request, SessionData data) { @@ -104,7 +104,7 @@ public class Session implements SessionManager.SessionIf /** * Re-create an existing session - * @param data + * @param data the session data */ public Session (SessionData data) { @@ -116,7 +116,7 @@ public class Session implements SessionManager.SessionIf * Should call this method with a lock held if you want to * make decision on what to do with the session * - * @return + * @return the number of active requests for this session */ public long getRequests() { @@ -206,18 +206,12 @@ public class Session implements SessionManager.SessionIf /* ------------------------------------------------------------ */ - /** - * @param nodename - */ public void setLastNode (String nodename) { _sessionData.setLastNode(nodename); } /* ------------------------------------------------------------ */ - /** - * @return - */ public String getLastNode () { return _sessionData.getLastNode(); @@ -479,9 +473,6 @@ public class Session implements SessionManager.SessionIf /* ------------------------------------------------------------- */ - /** - * @throws IllegalStateException - */ protected void checkLocked () throws IllegalStateException { @@ -640,9 +631,6 @@ public class Session implements SessionManager.SessionIf } /* ------------------------------------------------------------ */ - /** - * @param request - */ public void renewId(HttpServletRequest request) { if (_manager == null) @@ -663,7 +651,7 @@ public class Session implements SessionManager.SessionIf /* ------------------------------------------------------------- */ - /** Swap the id on a session from old to new, keeping the object + /* * Swap the id on a session from old to new, keeping the object * the same. * * @param oldId @@ -739,22 +727,16 @@ public class Session implements SessionManager.SessionIf LOG.warn(e); } } - - - + /* ------------------------------------------------------------- */ /** Grab the lock on the session - * @return + * @return the lock */ public Lock lock () { return _lock.lock(); } - - - - /* ------------------------------------------------------------- */ protected void doInvalidate() throws IllegalStateException { @@ -863,21 +845,12 @@ public class Session implements SessionManager.SessionIf _passivationState = PassivationState.ACTIVE; } - - /** - * @return - */ public boolean isActive () { checkLocked(); return _passivationState == PassivationState.ACTIVE; } - - - /** - * @return - */ public boolean isPassivated () { checkLocked(); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionContext.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionContext.java index 14cd396f0b0..10c9bf71831 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionContext.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionContext.java @@ -78,7 +78,7 @@ public class SessionContext /** * Run a runnable in the context (with context classloader set) if * there is one, otherwise just run it. - * @param r + * @param r the runnable */ public void run (Runnable r) { @@ -120,8 +120,8 @@ public class SessionContext /** * Make an acceptable name from a context path. * - * @param path - * @return + * @param path the path to normalize/fix + * @return the clean/acceptable form of the path */ private String canonicalize (String path) { diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionData.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionData.java index 3f05b08aca1..0dc66ab09de 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionData.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionData.java @@ -62,16 +62,6 @@ public class SessionData implements Serializable protected boolean _dirty; protected long _lastSaved; //time in msec since last save - - /** - * @param id - * @param cpath - * @param vhost - * @param created - * @param accessed - * @param lastAccessed - * @param maxInactiveMs - */ public SessionData (String id, String cpath, String vhost, long created, long accessed, long lastAccessed, long maxInactiveMs) { _id = id; @@ -123,10 +113,6 @@ public class SessionData implements Serializable } - - /** - * @param lastSaved - */ public void setLastSaved(long lastSaved) { _lastSaved = lastSaved; @@ -141,16 +127,13 @@ public class SessionData implements Serializable return _dirty; } - /** - * @param dirty - */ public void setDirty(boolean dirty) { _dirty = dirty; } /** - * @param name + * @param name the name of the attribute * @return the value of the attribute named */ public Object getAttribute (String name) @@ -166,11 +149,6 @@ public class SessionData implements Serializable return _attributes.keySet(); } - /** - * @param name - * @param value - * @return - */ public Object setAttribute (String name, Object value) { Object old = (value==null?_attributes.remove(name):_attributes.put(name,value)); @@ -181,20 +159,11 @@ public class SessionData implements Serializable return old; } - - /** - * @param name - */ public void setDirty (String name) { setDirty (true); } - - - /** - * @param attributes - */ public void putAllAttributes (Map attributes) { _attributes.putAll(attributes); @@ -224,9 +193,6 @@ public class SessionData implements Serializable return _id; } - /** - * @param id - */ public void setId(String id) { _id = id; @@ -240,9 +206,6 @@ public class SessionData implements Serializable return _contextPath; } - /** - * @param contextPath - */ public void setContextPath(String contextPath) { _contextPath = contextPath; @@ -256,9 +219,6 @@ public class SessionData implements Serializable return _vhost; } - /** - * @param vhost - */ public void setVhost(String vhost) { _vhost = vhost; @@ -272,9 +232,6 @@ public class SessionData implements Serializable return _lastNode; } - /** - * @param lastNode - */ public void setLastNode(String lastNode) { _lastNode = lastNode; @@ -288,25 +245,16 @@ public class SessionData implements Serializable return _expiry; } - /** - * @param expiry - */ public void setExpiry(long expiry) { _expiry = expiry; } - /** - * @return - */ public long getCreated() { return _created; } - /** - * @param created - */ public void setCreated(long created) { _created = created; @@ -320,9 +268,6 @@ public class SessionData implements Serializable return _cookieSet; } - /** - * @param cookieSet - */ public void setCookieSet(long cookieSet) { _cookieSet = cookieSet; @@ -336,9 +281,6 @@ public class SessionData implements Serializable return _accessed; } - /** - * @param accessed - */ public void setAccessed(long accessed) { _accessed = accessed; @@ -352,35 +294,21 @@ public class SessionData implements Serializable return _lastAccessed; } - /** - * @param lastAccessed - */ public void setLastAccessed(long lastAccessed) { _lastAccessed = lastAccessed; } - - /** - * @return - */ public long getMaxInactiveMs() { return _maxInactiveMs; } - /** - * @param maxInactive - */ public void setMaxInactiveMs(long maxInactive) { _maxInactiveMs = maxInactive; } - /** - * @param out - * @throws IOException - */ private void writeObject(java.io.ObjectOutputStream out) throws IOException { out.writeUTF(_id); //session id @@ -398,11 +326,6 @@ public class SessionData implements Serializable out.writeObject(_attributes); } - /** - * @param in - * @throws IOException - * @throws ClassNotFoundException - */ private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { _id = in.readUTF(); @@ -419,11 +342,6 @@ public class SessionData implements Serializable _attributes = (ConcurrentHashMap)in.readObject(); } - - /** - * @param time - * @return - */ public boolean isExpiredAt (long time) { if (LOG.isDebugEnabled()) @@ -433,4 +351,23 @@ public class SessionData implements Serializable return (getExpiry() < time); } + + /** + * @see java.lang.Object#toString() + */ + public String toString() + { + StringBuilder builder = new StringBuilder(); + builder.append("id="+_id); + builder.append(", contextpath="+_contextPath); + builder.append(", vhost="+_vhost); + builder.append(", accessed="+_accessed); + builder.append(", lastaccessed="+_lastAccessed); + builder.append(", created="+_created); + builder.append(", cookieset="+_cookieSet); + builder.append(", lastnode="+_lastNode); + builder.append(", expiry="+_expiry); + builder.append(", maxinactive="+_maxInactiveMs); + return builder.toString(); + } } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionDataStore.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionDataStore.java index 367d6f549d7..b7ffd1edd95 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionDataStore.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionDataStore.java @@ -46,18 +46,18 @@ public interface SessionDataStore extends LifeCycle * Read in session data from storage * @param id identity of session to load * @return the SessionData matching the id - * @throws Exception + * @throws Exception if unable to load session data */ public SessionData load (String id) throws Exception; /** * Create a new SessionData - * @param id - * @param created - * @param accessed - * @param lastAccessed - * @param maxInactiveMs + * @param id the id + * @param created the timestamp when created + * @param accessed the timestamp when accessed + * @param lastAccessed the timestamp when last accessed + * @param maxInactiveMs the max inactive time in milliseconds * @return a new SessionData object */ public SessionData newSessionData (String id, long created, long accessed, long lastAccessed, long maxInactiveMs); @@ -69,7 +69,7 @@ public interface SessionDataStore extends LifeCycle * Write out session data to storage * @param id identity of session to store * @param data info of session to store - * @throws Exception + * @throws Exception if unable to write session data */ public void store (String id, SessionData data) throws Exception; @@ -79,7 +79,7 @@ public interface SessionDataStore extends LifeCycle * Delete session data from storage * @param id identity of session to delete * @return true if the session was deleted from the permanent store - * @throws Exception + * @throws Exception if unable to delete session data */ public boolean delete (String id) throws Exception; @@ -93,9 +93,10 @@ public interface SessionDataStore extends LifeCycle * @param candidates if provided, these are keys of sessions that * the SessionStore thinks has expired and should be verified by the * SessionDataStore + * @param scavengePeriodSec the time to attempt scavenge (in seconds) * @return set of session ids */ - public Set getExpired (Set candidates); + public Set getExpired (Set candidates, int scavengePeriodSec); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionManager.java index 543c61e2fcf..e0210616498 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionManager.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionManager.java @@ -19,8 +19,6 @@ package org.eclipse.jetty.server.session; -import static java.lang.Math.round; - import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; @@ -53,7 +51,8 @@ import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.statistic.CounterStatistic; import org.eclipse.jetty.util.statistic.SampleStatistic; -import org.eclipse.jetty.util.thread.Locker.Lock; + +import static java.lang.Math.round; @@ -766,7 +765,7 @@ public class SessionManager extends ContainerLifeCycle implements org.eclipse.je /* ------------------------------------------------------------ */ /** - * @return + * @return the session store */ public SessionStore getSessionStore () { @@ -1006,7 +1005,7 @@ public class SessionManager extends ContainerLifeCycle implements org.eclipse.je * Called either when a session has expired, or the app has * invalidated it. * - * @param id + * @param id the id to invalidate */ public void invalidate (String id) { @@ -1032,9 +1031,6 @@ public class SessionManager extends ContainerLifeCycle implements org.eclipse.je /* ------------------------------------------------------------ */ - /** - * @return - */ public void inspect () { //don't attempt to scavenge if we are shutting down diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/StalePeriodStrategy.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/StalePeriodStrategy.java index 6dd9756c693..7de75b846db 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/StalePeriodStrategy.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/StalePeriodStrategy.java @@ -55,10 +55,6 @@ public class StalePeriodStrategy implements StalenessStrategy } - - /** - * @return - */ public long getStaleSec () { return (_staleMs<=0?0L:_staleMs/1000L); @@ -67,7 +63,7 @@ public class StalePeriodStrategy implements StalenessStrategy /** * The amount of time in seconds that a session can be held * in memory without being refreshed from the cluster. - * @param sec + * @param sec the time in seconds */ public void setStaleSec (long sec) { diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ShutdownMonitorTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ShutdownMonitorTest.java index 3a40ec3a83e..fadd4c9e888 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/ShutdownMonitorTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ShutdownMonitorTest.java @@ -18,196 +18,151 @@ package org.eclipse.jetty.server; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - import java.io.InputStreamReader; import java.io.LineNumberReader; import java.io.OutputStream; import java.net.InetAddress; import java.net.Socket; -import java.util.concurrent.TimeUnit; import org.eclipse.jetty.util.thread.ShutdownThread; import org.junit.Test; -/** - * ShutdownMonitorTest - */ +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + public class ShutdownMonitorTest { - public class TestableServer extends Server - { - boolean destroyed = false; - boolean stopped = false; - @Override - protected void doStop() throws Exception - { - stopped = true; - super.doStop(); - } - @Override - public void destroy() - { - destroyed = true; - super.destroy(); - } - @Override - protected void doStart() throws Exception - { - stopped = false; - destroyed = false; - super.doStart(); - } - } - - @Test public void testShutdownMonitor() throws Exception { - // test port and key assignment - ShutdownMonitor.getInstance().setPort(0); - ShutdownMonitor.getInstance().setExitVm(false); - ShutdownMonitor.getInstance().start(); - String key = ShutdownMonitor.getInstance().getKey(); - int port = ShutdownMonitor.getInstance().getPort(); + ShutdownMonitor monitor = ShutdownMonitor.getInstance(); + monitor.setDebug(true); + monitor.setPort(0); + monitor.setExitVm(false); + monitor.start(); + String key = monitor.getKey(); + int port = monitor.getPort(); // try starting a 2nd time (should be ignored) - ShutdownMonitor.getInstance().start(); + monitor.start(); - stop("stop", port,key,true); - assertTrue(!ShutdownMonitor.getInstance().isAlive()); + stop("stop", port, key, true); + monitor.await(); + assertTrue(!monitor.isAlive()); // should be able to change port and key because it is stopped - ShutdownMonitor.getInstance().setPort(0); - ShutdownMonitor.getInstance().setKey("foo"); - ShutdownMonitor.getInstance().start(); + monitor.setPort(0); + String newKey = "foo"; + monitor.setKey(newKey); + monitor.start(); - key = ShutdownMonitor.getInstance().getKey(); - port = ShutdownMonitor.getInstance().getPort(); - assertTrue(ShutdownMonitor.getInstance().isAlive()); + key = monitor.getKey(); + assertEquals(newKey, key); + port = monitor.getPort(); + assertTrue(monitor.isAlive()); - stop("stop", port,key,true); - assertTrue(!ShutdownMonitor.getInstance().isAlive()); + stop("stop", port, key, true); + monitor.await(); + assertTrue(!monitor.isAlive()); } - - + @Test public void testForceStopCommand() throws Exception { - //create a testable Server with stop(), destroy() overridden to instrument - //start server - //call "forcestop" and check that server stopped but not destroyed - // test port and key assignment - System.setProperty("DEBUG", "true"); - ShutdownMonitor.getInstance().setPort(0); + ShutdownMonitor monitor = ShutdownMonitor.getInstance(); + monitor.setPort(0); TestableServer server = new TestableServer(); server.start(); - + //shouldn't be registered for shutdown on jvm assertTrue(!ShutdownThread.isRegistered(server)); assertTrue(ShutdownMonitor.isRegistered(server)); - - String key = ShutdownMonitor.getInstance().getKey(); - int port = ShutdownMonitor.getInstance().getPort(); - - stop("forcestop", port,key,true); - - assertTrue(!ShutdownMonitor.getInstance().isAlive()); + + String key = monitor.getKey(); + int port = monitor.getPort(); + + stop("forcestop", port, key, true); + monitor.await(); + + assertTrue(!monitor.isAlive()); assertTrue(server.stopped); assertTrue(!server.destroyed); assertTrue(!ShutdownThread.isRegistered(server)); assertTrue(!ShutdownMonitor.isRegistered(server)); } - + @Test public void testOldStopCommandWithStopOnShutdownTrue() throws Exception { - - //create a testable Server with stop(), destroy() overridden to instrument - //call server.setStopAtShudown(true); - //start server - //call "stop" and check that server stopped but not destroyed - - //stop server - - //call server.setStopAtShutdown(false); - //start server - //call "stop" and check that the server is not stopped and not destroyed - System.setProperty("DEBUG", "true"); - ShutdownMonitor.getInstance().setExitVm(false); - - ShutdownMonitor.getInstance().setPort(0); + ShutdownMonitor monitor = ShutdownMonitor.getInstance(); + monitor.setExitVm(false); + + monitor.setPort(0); TestableServer server = new TestableServer(); server.setStopAtShutdown(true); server.start(); - + //should be registered for shutdown on exit assertTrue(ShutdownThread.isRegistered(server)); assertTrue(ShutdownMonitor.isRegistered(server)); - - String key = ShutdownMonitor.getInstance().getKey(); - int port = ShutdownMonitor.getInstance().getPort(); - + + String key = monitor.getKey(); + int port = monitor.getPort(); + stop("stop", port, key, true); - assertTrue(!ShutdownMonitor.getInstance().isAlive()); + monitor.await(); + + assertTrue(!monitor.isAlive()); assertTrue(server.stopped); assertTrue(!server.destroyed); assertTrue(!ShutdownThread.isRegistered(server)); assertTrue(!ShutdownMonitor.isRegistered(server)); } - + @Test public void testOldStopCommandWithStopOnShutdownFalse() throws Exception { - //change so stopatshutdown is false, so stop does nothing in this case (as exitVm is false otherwise we couldn't run test) - ShutdownMonitor.getInstance().setExitVm(false); - System.setProperty("DEBUG", "true"); - ShutdownMonitor.getInstance().setPort(0); + ShutdownMonitor monitor = ShutdownMonitor.getInstance(); + monitor.setExitVm(false); + monitor.setPort(0); TestableServer server = new TestableServer(); server.setStopAtShutdown(false); server.start(); - + assertTrue(!ShutdownThread.isRegistered(server)); assertTrue(ShutdownMonitor.isRegistered(server)); - - String key = ShutdownMonitor.getInstance().getKey(); - int port = ShutdownMonitor.getInstance().getPort(); - - stop ("stop", port, key, true); - assertTrue(!ShutdownMonitor.getInstance().isAlive()); + + String key = monitor.getKey(); + int port = monitor.getPort(); + + stop("stop", port, key, true); + monitor.await(); + + assertTrue(!monitor.isAlive()); assertTrue(!server.stopped); assertTrue(!server.destroyed); assertTrue(!ShutdownThread.isRegistered(server)); assertTrue(ShutdownMonitor.isRegistered(server)); } - - - public void stop(String command, int port, String key, boolean check) throws Exception { - System.out.printf("Attempting to send "+command+" to localhost:%d (%b)%n",port,check); - try (Socket s = new Socket(InetAddress.getByName("127.0.0.1"),port)) + System.out.printf("Attempting to send " + command + " to localhost:%d (%b)%n", port, check); + try (Socket s = new Socket(InetAddress.getByName("127.0.0.1"), port)) { // send stop command try (OutputStream out = s.getOutputStream()) { - out.write((key + "\r\n"+command+"\r\n").getBytes()); + out.write((key + "\r\n" + command + "\r\n").getBytes()); out.flush(); if (check) { - // wait a little - TimeUnit.MILLISECONDS.sleep(600); - // check for stop confirmation LineNumberReader lin = new LineNumberReader(new InputStreamReader(s.getInputStream())); String response; if ((response = lin.readLine()) != null) - { - assertEquals("Stopped",response); - } + assertEquals("Stopped", response); else throw new IllegalStateException("No stop confirmation"); } @@ -215,4 +170,31 @@ public class ShutdownMonitorTest } } + public class TestableServer extends Server + { + boolean destroyed = false; + boolean stopped = false; + + @Override + protected void doStop() throws Exception + { + stopped = true; + super.doStop(); + } + + @Override + public void destroy() + { + destroyed = true; + super.destroy(); + } + + @Override + protected void doStart() throws Exception + { + stopped = false; + destroyed = false; + super.doStart(); + } + } } diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/session/SessionCookieTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/session/SessionCookieTest.java index e9b6b82039d..f7ab4636360 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/session/SessionCookieTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/session/SessionCookieTest.java @@ -18,22 +18,18 @@ package org.eclipse.jetty.server.session; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import java.util.Enumeration; -import java.util.Map; -import java.util.Set; import java.util.stream.Stream; import javax.servlet.SessionCookieConfig; import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpSession; import org.eclipse.jetty.http.HttpCookie; import org.eclipse.jetty.server.Server; import org.junit.Test; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + /** * SessionCookieTest */ @@ -46,7 +42,7 @@ public class SessionCookieTest { /** - * @see org.eclipse.jetty.server.session.SessionStore#newSession(org.eclipse.jetty.server.session.SessionKey, long, long, long, long) + * @see org.eclipse.jetty.server.session.SessionStore#newSession(HttpServletRequest, String, long, long) */ @Override public Session newSession(HttpServletRequest request, String key, long time, long maxInactiveMs) @@ -76,7 +72,7 @@ public class SessionCookieTest } /** - * @see org.eclipse.jetty.server.session.AbstractSessionStore#doGet(org.eclipse.jetty.server.session.SessionKey) + * @see org.eclipse.jetty.server.session.AbstractSessionStore#doGet(String) */ @Override public Session doGet(String key) @@ -86,7 +82,7 @@ public class SessionCookieTest } /** - * @see org.eclipse.jetty.server.session.AbstractSessionStore#doPutIfAbsent(org.eclipse.jetty.server.session.SessionKey, org.eclipse.jetty.server.session.Session) + * @see org.eclipse.jetty.server.session.AbstractSessionStore#doPutIfAbsent(String, Session) */ @Override public Session doPutIfAbsent(String key, Session session) @@ -95,7 +91,7 @@ public class SessionCookieTest } /** - * @see org.eclipse.jetty.server.session.AbstractSessionStore#doExists(org.eclipse.jetty.server.session.SessionKey) + * @see org.eclipse.jetty.server.session.AbstractSessionStore#doExists(String) */ @Override public boolean doExists(String key) @@ -105,7 +101,7 @@ public class SessionCookieTest } /** - * @see org.eclipse.jetty.server.session.AbstractSessionStore#doDelete(org.eclipse.jetty.server.session.SessionKey) + * @see org.eclipse.jetty.server.session.AbstractSessionStore#doDelete(String) */ @Override public Session doDelete(String key) @@ -140,10 +136,6 @@ public class SessionCookieTest public class MockSessionIdManager extends AbstractSessionIdManager { - - /** - * @param server - */ public MockSessionIdManager(Server server) { super(server); @@ -175,7 +167,7 @@ public class SessionCookieTest } /** - * @see org.eclipse.jetty.server.SessionIdManager#useId(java.lang.String) + * @see org.eclipse.jetty.server.SessionIdManager#useId(Session) */ @Override public void useId(Session session) diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SslConnectionFactoryTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SslConnectionFactoryTest.java index 9f0e43358fd..d88076645d0 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SslConnectionFactoryTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SslConnectionFactoryTest.java @@ -21,6 +21,7 @@ package org.eclipse.jetty.server.ssl; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.nio.charset.StandardCharsets; @@ -59,11 +60,11 @@ import org.junit.Before; import org.junit.Test; public class SslConnectionFactoryTest -{ +{ Server _server; ServerConnector _connector; int _port; - + @Before public void before() throws Exception { @@ -83,7 +84,7 @@ public class SslConnectionFactoryTest HttpConfiguration https_config = new HttpConfiguration(http_config); https_config.addCustomizer(new SecureRequestCustomizer()); - + SslContextFactory sslContextFactory = new SslContextFactory(); sslContextFactory.setKeyStorePath(keystoreFile.getAbsolutePath()); sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"); @@ -96,7 +97,7 @@ public class SslConnectionFactoryTest https.setIdleTimeout(30000); _server.addConnector(https); - + _server.setHandler(new AbstractHandler() { @Override @@ -107,30 +108,30 @@ public class SslConnectionFactoryTest response.flushBuffer(); } }); - + _server.start(); - _port=https.getLocalPort(); + _port=https.getLocalPort(); } - + @After public void after() throws Exception { _server.stop(); _server=null; } - + @Test public void testConnect() throws Exception { - String response= getResponse("127.0.0.1",null); + String response= getResponse("127.0.0.1",null); Assert.assertThat(response,Matchers.containsString("host=127.0.0.1")); } - + @Test public void testSNIConnect() throws Exception { String response; - + response= getResponse("localhost","localhost","jetty.eclipse.org"); Assert.assertThat(response,Matchers.containsString("host=localhost")); } @@ -151,22 +152,32 @@ public class SslConnectionFactoryTest { out.write("Rubbish".getBytes()); out.flush(); - - Assert.assertThat(socket.getInputStream().read(),Matchers.equalTo(-1)); + + socket.setSoTimeout(1000); + InputStream input = socket.getInputStream(); + int read = input.read(); + // TLS Alert message type == 21. + Assert.assertThat(read, Matchers.equalTo(21)); + int reads = 0; + while (read >= 0) + { + read = input.read(); + ++reads; + } + Assert.assertThat(reads, Matchers.lessThan(32)); } - } - + private String getResponse(String sniHost,String reqHost, String cn) throws Exception { SslContextFactory clientContextFactory = new SslContextFactory(true); clientContextFactory.start(); SSLSocketFactory factory = clientContextFactory.getSslContext().getSocketFactory(); - + SSLSocket sslSocket = (SSLSocket)factory.createSocket("127.0.0.1", _port); if (cn!=null) - { + { SNIHostName serverName = new SNIHostName(sniHost); List serverNames = new ArrayList<>(); serverNames.add(serverName); @@ -177,35 +188,35 @@ public class SslConnectionFactoryTest } sslSocket.startHandshake(); - + if (cn!=null) - { + { X509Certificate cert = ((X509Certificate)sslSocket.getSession().getPeerCertificates()[0]); - + Assert.assertThat(cert.getSubjectX500Principal().getName("CANONICAL"), Matchers.startsWith("cn="+cn)); } sslSocket.getOutputStream().write(("GET /ctx/path HTTP/1.0\r\nHost: "+reqHost+":"+_port+"\r\n\r\n").getBytes(StandardCharsets.ISO_8859_1)); String response = IO.toString(sslSocket.getInputStream()); - + sslSocket.close(); clientContextFactory.stop(); return response; } - + @Test public void testSocketCustomization() throws Exception { final Queue history = new ConcurrentArrayQueue<>(); - + _connector.addBean(new SocketCustomizationListener() { @Override protected void customize(Socket socket, Class connection, boolean ssl) { history.add("customize connector "+connection+","+ssl); - } + } }); _connector.getBean(SslConnectionFactory.class).addBean(new SocketCustomizationListener() @@ -214,26 +225,26 @@ public class SslConnectionFactoryTest protected void customize(Socket socket, Class connection, boolean ssl) { history.add("customize ssl "+connection+","+ssl); - } + } }); - + _connector.getBean(HttpConnectionFactory.class).addBean(new SocketCustomizationListener() { @Override protected void customize(Socket socket, Class connection, boolean ssl) { history.add("customize http "+connection+","+ssl); - } + } }); - String response= getResponse("127.0.0.1",null); + String response= getResponse("127.0.0.1",null); Assert.assertThat(response,Matchers.containsString("host=127.0.0.1")); - + Assert.assertEquals("customize connector class org.eclipse.jetty.io.ssl.SslConnection,false",history.poll()); Assert.assertEquals("customize ssl class org.eclipse.jetty.io.ssl.SslConnection,false",history.poll()); Assert.assertEquals("customize connector class org.eclipse.jetty.server.HttpConnection,true",history.poll()); Assert.assertEquals("customize http class org.eclipse.jetty.server.HttpConnection,true",history.poll()); Assert.assertEquals(0,history.size()); } - + } diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartInputStreamParser.java b/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartInputStreamParser.java index ab27d80e0e1..f9748caff7c 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartInputStreamParser.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartInputStreamParser.java @@ -771,11 +771,15 @@ public class MultiPartInputStreamParser } finally { - part.close(); } } - if (!lastPart) + if (lastPart) + { + while(line!=null) + line=((ReadLineInputStream)_in).readLine(); + } + else throw new IOException("Incomplete parts"); } catch (Exception e) diff --git a/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/ServerContainer.java b/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/ServerContainer.java index 8d114c37e05..b21344a1163 100644 --- a/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/ServerContainer.java +++ b/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/ServerContainer.java @@ -245,12 +245,14 @@ public class ServerContainer extends ClientContainer implements javax.websocket. @Override public void onSessionClosed(WebSocketSession session) { + super.onSessionClosed(session); webSocketServerFactory.onSessionClosed(session); } @Override public void onSessionOpened(WebSocketSession session) { + super.onSessionOpened(session); webSocketServerFactory.onSessionOpened(session); } } diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/browser/JsrBrowserDebugTool.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/browser/JsrBrowserDebugTool.java index ce8d230bbb6..ac5f8410f61 100644 --- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/browser/JsrBrowserDebugTool.java +++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/browser/JsrBrowserDebugTool.java @@ -18,6 +18,11 @@ package org.eclipse.jetty.websocket.jsr356.server.browser; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.Objects; + import javax.servlet.ServletException; import javax.websocket.DeploymentException; @@ -30,6 +35,7 @@ import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; +import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.websocket.jsr356.server.ServerContainer; import org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer; @@ -78,7 +84,7 @@ public class JsrBrowserDebugTool server.join(); } - private void setupServer(int port) throws DeploymentException, ServletException + private void setupServer(int port) throws DeploymentException, ServletException, URISyntaxException, MalformedURLException { server = new Server(); @@ -89,10 +95,16 @@ public class JsrBrowserDebugTool connector.setPort(port); server.addConnector(connector); + String resourcePath = "/jsr-browser-debug-tool/index.html"; + URL urlStatics = JsrBrowserDebugTool.class.getResource(resourcePath); + Objects.requireNonNull(urlStatics,"Unable to find " + resourcePath + " in classpath"); + String urlBase = urlStatics.toURI().toASCIIString().replaceFirst("/[^/]*$","/"); + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); context.setContextPath("/"); + context.setBaseResource(Resource.newResource(urlBase)); + ServletHolder holder = context.addServlet(DefaultServlet.class,"/"); - holder.setInitParameter("resourceBase","src/test/resources/jsr-browser-debug-tool"); holder.setInitParameter("dirAllowed","true"); server.setHandler(context); diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/browser/JsrBrowserSocket.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/browser/JsrBrowserSocket.java index 3b17bdb3c0e..d724913577a 100644 --- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/browser/JsrBrowserSocket.java +++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/browser/JsrBrowserSocket.java @@ -23,6 +23,7 @@ import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Locale; import java.util.Random; +import java.util.Set; import javax.websocket.CloseReason; import javax.websocket.OnClose; @@ -129,6 +130,14 @@ public class JsrBrowserSocket { writeMessage("Client Sec-WebSocket-Extensions: " + this.requestedExtensions); } + + Set openSessions = session.getOpenSessions(); + writeMessage("OpenSessions.size() = " + openSessions.size()); + int i = 0; + for (Session open : openSessions) + { + writeMessage(" OpenSession[%d] = %s", i++, open); + } break; } case "many": diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/resources/jetty-logging.properties b/jetty-websocket/javax-websocket-server-impl/src/test/resources/jetty-logging.properties index 9681d9168b4..c5a50f62391 100644 --- a/jetty-websocket/javax-websocket-server-impl/src/test/resources/jetty-logging.properties +++ b/jetty-websocket/javax-websocket-server-impl/src/test/resources/jetty-logging.properties @@ -6,9 +6,6 @@ org.eclipse.jetty.LEVEL=WARN # org.eclipse.jetty.websocket.LEVEL=WARN # org.eclipse.jetty.websocket.common.io.LEVEL=DEBUG -org.eclipse.jetty.websocket.common.WebSocketSession.LEVEL=DEBUG -org.eclipse.jetty.websocket.jsr356.LEVEL=DEBUG - ### Show state changes on BrowserDebugTool # -- LEAVE THIS AT DEBUG LEVEL -- org.eclipse.jetty.websocket.jsr356.server.browser.LEVEL=DEBUG diff --git a/pom.xml b/pom.xml index 4198564f590..de39a4da86a 100644 --- a/pom.xml +++ b/pom.xml @@ -368,15 +368,16 @@ org.apache.maven.plugins maven-javadoc-plugin + 2.8 true - false + true true + protected com.acme.*;org.slf4j.*;org.mortbay.* http://docs.oracle.com/javase/8/docs/api/ http://docs.oracle.com/javaee/7/api/ - http://junit.org/javadoc/latest/ http://download.eclipse.org/jetty/stable-9/apidocs/ diff --git a/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientTest.java b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientTest.java index 415bbe3700d..b0f3d177351 100644 --- a/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientTest.java +++ b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientTest.java @@ -20,6 +20,7 @@ package org.eclipse.jetty.http.client; import java.io.IOException; import java.io.InterruptedIOException; +import java.util.EnumSet; import java.util.Random; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; @@ -39,7 +40,10 @@ import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http2.FlowControlStrategy; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.handler.AbstractHandler; +import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.junit.Assert; +import org.junit.Assume; import org.junit.Test; public class HttpClientTest extends AbstractTest @@ -285,6 +289,33 @@ public class HttpClientTest extends AbstractTest Assert.assertEquals(response.getStatus(), 200); } + @Test(expected = ExecutionException.class) + public void testClientCannotValidateServerCertificate() throws Exception + { + // Only run this test for transports over TLS. + Assume.assumeTrue(EnumSet.of(Transport.HTTPS, Transport.H2).contains(transport)); + + startServer(new AbstractHandler() + { + @Override + public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException + { + baseRequest.setHandled(true); + } + }); + + // Use a default SslContextFactory, requests should fail because the server certificate is unknown. + client = newHttpClient(provideClientTransport(transport), new SslContextFactory()); + QueuedThreadPool clientThreads = new QueuedThreadPool(); + clientThreads.setName("client"); + client.setExecutor(clientThreads); + client.start(); + + client.newRequest(newURI()) + .timeout(5, TimeUnit.SECONDS) + .send(); + } + private void sleep(long time) throws IOException { try diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionInvalidateAndCreateTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionInvalidateAndCreateTest.java index 4e98e91f87e..b1c1d4286cc 100644 --- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionInvalidateAndCreateTest.java +++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionInvalidateAndCreateTest.java @@ -139,7 +139,7 @@ public abstract class AbstractSessionInvalidateAndCreateTest assertTrue(listener.destroys.contains("session1")); assertTrue(listener.destroys.contains("session2")); //session2's HttpSessionBindingListener should have been called when it was scavenged - assertTrue(servlet.unbound); + assertTrue(servlet.listener.unbound); } finally { @@ -151,24 +151,33 @@ public abstract class AbstractSessionInvalidateAndCreateTest server.stop(); } } + + public static class Foo implements Serializable + { + public boolean bar = false; + + public boolean getBar() { return bar;}; + } - public static class TestServlet extends HttpServlet + public static class MySessionBindingListener implements HttpSessionBindingListener, Serializable { private boolean unbound = false; - public class MySessionBindingListener implements HttpSessionBindingListener, Serializable + public void valueUnbound(HttpSessionBindingEvent event) + { + unbound = true; + } + + public void valueBound(HttpSessionBindingEvent event) { - public void valueUnbound(HttpSessionBindingEvent event) - { - unbound = true; - } - - public void valueBound(HttpSessionBindingEvent event) - { - - } } + } + + public static class TestServlet extends HttpServlet + { + public MySessionBindingListener listener = new MySessionBindingListener(); + @Override protected void doGet(HttpServletRequest request, HttpServletResponse httpServletResponse) throws ServletException, IOException @@ -190,7 +199,7 @@ public abstract class AbstractSessionInvalidateAndCreateTest //now make a new session session = request.getSession(true); session.setAttribute("identity", "session2"); - session.setAttribute("listener", new MySessionBindingListener()); + session.setAttribute("listener", listener); } else fail("Session already missing"); diff --git a/tests/test-webapps/pom.xml b/tests/test-webapps/pom.xml index b99dcb74e5f..463a0119535 100644 --- a/tests/test-webapps/pom.xml +++ b/tests/test-webapps/pom.xml @@ -20,7 +20,7 @@ true - + diff --git a/tests/test-webapps/test-jetty-webapp/pom.xml b/tests/test-webapps/test-jetty-webapp/pom.xml index 7d54ced77eb..66cabff4e54 100644 --- a/tests/test-webapps/test-jetty-webapp/pom.xml +++ b/tests/test-webapps/test-jetty-webapp/pom.xml @@ -129,6 +129,14 @@ + + org.apache.maven.plugins + maven-deploy-plugin + + + false + + diff --git a/tests/test-webapps/test-proxy-webapp/pom.xml b/tests/test-webapps/test-proxy-webapp/pom.xml index ef027e0db7d..ec11b5b9c38 100644 --- a/tests/test-webapps/test-proxy-webapp/pom.xml +++ b/tests/test-webapps/test-proxy-webapp/pom.xml @@ -24,6 +24,14 @@ + + org.apache.maven.plugins + maven-deploy-plugin + + + false + +