Merge branch 'master' into jetty-9.4.x-Feature

This commit is contained in:
Greg Wilkins 2016-03-10 07:20:05 +11:00
commit 36b5687329
76 changed files with 1513 additions and 1203 deletions

View File

@ -26,6 +26,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
import org.eclipse.jetty.client.api.Authentication; import org.eclipse.jetty.client.api.Authentication;
import org.eclipse.jetty.client.api.AuthenticationStore; import org.eclipse.jetty.client.api.AuthenticationStore;
import org.eclipse.jetty.client.util.AbstractAuthentication;
public class HttpAuthenticationStore implements AuthenticationStore public class HttpAuthenticationStore implements AuthenticationStore
{ {
@ -85,7 +86,7 @@ public class HttpAuthenticationStore implements AuthenticationStore
// TODO: I should match the longest URI // TODO: I should match the longest URI
for (Map.Entry<URI, Authentication.Result> entry : results.entrySet()) for (Map.Entry<URI, Authentication.Result> entry : results.entrySet())
{ {
if (uri.toString().startsWith(entry.getKey().toString())) if (AbstractAuthentication.matchesURI(entry.getKey(), uri))
return entry.getValue(); return entry.getValue();
} }
return null; return null;

View File

@ -1035,7 +1035,7 @@ public class HttpClient extends ContainerLifeCycle
return host; return host;
} }
protected int normalizePort(String scheme, int port) public static int normalizePort(String scheme, int port)
{ {
if (port > 0) if (port > 0)
return port; return port;
@ -1053,7 +1053,7 @@ public class HttpClient extends ContainerLifeCycle
return port == 80; return port == 80;
} }
public boolean isSchemeSecure(String scheme) static boolean isSchemeSecure(String scheme)
{ {
return HttpScheme.HTTPS.is(scheme) || HttpScheme.WSS.is(scheme); return HttpScheme.HTTPS.is(scheme) || HttpScheme.WSS.is(scheme);
} }

View File

@ -126,7 +126,7 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
public boolean isSecure() public boolean isSecure()
{ {
return client.isSchemeSecure(getScheme()); return HttpClient.isSchemeSecure(getScheme());
} }
public HttpClient getHttpClient() public HttpClient getHttpClient()

View File

@ -88,7 +88,7 @@ public class HttpRequest implements Request
this.conversation = conversation; this.conversation = conversation;
scheme = uri.getScheme(); scheme = uri.getScheme();
host = client.normalizeHost(uri.getHost()); host = client.normalizeHost(uri.getHost());
port = client.normalizePort(scheme, uri.getPort()); port = HttpClient.normalizePort(scheme, uri.getPort());
path = uri.getRawPath(); path = uri.getRawPath();
query = uri.getRawQuery(); query = uri.getRawQuery();
extractParams(query); extractParams(query);

View File

@ -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;
}
}

View File

@ -22,7 +22,6 @@ import java.net.URI;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import org.eclipse.jetty.client.HttpClient; 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.AuthenticationStore;
import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Request;
@ -37,10 +36,8 @@ import org.eclipse.jetty.util.B64Code;
* {@link AuthenticationStore} retrieved from the {@link HttpClient} * {@link AuthenticationStore} retrieved from the {@link HttpClient}
* via {@link HttpClient#getAuthenticationStore()}. * 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 user;
private final String password; private final String password;
@ -52,48 +49,39 @@ public class BasicAuthentication implements Authentication
*/ */
public BasicAuthentication(URI uri, String realm, String user, String password) public BasicAuthentication(URI uri, String realm, String user, String password)
{ {
this.uri = uri; super(uri, realm);
this.realm = realm;
this.user = user; this.user = user;
this.password = password; this.password = password;
} }
@Override @Override
public boolean matches(String type, URI uri, String realm) public String getType()
{ {
if (!"basic".equalsIgnoreCase(type)) return "Basic";
return false;
if (!uri.toString().startsWith(this.uri.toString()))
return false;
return this.realm.equals(realm);
} }
@Override @Override
public Result authenticate(Request request, ContentResponse response, HeaderInfo headerInfo, Attributes context) public Result authenticate(Request request, ContentResponse response, HeaderInfo headerInfo, Attributes context)
{ {
String value = "Basic " + B64Code.encode(user + ":" + password, StandardCharsets.ISO_8859_1); 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 HttpHeader header;
private final URI uri;
private final String value; private final String value;
public BasicResult(HttpHeader header, URI uri, String value) public BasicResult(HttpHeader header, String value)
{ {
this.header = header; this.header = header;
this.uri = uri;
this.value = value; this.value = value;
} }
@Override @Override
public URI getURI() public URI getURI()
{ {
return uri; return BasicAuthentication.this.getURI();
} }
@Override @Override
@ -105,7 +93,7 @@ public class BasicAuthentication implements Authentication
@Override @Override
public String toString() public String toString()
{ {
return String.format("Basic authentication result for %s", uri); return String.format("Basic authentication result for %s", getURI());
} }
} }
} }

View File

@ -23,7 +23,6 @@ import java.nio.charset.StandardCharsets;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@ -34,7 +33,6 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.eclipse.jetty.client.HttpClient; 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.AuthenticationStore;
import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Request;
@ -50,12 +48,10 @@ import org.eclipse.jetty.util.TypeUtil;
* {@link AuthenticationStore} retrieved from the {@link HttpClient} * {@link AuthenticationStore} retrieved from the {@link HttpClient}
* via {@link HttpClient#getAuthenticationStore()}. * via {@link HttpClient#getAuthenticationStore()}.
*/ */
public class DigestAuthentication implements Authentication public class DigestAuthentication extends AbstractAuthentication
{ {
private static final Pattern PARAM_PATTERN = Pattern.compile("([^=]+)=(.*)"); private static final Pattern PARAM_PATTERN = Pattern.compile("([^=]+)=(.*)");
private final URI uri;
private final String realm;
private final String user; private final String user;
private final String password; private final String password;
@ -67,22 +63,15 @@ public class DigestAuthentication implements Authentication
*/ */
public DigestAuthentication(URI uri, String realm, String user, String password) public DigestAuthentication(URI uri, String realm, String user, String password)
{ {
this.uri = uri; super(uri, realm);
this.realm = realm;
this.user = user; this.user = user;
this.password = password; this.password = password;
} }
@Override @Override
public boolean matches(String type, URI uri, String realm) public String getType()
{ {
if (!"digest".equalsIgnoreCase(type)) return "Digest";
return false;
if (!uri.toString().startsWith(this.uri.toString()))
return false;
return this.realm.equals(realm);
} }
@Override @Override
@ -110,7 +99,7 @@ public class DigestAuthentication implements Authentication
clientQOP = "auth-int"; 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<String, String> parseParameters(String wwwAuthenticate) private Map<String, String> parseParameters(String wwwAuthenticate)
@ -181,7 +170,6 @@ public class DigestAuthentication implements Authentication
{ {
private final AtomicInteger nonceCount = new AtomicInteger(); private final AtomicInteger nonceCount = new AtomicInteger();
private final HttpHeader header; private final HttpHeader header;
private final URI uri;
private final byte[] content; private final byte[] content;
private final String realm; private final String realm;
private final String user; private final String user;
@ -191,10 +179,9 @@ public class DigestAuthentication implements Authentication
private final String qop; private final String qop;
private final String opaque; 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.header = header;
this.uri = uri;
this.content = content; this.content = content;
this.realm = realm; this.realm = realm;
this.user = user; this.user = user;
@ -208,7 +195,7 @@ public class DigestAuthentication implements Authentication
@Override @Override
public URI getURI() public URI getURI()
{ {
return uri; return DigestAuthentication.this.getURI();
} }
@Override @Override

View File

@ -125,7 +125,7 @@ public class HostnameVerificationTest
if (cause instanceof SSLHandshakeException) if (cause instanceof SSLHandshakeException)
Assert.assertThat(cause.getCause().getCause(), Matchers.instanceOf(CertificateException.class)); Assert.assertThat(cause.getCause().getCause(), Matchers.instanceOf(CertificateException.class));
else else
Assert.assertThat(cause.getCause(), Matchers.instanceOf(ClosedChannelException.class)); Assert.assertThat(cause, Matchers.instanceOf(ClosedChannelException.class));
} }
} }

View File

@ -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);
}
}

View File

@ -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.
}
}
}

View File

