Merge branch 'master' into jetty-9.4.x-Feature
This commit is contained in:
commit
36b5687329
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
@ -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}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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.
|
||||||
*
|
*
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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><0 means never inspect</li>
|
||||||
* >0 means inspect at that interval
|
* <li>0 means always inspect</li>
|
||||||
|
* <li>>0 means inspect at that interval</li>
|
||||||
|
* </ul>
|
||||||
*/
|
*/
|
||||||
protected int _timeoutSec = -1;
|
protected int _timeoutSec = -1;
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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()
|
||||||
{
|
{
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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 ()
|
||||||
{
|
{
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 ()
|
||||||
{
|
{
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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":
|
||||||
|
|
|
@ -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
|
||||||
|
|
5
pom.xml
5
pom.xml
|
@ -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>
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in New Issue