@ -18,8 +18,6 @@
package org.eclipse.jetty.client.ssl; package org.eclipse.jetty.client.ssl;
import static org.hamcrest.Matchers.nullValue;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.EOFException; import java.io.EOFException;
import java.io.File; import java.io.File;
@ -368,7 +366,7 @@ public class SslBytesServerTest extends SslBytesTest
System.arraycopy(doneBytes, 0, chunk, recordBytes.length, doneBytes.length); System.arraycopy(doneBytes, 0, chunk, recordBytes.length, doneBytes.length);
System.arraycopy(closeRecordBytes, 0, chunk, recordBytes.length + doneBytes.length, closeRecordBytes.length); System.arraycopy(closeRecordBytes, 0, chunk, recordBytes.length + doneBytes.length, closeRecordBytes.length);
proxy.flushToServer(0, chunk); proxy.flushToServer(0, chunk);
// Close the raw socket // Close the raw socket
proxy.flushToServer(null); proxy.flushToServer(null);
@ -380,7 +378,7 @@ public class SslBytesServerTest extends SslBytesTest
Assert.assertEquals(Type.ALERT,record.getType()); Assert.assertEquals(Type.ALERT,record.getType());
record = proxy.readFromServer(); record = proxy.readFromServer();
} }
Assert.assertNull(record); Assert.assertNull(record);
// Check that we did not spin // Check that we did not spin
@ -488,7 +486,7 @@ public class SslBytesServerTest extends SslBytesTest
if (record!=null) if (record!=null)
{ {
Assert.assertEquals(record.getType(),Type.ALERT); Assert.assertEquals(record.getType(),Type.ALERT);
// Now should be a raw close // Now should be a raw close
record = proxy.readFromServer(); record = proxy.readFromServer();
Assert.assertNull(String.valueOf(record), record); Assert.assertNull(String.valueOf(record), record);
@ -784,7 +782,7 @@ public class SslBytesServerTest extends SslBytesTest
if (record!=null) if (record!=null)
{ {
Assert.assertEquals(record.getType(),Type.ALERT); Assert.assertEquals(record.getType(),Type.ALERT);
// Now should be a raw close // Now should be a raw close
record = proxy.readFromServer(); record = proxy.readFromServer();
Assert.assertNull(String.valueOf(record), record); Assert.assertNull(String.valueOf(record), record);
@ -846,7 +844,7 @@ public class SslBytesServerTest extends SslBytesTest
if (record!=null) if (record!=null)
{ {
Assert.assertEquals(record.getType(),Type.ALERT); Assert.assertEquals(record.getType(),Type.ALERT);
// Now should be a raw close // Now should be a raw close
record = proxy.readFromServer(); record = proxy.readFromServer();
Assert.assertNull(String.valueOf(record), record); Assert.assertNull(String.valueOf(record), record);
@ -921,7 +919,7 @@ public class SslBytesServerTest extends SslBytesTest
if (record!=null) if (record!=null)
{ {
Assert.assertEquals(record.getType(),Type.ALERT); Assert.assertEquals(record.getType(),Type.ALERT);
// Now should be a raw close // Now should be a raw close
record = proxy.readFromServer(); record = proxy.readFromServer();
Assert.assertNull(String.valueOf(record), record); Assert.assertNull(String.valueOf(record), record);
@ -983,7 +981,7 @@ public class SslBytesServerTest extends SslBytesTest
if (record!=null) if (record!=null)
{ {
Assert.assertEquals(record.getType(),Type.ALERT); Assert.assertEquals(record.getType(),Type.ALERT);
// Now should be a raw close // Now should be a raw close
record = proxy.readFromServer(); record = proxy.readFromServer();
Assert.assertNull(String.valueOf(record), record); Assert.assertNull(String.valueOf(record), record);
@ -1040,7 +1038,7 @@ public class SslBytesServerTest extends SslBytesTest
if (record!=null) if (record!=null)
{ {
Assert.assertEquals(record.getType(),Type.ALERT); Assert.assertEquals(record.getType(),Type.ALERT);
// Now should be a raw close // Now should be a raw close
record = proxy.readFromServer(); record = proxy.readFromServer();
Assert.assertNull(String.valueOf(record), record); Assert.assertNull(String.valueOf(record), record);
@ -1060,7 +1058,7 @@ public class SslBytesServerTest extends SslBytesTest
{ {
// Don't run on Windows (buggy JVM) // Don't run on Windows (buggy JVM)
Assume.assumeTrue(!OS.IS_WINDOWS); Assume.assumeTrue(!OS.IS_WINDOWS);
final SSLSocket client = newClient(); final SSLSocket client = newClient();
SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow(); SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow();
@ -1121,7 +1119,7 @@ public class SslBytesServerTest extends SslBytesTest
{ {
// Don't run on Windows (buggy JVM) // Don't run on Windows (buggy JVM)
Assume.assumeTrue(!OS.IS_WINDOWS); Assume.assumeTrue(!OS.IS_WINDOWS);
final SSLSocket client = newClient(); final SSLSocket client = newClient();
SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow(); SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow();
@ -1247,7 +1245,7 @@ public class SslBytesServerTest extends SslBytesTest
if (record!=null) if (record!=null)
{ {
Assert.assertEquals(record.getType(),Type.ALERT); Assert.assertEquals(record.getType(),Type.ALERT);
// Now should be a raw close // Now should be a raw close
record = proxy.readFromServer(); record = proxy.readFromServer();
Assert.assertNull(String.valueOf(record), record); 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 // 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)); 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(); TLSRecord record = proxy.readFromServer();
Assert.assertNotNull(record);
Assert.assertEquals(TLSRecord.Type.ALERT, record.getType());
record = proxy.readFromServer();
Assert.assertNull(String.valueOf(record), record); Assert.assertNull(String.valueOf(record), record);
// Check that we did not spin // Check that we did not spin
@ -1982,6 +1983,6 @@ public class SslBytesServerTest extends SslBytesTest
Assert.assertEquals(record.getType(),Type.ALERT); Assert.assertEquals(record.getType(),Type.ALERT);
record = proxy.readFromServer(); record = proxy.readFromServer();
} }
Assert.assertThat(record,nullValue()); Assert.assertThat(record, Matchers.nullValue());
} }
} }

View File

@ -28,7 +28,6 @@ import java.net.Socket;
import java.net.SocketTimeoutException; import java.net.SocketTimeoutException;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future; import java.util.concurrent.Future;
@ -49,16 +48,16 @@ public abstract class SslBytesTest
public static class TLSRecord public static class TLSRecord
{ {
private final SslBytesServerTest.TLSRecord.Type type; private final Type type;
private final byte[] bytes; private final byte[] bytes;
public TLSRecord(SslBytesServerTest.TLSRecord.Type type, byte[] bytes) public TLSRecord(Type type, byte[] bytes)
{ {
this.type = type; this.type = type;
this.bytes = bytes; this.bytes = bytes;
} }
public SslBytesServerTest.TLSRecord.Type getType() public Type getType()
{ {
return type; return type;
} }
@ -80,15 +79,15 @@ public abstract class SslBytesTest
private int code; private int code;
private Type(int code) Type(int code)
{ {
this.code = 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) if (result == null)
throw new IllegalArgumentException("Invalid TLSRecord.Type " + code); throw new IllegalArgumentException("Invalid TLSRecord.Type " + code);
return result; return result;
@ -96,7 +95,7 @@ public abstract class SslBytesTest
private static class Mapper private static class Mapper
{ {
private static final Map<Integer, SslBytesServerTest.TLSRecord.Type> codes = new HashMap<>(); private static final Map<Integer, Type> 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) 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 startLatch = new CountDownLatch(2);
final CountDownLatch stopLatch = new CountDownLatch(2); final CountDownLatch stopLatch = new CountDownLatch(2);
Future<Object> clientToServer = threadPool.submit(new Callable<Object>() Future<Object> clientToServer = threadPool.submit(() ->
{ {
public Object call() throws Exception startLatch.countDown();
logger.debug("Automatic flow C --> S started");
try
{ {
startLatch.countDown(); while (true)
logger.debug("Automatic flow C --> S started");
try
{ {
while (true) flushToServer(readFromClient(), 0);
{
flushToServer(readFromClient(), 0);
}
}
catch (InterruptedIOException x)
{
return null;
}
finally
{
stopLatch.countDown();
logger.debug("Automatic flow C --> S finished");
} }
} }
}); catch (InterruptedIOException x)
Future<Object> serverToClient = threadPool.submit(new Callable<Object>()
{
public Object call() throws Exception
{ {
startLatch.countDown(); return null;
logger.debug("Automatic flow C <-- S started"); }
try finally
{
stopLatch.countDown();
logger.debug("Automatic flow C --> S finished");
}
});
Future<Object> serverToClient = threadPool.submit(() ->
{
startLatch.countDown();
logger.debug("Automatic flow C <-- S started");
try
{
while (true)
{ {
while (true) flushToClient(readFromServer());
{
flushToClient(readFromServer());
}
}
catch (InterruptedIOException x)
{
return null;
}
finally
{
stopLatch.countDown();
logger.debug("Automatic flow C <-- S finished");
} }
} }
catch (InterruptedIOException x)
{
return null;
}
finally
{
stopLatch.countDown();
logger.debug("Automatic flow C <-- S finished");
}
}); });
Assert.assertTrue(startLatch.await(5, TimeUnit.SECONDS)); Assert.assertTrue(startLatch.await(5, TimeUnit.SECONDS));
return new SslBytesServerTest.SimpleProxy.AutomaticFlow(stopLatch, clientToServer, serverToClient); return new SslBytesServerTest.SimpleProxy.AutomaticFlow(stopLatch, clientToServer, serverToClient);

View File

@ -185,11 +185,9 @@ public class MultiPartContentProviderTest extends AbstractHttpClientServerTest
.content(multiPart) .content(multiPart)
.send(result -> .send(result ->
{ {
if (result.isSucceeded()) Assert.assertTrue(String.valueOf(result.getFailure()), result.isSucceeded());
{ Assert.assertEquals(200, result.getResponse().getStatus());
Assert.assertEquals(200, result.getResponse().getStatus()); responseLatch.countDown();
responseLatch.countDown();
}
}); });
// Wait until the request has been sent. // Wait until the request has been sent.
@ -408,11 +406,9 @@ public class MultiPartContentProviderTest extends AbstractHttpClientServerTest
.content(multiPart) .content(multiPart)
.send(result -> .send(result ->
{ {
if (result.isSucceeded()) Assert.assertTrue(String.valueOf(result.getFailure()), result.isSucceeded());
{ Assert.assertEquals(200, result.getResponse().getStatus());
Assert.assertEquals(200, result.getResponse().getStatus()); responseLatch.countDown();
responseLatch.countDown();
}
}); });
// Wait until the request has been sent. // Wait until the request has been sent.

View File

@ -19,20 +19,6 @@
package org.eclipse.jetty.gcloud.session; 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.Blob;
import com.google.gcloud.datastore.Datastore; import com.google.gcloud.datastore.Datastore;
import com.google.gcloud.datastore.DatastoreFactory; 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.ProjectionEntityQueryBuilder;
import com.google.gcloud.datastore.StructuredQuery.PropertyFilter; 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 * GCloudSessionDataStore
* *
@ -78,11 +79,6 @@ public class GCloudSessionDataStore extends AbstractSessionDataStore
private KeyFactory _keyFactory; private KeyFactory _keyFactory;
private int _maxResults = DEFAULT_MAX_QUERY_RESULTS; 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 @Override
public Set<String> getExpired(Set<String> candidates) public Set<String> doGetExpired(Set<String> candidates, int expiryTimeoutSec)
{ {
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
Set<String> expired = new HashSet<String>(); Set<String> expired = new HashSet<String>();
//get up to maxResult number of sessions that have expired try
ProjectionEntityQueryBuilder pbuilder = Query.projectionEntityQueryBuilder(); {
pbuilder.addProjection(Projection.property(ID)); //get up to maxResult number of sessions that have expired
pbuilder.filter(CompositeFilter.and(PropertyFilter.gt(EXPIRY, 0), PropertyFilter.le(EXPIRY, now))); ProjectionEntityQueryBuilder pbuilder = Query.projectionEntityQueryBuilder();
pbuilder.limit(_maxResults); pbuilder.addProjection(Projection.property(ID), Projection.property(LASTNODE), Projection.property(EXPIRY));
pbuilder.kind(KIND); pbuilder.filter(CompositeFilter.and(PropertyFilter.gt(EXPIRY, 0), PropertyFilter.le(EXPIRY, now)));
StructuredQuery<ProjectionEntity> pquery = pbuilder.build(); pbuilder.limit(_maxResults);
QueryResults<ProjectionEntity> presults = _datastore.run(pquery); pbuilder.kind(KIND);
StructuredQuery<ProjectionEntity> pquery = pbuilder.build();
while (presults.hasNext()) QueryResults<ProjectionEntity> presults = _datastore.run(pquery);
{
ProjectionEntity pe = presults.next(); while (presults.hasNext())
String id = pe.getString(ID); {
expired.add(id); ProjectionEntity pe = presults.next();
} String id = pe.getString(ID);
String lastNode = pe.getString(LASTNODE);
//reconcile against ids that the SessionStore thinks are expired long expiry = pe.getLong(EXPIRY);
Set<String> tmp = new HashSet<String>(candidates);
tmp.removeAll(expired); if (StringUtil.isBlank(lastNode))
if (!tmp.isEmpty()) expired.add(id); //nobody managing it
{ else
//sessionstore thinks these are expired, but they are either no {
//longer in the db or not expired in the db, or we exceeded the if (_context.getWorkerName().equals(lastNode))
//number of records retrieved by the expiry query, so check them expired.add(id); //we're managing it, we can expire it
//individually else
for (String s:tmp) {
{ if (_lastExpiryCheckTime <= 0)
try {
{ //our first check, just look for sessions that we managed by another node that
KeyQueryBuilder kbuilder = Query.keyQueryBuilder(); //expired at least 3 graceperiods ago
kbuilder.filter(PropertyFilter.eq(ID, s)); if (expiry < (now - (1000L * (3 * _gracePeriodSec))))
kbuilder.kind(KIND); expired.add(id);
StructuredQuery<Key> kq = kbuilder.build(); }
QueryResults<Key> kresults = _datastore.run(kq); else
if (!kresults.hasNext()) {
expired.add(s); //not in db, can be expired //another node was last managing it, only expire it if it expired a graceperiod ago
} if (expiry < (now - (1000L * _gracePeriodSec)))
catch (Exception e) expired.add(id);
{ }
LOG.warn(e); }
} }
} }
}
//reconcile against ids that the SessionStore thinks are expired
return expired; Set<String> tmp = new HashSet<String>(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<Key> kq = kbuilder.build();
QueryResults<Key> 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
* </ol> * </ol>
* *
* *
* @param session * @param id the id
* @return * @param context the session context
* @return the key
*/ */
private Key makeKey (String id, SessionContext context) private Key makeKey (String id, SessionContext context)
{ {
@ -279,9 +309,9 @@ public class GCloudSessionDataStore extends AbstractSessionDataStore
/** /**
* Generate a gcloud datastore Entity from SessionData * Generate a gcloud datastore Entity from SessionData
* @param session * @param session the session data
* @param key * @param key the key
* @return * @return the entity
* @throws Exception * @throws Exception
*/ */
private Entity entityFromSession (SessionData session, Key key) 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.writeObject(session.getAllAttributes());
oos.flush(); oos.flush();
try
{
//turn a session into an entity //turn a session into an entity
entity = Entity.builder(key) entity = Entity.builder(key)
.set(ID, session.getId()) .set(ID, session.getId())
@ -310,6 +342,12 @@ public class GCloudSessionDataStore extends AbstractSessionDataStore
.set(EXPIRY, session.getExpiry()) .set(EXPIRY, session.getExpiry())
.set(MAXINACTIVE, session.getMaxInactiveMs()) .set(MAXINACTIVE, session.getMaxInactiveMs())
.set(ATTRIBUTES, Blob.copyFrom(baos.toByteArray())).build(); .set(ATTRIBUTES, Blob.copyFrom(baos.toByteArray())).build();
}
catch (Exception e)
{
e.printStackTrace();
throw e;
}
return entity; return entity;
} }

View File

@ -18,11 +18,6 @@
package org.eclipse.jetty.gcloud.session; 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.AbstractSessionStore;
import org.eclipse.jetty.server.session.MemorySessionStore; import org.eclipse.jetty.server.session.MemorySessionStore;
import org.eclipse.jetty.server.session.SessionManager; import org.eclipse.jetty.server.session.SessionManager;
@ -49,9 +44,7 @@ public class GCloudSessionManager extends SessionManager
/* /* *
*//**
* Session * Session
* *
* Representation of a session in local memory. * Representation of a session in local memory.
@ -75,7 +68,8 @@ public class GCloudSessionManager extends SessionManager
*//** */
/* *
* Called on entry to the session. * Called on entry to the session.
* *
* @see org.eclipse.jetty.server.session.AbstractSession#access(long) * @see org.eclipse.jetty.server.session.AbstractSession#access(long)
@ -122,7 +116,7 @@ public class GCloudSessionManager extends SessionManager
} }
*//** *//* *
* Exit from session * Exit from session
* @see org.eclipse.jetty.server.session.AbstractSession#complete() * @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 * @param atTime
* @return * @return
*//* *//*
@ -184,7 +178,7 @@ public class GCloudSessionManager extends SessionManager
} }
*//** *//* *
* Reload the session from the cluster. If the node that * Reload the session from the cluster. If the node that
* last managed the session from the cluster is ourself, * last managed the session from the cluster is ourself,
* then the session does not need refreshing. * then the session does not need refreshing.
@ -295,7 +289,7 @@ public class GCloudSessionManager extends SessionManager
/** /**
* Start the session manager. * Start the session manager.
* *
* @see org.eclipse.jetty.server.session.AbstractSessionManager#doStart() * @see org.eclipse.jetty.server.session.SessionManager#doStart()
*/ */
@Override @Override
public void doStart() throws Exception public void doStart() throws Exception
@ -308,7 +302,7 @@ public class GCloudSessionManager extends SessionManager
/** /**
* Stop the session manager. * Stop the session manager.
* *
* @see org.eclipse.jetty.server.session.AbstractSessionManager#doStop() * @see org.eclipse.jetty.server.session.SessionManager#doStop()
*/ */
@Override @Override
public void doStop() throws Exception public void doStop() throws Exception

View File

@ -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 * @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. * previously been allocated and returned by the {@link HttpContent#getDirectBuffer()} or {@link HttpContent#getIndirectBuffer()} calls.
* @return A {@link HttpContent} * @return A {@link HttpContent}
* @throws IOException * @throws IOException if unable to get content
*/ */
HttpContent getContent(String path,int maxBuffer) throws IOException; HttpContent getContent(String path,int maxBuffer) throws IOException;
} }

View File

@ -1174,7 +1174,7 @@ public class HttpParser
{ {
_value=null; _value=null;
_string.setLength(0); _string.setLength(0);
_valueString=_compliance.ordinal()<=HttpCompliance.RFC2616.ordinal()?"":null; _valueString="";
_length=-1; _length=-1;
setState(State.HEADER); setState(State.HEADER);

View File

@ -177,9 +177,10 @@ public class HttpURI
} }
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
/** Parse according to https://tools.ietf.org/html/rfc7230#section-5.3 /**
* @param method * Parse according to https://tools.ietf.org/html/rfc7230#section-5.3
* @param uri * @param method the request method
* @param uri the request uri
*/ */
public void parseRequestTarget(String method,String uri) public void parseRequestTarget(String method,String uri)
{ {

View File

@ -62,8 +62,7 @@ public enum PathSpecGroup
MIDDLE_GLOB, MIDDLE_GLOB,
/** /**
* For path specs that have a hardcoded prefix and a trailing wildcard glob. * For path specs that have a hardcoded prefix and a trailing wildcard glob.
* <p> *
*
* <pre> * <pre>
* "/downloads/*" - servlet spec * "/downloads/*" - servlet spec
* "/api/*" - servlet spec * "/api/*" - servlet spec

View File

@ -13,6 +13,7 @@ bin=application/octet-stream
bmp=image/bmp bmp=image/bmp
cab=application/x-cabinet cab=application/x-cabinet
cdf=application/x-netcdf cdf=application/x-netcdf
chm=application/vnd.ms-htmlhelp
class=application/java-vm class=application/java-vm
cpio=application/x-cpio cpio=application/x-cpio
cpt=application/mac-compactpro cpt=application/mac-compactpro

View File

@ -334,9 +334,8 @@ public class HttpParserTest
} }
@Test @Test
public void test2616NoValue() throws Exception public void testNoValue() throws Exception
{ {
ByteBuffer buffer= BufferUtil.toBuffer( ByteBuffer buffer= BufferUtil.toBuffer(
"GET / HTTP/1.0\015\012" + "GET / HTTP/1.0\015\012" +
@ -366,37 +365,6 @@ public class HttpParserTest
assertEquals(3, _headers); 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 @Test
public void testHeaderParseDirect() throws Exception public void testHeaderParseDirect() throws Exception
{ {

View File

@ -19,22 +19,24 @@
package org.eclipse.jetty.http; 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.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import org.eclipse.jetty.util.URIUtil;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.junit.runners.Parameterized; import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter; import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters; 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) @RunWith(Parameterized.class)
public class HttpURIParseTest 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#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",null,null,null,"/path/info;param","param","query",null},
{"/path/info;param?query#fragment",null,null,null,"/path/info;param","param","query","fragment"}, {"/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 // Protocol Less (aka scheme-less) URIs
{"//host/path/info",null,"host",null,"/path/info",null,null,null}, {"//host/path/info",null,"host",null,"/path/info",null,null,null},
{"//user@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) // Interpreted as relative path of "*" (no host/port/scheme/query/fragment)
{"*",null,null,null,"*",null, null,null}, {"*",null,null,null,"*",null, null,null},
// Path detection Tests (seen from JSP/JSTL and <c:url> use // Path detection Tests (seen from JSP/JSTL and <c:url> use)
{"http://host:8080/path/info?q1=v1&q2=v2","http","host","8080","/path/info",null,"q1=v1&q2=v2",null}, {"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}, {"/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",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}, {"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 <c:url> use // Path-less, query only (seen from JSP/JSTL and <c:url> use)
{"?q1=v1&q2=v2",null,null,null,"",null,"q1=v1&q2=v2",null} {"?q1=v1&q2=v2",null,null,null,"",null,"q1=v1&q2=v2",null}
}; };

View File

@ -91,12 +91,17 @@ public class ALPNNegotiationTest extends AbstractALPNTest
Assert.assertTrue(read > 0); Assert.assertTrue(read > 0);
// Cannot decrypt, as the SSLEngine has been already closed // 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(); encrypted.clear();
read = channel.read(encrypted); read = channel.read(encrypted);
// Sending a TLS Close Alert during handshake results in an exception when if (read > 0)
// unwrapping that the server react to by closing the connection abruptly. {
Assert.assertTrue(read < 0); encrypted.flip();
// TLS Alert message type == 21.
Assert.assertEquals(21, encrypted.get() & 0xFF);
encrypted.clear();
Assert.assertEquals(-1, channel.read(encrypted));
}
} }
} }

View File

@ -138,6 +138,11 @@ public class HttpClientTransportOverHTTP2 extends ContainerLifeCycle implements
return new HttpConnectionOverHTTP2(destination, session); return new HttpConnectionOverHTTP2(destination, session);
} }
protected void onClose(HttpConnectionOverHTTP2 connection, GoAwayFrame frame)
{
connection.close();
}
private class SessionListenerPromise extends Session.Listener.Adapter implements Promise<Session> private class SessionListenerPromise extends Session.Listener.Adapter implements Promise<Session>
{ {
private final HttpDestinationOverHTTP2 destination; private final HttpDestinationOverHTTP2 destination;
@ -182,7 +187,7 @@ public class HttpClientTransportOverHTTP2 extends ContainerLifeCycle implements
@Override @Override
public void onClose(Session session, GoAwayFrame frame) public void onClose(Session session, GoAwayFrame frame)
{ {
connection.close(); HttpClientTransportOverHTTP2.this.onClose(connection, frame);
} }
@Override @Override
@ -194,7 +199,9 @@ public class HttpClientTransportOverHTTP2 extends ContainerLifeCycle implements
@Override @Override
public void onFailure(Session session, Throwable failure) public void onFailure(Session session, Throwable failure)
{ {
connection.close(failure); HttpConnectionOverHTTP2 c = connection;
if (c != null)
c.close(failure);
} }
} }
} }

View File

@ -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 @Override
public SessionData load(String id) throws Exception 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 @Override
public boolean delete(String id) throws Exception 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 @Override
public Set<String> getExpired(Set<String> candidates) public Set<String> doGetExpired(Set<String> candidates, int expiryTimeoutSec)
{ {
if (candidates == null || candidates.isEmpty()) if (candidates == null || candidates.isEmpty())
return candidates; return candidates;
@ -143,6 +143,9 @@ public class InfinispanSessionDataStore extends AbstractSessionDataStore
Set<String> expired = new HashSet<String>(); Set<String> expired = new HashSet<String>();
//TODO if there is NOT an idle timeout set, need to check other sessions that
//might have expired
for (String candidate:candidates) for (String candidate:candidates)
{ {
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())
@ -151,12 +154,43 @@ public class InfinispanSessionDataStore extends AbstractSessionDataStore
{ {
SessionData sd = load(candidate); SessionData sd = load(candidate);
if (sd == null || sd.isExpiredAt(now)) //if the session no longer exists
if (sd == null)
{ {
expired.add(candidate); expired.add(candidate);
if (LOG.isDebugEnabled()) 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) 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 @Override
public void doStore(String id, SessionData data, long lastSaveTime) throws Exception 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) public static String getCacheKey (String id, SessionContext context)
{ {
return context.getCanonicalContextPath()+"_"+context.getVhost()+"_"+id; 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) public void setInfinispanIdleTimeoutSec (int sec)
{ {
_infinispanIdleTimeoutSec = sec; _infinispanIdleTimeoutSec = sec;
} }
/**
* @return
*/
public int getInfinispanIdleTimeoutSec () public int getInfinispanIdleTimeoutSec ()
{ {
return _infinispanIdleTimeoutSec; return _infinispanIdleTimeoutSec;

View File

@ -21,17 +21,9 @@ package org.eclipse.jetty.session.infinispan;
import java.util.Random; import java.util.Random;
import java.util.concurrent.TimeUnit; 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.Server;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.session.AbstractSessionIdManager; import org.eclipse.jetty.server.session.AbstractSessionIdManager;
import org.eclipse.jetty.server.session.Session; 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.Log;
import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.log.Logger;
import org.infinispan.commons.api.BasicCache; 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 @Override
public void useId(Session session) public void useId(Session session)

View File

@ -51,7 +51,7 @@ public class InfinispanSessionManager extends SessionManager
/** /**
* Start the session manager. * Start the session manager.
* *
* @see org.eclipse.jetty.server.session.AbstractSessionManager#doStart() * @see org.eclipse.jetty.server.session.SessionManager#doStart()
*/ */
@Override @Override
public void doStart() throws Exception public void doStart() throws Exception
@ -69,7 +69,7 @@ public class InfinispanSessionManager extends SessionManager
/** /**
* Stop the session manager. * Stop the session manager.
* *
* @see org.eclipse.jetty.server.session.AbstractSessionManager#doStop() * @see org.eclipse.jetty.server.session.SessionManager#doStop()
*/ */
@Override @Override
public void doStop() throws Exception public void doStop() throws Exception

View File

@ -138,7 +138,7 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
private ManagedSelector chooseSelector(SelectableChannel channel) private ManagedSelector chooseSelector(SelectableChannel channel)
{ {
// Ideally we would like to have all connections from the same client end // 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 // 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 // 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. // 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); LOG.warn("Exception while notifying connection " + connection, x);
else else
LOG.debug("Exception while notifying connection " + connection, x); LOG.debug("Exception while notifying connection " + connection, x);
throw x;
} }
} }

View File

@ -678,12 +678,12 @@ public class SslConnection extends AbstractConnection
{ {
// Some internal error in SSLEngine // Some internal error in SSLEngine
LOG.debug(e); LOG.debug(e);
getEndPoint().close(); close();
throw new EofException(e); throw new EofException(e);
} }
catch (Exception e) catch (Exception e)
{ {
getEndPoint().close(); close();
throw e; throw e;
} }
finally finally

View File

@ -40,15 +40,6 @@ public abstract class NoSqlSessionDataStore extends AbstractSessionDataStore
private Set<String> _dirtyAttributes = new HashSet<String>(); private Set<String> _dirtyAttributes = new HashSet<String>();
/**
* @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) public NoSqlSessionData(String id, String cpath, String vhost, long created, long accessed, long lastAccessed, long maxInactiveMs)
{ {
super(id, cpath, vhost, created, accessed, lastAccessed, maxInactiveMs); super(id, cpath, vhost, created, accessed, lastAccessed, maxInactiveMs);

View File

@ -19,6 +19,12 @@
package org.eclipse.jetty.nosql.mongodb; 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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; 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.Log;
import org.eclipse.jetty.util.log.Logger; 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 * MongoSessionDataStore
* *
@ -153,43 +152,21 @@ public class MongoSessionDataStore extends NoSqlSessionDataStore
private DBCollection _dbSessions; private DBCollection _dbSessions;
private long _gracePeriodMs = 1000L * 60 * 60; //default grace period is 1hr
public void setDBCollection (DBCollection collection) public void setDBCollection (DBCollection collection)
{ {
_dbSessions = collection; _dbSessions = collection;
} }
/**
* @return
*/
public DBCollection getDBCollection () public DBCollection getDBCollection ()
{ {
return _dbSessions; 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 @Override
public SessionData load(String id) throws Exception 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 @Override
public boolean delete(String id) throws Exception 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 @Override
public Set<String> getExpired(Set<String> candidates) public Set<String> doGetExpired(Set<String> candidates, int expiryTimeoutSec)
{ {
long upperBound = System.currentTimeMillis(); long now = System.currentTimeMillis();
long upperBound = now;
Set<String> expiredSessions = new HashSet<>(); Set<String> 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(); BasicDBObject query = new BasicDBObject();
query.put(__ID,new BasicDBObject("$in", candidates)); query.put(__ID,new BasicDBObject("$in", candidates));
query.put(__EXPIRY, new BasicDBObject("$gt", 0)); query.put(__EXPIRY, new BasicDBObject("$gt", 0));
@ -369,9 +348,13 @@ public class MongoSessionDataStore extends NoSqlSessionDataStore
if (verifiedExpiredSessions != null) verifiedExpiredSessions.close(); if (verifiedExpiredSessions != null) verifiedExpiredSessions.close();
} }
//now ask mongo to find sessions last managed by other nodes that expired a while ago
//now ask mongo to find sessions that expired a while ago //if this is our first expiry check, make sure that we only grab really old sessions
upperBound = upperBound - (3 * _gracePeriodMs); if (_lastExpiryCheckTime <= 0)
upperBound = (now - (3*(1000L * _gracePeriodSec)));
else
upperBound = _lastExpiryCheckTime - (1000L * _gracePeriodSec);
query.clear(); query.clear();
query.put(__EXPIRY, new BasicDBObject("$gt", 0)); query.put(__EXPIRY, new BasicDBObject("$gt", 0));
query.put(__EXPIRY, new BasicDBObject("$lt", upperBound)); 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 @Override
public void doStore(String id, SessionData data, long lastSaveTime) throws Exception public void doStore(String id, SessionData data, long lastSaveTime) throws Exception

View File

@ -18,21 +18,20 @@
package org.eclipse.jetty.nosql.mongodb; package org.eclipse.jetty.nosql.mongodb;
import com.mongodb.DBCollection;
import com.mongodb.MongoException;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import org.eclipse.jetty.server.SessionIdManager; import org.eclipse.jetty.server.SessionIdManager;
import org.eclipse.jetty.server.session.AbstractSessionStore; import org.eclipse.jetty.server.session.AbstractSessionStore;
import org.eclipse.jetty.server.session.MemorySessionStore; 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.server.session.SessionManager;
import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject; import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.log.Logger;
import com.mongodb.DBCollection;
import com.mongodb.MongoException;
/** /**
* MongoSessionManager * MongoSessionManager
@ -116,9 +115,6 @@ public class MongoSessionManager extends SessionManager
} }
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.server.session.AbstractSessionManager#setSessionIdManager(org.eclipse.jetty.server.SessionIdManager)
*/
@Override @Override
public void setSessionIdManager(SessionIdManager metaManager) public void setSessionIdManager(SessionIdManager metaManager)
{ {

View File

@ -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 override {@link #logExtended(StringBuilder, Request, Response)} instead
*/ */
@Deprecated @Deprecated

View File

@ -179,7 +179,7 @@ public interface PushBuilder
* Set the URI path to be used for the push. The path may start * 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, * with "/" in which case it is treated as an absolute path,
* otherwise it is relative to the context path of the associated * 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 <code>path(String)</code> must
* be called before every call to {@link #push()}. If a query * be called before every call to {@link #push()}. If a query
* string is present in the argument {@code path}, its contents must * string is present in the argument {@code path}, its contents must
* be merged with the contents previously passed to {@link * be merged with the contents previously passed to {@link

View File

@ -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 <code>takeNewContext()</code> since the last
* {@link #setContext(org.eclipse.jetty.server.handler.ContextHandler.Context)} call. * {@link #setContext(org.eclipse.jetty.server.handler.ContextHandler.Context)} call.
*/ */
public boolean takeNewContext() public boolean takeNewContext()

View File

@ -56,6 +56,8 @@ public class SecureRequestCustomizer implements HttpConfiguration.Customizer
*/ */
public static final String CACHED_INFO_ATTR = CachedInfo.class.getName(); public static final String CACHED_INFO_ATTR = CachedInfo.class.getName();
private String sslSessionAttribute = "org.eclipse.jetty.servlet.request.ssl_session";
private boolean _sniHostCheck; private boolean _sniHostCheck;
private long _stsMaxAge=-1; private long _stsMaxAge=-1;
private boolean _stsIncludeSubDomains; 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.cipher_suite",cipherSuite);
request.setAttribute("javax.servlet.request.key_size",keySize); request.setAttribute("javax.servlet.request.key_size",keySize);
request.setAttribute("javax.servlet.request.ssl_session_id", idStr); request.setAttribute("javax.servlet.request.ssl_session_id", idStr);
request.setAttribute(getSslSessionAttribute(), sslSession);
} }
catch (Exception e) catch (Exception e)
{ {
LOG.warn(Log.EXCEPTION,e); LOG.warn(Log.EXCEPTION,e);
} }
} }
public void setSslSessionAttribute(String attribute)
{
this.sslSessionAttribute = attribute;
}
public String getSslSessionAttribute()
{
return sslSessionAttribute;
}
@Override @Override
public String toString() public String toString()

View File

@ -32,15 +32,9 @@ import org.eclipse.jetty.server.session.SessionHandler;
import org.eclipse.jetty.server.session.SessionStore; import org.eclipse.jetty.server.session.SessionStore;
import org.eclipse.jetty.util.component.LifeCycle; import org.eclipse.jetty.util.component.LifeCycle;
/* --------------------------------------------------------------------- */
/** /**
* Session Manager. * Session Manager.
* The API required to manage sessions for a servlet context. * The API required to manage sessions for a servlet context.
*
*/
/* ------------------------------------------------------------ */
/**
*/ */
public interface SessionManager extends LifeCycle public interface SessionManager extends LifeCycle
{ {
@ -313,7 +307,7 @@ public interface SessionManager extends LifeCycle
/** /**
* Get the session store for this manager * Get the session store for this manager
* @return * @return the session store
*/ */
public SessionStore getSessionStore(); public SessionStore getSessionStore();

View File

@ -27,10 +27,12 @@ import java.net.InetSocketAddress;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.net.Socket; import java.net.Socket;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Properties; import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set; 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.Destroyable;
import org.eclipse.jetty.util.component.LifeCycle; import org.eclipse.jetty.util.component.LifeCycle;
@ -39,19 +41,20 @@ import org.eclipse.jetty.util.thread.ShutdownThread;
/** /**
* Shutdown/Stop Monitor thread. * Shutdown/Stop Monitor thread.
* <p> * <p>
* 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 * This thread listens on the host/port specified by the STOP.HOST/STOP.PORT
* request authenticated with the key given by the STOP.KEY system parameter (defaults to "eclipse") for admin requests. * 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.
* <p> * <p>
* 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.
* <p> * <p>
* Commands "stop" and "status" are currently supported. * Commands "stop" and "status" are currently supported.
*/ */
public class ShutdownMonitor public class ShutdownMonitor
{ {
private final Set<LifeCycle> _lifeCycles = new CopyOnWriteArraySet<LifeCycle>();
// Implementation of safe lazy init, using Initialization on Demand Holder technique. // Implementation of safe lazy init, using Initialization on Demand Holder technique.
static class Holder private static class Holder
{ {
static ShutdownMonitor instance = new ShutdownMonitor(); static ShutdownMonitor instance = new ShutdownMonitor();
} }
@ -60,283 +63,32 @@ public class ShutdownMonitor
{ {
return Holder.instance; return Holder.instance;
} }
/* ------------------------------------------------------------ */ public static void register(LifeCycle... lifeCycles)
public static synchronized void register(LifeCycle... lifeCycles)
{ {
getInstance()._lifeCycles.addAll(Arrays.asList(lifeCycles)); getInstance().addLifeCycles(lifeCycles);
} }
public static void deregister(LifeCycle lifeCycle)
/* ------------------------------------------------------------ */
public static synchronized void deregister(LifeCycle lifeCycle)
{ {
getInstance()._lifeCycles.remove(lifeCycle); getInstance().removeLifeCycle(lifeCycle);
} }
/* ------------------------------------------------------------ */ public static boolean isRegistered(LifeCycle lifeCycle)
public static synchronized 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) private final Set<LifeCycle> _lifeCycles = new LinkedHashSet<>();
{ private boolean debug;
Socket socket = null; private final String host;
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 int port; private int port;
private String key; private String key;
private boolean exitVm; private boolean exitVm;
private ServerSocket serverSocket; private boolean alive;
private Thread thread;
/** /**
* Create a ShutdownMonitor using configuration from the System properties. * Creates a ShutdownMonitor using configuration from the System properties.
* <p> * <p>
* <code>STOP.PORT</code> = the port to listen on (empty, null, or values less than 0 disable the stop ability)<br> * <code>STOP.PORT</code> = the port to listen on (empty, null, or values less than 0 disable the stop ability)<br>
* <code>STOP.KEY</code> = the magic key/passphrase to allow the stop (defaults to "eclipse")<br> * <code>STOP.KEY</code> = the magic key/passphrase to allow the stop (defaults to "eclipse")<br>
@ -345,106 +97,76 @@ public class ShutdownMonitor
*/ */
private ShutdownMonitor() private ShutdownMonitor()
{ {
this.DEBUG = System.getProperty("DEBUG") != null; this.debug = System.getProperty("DEBUG") != null;
this.host = System.getProperty("STOP.HOST", "127.0.0.1");
// Use values passed thru via /jetty-start/ this.port = Integer.parseInt(System.getProperty("STOP.PORT", "-1"));
this.host = System.getProperty("STOP.HOST","127.0.0.1"); this.key = System.getProperty("STOP.KEY", null);
this.port = Integer.parseInt(System.getProperty("STOP.PORT","-1"));
this.key = System.getProperty("STOP.KEY",null);
this.exitVm = true; this.exitVm = true;
} }
private void close(ServerSocket server) private void addLifeCycles(LifeCycle... lifeCycles)
{ {
if (server == null) synchronized (this)
{ {
return; _lifeCycles.addAll(Arrays.asList(lifeCycles));
}
try
{
server.close();
}
catch (IOException ignore)
{
debug(ignore);
} }
} }
private void close(Socket socket) private void removeLifeCycle(LifeCycle lifeCycle)
{ {
if (socket == null) synchronized (this)
{ {
return; _lifeCycles.remove(lifeCycle);
}
try
{
socket.close();
}
catch (IOException ignore)
{
debug(ignore);
} }
} }
private boolean containsLifeCycle(LifeCycle lifeCycle)
private void shutdownInput(Socket socket)
{ {
if (socket == null) synchronized (this)
return;
try
{ {
socket.shutdownInput(); return _lifeCycles.contains(lifeCycle);
}
catch (IOException ignore)
{
debug(ignore);
} }
} }
private void debug(String format, Object... args) private void debug(String format, Object... args)
{ {
if (DEBUG) if (debug)
{ System.err.printf("[ShutdownMonitor] " + format + "%n", args);
System.err.printf("[ShutdownMonitor] " + format + "%n",args);
}
} }
private void debug(Throwable t) private void debug(Throwable t)
{ {
if (DEBUG) if (debug)
{
t.printStackTrace(System.err); t.printStackTrace(System.err);
}
} }
public String getKey() public String getKey()
{ {
return key; synchronized (this)
{
return key;
}
} }
public int getPort() public int getPort()
{ {
return port; synchronized (this)
} {
return port;
public ServerSocket getServerSocket() }
{
return serverSocket;
} }
public boolean isExitVm() public boolean isExitVm()
{ {
return exitVm; synchronized (this)
{
return exitVm;
}
} }
public void setDebug(boolean flag) public void setDebug(boolean flag)
{ {
this.DEBUG = flag; this.debug = flag;
} }
/** /**
@ -454,10 +176,8 @@ public class ShutdownMonitor
{ {
synchronized (this) synchronized (this)
{ {
if (thread != null && thread.isAlive()) if (alive)
{ throw new IllegalStateException("ShutdownMonitor already started");
throw new IllegalStateException("ShutdownMonitorThread already started");
}
this.exitVm = exitVm; this.exitVm = exitVm;
} }
} }
@ -466,10 +186,8 @@ public class ShutdownMonitor
{ {
synchronized (this) synchronized (this)
{ {
if (thread != null && thread.isAlive()) if (alive)
{ throw new IllegalStateException("ShutdownMonitor already started");
throw new IllegalStateException("ShutdownMonitorThread already started");
}
this.key = key; this.key = key;
} }
} }
@ -478,52 +196,254 @@ public class ShutdownMonitor
{ {
synchronized (this) synchronized (this)
{ {
if (thread != null && thread.isAlive()) if (alive)
{ throw new IllegalStateException("ShutdownMonitor already started");
throw new IllegalStateException("ShutdownMonitorThread already started");
}
this.port = port; this.port = port;
} }
} }
protected void start() throws Exception protected void start() throws Exception
{ {
Thread t = null;
synchronized (this) synchronized (this)
{ {
if (thread != null && thread.isAlive()) if (alive)
{ {
if (DEBUG) debug("Already started");
System.err.printf("ShutdownMonitorThread already started");
return; // cannot start it again return; // cannot start it again
} }
ServerSocket serverSocket = listen();
thread = new Thread(new ShutdownMonitorRunnable()); if (serverSocket != null)
thread.setDaemon(true); {
thread.setName("ShutdownMonitor"); alive = true;
t = thread; Thread thread = new Thread(new ShutdownMonitorRunnable(serverSocket));
thread.setDaemon(true);
thread.setName("ShutdownMonitor");
thread.start();
}
} }
if (t != null)
t.start();
} }
private void stop()
protected boolean isAlive ()
{ {
boolean result = false;
synchronized (this) 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 @Override
public String toString() 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<LifeCycle> predicate, boolean destroy)
{
List<LifeCycle> 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);
}
}
}
} }
} }

View File

@ -754,10 +754,10 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
if (_mimeTypes == null) if (_mimeTypes == null)
_mimeTypes = new MimeTypes(); _mimeTypes = new MimeTypes();
try try
{ {
// Set the classloader // Set the classloader, context and enter scope
if (_classLoader != null) if (_classLoader != null)
{ {
current_thread = Thread.currentThread(); current_thread = Thread.currentThread();

View File

@ -49,7 +49,7 @@ import org.eclipse.jetty.util.log.Logger;
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
/** Handler for Error pages /** Handler for Error pages
* An ErrorHandler is registered with {@link ContextHandler#setErrorHandler(ErrorHandler)} or * 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)} * 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. * or via {@link #badMessageError(int, String, HttpFields)} for bad requests for which a dispatch cannot be done.
* *

View File

@ -18,8 +18,6 @@
package org.eclipse.jetty.server.handler.gzip; package org.eclipse.jetty.server.handler.gzip;
import static org.eclipse.jetty.http.GzipHttpContent.ETAG_GZIP_QUOTE;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.Set; 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.Log;
import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.log.Logger;
/** /**
* A Handler that can dynamically GZIP compress responses. Unlike * A Handler that can dynamically GZIP compress responses. Unlike
* previous and 3rd party GzipFilters, this mechanism works with asynchronously * 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; private boolean _syncFlush = false;
// non-static, as other GzipHandler instances may have different configurations // non-static, as other GzipHandler instances may have different configurations
private final ThreadLocal<Deflater> _deflater = new ThreadLocal<Deflater>(); private final ThreadLocal<Deflater> _deflater = new ThreadLocal<>();
private final IncludeExclude<String> _agentPatterns=new IncludeExclude<>(RegexSet.class); private final IncludeExclude<String> _agentPatterns=new IncludeExclude<>(RegexSet.class);
private final IncludeExclude<String> _methods = new IncludeExclude<>(); private final IncludeExclude<String> _methods = new IncludeExclude<>();
@ -398,6 +395,11 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
return _minGzipSize; 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) * @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); String etag = baseRequest.getHttpFields().get(HttpHeader.IF_NONE_MATCH);
if (etag!=null) if (etag!=null)
{ {
int i=etag.indexOf(ETAG_GZIP_QUOTE); int i=etag.indexOf(GzipHttpContent.ETAG_GZIP_QUOTE);
if (i>0) if (i>0)
{ {
while (i>=0) while (i>=0)
{ {
etag=etag.substring(0,i)+etag.substring(i+GzipHttpContent.ETAG_GZIP.length()); 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)); baseRequest.getHttpFields().put(new HttpField(HttpHeader.IF_NONE_MATCH,etag));
} }
} }
// install interceptor and handle // 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) if (_handler!=null)
_handler.handle(target,baseRequest, request, response); _handler.handle(target,baseRequest, request, response);
} }

View File

@ -32,6 +32,7 @@ import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.http.PreEncodedHttpField; import org.eclipse.jetty.http.PreEncodedHttpField;
import org.eclipse.jetty.server.HttpChannel; import org.eclipse.jetty.server.HttpChannel;
import org.eclipse.jetty.server.HttpOutput; import org.eclipse.jetty.server.HttpOutput;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.IteratingNestedCallback; 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_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()); 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 enum GZState { MIGHT_COMPRESS, NOT_COMPRESSING, COMMITTING, COMPRESSING, FINISHED};
private final AtomicReference<GZState> _state = new AtomicReference<>(GZState.MIGHT_COMPRESS); private final AtomicReference<GZState> _state = new AtomicReference<>(GZState.MIGHT_COMPRESS);
private final CRC32 _crc = new CRC32(); private final CRC32 _crc = new CRC32();
@ -57,7 +58,7 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor
private final HttpField _vary; private final HttpField _vary;
private final int _bufferSize; private final int _bufferSize;
private final boolean _syncFlush; private final boolean _syncFlush;
private Deflater _deflater; private Deflater _deflater;
private ByteBuffer _buffer; 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); 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) public GzipHttpOutputInterceptor(GzipFactory factory, HttpField vary, HttpChannel channel, HttpOutput.Interceptor next,boolean syncFlush)
{ {
this(factory,vary,channel.getHttpConfiguration().getOutputBufferSize(),channel,next,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) public GzipHttpOutputInterceptor(GzipFactory factory, HttpField vary, int bufferSize, HttpChannel channel, HttpOutput.Interceptor next,boolean syncFlush)
{ {
_factory=factory; _factory=factory;
@ -85,14 +86,14 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor
{ {
return _interceptor; return _interceptor;
} }
@Override @Override
public boolean isOptimizedForDirectBuffers() public boolean isOptimizedForDirectBuffers()
{ {
return false; // No point as deflator is in user space. return false; // No point as deflator is in user space.
} }
@Override @Override
public void write(ByteBuffer content, boolean complete, Callback callback) public void write(ByteBuffer content, boolean complete, Callback callback)
{ {
@ -101,11 +102,11 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor
case MIGHT_COMPRESS: case MIGHT_COMPRESS:
commit(content,complete,callback); commit(content,complete,callback);
break; break;
case NOT_COMPRESSING: case NOT_COMPRESSING:
_interceptor.write(content, complete, callback); _interceptor.write(content, complete, callback);
return; return;
case COMMITTING: case COMMITTING:
callback.failed(new WritePendingException()); callback.failed(new WritePendingException());
break; break;
@ -124,21 +125,21 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor
{ {
int i=_buffer.limit(); int i=_buffer.limit();
_buffer.limit(i+8); _buffer.limit(i+8);
int v=(int)_crc.getValue(); int v=(int)_crc.getValue();
_buffer.put(i++,(byte)(v & 0xFF)); _buffer.put(i++,(byte)(v & 0xFF));
_buffer.put(i++,(byte)((v>>>8) & 0xFF)); _buffer.put(i++,(byte)((v>>>8) & 0xFF));
_buffer.put(i++,(byte)((v>>>16) & 0xFF)); _buffer.put(i++,(byte)((v>>>16) & 0xFF));
_buffer.put(i++,(byte)((v>>>24) & 0xFF)); _buffer.put(i++,(byte)((v>>>24) & 0xFF));
v=_deflater.getTotalIn(); v=_deflater.getTotalIn();
_buffer.put(i++,(byte)(v & 0xFF)); _buffer.put(i++,(byte)(v & 0xFF));
_buffer.put(i++,(byte)((v>>>8) & 0xFF)); _buffer.put(i++,(byte)((v>>>8) & 0xFF));
_buffer.put(i++,(byte)((v>>>16) & 0xFF)); _buffer.put(i++,(byte)((v>>>16) & 0xFF));
_buffer.put(i++,(byte)((v>>>24) & 0xFF)); _buffer.put(i++,(byte)((v>>>24) & 0xFF));
} }
private void gzip(ByteBuffer content, boolean complete, final Callback callback) private void gzip(ByteBuffer content, boolean complete, final Callback callback)
{ {
if (content.hasRemaining() || complete) if (content.hasRemaining() || complete)
@ -150,7 +151,8 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor
protected void commit(ByteBuffer content, boolean complete, Callback callback) protected void commit(ByteBuffer content, boolean complete, Callback callback)
{ {
// Are we excluding because of status? // 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)) if (sc>0 && (sc<200 || sc==204 || sc==205 || sc>=300))
{ {
LOG.debug("{} exclude by status {}",this,sc); LOG.debug("{} exclude by status {}",this,sc);
@ -158,9 +160,9 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor
_interceptor.write(content, complete, callback); _interceptor.write(content, complete, callback);
return; return;
} }
// Are we excluding because of mime-type? // Are we excluding because of mime-type?
String ct = _channel.getResponse().getContentType(); String ct = response.getContentType();
if (ct!=null) if (ct!=null)
{ {
ct=MimeTypes.getContentTypeWithoutCharset(ct); ct=MimeTypes.getContentTypeWithoutCharset(ct);
@ -172,9 +174,9 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor
return; return;
} }
} }
// Has the Content-Encoding header already been set? // Has the Content-Encoding header already been set?
String ce=_channel.getResponse().getHeader("Content-Encoding"); String ce=response.getHeader("Content-Encoding");
if (ce != null) if (ce != null)
{ {
LOG.debug("{} exclude by content-encoding {}",this,ce); LOG.debug("{} exclude by content-encoding {}",this,ce);
@ -182,20 +184,21 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor
_interceptor.write(content, complete, callback); _interceptor.write(content, complete, callback);
return; return;
} }
// Are we the thread that commits? // Are we the thread that commits?
if (_state.compareAndSet(GZState.MIGHT_COMPRESS,GZState.COMMITTING)) if (_state.compareAndSet(GZState.MIGHT_COMPRESS,GZState.COMMITTING))
{ {
// We are varying the response due to accept encoding header. // We are varying the response due to accept encoding header.
HttpFields fields = _channel.getResponse().getHttpFields(); HttpFields fields = response.getHttpFields();
fields.add(_vary); if (_vary != null)
fields.add(_vary);
long content_length = _channel.getResponse().getContentLength(); long content_length = response.getContentLength();
if (content_length<0 && complete) if (content_length<0 && complete)
content_length=content.remaining(); content_length=content.remaining();
_deflater = _factory.getDeflater(_channel.getRequest(),content_length); _deflater = _factory.getDeflater(_channel.getRequest(),content_length);
if (_deflater==null) if (_deflater==null)
{ {
LOG.debug("{} exclude no deflater",this); 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); BufferUtil.fill(_buffer,GZIP_HEADER,0,GZIP_HEADER.length);
// Adjust headers // Adjust headers
_channel.getResponse().setContentLength(-1); response.setContentLength(-1);
String etag=fields.get(HttpHeader.ETAG); String etag=fields.get(HttpHeader.ETAG);
if (etag!=null) 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; etag=(etag.charAt(end)=='"')?etag.substring(0,end)+GzipHttpContent.ETAG_GZIP+'"':etag+GzipHttpContent.ETAG_GZIP;
fields.put(HttpHeader.ETAG,etag); fields.put(HttpHeader.ETAG,etag);
} }
LOG.debug("{} compressing {}",this,_deflater); LOG.debug("{} compressing {}",this,_deflater);
_state.set(GZState.COMPRESSING); _state.set(GZState.COMPRESSING);
gzip(content,complete,callback); gzip(content,complete,callback);
} }
else else
@ -268,14 +271,14 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor
} }
} }
} }
public boolean mightCompress() public boolean mightCompress()
{ {
return _state.get()==GZState.MIGHT_COMPRESS; return _state.get()==GZState.MIGHT_COMPRESS;
} }
private class GzipBufferCB extends IteratingNestedCallback private class GzipBufferCB extends IteratingNestedCallback
{ {
private ByteBuffer _copy; private ByteBuffer _copy;
private final ByteBuffer _content; private final ByteBuffer _content;
private final boolean _last; private final boolean _last;
@ -291,11 +294,11 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor
{ {
if (_deflater==null) if (_deflater==null)
return Action.SUCCEEDED; return Action.SUCCEEDED;
if (_deflater.needsInput()) if (_deflater.needsInput())
{ {
if (BufferUtil.isEmpty(_content)) if (BufferUtil.isEmpty(_content))
{ {
if (_deflater.finished()) if (_deflater.finished())
{ {
_factory.recycle(_deflater); _factory.recycle(_deflater);
@ -309,12 +312,12 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor
} }
return Action.SUCCEEDED; return Action.SUCCEEDED;
} }
if (!_last) if (!_last)
{ {
return Action.SUCCEEDED; return Action.SUCCEEDED;
} }
_deflater.finish(); _deflater.finish();
} }
else if (_content.hasArray()) else if (_content.hasArray())
@ -323,9 +326,9 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor
int off=_content.arrayOffset()+_content.position(); int off=_content.arrayOffset()+_content.position();
int len=_content.remaining(); int len=_content.remaining();
BufferUtil.clear(_content); BufferUtil.clear(_content);
_crc.update(array,off,len); _crc.update(array,off,len);
_deflater.setInput(array,off,len); _deflater.setInput(array,off,len);
if (_last) if (_last)
_deflater.finish(); _deflater.finish();
} }
@ -338,13 +341,13 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor
BufferUtil.flipToFlush(_copy,0); BufferUtil.flipToFlush(_copy,0);
if (took==0) if (took==0)
throw new IllegalStateException(); throw new IllegalStateException();
byte[] array=_copy.array(); byte[] array=_copy.array();
int off=_copy.arrayOffset()+_copy.position(); int off=_copy.arrayOffset()+_copy.position();
int len=_copy.remaining(); int len=_copy.remaining();
_crc.update(array,off,len); _crc.update(array,off,len);
_deflater.setInput(array,off,len); _deflater.setInput(array,off,len);
if (_last && BufferUtil.isEmpty(_content)) if (_last && BufferUtil.isEmpty(_content))
_deflater.finish(); _deflater.finish();
} }
@ -359,10 +362,10 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor
_buffer.limit(_buffer.limit()+produced); _buffer.limit(_buffer.limit()+produced);
} }
boolean finished=_deflater.finished(); boolean finished=_deflater.finished();
if (finished) if (finished)
addTrailer(); addTrailer();
_interceptor.write(_buffer,finished,this); _interceptor.write(_buffer,finished,this);
return Action.SCHEDULED; return Action.SCHEDULED;
} }

View File

@ -20,6 +20,8 @@
package org.eclipse.jetty.server.session; package org.eclipse.jetty.server.session;
import java.util.Set;
import org.eclipse.jetty.util.component.AbstractLifeCycle; 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 public abstract class AbstractSessionDataStore extends AbstractLifeCycle implements SessionDataStore
{ {
protected SessionContext _context; //context associated with this session data store 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 id identity of session to store
* @param data info of the session * @param data info of the session
* @param lastSaveTime time of previous save or 0 if never saved * @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; 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<String> doGetExpired (Set<String> 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<String> getExpired(Set<String> 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) * @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); return new SessionData(id, _context.getCanonicalContextPath(), _context.getVhost(), created, accessed, lastAccessed, maxInactiveMs);
} }
/**
* @throws IllegalStateException
*/
protected void checkStarted () throws IllegalStateException protected void checkStarted () throws IllegalStateException
{ {
if (isStarted()) if (isStarted())
throw new IllegalStateException("Already started"); throw new IllegalStateException("Already started");
} }
@Override @Override
protected void doStart() throws Exception protected void doStart() throws Exception
{ {
@ -110,7 +136,14 @@ public abstract class AbstractSessionDataStore extends AbstractLifeCycle impleme
super.doStart(); super.doStart();
} }
public int getGracePeriodSec()
{
return _gracePeriodSec;
}
public void setGracePeriodSec(int sec)
{
_gracePeriodSec = sec;
}
} }

View File

@ -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) 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 * @return a new unique session id
*/ */
public String newSessionId(long seedTerm) public String newSessionId(long seedTerm)
@ -399,9 +399,6 @@ public abstract class AbstractSessionIdManager extends AbstractLifeCycle impleme
} }
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
/**
* @param id
*/
public void invalidateAll (String id) public void invalidateAll (String id)
{ {
//take the id out of the list of known sessionids for this node //take the id out of the list of known sessionids for this node

View File

@ -21,15 +21,15 @@ package org.eclipse.jetty.server.session;
/** /**
* AbstractInspector * AbstractInspector
*
*
*/ */
public abstract class AbstractSessionInspector implements SessionInspector public abstract class AbstractSessionInspector implements SessionInspector
{ {
/** /**
* <0 means never inspect * <ul>
* 0 means always inspect * <li>&lt;0 means never inspect</li>
* >0 means inspect at that interval * <li>0 means always inspect</li>
* <li>&gt;0 means inspect at that interval</li>
* </ul>
*/ */
protected int _timeoutSec = -1; protected int _timeoutSec = -1;

View File

@ -21,7 +21,6 @@ package org.eclipse.jetty.server.session;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -56,7 +55,7 @@ public abstract class AbstractSessionStore extends AbstractLifeCycle implements
/** /**
* Create a new Session object from session data * Create a new Session object from session data
* @param data * @param data the session data
* @return a new Session object * @return a new Session object
*/ */
public abstract Session newSession (SessionData data); 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 * Replace the mapping from id to oldValue with newValue
* @param id * @param id the id
* @param oldValue * @param oldValue the old value
* @param newValue * @param newValue the new value
* @return true if replacement was done * @return true if replacement was done
*/ */
public abstract boolean doReplace (String id, Session oldValue, Session newValue); 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 * 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 * @return true if the Session object exists in the session store
*/ */
public abstract boolean doExists (String id); 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 * Remove the session with this identity from the store
* @param id * @param id the id
* @return true if removed false otherwise * @return true if removed false otherwise
*/ */
public abstract Session doDelete (String id); public abstract Session doDelete (String id);
@ -113,14 +112,12 @@ public abstract class AbstractSessionStore extends AbstractLifeCycle implements
/** /**
* PlaceHolder * PlaceHolder
*
*
*/ */
protected class PlaceHolderSession extends Session protected class PlaceHolderSession extends Session
{ {
/** /**
* @param data * @param data the session data
*/ */
public PlaceHolderSession(SessionData 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) 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) 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 * 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 * @return a Session object filled with data or null if the session doesn't exist
* @throws Exception * @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 @Override
public Set<String> checkExpiration(Set<String> candidates) public Set<String> checkExpiration(Set<String> candidates)
@ -565,7 +562,7 @@ public abstract class AbstractSessionStore extends AbstractLifeCycle implements
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())
LOG.debug("SessionStore checking expiration on {}", candidates); 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) public void setPassivateOnComplete (boolean passivateOnComplete)
{ {
_passivateOnComplete = passivateOnComplete; _passivateOnComplete = passivateOnComplete;
} }
/**
* @return
*/
public boolean isPassivateOnComplete () public boolean isPassivateOnComplete ()
{ {
return _passivateOnComplete; return _passivateOnComplete;
} }
/** /**
* @see org.eclipse.jetty.server.session.SessionStore#newSession(javax.servlet.http.HttpServletRequest, java.lang.String, long, long) * @see org.eclipse.jetty.server.session.SessionStore#newSession(javax.servlet.http.HttpServletRequest, java.lang.String, long, long)

View File

@ -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 @Override
public Set<String> getExpired(Set<String> candidates) public Set<String> doGetExpired(Set<String> candidates, int expiryTimeoutSec)
{ {
// TODO Auto-generated method stub // TODO Auto-generated method stub
return null; return null;

View File

@ -42,8 +42,8 @@ public class ExpiryInspector extends AbstractSessionInspector
/** /**
* @param sessionStore * @param sessionStore the session store
* @param idManager * @param idManager the session id manager
*/ */
public ExpiryInspector (AbstractSessionStore sessionStore, SessionIdManager idManager) 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 @Override
public void inspect(Session s) public void inspect(Session s)

View File

@ -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 @Override
public Set<String> getExpired(Set<String> candidates) public Set<String> doGetExpired(Set<String> candidates, int expiryTimeoutSec)
{ {
//we don't want to open up each file and check, so just leave it up to the SessionStore //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 //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 * @param id identity of session
* @return * @return the filename of the session data store
*/ */
private String getFileName (String id) private String getFileName (String id)
{ {
@ -268,7 +268,7 @@ public class FileSessionDataStore extends AbstractSessionDataStore
/** /**
* @param is inputstream containing session data * @param is inputstream containing session data
* @return * @return the session data
* @throws Exception * @throws Exception
*/ */
private SessionData load (InputStream is) private SessionData load (InputStream is)

View File

@ -54,7 +54,7 @@ public class FileSessionManager extends SessionManager
/** /**
* Get the SessionDataStore to configure it * Get the SessionDataStore to configure it
* @return * @return the session datastore
*/ */
public FileSessionDataStore getSessionDataStore() public FileSessionDataStore getSessionDataStore()
{ {

View File

@ -32,7 +32,7 @@ import org.eclipse.jetty.util.ConcurrentHashSet;
public class HashSessionIdManager extends AbstractSessionIdManager public class HashSessionIdManager extends AbstractSessionIdManager
{ {
/** /**
* @param server * @param server the server
*/ */
public HashSessionIdManager(Server server) public HashSessionIdManager(Server server)
{ {

View File

@ -39,7 +39,7 @@ public class IdleInspector extends AbstractSessionInspector
protected AbstractSessionStore _sessionStore; protected AbstractSessionStore _sessionStore;
/** /**
* @param sessionStore * @param sessionStore the session store
*/ */
public IdleInspector (AbstractSessionStore sessionStore) public IdleInspector (AbstractSessionStore sessionStore)
{ {

View File

@ -58,8 +58,7 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore
private int _attempts = -1; // <= 0 means unlimited attempts to load a session 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 boolean _deleteUnloadables = false; //true means if attempts exhausted delete the session
private long _gracePeriodMs = 1000L * 60 * 60; //default grace period is 1hr
/** /**
* SessionTableSchema * 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 throws SQLException
{ {
if (_dbAdaptor == null) if (_dbAdaptor == null)
@ -317,6 +316,42 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore
return statement; 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) public PreparedStatement getAllAncientExpiredSessionsStatement (Connection connection)
@ -483,10 +518,7 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore
/** /**
* Set up the tables in the database * Set up the tables in the database
* @throws SQLException * @throws SQLException if unable to prepare tables
*/
/**
* @throws SQLException
*/ */
public void prepareTables() public void prepareTables()
throws SQLException 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 @Override
public void doStore(String id, SessionData data, long lastSaveTime) throws Exception 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) private void doUpdate (String id, SessionData data)
throws Exception 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 @Override
public Set<String> getExpired(Set<String> candidates) public Set<String> doGetExpired(Set<String> candidates, int scavengeIntervalSec)
{ {
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())
LOG.debug("Getting expired sessions "+System.currentTimeMillis()); LOG.debug("Getting expired sessions "+System.currentTimeMillis());
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
Set<String> expiredSessionKeys = new HashSet<>(); Set<String> expiredSessionKeys = new HashSet<>();
try (Connection connection = _dbAdaptor.getConnection()) try (Connection connection = _dbAdaptor.getConnection())
{ {
connection.setAutoCommit(true); 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; long upperBound = now;
if (LOG.isDebugEnabled()) 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()) 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)) try (PreparedStatement selectExpiredSessions = _sessionTableSchema.getAllAncientExpiredSessionsStatement(connection))
{ {
upperBound = now - (3 * _gracePeriodMs); if (_lastExpiryCheckTime <= 0)
if (upperBound > 0) upperBound = (now - (3*(1000L * _gracePeriodSec)));
{ else
if (LOG.isDebugEnabled()) LOG.debug("{}- Pass 2: Searching for sessions expired before {}",_context.getWorkerName(), upperBound); upperBound = _lastExpiryCheckTime - (1000L * _gracePeriodSec);
selectExpiredSessions.setLong(1, upperBound); if (LOG.isDebugEnabled()) LOG.debug("{}- Pass 2: Searching for sessions expired before {}",_context.getWorkerName(), upperBound);
try (ResultSet result = selectExpiredSessions.executeQuery())
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 sessionId = result.getString(_sessionTableSchema.getIdColumn()); String vh = result.getString(_sessionTableSchema.getVirtualHostColumn());
String ctxtpth = result.getString(_sessionTableSchema.getContextPathColumn()); expiredSessionKeys.add(sessionId);
String vh = result.getString(_sessionTableSchema.getVirtualHostColumn()); if (LOG.isDebugEnabled()) LOG.debug ("{}- Found expired sessionId=",_context.getWorkerName(), sessionId);
expiredSessionKeys.add(sessionId);
if (LOG.isDebugEnabled()) LOG.debug ("{}- Found expired sessionId=",_context.getWorkerName(), sessionId);
}
} }
} }
} }
Set<String> notExpiredInDB = new HashSet<>(); Set<String> notExpiredInDB = new HashSet<>();
for (String k: candidates) for (String k: candidates)
@ -958,20 +993,8 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore
LOG.warn(e); LOG.warn(e);
return expiredSessionKeys; //return whatever we got 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) public void setDatabaseAdaptor (DatabaseAdaptor dbAdaptor)
{ {
@ -1010,6 +1033,9 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore
_deleteUnloadables = delete; _deleteUnloadables = delete;
} }
/**
* @return true if we should delete data for sessions that we cant reconstitute
*/
public boolean isDeleteUnloadableSessions () public boolean isDeleteUnloadableSessions ()
{ {
return _deleteUnloadables; 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) public int getLoadAttempts (String id)
{ {
AtomicInteger i = _unloadables.get(id); AtomicInteger i = _unloadables.get(id);
@ -1035,18 +1065,21 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore
return i.get(); return i.get();
} }
/**
* @return how many sessions we've failed to load
*/
public Set<String> getUnloadableSessions () public Set<String> getUnloadableSessions ()
{ {
return new HashSet<String>(_unloadables.keySet()); return new HashSet<String>(_unloadables.keySet());
} }
public void clearUnloadableSessions()
{
_unloadables.clear();
}
/**
*
*/
public void clearUnloadableSessions()
{
_unloadables.clear();
}
/** /**

View File

@ -55,7 +55,7 @@ public class JDBCSessionManager extends SessionManager
/** /**
* Get the db adaptor to configure jdbc settings * Get the db adaptor to configure jdbc settings
* @return * @return the database adaptor
*/ */
public DatabaseAdaptor getDatabaseAdaptor() public DatabaseAdaptor getDatabaseAdaptor()
{ {
@ -64,7 +64,7 @@ public class JDBCSessionManager extends SessionManager
/** /**
* Get the SessionDataStore to configure it * Get the SessionDataStore to configure it
* @return * @return the session data store
*/ */
public JDBCSessionDataStore getSessionDataStore () public JDBCSessionDataStore getSessionDataStore ()
{ {

View File

@ -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 @Override
public Set<String> getExpired(Set<String> candidates) public Set<String> doGetExpired(Set<String> candidates, int expiryTimeoutSec)
{ {
return candidates; //whatever is suggested we accept return candidates; //whatever is suggested we accept
} }

View File

@ -19,7 +19,6 @@
package org.eclipse.jetty.server.session; package org.eclipse.jetty.server.session;
import java.util.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.server.SessionIdManager; import org.eclipse.jetty.server.SessionIdManager;
@ -77,7 +76,7 @@ public class PeriodicSessionInspector extends AbstractLifeCycle
/** /**
* SessionIdManager associated with this scavenger * SessionIdManager associated with this scavenger
* @param sessionIdManager * @param sessionIdManager the session id manager
*/ */
public void setSessionIdManager (SessionIdManager sessionIdManager) public void setSessionIdManager (SessionIdManager sessionIdManager)
{ {
@ -137,7 +136,7 @@ public class PeriodicSessionInspector extends AbstractLifeCycle
/** /**
* Set the period between scavenge cycles * Set the period between scavenge cycles
* @param sec * @param sec the interval (in seconds)
*/ */
public void setIntervalSec (long sec) public void setIntervalSec (long sec)
{ {
@ -176,7 +175,7 @@ public class PeriodicSessionInspector extends AbstractLifeCycle
/** /**
* Get the period between inspection cycles. * Get the period between inspection cycles.
* *
* @return * @return the interval (in seconds)
*/ */
public long getIntervalSec () public long getIntervalSec ()
{ {

View File

@ -90,8 +90,8 @@ public class Session implements SessionManager.SessionIf
/** /**
* Create a new session * Create a new session
* *
* @param request * @param request the request the session should be based on
* @param data * @param data the session data
*/ */
public Session (HttpServletRequest request, SessionData data) public Session (HttpServletRequest request, SessionData data)
{ {
@ -104,7 +104,7 @@ public class Session implements SessionManager.SessionIf
/** /**
* Re-create an existing session * Re-create an existing session
* @param data * @param data the session data
*/ */
public Session (SessionData 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 * Should call this method with a lock held if you want to
* make decision on what to do with the session * make decision on what to do with the session
* *
* @return * @return the number of active requests for this session
*/ */
public long getRequests() public long getRequests()
{ {
@ -206,18 +206,12 @@ public class Session implements SessionManager.SessionIf
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
/**
* @param nodename
*/
public void setLastNode (String nodename) public void setLastNode (String nodename)
{ {
_sessionData.setLastNode(nodename); _sessionData.setLastNode(nodename);
} }
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
/**
* @return
*/
public String getLastNode () public String getLastNode ()
{ {
return _sessionData.getLastNode(); return _sessionData.getLastNode();
@ -479,9 +473,6 @@ public class Session implements SessionManager.SessionIf
/* ------------------------------------------------------------- */ /* ------------------------------------------------------------- */
/**
* @throws IllegalStateException
*/
protected void checkLocked () protected void checkLocked ()
throws IllegalStateException throws IllegalStateException
{ {
@ -640,9 +631,6 @@ public class Session implements SessionManager.SessionIf
} }
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
/**
* @param request
*/
public void renewId(HttpServletRequest request) public void renewId(HttpServletRequest request)
{ {
if (_manager == null) 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. * the same.
* *
* @param oldId * @param oldId
@ -739,22 +727,16 @@ public class Session implements SessionManager.SessionIf
LOG.warn(e); LOG.warn(e);
} }
} }
/* ------------------------------------------------------------- */ /* ------------------------------------------------------------- */
/** Grab the lock on the session /** Grab the lock on the session
* @return * @return the lock
*/ */
public Lock lock () public Lock lock ()
{ {
return _lock.lock(); return _lock.lock();
} }
/* ------------------------------------------------------------- */ /* ------------------------------------------------------------- */
protected void doInvalidate() throws IllegalStateException protected void doInvalidate() throws IllegalStateException
{ {
@ -863,21 +845,12 @@ public class Session implements SessionManager.SessionIf
_passivationState = PassivationState.ACTIVE; _passivationState = PassivationState.ACTIVE;
} }
/**
* @return
*/
public boolean isActive () public boolean isActive ()
{ {
checkLocked(); checkLocked();
return _passivationState == PassivationState.ACTIVE; return _passivationState == PassivationState.ACTIVE;
} }
/**
* @return
*/
public boolean isPassivated () public boolean isPassivated ()
{ {
checkLocked(); checkLocked();

View File

@ -78,7 +78,7 @@ public class SessionContext
/** /**
* Run a runnable in the context (with context classloader set) if * Run a runnable in the context (with context classloader set) if
* there is one, otherwise just run it. * there is one, otherwise just run it.
* @param r * @param r the runnable
*/ */
public void run (Runnable r) public void run (Runnable r)
{ {
@ -120,8 +120,8 @@ public class SessionContext
/** /**
* Make an acceptable name from a context path. * Make an acceptable name from a context path.
* *
* @param path * @param path the path to normalize/fix
* @return * @return the clean/acceptable form of the path
*/ */
private String canonicalize (String path) private String canonicalize (String path)
{ {

View File

@ -62,16 +62,6 @@ public class SessionData implements Serializable
protected boolean _dirty; protected boolean _dirty;
protected long _lastSaved; //time in msec since last save 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) public SessionData (String id, String cpath, String vhost, long created, long accessed, long lastAccessed, long maxInactiveMs)
{ {
_id = id; _id = id;
@ -123,10 +113,6 @@ public class SessionData implements Serializable
} }
/**
* @param lastSaved
*/
public void setLastSaved(long lastSaved) public void setLastSaved(long lastSaved)
{ {
_lastSaved = lastSaved; _lastSaved = lastSaved;
@ -141,16 +127,13 @@ public class SessionData implements Serializable
return _dirty; return _dirty;
} }
/**
* @param dirty
*/
public void setDirty(boolean dirty) public void setDirty(boolean dirty)
{ {
_dirty = dirty; _dirty = dirty;
} }
/** /**
* @param name * @param name the name of the attribute
* @return the value of the attribute named * @return the value of the attribute named
*/ */
public Object getAttribute (String name) public Object getAttribute (String name)
@ -166,11 +149,6 @@ public class SessionData implements Serializable
return _attributes.keySet(); return _attributes.keySet();
} }
/**
* @param name
* @param value
* @return
*/
public Object setAttribute (String name, Object value) public Object setAttribute (String name, Object value)
{ {
Object old = (value==null?_attributes.remove(name):_attributes.put(name,value)); Object old = (value==null?_attributes.remove(name):_attributes.put(name,value));
@ -181,20 +159,11 @@ public class SessionData implements Serializable
return old; return old;
} }
/**
* @param name
*/
public void setDirty (String name) public void setDirty (String name)
{ {
setDirty (true); setDirty (true);
} }
/**
* @param attributes
*/
public void putAllAttributes (Map<String,Object> attributes) public void putAllAttributes (Map<String,Object> attributes)
{ {
_attributes.putAll(attributes); _attributes.putAll(attributes);
@ -224,9 +193,6 @@ public class SessionData implements Serializable
return _id; return _id;
} }
/**
* @param id
*/
public void setId(String id) public void setId(String id)
{ {
_id = id; _id = id;
@ -240,9 +206,6 @@ public class SessionData implements Serializable
return _contextPath; return _contextPath;
} }
/**
* @param contextPath
*/
public void setContextPath(String contextPath) public void setContextPath(String contextPath)
{ {
_contextPath = contextPath; _contextPath = contextPath;
@ -256,9 +219,6 @@ public class SessionData implements Serializable
return _vhost; return _vhost;
} }
/**
* @param vhost
*/
public void setVhost(String vhost) public void setVhost(String vhost)
{ {
_vhost = vhost; _vhost = vhost;
@ -272,9 +232,6 @@ public class SessionData implements Serializable
return _lastNode; return _lastNode;
} }
/**
* @param lastNode
*/
public void setLastNode(String lastNode) public void setLastNode(String lastNode)
{ {
_lastNode = lastNode; _lastNode = lastNode;
@ -288,25 +245,16 @@ public class SessionData implements Serializable
return _expiry; return _expiry;
} }
/**
* @param expiry
*/
public void setExpiry(long expiry) public void setExpiry(long expiry)
{ {
_expiry = expiry; _expiry = expiry;
} }
/**
* @return
*/
public long getCreated() public long getCreated()
{ {
return _created; return _created;
} }
/**
* @param created
*/
public void setCreated(long created) public void setCreated(long created)
{ {
_created = created; _created = created;
@ -320,9 +268,6 @@ public class SessionData implements Serializable
return _cookieSet; return _cookieSet;
} }
/**
* @param cookieSet
*/
public void setCookieSet(long cookieSet) public void setCookieSet(long cookieSet)
{ {
_cookieSet = cookieSet; _cookieSet = cookieSet;
@ -336,9 +281,6 @@ public class SessionData implements Serializable
return _accessed; return _accessed;
} }
/**
* @param accessed
*/
public void setAccessed(long accessed) public void setAccessed(long accessed)
{ {
_accessed = accessed; _accessed = accessed;
@ -352,35 +294,21 @@ public class SessionData implements Serializable
return _lastAccessed; return _lastAccessed;
} }
/**
* @param lastAccessed
*/
public void setLastAccessed(long lastAccessed) public void setLastAccessed(long lastAccessed)
{ {
_lastAccessed = lastAccessed; _lastAccessed = lastAccessed;
} }
/**
* @return
*/
public long getMaxInactiveMs() public long getMaxInactiveMs()
{ {
return _maxInactiveMs; return _maxInactiveMs;
} }
/**
* @param maxInactive
*/
public void setMaxInactiveMs(long maxInactive) public void setMaxInactiveMs(long maxInactive)
{ {
_maxInactiveMs = maxInactive; _maxInactiveMs = maxInactive;
} }
/**
* @param out
* @throws IOException
*/
private void writeObject(java.io.ObjectOutputStream out) throws IOException private void writeObject(java.io.ObjectOutputStream out) throws IOException
{ {
out.writeUTF(_id); //session id out.writeUTF(_id); //session id
@ -398,11 +326,6 @@ public class SessionData implements Serializable
out.writeObject(_attributes); out.writeObject(_attributes);
} }
/**
* @param in
* @throws IOException
* @throws ClassNotFoundException
*/
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException
{ {
_id = in.readUTF(); _id = in.readUTF();
@ -419,11 +342,6 @@ public class SessionData implements Serializable
_attributes = (ConcurrentHashMap<String,Object>)in.readObject(); _attributes = (ConcurrentHashMap<String,Object>)in.readObject();
} }
/**
* @param time
* @return
*/
public boolean isExpiredAt (long time) public boolean isExpiredAt (long time)
{ {
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())
@ -433,4 +351,23 @@ public class SessionData implements Serializable
return (getExpiry() < time); 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();
}
} }

View File

@ -46,18 +46,18 @@ public interface SessionDataStore extends LifeCycle
* Read in session data from storage * Read in session data from storage
* @param id identity of session to load * @param id identity of session to load
* @return the SessionData matching the id * @return the SessionData matching the id
* @throws Exception * @throws Exception if unable to load session data
*/ */
public SessionData load (String id) throws Exception; public SessionData load (String id) throws Exception;
/** /**
* Create a new SessionData * Create a new SessionData
* @param id * @param id the id
* @param created * @param created the timestamp when created
* @param accessed * @param accessed the timestamp when accessed
* @param lastAccessed * @param lastAccessed the timestamp when last accessed
* @param maxInactiveMs * @param maxInactiveMs the max inactive time in milliseconds
* @return a new SessionData object * @return a new SessionData object
*/ */
public SessionData newSessionData (String id, long created, long accessed, long lastAccessed, long maxInactiveMs); 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 * Write out session data to storage
* @param id identity of session to store * @param id identity of session to store
* @param data info 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; public void store (String id, SessionData data) throws Exception;
@ -79,7 +79,7 @@ public interface SessionDataStore extends LifeCycle
* Delete session data from storage * Delete session data from storage
* @param id identity of session to delete * @param id identity of session to delete
* @return true if the session was deleted from the permanent store * @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; 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 * @param candidates if provided, these are keys of sessions that
* the SessionStore thinks has expired and should be verified by the * the SessionStore thinks has expired and should be verified by the
* SessionDataStore * SessionDataStore
* @param scavengePeriodSec the time to attempt scavenge (in seconds)
* @return set of session ids * @return set of session ids
*/ */
public Set<String> getExpired (Set<String> candidates); public Set<String> getExpired (Set<String> candidates, int scavengePeriodSec);

View File

@ -19,8 +19,6 @@
package org.eclipse.jetty.server.session; package org.eclipse.jetty.server.session;
import static java.lang.Math.round;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Enumeration; 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.log.Logger;
import org.eclipse.jetty.util.statistic.CounterStatistic; import org.eclipse.jetty.util.statistic.CounterStatistic;
import org.eclipse.jetty.util.statistic.SampleStatistic; 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 () 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 * Called either when a session has expired, or the app has
* invalidated it. * invalidated it.
* *
* @param id * @param id the id to invalidate
*/ */
public void invalidate (String id) public void invalidate (String id)
{ {
@ -1032,9 +1031,6 @@ public class SessionManager extends ContainerLifeCycle implements org.eclipse.je
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
/**
* @return
*/
public void inspect () public void inspect ()
{ {
//don't attempt to scavenge if we are shutting down //don't attempt to scavenge if we are shutting down

View File

@ -55,10 +55,6 @@ public class StalePeriodStrategy implements StalenessStrategy
} }
/**
* @return
*/
public long getStaleSec () public long getStaleSec ()
{ {
return (_staleMs<=0?0L:_staleMs/1000L); 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 * The amount of time in seconds that a session can be held
* in memory without being refreshed from the cluster. * in memory without being refreshed from the cluster.
* @param sec * @param sec the time in seconds
*/ */
public void setStaleSec (long sec) public void setStaleSec (long sec)
{ {

View File

@ -18,196 +18,151 @@
package org.eclipse.jetty.server; package org.eclipse.jetty.server;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.LineNumberReader; import java.io.LineNumberReader;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.Socket; import java.net.Socket;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.util.thread.ShutdownThread; import org.eclipse.jetty.util.thread.ShutdownThread;
import org.junit.Test; import org.junit.Test;
/** import static org.junit.Assert.assertEquals;
* ShutdownMonitorTest import static org.junit.Assert.assertTrue;
*/
public class ShutdownMonitorTest 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 @Test
public void testShutdownMonitor() throws Exception public void testShutdownMonitor() throws Exception
{ {
// test port and key assignment ShutdownMonitor monitor = ShutdownMonitor.getInstance();
ShutdownMonitor.getInstance().setPort(0); monitor.setDebug(true);
ShutdownMonitor.getInstance().setExitVm(false); monitor.setPort(0);
ShutdownMonitor.getInstance().start(); monitor.setExitVm(false);
String key = ShutdownMonitor.getInstance().getKey(); monitor.start();
int port = ShutdownMonitor.getInstance().getPort(); String key = monitor.getKey();
int port = monitor.getPort();
// try starting a 2nd time (should be ignored) // try starting a 2nd time (should be ignored)
ShutdownMonitor.getInstance().start(); monitor.start();
stop("stop", port,key,true); stop("stop", port, key, true);
assertTrue(!ShutdownMonitor.getInstance().isAlive()); monitor.await();
assertTrue(!monitor.isAlive());
// should be able to change port and key because it is stopped // should be able to change port and key because it is stopped
ShutdownMonitor.getInstance().setPort(0); monitor.setPort(0);
ShutdownMonitor.getInstance().setKey("foo"); String newKey = "foo";
ShutdownMonitor.getInstance().start(); monitor.setKey(newKey);
monitor.start();
key = ShutdownMonitor.getInstance().getKey(); key = monitor.getKey();
port = ShutdownMonitor.getInstance().getPort(); assertEquals(newKey, key);
assertTrue(ShutdownMonitor.getInstance().isAlive()); port = monitor.getPort();
assertTrue(monitor.isAlive());
stop("stop", port,key,true); stop("stop", port, key, true);
assertTrue(!ShutdownMonitor.getInstance().isAlive()); monitor.await();
assertTrue(!monitor.isAlive());
} }
@Test @Test
public void testForceStopCommand() throws Exception public void testForceStopCommand() throws Exception
{ {
//create a testable Server with stop(), destroy() overridden to instrument ShutdownMonitor monitor = ShutdownMonitor.getInstance();
//start server monitor.setPort(0);
//call "forcestop" and check that server stopped but not destroyed
// test port and key assignment
System.setProperty("DEBUG", "true");
ShutdownMonitor.getInstance().setPort(0);
TestableServer server = new TestableServer(); TestableServer server = new TestableServer();
server.start(); server.start();
//shouldn't be registered for shutdown on jvm //shouldn't be registered for shutdown on jvm
assertTrue(!ShutdownThread.isRegistered(server)); assertTrue(!ShutdownThread.isRegistered(server));
assertTrue(ShutdownMonitor.isRegistered(server)); assertTrue(ShutdownMonitor.isRegistered(server));
String key = ShutdownMonitor.getInstance().getKey(); String key = monitor.getKey();
int port = ShutdownMonitor.getInstance().getPort(); int port = monitor.getPort();
stop("forcestop", port,key,true); stop("forcestop", port, key, true);
monitor.await();
assertTrue(!ShutdownMonitor.getInstance().isAlive());
assertTrue(!monitor.isAlive());
assertTrue(server.stopped); assertTrue(server.stopped);
assertTrue(!server.destroyed); assertTrue(!server.destroyed);
assertTrue(!ShutdownThread.isRegistered(server)); assertTrue(!ShutdownThread.isRegistered(server));
assertTrue(!ShutdownMonitor.isRegistered(server)); assertTrue(!ShutdownMonitor.isRegistered(server));
} }
@Test @Test
public void testOldStopCommandWithStopOnShutdownTrue() throws Exception public void testOldStopCommandWithStopOnShutdownTrue() throws Exception
{ {
ShutdownMonitor monitor = ShutdownMonitor.getInstance();
//create a testable Server with stop(), destroy() overridden to instrument monitor.setExitVm(false);
//call server.setStopAtShudown(true);
//start server monitor.setPort(0);
//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);
TestableServer server = new TestableServer(); TestableServer server = new TestableServer();
server.setStopAtShutdown(true); server.setStopAtShutdown(true);
server.start(); server.start();
//should be registered for shutdown on exit //should be registered for shutdown on exit
assertTrue(ShutdownThread.isRegistered(server)); assertTrue(ShutdownThread.isRegistered(server));
assertTrue(ShutdownMonitor.isRegistered(server)); assertTrue(ShutdownMonitor.isRegistered(server));
String key = ShutdownMonitor.getInstance().getKey(); String key = monitor.getKey();
int port = ShutdownMonitor.getInstance().getPort(); int port = monitor.getPort();
stop("stop", port, key, true); stop("stop", port, key, true);
assertTrue(!ShutdownMonitor.getInstance().isAlive()); monitor.await();
assertTrue(!monitor.isAlive());
assertTrue(server.stopped); assertTrue(server.stopped);
assertTrue(!server.destroyed); assertTrue(!server.destroyed);
assertTrue(!ShutdownThread.isRegistered(server)); assertTrue(!ShutdownThread.isRegistered(server));
assertTrue(!ShutdownMonitor.isRegistered(server)); assertTrue(!ShutdownMonitor.isRegistered(server));
} }
@Test @Test
public void testOldStopCommandWithStopOnShutdownFalse() throws Exception 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 monitor = ShutdownMonitor.getInstance();
ShutdownMonitor.getInstance().setExitVm(false); monitor.setExitVm(false);
System.setProperty("DEBUG", "true"); monitor.setPort(0);
ShutdownMonitor.getInstance().setPort(0);
TestableServer server = new TestableServer(); TestableServer server = new TestableServer();
server.setStopAtShutdown(false); server.setStopAtShutdown(false);
server.start(); server.start();
assertTrue(!ShutdownThread.isRegistered(server)); assertTrue(!ShutdownThread.isRegistered(server));
assertTrue(ShutdownMonitor.isRegistered(server)); assertTrue(ShutdownMonitor.isRegistered(server));
String key = ShutdownMonitor.getInstance().getKey(); String key = monitor.getKey();
int port = ShutdownMonitor.getInstance().getPort(); int port = monitor.getPort();
stop ("stop", port, key, true); stop("stop", port, key, true);
assertTrue(!ShutdownMonitor.getInstance().isAlive()); monitor.await();
assertTrue(!monitor.isAlive());
assertTrue(!server.stopped); assertTrue(!server.stopped);
assertTrue(!server.destroyed); assertTrue(!server.destroyed);
assertTrue(!ShutdownThread.isRegistered(server)); assertTrue(!ShutdownThread.isRegistered(server));
assertTrue(ShutdownMonitor.isRegistered(server)); assertTrue(ShutdownMonitor.isRegistered(server));
} }
public void stop(String command, int port, String key, boolean check) throws Exception 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); 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)) try (Socket s = new Socket(InetAddress.getByName("127.0.0.1"), port))
{ {
// send stop command // send stop command
try (OutputStream out = s.getOutputStream()) 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(); out.flush();
if (check) if (check)
{ {
// wait a little
TimeUnit.MILLISECONDS.sleep(600);
// check for stop confirmation // check for stop confirmation
LineNumberReader lin = new LineNumberReader(new InputStreamReader(s.getInputStream())); LineNumberReader lin = new LineNumberReader(new InputStreamReader(s.getInputStream()));
String response; String response;
if ((response = lin.readLine()) != null) if ((response = lin.readLine()) != null)
{ assertEquals("Stopped", response);
assertEquals("Stopped",response);
}
else else
throw new IllegalStateException("No stop confirmation"); 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();
}
}
} }

View File

@ -18,22 +18,18 @@
package org.eclipse.jetty.server.session; 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 java.util.stream.Stream;
import javax.servlet.SessionCookieConfig; import javax.servlet.SessionCookieConfig;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.eclipse.jetty.http.HttpCookie; import org.eclipse.jetty.http.HttpCookie;
import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.Server;
import org.junit.Test; import org.junit.Test;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/** /**
* SessionCookieTest * 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 @Override
public Session newSession(HttpServletRequest request, String key, long time, long maxInactiveMs) 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 @Override
public Session doGet(String key) 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 @Override
public Session doPutIfAbsent(String key, Session session) 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 @Override
public boolean doExists(String key) 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 @Override
public Session doDelete(String key) public Session doDelete(String key)
@ -140,10 +136,6 @@ public class SessionCookieTest
public class MockSessionIdManager extends AbstractSessionIdManager public class MockSessionIdManager extends AbstractSessionIdManager
{ {
/**
* @param server
*/
public MockSessionIdManager(Server server) public MockSessionIdManager(Server server)
{ {
super(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 @Override
public void useId(Session session) public void useId(Session session)

View File

@ -21,6 +21,7 @@ package org.eclipse.jetty.server.ssl;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.Socket; import java.net.Socket;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
@ -59,11 +60,11 @@ import org.junit.Before;
import org.junit.Test; import org.junit.Test;
public class SslConnectionFactoryTest public class SslConnectionFactoryTest
{ {
Server _server; Server _server;
ServerConnector _connector; ServerConnector _connector;
int _port; int _port;
@Before @Before
public void before() throws Exception public void before() throws Exception
{ {
@ -83,7 +84,7 @@ public class SslConnectionFactoryTest
HttpConfiguration https_config = new HttpConfiguration(http_config); HttpConfiguration https_config = new HttpConfiguration(http_config);
https_config.addCustomizer(new SecureRequestCustomizer()); https_config.addCustomizer(new SecureRequestCustomizer());
SslContextFactory sslContextFactory = new SslContextFactory(); SslContextFactory sslContextFactory = new SslContextFactory();
sslContextFactory.setKeyStorePath(keystoreFile.getAbsolutePath()); sslContextFactory.setKeyStorePath(keystoreFile.getAbsolutePath());
sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"); sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4");
@ -96,7 +97,7 @@ public class SslConnectionFactoryTest
https.setIdleTimeout(30000); https.setIdleTimeout(30000);
_server.addConnector(https); _server.addConnector(https);
_server.setHandler(new AbstractHandler() _server.setHandler(new AbstractHandler()
{ {
@Override @Override
@ -107,30 +108,30 @@ public class SslConnectionFactoryTest
response.flushBuffer(); response.flushBuffer();
} }
}); });
_server.start(); _server.start();
_port=https.getLocalPort(); _port=https.getLocalPort();
} }
@After @After
public void after() throws Exception public void after() throws Exception
{ {
_server.stop(); _server.stop();
_server=null; _server=null;
} }
@Test @Test
public void testConnect() throws Exception 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")); Assert.assertThat(response,Matchers.containsString("host=127.0.0.1"));
} }
@Test @Test
public void testSNIConnect() throws Exception public void testSNIConnect() throws Exception
{ {
String response; String response;
response= getResponse("localhost","localhost","jetty.eclipse.org"); response= getResponse("localhost","localhost","jetty.eclipse.org");
Assert.assertThat(response,Matchers.containsString("host=localhost")); Assert.assertThat(response,Matchers.containsString("host=localhost"));
} }
@ -151,22 +152,32 @@ public class SslConnectionFactoryTest
{ {
out.write("Rubbish".getBytes()); out.write("Rubbish".getBytes());
out.flush(); 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 private String getResponse(String sniHost,String reqHost, String cn) throws Exception
{ {
SslContextFactory clientContextFactory = new SslContextFactory(true); SslContextFactory clientContextFactory = new SslContextFactory(true);
clientContextFactory.start(); clientContextFactory.start();
SSLSocketFactory factory = clientContextFactory.getSslContext().getSocketFactory(); SSLSocketFactory factory = clientContextFactory.getSslContext().getSocketFactory();
SSLSocket sslSocket = (SSLSocket)factory.createSocket("127.0.0.1", _port); SSLSocket sslSocket = (SSLSocket)factory.createSocket("127.0.0.1", _port);
if (cn!=null) if (cn!=null)
{ {
SNIHostName serverName = new SNIHostName(sniHost); SNIHostName serverName = new SNIHostName(sniHost);
List<SNIServerName> serverNames = new ArrayList<>(); List<SNIServerName> serverNames = new ArrayList<>();
serverNames.add(serverName); serverNames.add(serverName);
@ -177,35 +188,35 @@ public class SslConnectionFactoryTest
} }
sslSocket.startHandshake(); sslSocket.startHandshake();
if (cn!=null) if (cn!=null)
{ {
X509Certificate cert = ((X509Certificate)sslSocket.getSession().getPeerCertificates()[0]); X509Certificate cert = ((X509Certificate)sslSocket.getSession().getPeerCertificates()[0]);
Assert.assertThat(cert.getSubjectX500Principal().getName("CANONICAL"), Matchers.startsWith("cn="+cn)); 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)); 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()); String response = IO.toString(sslSocket.getInputStream());
sslSocket.close(); sslSocket.close();
clientContextFactory.stop(); clientContextFactory.stop();
return response; return response;
} }
@Test @Test
public void testSocketCustomization() throws Exception public void testSocketCustomization() throws Exception
{ {
final Queue<String> history = new ConcurrentArrayQueue<>(); final Queue<String> history = new ConcurrentArrayQueue<>();
_connector.addBean(new SocketCustomizationListener() _connector.addBean(new SocketCustomizationListener()
{ {
@Override @Override
protected void customize(Socket socket, Class<? extends Connection> connection, boolean ssl) protected void customize(Socket socket, Class<? extends Connection> connection, boolean ssl)
{ {
history.add("customize connector "+connection+","+ssl); history.add("customize connector "+connection+","+ssl);
} }
}); });
_connector.getBean(SslConnectionFactory.class).addBean(new SocketCustomizationListener() _connector.getBean(SslConnectionFactory.class).addBean(new SocketCustomizationListener()
@ -214,26 +225,26 @@ public class SslConnectionFactoryTest
protected void customize(Socket socket, Class<? extends Connection> connection, boolean ssl) protected void customize(Socket socket, Class<? extends Connection> connection, boolean ssl)
{ {
history.add("customize ssl "+connection+","+ssl); history.add("customize ssl "+connection+","+ssl);
} }
}); });
_connector.getBean(HttpConnectionFactory.class).addBean(new SocketCustomizationListener() _connector.getBean(HttpConnectionFactory.class).addBean(new SocketCustomizationListener()
{ {
@Override @Override
protected void customize(Socket socket, Class<? extends Connection> connection, boolean ssl) protected void customize(Socket socket, Class<? extends Connection> connection, boolean ssl)
{ {
history.add("customize http "+connection+","+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.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 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 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 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("customize http class org.eclipse.jetty.server.HttpConnection,true",history.poll());
Assert.assertEquals(0,history.size()); Assert.assertEquals(0,history.size());
} }
} }

View File

@ -771,11 +771,15 @@ public class MultiPartInputStreamParser
} }
finally finally
{ {
part.close(); part.close();
} }
} }
if (!lastPart) if (lastPart)
{
while(line!=null)
line=((ReadLineInputStream)_in).readLine();
}
else
throw new IOException("Incomplete parts"); throw new IOException("Incomplete parts");
} }
catch (Exception e) catch (Exception e)

View File

@ -245,12 +245,14 @@ public class ServerContainer extends ClientContainer implements javax.websocket.
@Override @Override
public void onSessionClosed(WebSocketSession session) public void onSessionClosed(WebSocketSession session)
{ {
super.onSessionClosed(session);
webSocketServerFactory.onSessionClosed(session); webSocketServerFactory.onSessionClosed(session);
} }
@Override @Override
public void onSessionOpened(WebSocketSession session) public void onSessionOpened(WebSocketSession session)
{ {
super.onSessionOpened(session);
webSocketServerFactory.onSessionOpened(session); webSocketServerFactory.onSessionOpened(session);
} }
} }

View File

@ -18,6 +18,11 @@
package org.eclipse.jetty.websocket.jsr356.server.browser; 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.servlet.ServletException;
import javax.websocket.DeploymentException; import javax.websocket.DeploymentException;
@ -30,6 +35,7 @@ import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger; 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.ServerContainer;
import org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer; import org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer;
@ -78,7 +84,7 @@ public class JsrBrowserDebugTool
server.join(); server.join();
} }
private void setupServer(int port) throws DeploymentException, ServletException private void setupServer(int port) throws DeploymentException, ServletException, URISyntaxException, MalformedURLException
{ {
server = new Server(); server = new Server();
@ -89,10 +95,16 @@ public class JsrBrowserDebugTool
connector.setPort(port); connector.setPort(port);
server.addConnector(connector); 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); ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/"); context.setContextPath("/");
context.setBaseResource(Resource.newResource(urlBase));
ServletHolder holder = context.addServlet(DefaultServlet.class,"/"); ServletHolder holder = context.addServlet(DefaultServlet.class,"/");
holder.setInitParameter("resourceBase","src/test/resources/jsr-browser-debug-tool");
holder.setInitParameter("dirAllowed","true"); holder.setInitParameter("dirAllowed","true");
server.setHandler(context); server.setHandler(context);

View File

@ -23,6 +23,7 @@ import java.text.SimpleDateFormat;
import java.util.Calendar; import java.util.Calendar;
import java.util.Locale; import java.util.Locale;
import java.util.Random; import java.util.Random;
import java.util.Set;
import javax.websocket.CloseReason; import javax.websocket.CloseReason;
import javax.websocket.OnClose; import javax.websocket.OnClose;
@ -129,6 +130,14 @@ public class JsrBrowserSocket
{ {
writeMessage("Client Sec-WebSocket-Extensions: " + this.requestedExtensions); writeMessage("Client Sec-WebSocket-Extensions: " + this.requestedExtensions);
} }
Set<Session> openSessions = session.getOpenSessions();
writeMessage("OpenSessions.size() = " + openSessions.size());
int i = 0;
for (Session open : openSessions)
{
writeMessage(" OpenSession[%d] = %s", i++, open);
}
break; break;
} }
case "many": case "many":

View File

@ -6,9 +6,6 @@ org.eclipse.jetty.LEVEL=WARN
# org.eclipse.jetty.websocket.LEVEL=WARN # org.eclipse.jetty.websocket.LEVEL=WARN
# org.eclipse.jetty.websocket.common.io.LEVEL=DEBUG # 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 ### Show state changes on BrowserDebugTool
# -- LEAVE THIS AT DEBUG LEVEL -- # -- LEAVE THIS AT DEBUG LEVEL --
org.eclipse.jetty.websocket.jsr356.server.browser.LEVEL=DEBUG org.eclipse.jetty.websocket.jsr356.server.browser.LEVEL=DEBUG

View File

@ -368,15 +368,16 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId> <artifactId>maven-javadoc-plugin</artifactId>
<version>2.8</version>
<configuration> <configuration>
<docfilessubdirs>true</docfilessubdirs> <docfilessubdirs>true</docfilessubdirs>
<detectLinks>false</detectLinks> <detectLinks>true</detectLinks>
<detectJavaApiLink>true</detectJavaApiLink> <detectJavaApiLink>true</detectJavaApiLink>
<show>protected</show>
<excludePackageNames>com.acme.*;org.slf4j.*;org.mortbay.*</excludePackageNames> <excludePackageNames>com.acme.*;org.slf4j.*;org.mortbay.*</excludePackageNames>
<links> <links>
<link>http://docs.oracle.com/javase/8/docs/api/</link> <link>http://docs.oracle.com/javase/8/docs/api/</link>
<link>http://docs.oracle.com/javaee/7/api/</link> <link>http://docs.oracle.com/javaee/7/api/</link>
<link>http://junit.org/javadoc/latest/</link>
<link>http://download.eclipse.org/jetty/stable-9/apidocs/</link> <link>http://download.eclipse.org/jetty/stable-9/apidocs/</link>
</links> </links>
<tags> <tags>

View File

@ -20,6 +20,7 @@ package org.eclipse.jetty.http.client;
import java.io.IOException; import java.io.IOException;
import java.io.InterruptedIOException; import java.io.InterruptedIOException;
import java.util.EnumSet;
import java.util.Random; import java.util.Random;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit; 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.http2.FlowControlStrategy;
import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.AbstractHandler; 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.Assert;
import org.junit.Assume;
import org.junit.Test; import org.junit.Test;
public class HttpClientTest extends AbstractTest public class HttpClientTest extends AbstractTest
@ -285,6 +289,33 @@ public class HttpClientTest extends AbstractTest
Assert.assertEquals(response.getStatus(), 200); 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 private void sleep(long time) throws IOException
{ {
try try

View File

@ -139,7 +139,7 @@ public abstract class AbstractSessionInvalidateAndCreateTest
assertTrue(listener.destroys.contains("session1")); assertTrue(listener.destroys.contains("session1"));
assertTrue(listener.destroys.contains("session2")); assertTrue(listener.destroys.contains("session2"));
//session2's HttpSessionBindingListener should have been called when it was scavenged //session2's HttpSessionBindingListener should have been called when it was scavenged
assertTrue(servlet.unbound); assertTrue(servlet.listener.unbound);
} }
finally finally
{ {
@ -151,24 +151,33 @@ public abstract class AbstractSessionInvalidateAndCreateTest
server.stop(); 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; 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 @Override
protected void doGet(HttpServletRequest request, HttpServletResponse httpServletResponse) throws ServletException, IOException protected void doGet(HttpServletRequest request, HttpServletResponse httpServletResponse) throws ServletException, IOException
@ -190,7 +199,7 @@ public abstract class AbstractSessionInvalidateAndCreateTest
//now make a new session //now make a new session
session = request.getSession(true); session = request.getSession(true);
session.setAttribute("identity", "session2"); session.setAttribute("identity", "session2");
session.setAttribute("listener", new MySessionBindingListener()); session.setAttribute("listener", listener);
} }
else else
fail("Session already missing"); fail("Session already missing");

View File

@ -20,7 +20,7 @@
<!-- DO NOT DEPLOY (or Release) --> <!-- DO NOT DEPLOY (or Release) -->
<skip>true</skip> <skip>true</skip>
</configuration> </configuration>
</plugin> </plugin>
</plugins> </plugins>
</build> </build>
<modules> <modules>

View File

@ -129,6 +129,14 @@
</loginServices> </loginServices>
</configuration> </configuration>
</plugin> </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<!-- must deploy: required for jetty-distribution -->
<skip>false</skip>
</configuration>
</plugin>
</plugins> </plugins>
</build> </build>
<dependencies> <dependencies>

View File

@ -24,6 +24,14 @@
</archive> </archive>
</configuration> </configuration>
</plugin> </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<!-- must deploy: required for jetty-distribution -->
<skip>false</skip>
</configuration>
</plugin>
</plugins> </plugins>
</build> </build>
<dependencies> <dependencies>