Merge branch 'jetty-9.4.x' into jetty-9.4.x-issue-2496-skip-unsupported-packaging

This commit is contained in:
Joakim Erdfelt 2018-05-02 13:39:44 -05:00
commit a4d7670536
33 changed files with 884 additions and 350 deletions

View File

@ -11,6 +11,9 @@
<artifactId>examples-parent</artifactId>
<name>Jetty Examples :: Parent</name>
<packaging>pom</packaging>
<properties>
<sonar.skip>true</sonar.skip>
</properties>
<build>
<plugins>
<plugin>

View File

@ -258,3 +258,7 @@ This allows for some complex hierarchies of configuration details.
If the file does not exist at the given location, download it from the given http URI.
Note: location is always relative to `${jetty.base}`.
You might need to escape the slash "\|" to use this on some environments.
maven.repo.uri=[url]::
The url to use to download Maven dependencies.
Default is http://central.maven.org/maven2/.

View File

@ -377,11 +377,15 @@ The `AbstractLoginModule` does not support any caching, so if you want to cache
==== Other Goodies
===== ServletRequestCallback
This callback gives you access to the ServletRequest that is involved in the authentication, and thus to other features like the current Session. This callback can be configured in your custom LoginModule implementation. Note that none of the LoginModule implementations provided with Jetty currently use this callback.
===== RequestParameterCallback
As all servlet containers intercept and process a form submission with action `j_security_check`, it is usually not possible to insert any extra input fields onto a login form with which to perform authentication: you may only pass `j_username` and `j_password`.
For those rare occasions when this is not good enough, and you require more information from the user in order to authenticate them, you can use the JAAS callback handler `org.eclipse.jetty.jaas.callback.RequestParameterCallback`.
This callback handler gives you access to all parameters that were passed in the form submission.
This callback gives you access to all parameters that were passed in the form submission.
To use it, in the `login()` method of your custom login module, add the `RequestParameterCallback` to the list of callback handlers the login module uses, tell it which params you are interested in, and then get the value of the parameter back.
Here is an example:

View File

@ -31,15 +31,19 @@ import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.Configuration;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import javax.servlet.ServletRequest;
import org.eclipse.jetty.jaas.callback.ServletRequestCallback;
import org.eclipse.jetty.jaas.callback.DefaultCallbackHandler;
import org.eclipse.jetty.jaas.callback.ObjectCallback;
import org.eclipse.jetty.jaas.callback.RequestParameterCallback;
import org.eclipse.jetty.security.DefaultIdentityService;
import org.eclipse.jetty.security.IdentityService;
import org.eclipse.jetty.security.LoginService;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.UserIdentity;
import org.eclipse.jetty.util.ArrayUtil;
import org.eclipse.jetty.util.Loader;
@ -69,11 +73,10 @@ public class JAASLoginService extends AbstractLifeCycle implements LoginService
protected String _loginModuleName;
protected JAASUserPrincipal _defaultUser = new JAASUserPrincipal(null, null, null);
protected IdentityService _identityService;
protected Configuration _configuration;
/**
*
*/
public JAASLoginService()
{
}
@ -117,7 +120,28 @@ public class JAASLoginService extends AbstractLifeCycle implements LoginService
}
/** Get the identityService.
/**
* @return the configuration
*/
public Configuration getConfiguration()
{
return _configuration;
}
/**
* @param configuration the configuration to set
*/
public void setConfiguration(Configuration configuration)
{
_configuration = configuration;
}
/**
* Get the identityService.
* @return the identityService
*/
@Override
@ -127,7 +151,8 @@ public class JAASLoginService extends AbstractLifeCycle implements LoginService
}
/** Set the identityService.
/**
* Set the identityService.
* @param identityService the identityService to set
*/
@Override
@ -189,8 +214,6 @@ public class JAASLoginService extends AbstractLifeCycle implements LoginService
try
{
CallbackHandler callbackHandler = null;
if (_callbackHandlerClass == null)
{
callbackHandler = new CallbackHandler()
@ -218,6 +241,10 @@ public class JAASLoginService extends AbstractLifeCycle implements LoginService
if (request!=null)
rpc.setParameterValues(Arrays.asList(request.getParameterValues(rpc.getParameterName())));
}
else if (callback instanceof ServletRequestCallback)
{
((ServletRequestCallback)callback).setRequest(request);
}
else
throw new UnsupportedCallbackException(callback);
}
@ -228,11 +255,20 @@ public class JAASLoginService extends AbstractLifeCycle implements LoginService
{
Class<?> clazz = Loader.loadClass(_callbackHandlerClass);
callbackHandler = (CallbackHandler)clazz.getDeclaredConstructor().newInstance();
if (DefaultCallbackHandler.class.isAssignableFrom(clazz))
{
DefaultCallbackHandler dch = (DefaultCallbackHandler)callbackHandler;
if (request instanceof Request)
dch.setRequest((Request)request);
dch.setCredential(credentials);
dch.setUserName(username);
}
}
//set up the login context
//TODO jaspi requires we provide the Configuration parameter
Subject subject = new Subject();
LoginContext loginContext = new LoginContext(_loginModuleName, subject, callbackHandler);
LoginContext loginContext = (_configuration==null?new LoginContext(_loginModuleName, subject, callbackHandler)
:new LoginContext(_loginModuleName, subject, callbackHandler, _configuration));
loginContext.login();

View File

@ -30,7 +30,10 @@ import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.util.security.Password;
/**
* DefaultUsernameCredentialCallbackHandler
* DefaultCallbackHandler
*
* An implementation of the JAAS CallbackHandler. Users can provide
* their own implementation instead and set the name of its class on the JAASLoginService.
*/
public class DefaultCallbackHandler extends AbstractCallbackHandler
{
@ -38,7 +41,7 @@ public class DefaultCallbackHandler extends AbstractCallbackHandler
public void setRequest (Request request)
{
this._request = request;
_request = request;
}
@Override
@ -71,6 +74,10 @@ public class DefaultCallbackHandler extends AbstractCallbackHandler
RequestParameterCallback callback = (RequestParameterCallback)callbacks[i];
callback.setParameterValues(Arrays.asList(_request.getParameterValues(callback.getParameterName())));
}
else if (callbacks[i] instanceof ServletRequestCallback)
{
((ServletRequestCallback)callbacks[i]).setRequest(_request);
}
else
throw new UnsupportedCallbackException(callbacks[i]);
}

View File

@ -0,0 +1,44 @@
//
// ========================================================================
// Copyright (c) 1995-2018 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.jaas.callback;
import javax.security.auth.callback.Callback;
import javax.servlet.ServletRequest;
/**
* ServletRequestCallback
*
* Provides access to the request associated with the authentication.
*/
public class ServletRequestCallback implements Callback
{
protected ServletRequest _request;
public void setRequest (ServletRequest request)
{
_request = request;
}
public ServletRequest getRequest ()
{
return _request;
}
}

View File

@ -115,6 +115,12 @@ public abstract class AbstractLoginModule implements LoginModule
}
}
public abstract UserInfo getUserInfo (String username) throws Exception;
public Subject getSubject ()
{
return this.subject;
@ -198,7 +204,6 @@ public abstract class AbstractLoginModule implements LoginModule
public Callback[] configureCallbacks ()
{
Callback[] callbacks = new Callback[3];
callbacks[0] = new NameCallback("Enter user name");
callbacks[1] = new ObjectCallback();
@ -211,9 +216,7 @@ public abstract class AbstractLoginModule implements LoginModule
{
return false;
}
public abstract UserInfo getUserInfo (String username) throws Exception;

View File

@ -23,9 +23,15 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.security.Principal;
import java.util.Collections;
import javax.security.auth.Subject;
import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag;
import javax.security.auth.login.Configuration;
import org.eclipse.jetty.security.DefaultIdentityService;
import org.eclipse.jetty.server.Request;
import org.junit.Test;
/**
@ -35,6 +41,19 @@ import org.junit.Test;
*/
public class JAASLoginServiceTest
{
public static class TestConfiguration extends Configuration
{
AppConfigurationEntry _entry = new AppConfigurationEntry(TestLoginModule.class.getCanonicalName(), LoginModuleControlFlag.REQUIRED, Collections.emptyMap());
@Override
public AppConfigurationEntry[] getAppConfigurationEntry(String name)
{
return new AppConfigurationEntry[] {_entry};
}
}
interface SomeRole
{
@ -79,7 +98,24 @@ public class JAASLoginServiceTest
}
}
@Test
public void testServletRequestCallback () throws Exception
{
//Test with the DefaultCallbackHandler
JAASLoginService ls = new JAASLoginService("foo");
ls.setCallbackHandlerClass("org.eclipse.jetty.jaas.callback.DefaultCallbackHandler");
ls.setIdentityService(new DefaultIdentityService());
ls.setConfiguration(new TestConfiguration());
Request request = new Request(null, null);
ls.login("aaardvaark", "aaa", request);
//Test with the fallback CallbackHandler
ls = new JAASLoginService("foo");
ls.setIdentityService(new DefaultIdentityService());
ls.setConfiguration(new TestConfiguration());
ls.login("aaardvaark", "aaa", request);
}
@Test
public void testLoginServiceRoles () throws Exception

View File

@ -0,0 +1,59 @@
//
// ========================================================================
// Copyright (c) 1995-2018 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.jaas;
import static org.junit.Assert.assertNotNull;
import javax.security.auth.callback.Callback;
import javax.security.auth.login.LoginException;
import org.eclipse.jetty.jaas.callback.ServletRequestCallback;
import org.eclipse.jetty.jaas.spi.AbstractLoginModule;
import org.eclipse.jetty.jaas.spi.UserInfo;
import org.eclipse.jetty.util.ArrayUtil;
import org.eclipse.jetty.util.security.Password;
public class TestLoginModule extends AbstractLoginModule
{
public ServletRequestCallback _callback = new ServletRequestCallback();
@Override
public UserInfo getUserInfo(String username) throws Exception
{
return new UserInfo(username, new Password("aaa"));
}
@Override
public Callback[] configureCallbacks()
{
return ArrayUtil.addToArray(super.configureCallbacks(), _callback, Callback.class);
}
@Override
public boolean login() throws LoginException
{
boolean result = super.login();
assertNotNull(_callback.getRequest());
return result;
}
}

View File

@ -44,9 +44,12 @@ public class MemcachedSessionDataMapFactory implements SessionDataMapFactory
{
if (addresses == null)
_addresses = null;
_addresses = new ArrayList<>();
for (InetSocketAddress a:addresses)
_addresses.add(a);
else
{
_addresses = new ArrayList<>();
for (InetSocketAddress a:addresses)
_addresses.add(a);
}
}
/**

View File

@ -695,11 +695,11 @@ public abstract class AbstractProxyServlet extends HttpServlet
*/
protected static class TransparentDelegate
{
private final ProxyServlet proxyServlet;
private final AbstractProxyServlet proxyServlet;
private String _proxyTo;
private String _prefix;
protected TransparentDelegate(ProxyServlet proxyServlet)
protected TransparentDelegate(AbstractProxyServlet proxyServlet)
{
this.proxyServlet = proxyServlet;
}

View File

@ -234,7 +234,7 @@ public class AsyncMiddleManServlet extends AbstractProxyServlet
*
* @see org.eclipse.jetty.proxy.AbstractProxyServlet.TransparentDelegate
*/
public static class Transparent extends ProxyServlet
public static class Transparent extends AsyncMiddleManServlet
{
private final TransparentDelegate delegate = new TransparentDelegate(this);

View File

@ -89,6 +89,8 @@ import org.junit.Test;
public class AsyncMiddleManServletTest
{
private static final Logger LOG = Log.getLogger(AsyncMiddleManServletTest.class);
private static final String PROXIED_HEADER = "X-Proxied";
@Rule
public final TestTracker tracker = new TestTracker();
private HttpClient client;
@ -1483,6 +1485,41 @@ public class AsyncMiddleManServletTest
Assert.assertEquals(chunk1.capacity() + chunk2.capacity(), response.getContent().length);
}
@Test
public void testTransparentProxyWithIdentityContentTransformer() throws Exception
{
final String target = "/test";
startServer(new HttpServlet()
{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
if (req.getHeader("Via") != null)
resp.addHeader(PROXIED_HEADER, "true");
resp.setStatus(target.equals(req.getRequestURI()) ? 200 : 404);
}
});
final String proxyTo = "http://localhost:" + serverConnector.getLocalPort();
AsyncMiddleManServlet proxyServlet = new AsyncMiddleManServlet.Transparent() {
@Override
protected ContentTransformer newServerResponseContentTransformer(HttpServletRequest clientRequest, HttpServletResponse proxyResponse, Response serverResponse) {
return ContentTransformer.IDENTITY;
}
};
Map<String, String> initParams = new HashMap<>();
initParams.put("proxyTo", proxyTo);
startProxy(proxyServlet, initParams);
startClient();
// Make the request to the proxy, it should transparently forward to the server
ContentResponse response = client.newRequest("localhost", proxyConnector.getLocalPort())
.path(target)
.timeout(5, TimeUnit.SECONDS)
.send();
Assert.assertEquals(200, response.getStatus());
Assert.assertTrue(response.getHeaders().containsKey(PROXIED_HEADER));
}
private Path prepareTargetTestsDir() throws IOException
{
final Path targetTestsDir = MavenTestingUtils.getTargetTestingDir().toPath();

View File

@ -39,10 +39,8 @@ import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
/* ------------------------------------------------------------ */
/**
* ConnectionFactory for the PROXY Protocol.
* <p>ConnectionFactory for the PROXY Protocol.</p>
* <p>This factory can be placed in front of any other connection factory
* to process the proxy v1 or v2 line before the normal protocol handling</p>
*
@ -50,26 +48,26 @@ import org.eclipse.jetty.util.log.Logger;
*/
public class ProxyConnectionFactory extends AbstractConnectionFactory
{
public static final String TLS_VERSION = "TLS_VERSION";
private static final Logger LOG = Log.getLogger(ProxyConnectionFactory.class);
private final String _next;
private int _maxProxyHeader=1024;
public static final String TLS_VERSION = "TLS_VERSION";
/* ------------------------------------------------------------ */
/** Proxy Connection Factory that uses the next ConnectionFactory
private final String _next;
private int _maxProxyHeader = 1024;
/**
* Proxy Connection Factory that uses the next ConnectionFactory
* on the connector as the next protocol
*/
public ProxyConnectionFactory()
{
super("proxy");
_next=null;
_next = null;
}
public ProxyConnectionFactory(String nextProtocol)
{
super("proxy");
_next=nextProtocol;
_next = nextProtocol;
}
public int getMaxProxyHeader()
@ -85,34 +83,34 @@ public class ProxyConnectionFactory extends AbstractConnectionFactory
@Override
public Connection newConnection(Connector connector, EndPoint endp)
{
String next=_next;
if (next==null)
String next = _next;
if (next == null)
{
for (Iterator<String> i = connector.getProtocols().iterator();i.hasNext();)
for (Iterator<String> i = connector.getProtocols().iterator(); i.hasNext(); )
{
String p=i.next();
String p = i.next();
if (getProtocol().equalsIgnoreCase(p))
{
next=i.next();
next = i.next();
break;
}
}
}
return new ProxyProtocolV1orV2Connection(endp,connector,next);
return new ProxyProtocolV1orV2Connection(endp, connector, next);
}
public class ProxyProtocolV1orV2Connection extends AbstractConnection
{
private final Connector _connector;
private final String _next;
private ByteBuffer _buffer = BufferUtil.allocate(16);
protected ProxyProtocolV1orV2Connection(EndPoint endp, Connector connector, String next)
{
super(endp,connector.getExecutor());
_connector=connector;
_next=next;
super(endp, connector.getExecutor());
_connector = connector;
_next = next;
}
@Override
@ -127,16 +125,16 @@ public class ProxyConnectionFactory extends AbstractConnectionFactory
{
try
{
while(BufferUtil.space(_buffer)>0)
while (BufferUtil.space(_buffer) > 0)
{
// Read data
int fill=getEndPoint().fill(_buffer);
if (fill<0)
int fill = getEndPoint().fill(_buffer);
if (fill < 0)
{
getEndPoint().shutdownOutput();
return;
}
if (fill==0)
if (fill == 0)
{
fillInterested();
return;
@ -144,28 +142,28 @@ public class ProxyConnectionFactory extends AbstractConnectionFactory
}
// Is it a V1?
switch(_buffer.get(0))
switch (_buffer.get(0))
{
case 'P':
{
ProxyProtocolV1Connection v1 = new ProxyProtocolV1Connection(getEndPoint(),_connector,_next,_buffer);
ProxyProtocolV1Connection v1 = new ProxyProtocolV1Connection(getEndPoint(), _connector, _next, _buffer);
getEndPoint().upgrade(v1);
return;
}
case 0x0D:
{
ProxyProtocolV2Connection v2 = new ProxyProtocolV2Connection(getEndPoint(),_connector,_next,_buffer);
ProxyProtocolV2Connection v2 = new ProxyProtocolV2Connection(getEndPoint(), _connector, _next, _buffer);
getEndPoint().upgrade(v2);
return;
}
default:
LOG.warn("Not PROXY protocol for {}",getEndPoint());
close();
default:
LOG.warn("Not PROXY protocol for {}", getEndPoint());
close();
}
}
catch (Throwable x)
{
LOG.warn("PROXY error for "+getEndPoint(),x);
LOG.warn("PROXY error for " + getEndPoint(), x);
close();
}
}
@ -177,20 +175,20 @@ public class ProxyConnectionFactory extends AbstractConnectionFactory
// 98765432109876543210987654321
// PROXY P R.R.R.R L.L.L.L R Lrn
private final int[] __size = {29,23,21,13,5,3,1};
private final int[] __size = {29, 23, 21, 13, 5, 3, 1};
private final Connector _connector;
private final String _next;
private final StringBuilder _builder=new StringBuilder();
private final String[] _field=new String[6];
private final StringBuilder _builder = new StringBuilder();
private final String[] _field = new String[6];
private int _fields;
private int _length;
protected ProxyProtocolV1Connection(EndPoint endp, Connector connector, String next,ByteBuffer buffer)
protected ProxyProtocolV1Connection(EndPoint endp, Connector connector, String next, ByteBuffer buffer)
{
super(endp,connector.getExecutor());
_connector=connector;
_next=next;
_length=buffer.remaining();
super(endp, connector.getExecutor());
_connector = connector;
_next = next;
_length = buffer.remaining();
parse(buffer);
}
@ -200,24 +198,23 @@ public class ProxyConnectionFactory extends AbstractConnectionFactory
super.onOpen();
fillInterested();
}
private boolean parse(ByteBuffer buffer)
{
// parse fields
while (buffer.hasRemaining())
{
byte b = buffer.get();
if (_fields<6)
if (_fields < 6)
{
if (b==' ' || b=='\r' && _fields==5)
if (b == ' ' || b == '\r' && _fields == 5)
{
_field[_fields++]=_builder.toString();
_field[_fields++] = _builder.toString();
_builder.setLength(0);
}
else if (b<' ')
else if (b < ' ')
{
LOG.warn("Bad character {} for {}",b&0xFF,getEndPoint());
LOG.warn("Bad character {} for {}", b & 0xFF, getEndPoint());
close();
return false;
}
@ -228,55 +225,54 @@ public class ProxyConnectionFactory extends AbstractConnectionFactory
}
else
{
if (b=='\n')
if (b == '\n')
{
_fields=7;
_fields = 7;
return true;
}
LOG.warn("Bad CRLF for {}",getEndPoint());
LOG.warn("Bad CRLF for {}", getEndPoint());
close();
return false;
}
}
return true;
}
@Override
public void onFillable()
{
try
{
ByteBuffer buffer=null;
while(_fields<7)
ByteBuffer buffer = null;
while (_fields < 7)
{
// Create a buffer that will not read too much data
// since once read it is impossible to push back for the
// real connection to read it.
int size=Math.max(1,__size[_fields]-_builder.length());
if (buffer==null || buffer.capacity()!=size)
buffer=BufferUtil.allocate(size);
int size = Math.max(1, __size[_fields] - _builder.length());
if (buffer == null || buffer.capacity() != size)
buffer = BufferUtil.allocate(size);
else
BufferUtil.clear(buffer);
// Read data
int fill=getEndPoint().fill(buffer);
if (fill<0)
int fill = getEndPoint().fill(buffer);
if (fill < 0)
{
getEndPoint().shutdownOutput();
return;
}
if (fill==0)
if (fill == 0)
{
fillInterested();
return;
}
_length+=fill;
if (_length>=108)
_length += fill;
if (_length >= 108)
{
LOG.warn("PROXY line too long {} for {}",_length,getEndPoint());
LOG.warn("PROXY line too long {} for {}", _length, getEndPoint());
close();
return;
}
@ -288,44 +284,51 @@ public class ProxyConnectionFactory extends AbstractConnectionFactory
// Check proxy
if (!"PROXY".equals(_field[0]))
{
LOG.warn("Not PROXY protocol for {}",getEndPoint());
LOG.warn("Not PROXY protocol for {}", getEndPoint());
close();
return;
}
// Extract Addresses
InetSocketAddress remote=new InetSocketAddress(_field[2],Integer.parseInt(_field[4]));
InetSocketAddress local =new InetSocketAddress(_field[3],Integer.parseInt(_field[5]));
InetSocketAddress remote = new InetSocketAddress(_field[2], Integer.parseInt(_field[4]));
InetSocketAddress local = new InetSocketAddress(_field[3], Integer.parseInt(_field[5]));
// Create the next protocol
ConnectionFactory connectionFactory = _connector.getConnectionFactory(_next);
if (connectionFactory == null)
{
LOG.warn("No Next protocol '{}' for {}",_next,getEndPoint());
LOG.warn("No Next protocol '{}' for {}", _next, getEndPoint());
close();
return;
}
if (LOG.isDebugEnabled())
LOG.warn("Next protocol '{}' for {} r={} l={}",_next,getEndPoint(),remote,local);
EndPoint endPoint = new ProxyEndPoint(getEndPoint(),remote,local);
if (LOG.isDebugEnabled())
LOG.warn("Next protocol '{}' for {} r={} l={}", _next, getEndPoint(), remote, local);
EndPoint endPoint = new ProxyEndPoint(getEndPoint(), remote, local);
Connection newConnection = connectionFactory.newConnection(_connector, endPoint);
endPoint.upgrade(newConnection);
}
catch (Throwable x)
{
LOG.warn("PROXY error for "+getEndPoint(),x);
LOG.warn("PROXY error for " + getEndPoint(), x);
close();
}
}
}
enum Family { UNSPEC, INET, INET6, UNIX };
enum Transport { UNSPEC, STREAM, DGRAM };
private static final byte[] MAGIC = new byte[]{0x0D,0x0A,0x0D,0x0A,0x00,0x0D,0x0A,0x51,0x55,0x49,0x54,0x0A};
private enum Family
{
UNSPEC, INET, INET6, UNIX
}
private enum Transport
{
UNSPEC, STREAM, DGRAM
}
private static final byte[] MAGIC = new byte[]{0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A};
public class ProxyProtocolV2Connection extends AbstractConnection
{
private final Connector _connector;
@ -336,122 +339,135 @@ public class ProxyConnectionFactory extends AbstractConnectionFactory
private final int _length;
private final ByteBuffer _buffer;
protected ProxyProtocolV2Connection(EndPoint endp, Connector connector, String next,ByteBuffer buffer)
throws IOException
protected ProxyProtocolV2Connection(EndPoint endp, Connector connector, String next, ByteBuffer buffer) throws IOException
{
super(endp,connector.getExecutor());
_connector=connector;
_next=next;
if (buffer.remaining()!=16)
super(endp, connector.getExecutor());
_connector = connector;
_next = next;
if (buffer.remaining() != 16)
throw new IllegalStateException();
if (LOG.isDebugEnabled())
LOG.debug("PROXYv2 header {} for {}",BufferUtil.toHexSummary(buffer),this);
LOG.debug("PROXYv2 header {} for {}", BufferUtil.toHexSummary(buffer), this);
// struct proxy_hdr_v2 {
// uint8_t sig[12]; /* hex 0D 0A 0D 0A 00 0D 0A 51 55 49 54 0A */
// uint8_t ver_cmd; /* protocol version and command */
// uint8_t fam; /* protocol family and address */
// uint16_t len; /* number of following bytes part of the header */
// };
for (int i=0;i<MAGIC.length;i++)
if (buffer.get()!=MAGIC[i])
for (byte magic : MAGIC)
{
if (buffer.get() != magic)
throw new IOException("Bad PROXY protocol v2 signature");
}
int versionAndCommand = 0xff & buffer.get();
if ((versionAndCommand&0xf0) != 0x20)
if ((versionAndCommand & 0xf0) != 0x20)
throw new IOException("Bad PROXY protocol v2 version");
_local=(versionAndCommand&0xf)==0x00;
_local = (versionAndCommand & 0xf) == 0x00;
int transportAndFamily = 0xff & buffer.get();
switch(transportAndFamily>>4)
switch (transportAndFamily >> 4)
{
case 0: _family=Family.UNSPEC; break;
case 1: _family=Family.INET; break;
case 2: _family=Family.INET6; break;
case 3: _family=Family.UNIX; break;
case 0:
_family = Family.UNSPEC;
break;
case 1:
_family = Family.INET;
break;
case 2:
_family = Family.INET6;
break;
case 3:
_family = Family.UNIX;
break;
default:
throw new IOException("Bad PROXY protocol v2 family");
}
switch(0xf&transportAndFamily)
{
case 0: _transport=Transport.UNSPEC; break;
case 1: _transport=Transport.STREAM; break;
case 2: _transport=Transport.DGRAM; break;
default:
throw new IOException("Bad PROXY protocol v2 family");
}
_length = buffer.getChar();
if (!_local && (_family==Family.UNSPEC || _family==Family.UNIX || _transport!=Transport.STREAM))
throw new IOException(String.format("Unsupported PROXY protocol v2 mode 0x%x,0x%x",versionAndCommand,transportAndFamily));
if (_length>_maxProxyHeader)
throw new IOException(String.format("Unsupported PROXY protocol v2 mode 0x%x,0x%x,0x%x",versionAndCommand,transportAndFamily,_length));
_buffer = _length>0?BufferUtil.allocate(_length):BufferUtil.EMPTY_BUFFER;
switch (0xf & transportAndFamily)
{
case 0:
_transport = Transport.UNSPEC;
break;
case 1:
_transport = Transport.STREAM;
break;
case 2:
_transport = Transport.DGRAM;
break;
default:
throw new IOException("Bad PROXY protocol v2 family");
}
_length = buffer.getChar();
if (!_local && (_family == Family.UNSPEC || _family == Family.UNIX || _transport != Transport.STREAM))
throw new IOException(String.format("Unsupported PROXY protocol v2 mode 0x%x,0x%x", versionAndCommand, transportAndFamily));
if (_length > getMaxProxyHeader())
throw new IOException(String.format("Unsupported PROXY protocol v2 mode 0x%x,0x%x,0x%x", versionAndCommand, transportAndFamily, _length));
_buffer = _length > 0 ? BufferUtil.allocate(_length) : BufferUtil.EMPTY_BUFFER;
}
@Override
public void onOpen()
{
super.onOpen();
if (_buffer.remaining()==_length)
if (_buffer.remaining() == _length)
next();
else
fillInterested();
}
@Override
public void onFillable()
{
try
{
while(_buffer.remaining()<_length)
while (_buffer.remaining() < _length)
{
// Read data
int fill=getEndPoint().fill(_buffer);
if (fill<0)
int fill = getEndPoint().fill(_buffer);
if (fill < 0)
{
getEndPoint().shutdownOutput();
return;
}
if (fill==0)
if (fill == 0)
{
fillInterested();
return;
}
}
}
next();
}
catch (Throwable x)
{
LOG.warn("PROXY error for "+getEndPoint(),x);
LOG.warn("PROXY error for " + getEndPoint(), x);
close();
return;
}
next();
}
private void next()
{
if (LOG.isDebugEnabled())
LOG.debug("PROXYv2 next {} from {} for {}",_next,BufferUtil.toHexSummary(_buffer),this);
LOG.debug("PROXYv2 next {} from {} for {}", _next, BufferUtil.toHexSummary(_buffer), this);
// Create the next protocol
ConnectionFactory connectionFactory = _connector.getConnectionFactory(_next);
if (connectionFactory == null)
{
LOG.info("Next protocol '{}' for {}",_next,getEndPoint());
LOG.info("Next protocol '{}' for {}", _next, getEndPoint());
close();
return;
}
}
// Do we need to wrap the endpoint?
EndPoint endPoint=getEndPoint();
EndPoint endPoint = getEndPoint();
if (!_local)
{
try
@ -461,84 +477,80 @@ public class ProxyConnectionFactory extends AbstractConnectionFactory
int sp;
int dp;
switch(_family)
switch (_family)
{
case INET:
{
byte[] addr=new byte[4];
byte[] addr = new byte[4];
_buffer.get(addr);
src = Inet4Address.getByAddress(addr);
_buffer.get(addr);
dst = Inet4Address.getByAddress(addr);
sp = _buffer.getChar();
dp = _buffer.getChar();
break;
}
case INET6:
{
byte[] addr=new byte[16];
byte[] addr = new byte[16];
_buffer.get(addr);
src = Inet6Address.getByAddress(addr);
_buffer.get(addr);
dst = Inet6Address.getByAddress(addr);
sp = _buffer.getChar();
dp = _buffer.getChar();
break;
break;
}
default:
throw new IllegalStateException();
}
// Extract Addresses
InetSocketAddress remote=new InetSocketAddress(src,sp);
InetSocketAddress local =new InetSocketAddress(dst,dp);
ProxyEndPoint proxyEndPoint = new ProxyEndPoint(endPoint,remote,local);
InetSocketAddress remote = new InetSocketAddress(src, sp);
InetSocketAddress local = new InetSocketAddress(dst, dp);
ProxyEndPoint proxyEndPoint = new ProxyEndPoint(endPoint, remote, local);
endPoint = proxyEndPoint;
// Any additional info?
while(_buffer.hasRemaining())
while (_buffer.hasRemaining())
{
int type = 0xff & _buffer.get();
int length = _buffer.getShort();
byte[] value = new byte[length];
_buffer.get(value);
if (LOG.isDebugEnabled())
LOG.debug(String.format("T=%x L=%d V=%s for %s",type,length,TypeUtil.toHexString(value),this));
LOG.debug(String.format("T=%x L=%d V=%s for %s", type, length, TypeUtil.toHexString(value), this));
// TODO interpret these values
switch(type)
switch (type)
{
case 0x01: // PP2_TYPE_ALPN
break;
case 0x02: // PP2_TYPE_AUTHORITY
break;
case 0x20: // PP2_TYPE_SSL
{
int i=0;
{
int i = 0;
int client = 0xff & value[i++];
int verify = ((0xff & value[i++])<<24) + ((0xff & value[i++])<<16) + ((0xff & value[i++])<<8) + (0xff&value[i++]);
while(i<value.length)
while (i < value.length)
{
int ssl_type = 0xff & value[i++];
int ssl_length = (0xff & value[i++])*0x100 + (0xff&value[i++]);
int ssl_length = (0xff & value[i++]) * 0x100 + (0xff & value[i++]);
byte[] ssl_val = new byte[ssl_length];
System.arraycopy(value,i,ssl_val,0,ssl_length);
i+=ssl_length;
switch(ssl_type)
System.arraycopy(value, i, ssl_val, 0, ssl_length);
i += ssl_length;
switch (ssl_type)
{
case 0x21: // PP2_TYPE_SSL_VERSION
String version=new String(ssl_val,0,ssl_length,StandardCharsets.ISO_8859_1);
if (client==1)
proxyEndPoint.setAttribute(TLS_VERSION,version);
String version = new String(ssl_val, 0, ssl_length, StandardCharsets.ISO_8859_1);
if (client == 1)
proxyEndPoint.setAttribute(TLS_VERSION, version);
break;
default:
break;
}
@ -555,13 +567,12 @@ public class ProxyConnectionFactory extends AbstractConnectionFactory
break;
}
}
if (LOG.isDebugEnabled())
LOG.debug("{} {}",getEndPoint(),proxyEndPoint.toString());
if (LOG.isDebugEnabled())
LOG.debug("{} {}", getEndPoint(), proxyEndPoint.toString());
}
catch(Exception e)
catch (Exception e)
{
LOG.warn(e);
}
@ -569,9 +580,8 @@ public class ProxyConnectionFactory extends AbstractConnectionFactory
Connection newConnection = connectionFactory.newConnection(_connector, endPoint);
endPoint.upgrade(newConnection);
}
}
}
public static class ProxyEndPoint extends AttributesMap implements EndPoint
{
@ -581,9 +591,9 @@ public class ProxyConnectionFactory extends AbstractConnectionFactory
public ProxyEndPoint(EndPoint endp, InetSocketAddress remote, InetSocketAddress local)
{
_endp=endp;
_remote=remote;
_local=local;
_endp = endp;
_remote = remote;
_local = local;
}
@Override
@ -691,7 +701,7 @@ public class ProxyConnectionFactory extends AbstractConnectionFactory
@Override
public void write(Callback callback, ByteBuffer... buffers) throws WritePendingException
{
_endp.write(callback,buffers);
_endp.write(callback, buffers);
}
@Override

View File

@ -57,13 +57,11 @@ import org.eclipse.jetty.util.log.Logger;
@ManagedObject
public class FileSessionDataStore extends AbstractSessionDataStore
{
private final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session");
private File _storeDir;
private boolean _deleteUnrestorableFiles = false;
private Map<String,String> _sessionFileMap = new ConcurrentHashMap<>();
private String _contextString;
protected File _storeDir;
protected boolean _deleteUnrestorableFiles = false;
protected Map<String,String> _sessionFileMap = new ConcurrentHashMap<>();
protected String _contextString;
protected long _lastSweepTime = 0L;
@Override
@ -516,7 +514,7 @@ public class FileSessionDataStore extends AbstractSessionDataStore
* @param data the info of the session
* @throws IOException
*/
private void save(OutputStream os, String id, SessionData data) throws IOException
protected void save(OutputStream os, String id, SessionData data) throws IOException
{
DataOutputStream out = new DataOutputStream(os);
out.writeUTF(id);
@ -657,7 +655,7 @@ public class FileSessionDataStore extends AbstractSessionDataStore
* @return the session data
* @throws Exception
*/
private SessionData load (InputStream is, String expectedId)
protected SessionData load (InputStream is, String expectedId)
throws Exception
{
String id = null; //the actual id from inside the file
@ -703,7 +701,7 @@ public class FileSessionDataStore extends AbstractSessionDataStore
* @param data the data to restore to
* @throws Exception
*/
private void restoreAttributes (InputStream is, int size, SessionData data)
protected void restoreAttributes (InputStream is, int size, SessionData data)
throws Exception
{
if (size>0)

View File

@ -164,6 +164,10 @@ Advanced Commands:
Notes: location is always relative to ${jetty.base}.
you might need to escape the slash "\|" to use
this on some environments.
maven.repo.uri=[url]
The url to use to download Maven dependencies.
Default is http://central.maven.org/maven2/.
Properties:

View File

@ -74,7 +74,7 @@ public final class OpCode
public static boolean isDataFrame(byte opcode)
{
return (opcode == TEXT) || (opcode == BINARY);
return (opcode == TEXT) || (opcode == BINARY) || (opcode == CONTINUATION);
}
/**

View File

@ -185,8 +185,18 @@ public abstract class AbstractExtension extends AbstractLifeCycle implements Dum
protected void nextOutgoingFrame(Frame frame, WriteCallback callback, BatchMode batchMode)
{
log.debug("nextOutgoingFrame({})",frame);
this.nextOutgoing.outgoingFrame(frame,callback, batchMode);
try
{
log.debug("nextOutgoingFrame({})", frame);
this.nextOutgoing.outgoingFrame(frame, callback, batchMode);
}
catch (Throwable t)
{
if (callback != null)
callback.writeFailed(t);
else
log.warn(t);
}
}
public void setBufferPool(ByteBufferPool bufferPool)

View File

@ -124,14 +124,17 @@ public class FragmentExtension extends AbstractExtension
private boolean finished = true;
@Override
protected Action process() throws Exception
protected Action process()
{
if (finished)
{
current = pollEntry();
LOG.debug("Processing {}", current);
if (current == null)
{
LOG.debug("Processing IDLE", current);
return Action.IDLE;
}
LOG.debug("Processing {}", current);
fragment(current, true);
}
else
@ -146,6 +149,7 @@ public class FragmentExtension extends AbstractExtension
Frame frame = entry.frame;
ByteBuffer payload = frame.getPayload();
int remaining = payload.remaining();
int length = Math.min(remaining, maxLength);
finished = length == remaining;
@ -186,7 +190,10 @@ public class FragmentExtension extends AbstractExtension
{
// Notify first then call succeeded(), otherwise
// write callbacks may be invoked out of order.
notifyCallbackSuccess(current.callback);
// only notify current (original) frame on completion
if (finished)
notifyCallbackSuccess(current.callback);
succeeded();
}

View File

@ -34,6 +34,7 @@ import org.eclipse.jetty.websocket.api.extensions.OutgoingFrames;
public class SaneFrameOrderingAssertion implements OutgoingFrames
{
boolean priorDataFrame = false;
public int frameCount = 0;
@Override
public void outgoingFrame(Frame frame, WriteCallback callback, BatchMode batchMode)
@ -63,11 +64,13 @@ public class SaneFrameOrderingAssertion implements OutgoingFrames
break;
}
if (OpCode.isDataFrame(frame.getOpCode()))
if (OpCode.isDataFrame(opcode))
{
priorDataFrame = !frame.isFin();
}
frameCount++;
if (callback != null)
callback.writeSuccess();
}

View File

@ -18,36 +18,48 @@
package org.eclipse.jetty.websocket.common.extensions;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.MappedByteBufferPool;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.api.BatchMode;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
import org.eclipse.jetty.websocket.api.extensions.Frame;
import org.eclipse.jetty.websocket.common.OpCode;
import org.eclipse.jetty.websocket.common.SaneFrameOrderingAssertion;
import org.eclipse.jetty.websocket.common.WebSocketFrame;
import org.eclipse.jetty.websocket.common.extensions.fragment.FragmentExtension;
import org.eclipse.jetty.websocket.common.frames.ContinuationFrame;
import org.eclipse.jetty.websocket.common.frames.PingFrame;
import org.eclipse.jetty.websocket.common.frames.TextFrame;
import org.eclipse.jetty.websocket.common.io.FutureWriteCallback;
import org.eclipse.jetty.websocket.common.test.ByteBufferAssert;
import org.eclipse.jetty.websocket.common.test.IncomingFramesCapture;
import org.eclipse.jetty.websocket.common.test.OutgoingFramesCapture;
import org.junit.Assert;
import org.junit.Test;
@SuppressWarnings("Duplicates")
public class FragmentExtensionTest
{
private static final Logger LOG = Log.getLogger(FragmentExtensionTest.class);
public ByteBufferPool bufferPool = new MappedByteBufferPool();
/**
@ -139,6 +151,7 @@ public class FragmentExtensionTest
/**
* Verify that outgoing text frames are fragmented by the maxLength configuration.
*
* @throws IOException on test failure
*/
@Test
@ -211,11 +224,12 @@ public class FragmentExtensionTest
}
/**
* Verify that outgoing text frames are fragmented by default configuration
* Verify that outgoing text frames are not fragmented by default configuration (which has no maxLength specified)
*
* @throws IOException on test failure
*/
@Test
public void testOutgoingFramesDefaultConfig() throws IOException
public void testOutgoingFramesDefaultConfig() throws Exception
{
OutgoingFramesCapture capture = new OutgoingFramesCapture();
@ -277,6 +291,7 @@ public class FragmentExtensionTest
/**
* Outgoing PING (Control Frame) should pass through extension unmodified
*
* @throws IOException on test failure
*/
@Test
@ -312,4 +327,72 @@ public class FragmentExtensionTest
Assert.assertThat("Frame.payloadLength", actual.getPayloadLength(), is(expected.remaining()));
ByteBufferAssert.assertEquals("Frame.payload", expected, actual.getPayload().slice());
}
/**
* Ensure that FragmentExtension honors the correct order of websocket frames.
*
* @see <a href="https://github.com/eclipse/jetty.project/issues/2491">eclipse/jetty.project#2491</a>
*/
@Test
public void testLargeSmallTextAlternating() throws Exception
{
final int largeMessageSize = 60000;
byte buf[] = new byte[largeMessageSize];
Arrays.fill(buf, (byte) 'x');
String largeMessage = new String(buf, UTF_8);
final int fragmentCount = 10;
final int fragmentLength = largeMessageSize / fragmentCount;
final int messageCount = 10000;
FragmentExtension ext = new FragmentExtension();
ext.setBufferPool(bufferPool);
ext.setPolicy(WebSocketPolicy.newServerPolicy());
ExtensionConfig config = ExtensionConfig.parse("fragment;maxLength=" + fragmentLength);
ext.setConfig(config);
SaneFrameOrderingAssertion saneFrameOrderingAssertion = new SaneFrameOrderingAssertion();
ext.setNextOutgoingFrames(saneFrameOrderingAssertion);
CompletableFuture<Integer> enqueuedFrameCountFut = new CompletableFuture<>();
CompletableFuture.runAsync(() -> {
// Run Server Task
int frameCount = 0;
BatchMode batchMode = BatchMode.OFF;
try
{
for (int i = 0; i < messageCount; i++)
{
int messageId = i;
FutureWriteCallback callback = new FutureWriteCallback();
WebSocketFrame frame;
if (i % 2 == 0)
{
frame = new TextFrame().setPayload(largeMessage);
frameCount += fragmentCount;
}
else
{
frame = new TextFrame().setPayload("Short Message: " + i);
frameCount++;
}
ext.outgoingFrame(frame, callback, batchMode);
callback.get();
}
enqueuedFrameCountFut.complete(frameCount);
}
catch (Throwable t)
{
enqueuedFrameCountFut.completeExceptionally(t);
}
});
int enqueuedFrameCount = enqueuedFrameCountFut.get(5, SECONDS);
int expectedFrameCount = (messageCount/2) * fragmentCount; // large messages
expectedFrameCount += (messageCount/2); // + short messages
assertThat("Saw expected frame count", saneFrameOrderingAssertion.frameCount, is(expectedFrameCount));
assertThat("Enqueued expected frame count", enqueuedFrameCount, is(expectedFrameCount));
}
}

View File

@ -23,6 +23,8 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
@ -33,6 +35,7 @@ import javax.servlet.http.HttpSession;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.junit.Test;
@ -56,20 +59,27 @@ public abstract class AbstractClusteredInvalidationSessionTest extends AbstractT
String servletMapping = "/server";
int maxInactiveInterval = 30;
int scavengeInterval = 1;
DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory();
cacheFactory.setEvictionPolicy(SessionCache.EVICT_ON_SESSION_EXIT);
SessionDataStoreFactory storeFactory = createSessionDataStoreFactory();
((AbstractSessionDataStoreFactory)storeFactory).setGracePeriodSec(scavengeInterval);
DefaultSessionCacheFactory cacheFactory1 = new DefaultSessionCacheFactory();
cacheFactory1.setEvictionPolicy(SessionCache.EVICT_ON_SESSION_EXIT);
SessionDataStoreFactory storeFactory1 = createSessionDataStoreFactory();
((AbstractSessionDataStoreFactory)storeFactory1).setGracePeriodSec(scavengeInterval);
TestServer server1 = new TestServer(0, maxInactiveInterval, scavengeInterval, cacheFactory, storeFactory);
server1.addContext(contextPath).addServlet(TestServlet.class, servletMapping);
TestServer server1 = new TestServer(0, maxInactiveInterval, scavengeInterval, cacheFactory1, storeFactory1);
ServletContextHandler context = server1.addContext(contextPath);
context.addServlet(TestServlet.class, servletMapping);
TestContextScopeListener scopeListener = new TestContextScopeListener();
context.addEventListener(scopeListener);
try
{
server1.start();
int port1 = server1.getPort();
TestServer server2 = new TestServer(0, maxInactiveInterval, scavengeInterval, cacheFactory, storeFactory);
DefaultSessionCacheFactory cacheFactory2 = new DefaultSessionCacheFactory();
cacheFactory2.setEvictionPolicy(SessionCache.EVICT_ON_SESSION_EXIT);
SessionDataStoreFactory storeFactory2 = createSessionDataStoreFactory();
((AbstractSessionDataStoreFactory)storeFactory2).setGracePeriodSec(scavengeInterval);
TestServer server2 = new TestServer(0, maxInactiveInterval, scavengeInterval, cacheFactory2, storeFactory2);
server2.addContext(contextPath).addServlet(TestServlet.class, servletMapping);
try
@ -88,6 +98,8 @@ public abstract class AbstractClusteredInvalidationSessionTest extends AbstractT
urls[1] = "http://localhost:" + port2 + contextPath + servletMapping.substring(1);
// Create the session on node1
CountDownLatch latch = new CountDownLatch(1);
scopeListener.setExitSynchronizer(latch);
ContentResponse response1 = client.GET(urls[0] + "?action=init");
assertEquals(HttpServletResponse.SC_OK,response1.getStatus());
@ -96,16 +108,29 @@ public abstract class AbstractClusteredInvalidationSessionTest extends AbstractT
// Mangle the cookie, replacing Path with $Path, etc.
sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
//ensure request is fully finished processing
latch.await(5, TimeUnit.SECONDS);
// Be sure the session is also present in node2
latch = new CountDownLatch(1);
scopeListener.setExitSynchronizer(latch);
Request request2 = client.newRequest(urls[1] + "?action=increment");
request2.header("Cookie", sessionCookie);
ContentResponse response2 = request2.send();
assertEquals(HttpServletResponse.SC_OK,response2.getStatus());
//ensure request is fully finished processing
latch.await(5, TimeUnit.SECONDS);
// Invalidate on node1
latch = new CountDownLatch(1);
scopeListener.setExitSynchronizer(latch);
Request request1 = client.newRequest(urls[0] + "?action=invalidate");
response1 = request1.send();
assertEquals(HttpServletResponse.SC_OK, response1.getStatus());
assertEquals(HttpServletResponse.SC_OK, response1.getStatus());
//ensure request is fully finished processing
latch.await(5, TimeUnit.SECONDS);
// Be sure on node2 we don't see the session anymore
request2 = client.newRequest(urls[1] + "?action=test");

View File

@ -55,15 +55,15 @@ public abstract class AbstractClusteredOrphanedSessionTest extends AbstractTestB
String contextPath = "/";
String servletMapping = "/server";
int inactivePeriod = 5;
DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory();
cacheFactory.setEvictionPolicy(SessionCache.NEVER_EVICT);
SessionDataStoreFactory storeFactory = createSessionDataStoreFactory();
if (storeFactory instanceof AbstractSessionDataStoreFactory)
DefaultSessionCacheFactory cacheFactory1 = new DefaultSessionCacheFactory();
cacheFactory1.setEvictionPolicy(SessionCache.NEVER_EVICT);
SessionDataStoreFactory storeFactory1 = createSessionDataStoreFactory();
if (storeFactory1 instanceof AbstractSessionDataStoreFactory)
{
((AbstractSessionDataStoreFactory)storeFactory).setGracePeriodSec(0);
((AbstractSessionDataStoreFactory)storeFactory1).setGracePeriodSec(0);
}
TestServer server1 = new TestServer(0, inactivePeriod, -1, cacheFactory, storeFactory);
TestServer server1 = new TestServer(0, inactivePeriod, -1, cacheFactory1, storeFactory1);
server1.addContext(contextPath).addServlet(TestServlet.class, servletMapping);
try
{
@ -71,10 +71,13 @@ public abstract class AbstractClusteredOrphanedSessionTest extends AbstractTestB
int port1 = server1.getPort();
int scavengePeriod = 2;
DefaultSessionCacheFactory evictCacheFactory = new DefaultSessionCacheFactory();
// cacheFactory.setEvictionPolicy(2);//evict after idle for 2 sec
TestServer server2 = new TestServer(0, inactivePeriod, scavengePeriod, evictCacheFactory, storeFactory);
DefaultSessionCacheFactory cacheFactory2 = new DefaultSessionCacheFactory();
SessionDataStoreFactory storeFactory2 = createSessionDataStoreFactory();
if (storeFactory2 instanceof AbstractSessionDataStoreFactory)
{
((AbstractSessionDataStoreFactory)storeFactory2).setGracePeriodSec(0);
}
TestServer server2 = new TestServer(0, inactivePeriod, scavengePeriod, cacheFactory2, storeFactory2);
server2.addContext(contextPath).addServlet(TestServlet.class, servletMapping);
try
{

View File

@ -71,13 +71,13 @@ public abstract class AbstractClusteredSessionScavengingTest extends AbstractTes
int maxInactivePeriod = 5; //session will timeout after 5 seconds
int scavengePeriod = 1; //scavenging occurs every 1 seconds
DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory();
cacheFactory.setEvictionPolicy(SessionCache.NEVER_EVICT); //don't evict sessions
SessionDataStoreFactory storeFactory = createSessionDataStoreFactory();
((AbstractSessionDataStoreFactory)storeFactory).setGracePeriodSec(scavengePeriod);
((AbstractSessionDataStoreFactory)storeFactory).setSavePeriodSec(0); //always save when the session exits
DefaultSessionCacheFactory cacheFactory1 = new DefaultSessionCacheFactory();
cacheFactory1.setEvictionPolicy(SessionCache.NEVER_EVICT); //don't evict sessions
SessionDataStoreFactory storeFactory1 = createSessionDataStoreFactory();
((AbstractSessionDataStoreFactory)storeFactory1).setGracePeriodSec(scavengePeriod);
((AbstractSessionDataStoreFactory)storeFactory1).setSavePeriodSec(0); //always save when the session exits
TestServer server1 = new TestServer(0, maxInactivePeriod, scavengePeriod, cacheFactory, storeFactory);
TestServer server1 = new TestServer(0, maxInactivePeriod, scavengePeriod, cacheFactory1, storeFactory1);
TestServlet servlet1 = new TestServlet();
ServletHolder holder1 = new ServletHolder(servlet1);
ServletContextHandler context = server1.addContext(contextPath);
@ -92,7 +92,12 @@ public abstract class AbstractClusteredSessionScavengingTest extends AbstractTes
server1.start();
int port1=server1.getPort();
TestServer server2 = new TestServer(0, maxInactivePeriod, scavengePeriod, cacheFactory, storeFactory);
DefaultSessionCacheFactory cacheFactory2 = new DefaultSessionCacheFactory();
cacheFactory2.setEvictionPolicy(SessionCache.NEVER_EVICT); //don't evict sessions
SessionDataStoreFactory storeFactory2 = createSessionDataStoreFactory();
((AbstractSessionDataStoreFactory)storeFactory2).setGracePeriodSec(scavengePeriod);
((AbstractSessionDataStoreFactory)storeFactory2).setSavePeriodSec(0); //always save when the session exits
TestServer server2 = new TestServer(0, maxInactivePeriod, scavengePeriod, cacheFactory2, storeFactory2);
ServletContextHandler context2 = server2.addContext(contextPath);
context2.addServlet(TestServlet.class, servletMapping);
SessionHandler m2 = context2.getSessionHandler();

View File

@ -0,0 +1,68 @@
//
// ========================================================================
// Copyright (c) 1995-2018 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.server.session;
import java.util.concurrent.CountDownLatch;
import org.eclipse.jetty.server.handler.ContextHandler.Context;
import org.eclipse.jetty.server.handler.ContextHandler.ContextScopeListener;
public class TestContextScopeListener implements ContextScopeListener
{
CountDownLatch _exitSynchronizer;
/**
* @return the exitSynchronizer
*/
public CountDownLatch getExitSynchronizer()
{
return _exitSynchronizer;
}
/**
* @param exitSynchronizer the exitSynchronizer to set
*/
public void setExitSynchronizer(CountDownLatch exitSynchronizer)
{
_exitSynchronizer = exitSynchronizer;
}
@Override
public void enterScope(Context context, org.eclipse.jetty.server.Request request, Object reason)
{
//noop
}
@Override
public void exitScope(Context context, org.eclipse.jetty.server.Request request)
{
if (_exitSynchronizer != null)
_exitSynchronizer.countDown();
}
}

View File

@ -45,7 +45,6 @@ import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.StacklessLogging;
import org.junit.Before;
import org.junit.Test;
@ -59,16 +58,6 @@ import org.junit.Test;
public class CreationTest
{
protected TestServlet _servlet;
protected TestServer _server1 = null;
protected CountDownLatch _synchronizer;
@Before
public void setUp ()
{
_synchronizer = new CountDownLatch(1);
_servlet = new TestServlet(_synchronizer);
}
/**
@ -79,6 +68,7 @@ public class CreationTest
@Test
public void testSessionCreateWithEviction() throws Exception
{
String contextPath = "";
String servletMapping = "/server";
@ -86,14 +76,16 @@ public class CreationTest
cacheFactory.setEvictionPolicy(SessionCache.EVICT_ON_SESSION_EXIT);
SessionDataStoreFactory storeFactory = new TestSessionDataStoreFactory();
_server1 = new TestServer(0, -1, -1, cacheFactory, storeFactory);
ServletHolder holder = new ServletHolder(_servlet);
ServletContextHandler contextHandler = _server1.addContext(contextPath);
TestServer server1 = new TestServer(0, -1, -1, cacheFactory, storeFactory);
TestServlet servlet = new TestServlet();
ServletHolder holder = new ServletHolder(servlet);
ServletContextHandler contextHandler = server1.addContext(contextPath);
TestContextScopeListener scopeListener = new TestContextScopeListener();
contextHandler.addEventListener(scopeListener);
contextHandler.addServlet(holder, servletMapping);
_servlet.setStore(contextHandler.getSessionHandler().getSessionCache().getSessionDataStore());
_server1.start();
int port1 = _server1.getPort();
servlet.setStore(contextHandler.getSessionHandler().getSessionCache().getSessionDataStore());
server1.start();
int port1 = server1.getPort();
try (StacklessLogging stackless = new StacklessLogging(Log.getLogger("org.eclipse.jetty.server.session")))
{
@ -101,31 +93,40 @@ public class CreationTest
client.start();
String url = "http://localhost:" + port1 + contextPath + servletMapping+"?action=create&check=false";
CountDownLatch synchronizer = new CountDownLatch(1);
scopeListener.setExitSynchronizer(synchronizer);
//make a request to set up a session on the server
ContentResponse response = client.GET(url);
assertEquals(HttpServletResponse.SC_OK,response.getStatus());
String sessionCookie = response.getHeaders().get("Set-Cookie");
assertTrue(sessionCookie != null);
//ensure request has finished being handled
synchronizer.await(5, TimeUnit.SECONDS);
//session should now be evicted from the cache
String id = TestServer.extractSessionId(sessionCookie);
String id = TestServer.extractSessionId(sessionCookie);
assertFalse(contextHandler.getSessionHandler().getSessionCache().contains(id));
assertTrue(contextHandler.getSessionHandler().getSessionCache().getSessionDataStore().exists(id));
synchronizer = new CountDownLatch(1);
scopeListener.setExitSynchronizer(synchronizer);
//make another request for the same session
Request request = client.newRequest("http://localhost:" + port1 + contextPath + servletMapping + "?action=test");
response = request.send();
assertEquals(HttpServletResponse.SC_OK,response.getStatus());
//ensure request has finished being handled
synchronizer.await(5, TimeUnit.SECONDS);
//session should now be evicted from the cache again
assertFalse(contextHandler.getSessionHandler().getSessionCache().contains(TestServer.extractSessionId(sessionCookie)));
}
finally
{
_server1.stop();
server1.stop();
}
}
@ -146,14 +147,16 @@ public class CreationTest
DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory();
cacheFactory.setEvictionPolicy(SessionCache.NEVER_EVICT);
SessionDataStoreFactory storeFactory = new TestSessionDataStoreFactory();
_server1 = new TestServer(0, inactivePeriod, scavengePeriod, cacheFactory, storeFactory);
ServletHolder holder = new ServletHolder(_servlet);
ServletContextHandler contextHandler = _server1.addContext(contextPath);
TestServer server1 = new TestServer(0, inactivePeriod, scavengePeriod, cacheFactory, storeFactory);
TestServlet servlet = new TestServlet();
ServletHolder holder = new ServletHolder(servlet);
ServletContextHandler contextHandler = server1.addContext(contextPath);
TestContextScopeListener scopeListener = new TestContextScopeListener();
contextHandler.addEventListener(scopeListener);
contextHandler.addServlet(holder, servletMapping);
_servlet.setStore(contextHandler.getSessionHandler().getSessionCache().getSessionDataStore());
_server1.start();
int port1 = _server1.getPort();
servlet.setStore(contextHandler.getSessionHandler().getSessionCache().getSessionDataStore());
server1.start();
int port1 = server1.getPort();
try (StacklessLogging stackless = new StacklessLogging(Log.getLogger("org.eclipse.jetty.server.session")))
{
@ -161,16 +164,22 @@ public class CreationTest
client.start();
String url = "http://localhost:" + port1 + contextPath + servletMapping+"?action=createinv&check=false";
CountDownLatch synchronizer = new CountDownLatch(1);
scopeListener.setExitSynchronizer(synchronizer);
//make a request to set up a session on the server
ContentResponse response = client.GET(url);
assertEquals(HttpServletResponse.SC_OK,response.getStatus());
//ensure request has finished being handled
synchronizer.await(5, TimeUnit.SECONDS);
//check that the session does not exist
assertFalse(contextHandler.getSessionHandler().getSessionCache().getSessionDataStore().exists(_servlet._id));
assertFalse(contextHandler.getSessionHandler().getSessionCache().getSessionDataStore().exists(servlet._id));
}
finally
{
_server1.stop();
server1.stop();
}
}
@ -194,30 +203,38 @@ public class CreationTest
cacheFactory.setEvictionPolicy(SessionCache.NEVER_EVICT);
cacheFactory.setSaveOnCreate(true);
SessionDataStoreFactory storeFactory = new TestSessionDataStoreFactory();
_server1 = new TestServer(0, inactivePeriod, scavengePeriod, cacheFactory, storeFactory);
ServletHolder holder = new ServletHolder(_servlet);
ServletContextHandler contextHandler = _server1.addContext(contextPath);
TestServer server1 = new TestServer(0, inactivePeriod, scavengePeriod, cacheFactory, storeFactory);
TestServlet servlet = new TestServlet();
ServletHolder holder = new ServletHolder(servlet);
ServletContextHandler contextHandler = server1.addContext(contextPath);
TestContextScopeListener scopeListener = new TestContextScopeListener();
contextHandler.addEventListener(scopeListener);
contextHandler.addServlet(holder, servletMapping);
_servlet.setStore(contextHandler.getSessionHandler().getSessionCache().getSessionDataStore());
_server1.start();
int port1 = _server1.getPort();
servlet.setStore(contextHandler.getSessionHandler().getSessionCache().getSessionDataStore());
server1.start();
int port1 = server1.getPort();
try (StacklessLogging stackless = new StacklessLogging(Log.getLogger("org.eclipse.jetty.server.session")))
{
HttpClient client = new HttpClient();
client.start();
String url = "http://localhost:" + port1 + contextPath + servletMapping+"?action=createinv&check=true";
CountDownLatch synchronizer = new CountDownLatch(1);
scopeListener.setExitSynchronizer(synchronizer);
//make a request to set up a session on the server
ContentResponse response = client.GET(url);
assertEquals(HttpServletResponse.SC_OK,response.getStatus());
synchronizer.await(5, TimeUnit.SECONDS);
//check that the session does not exist
assertFalse(contextHandler.getSessionHandler().getSessionCache().getSessionDataStore().exists(_servlet._id));
assertFalse(contextHandler.getSessionHandler().getSessionCache().getSessionDataStore().exists(servlet._id));
}
finally
{
_server1.stop();
server1.stop();
}
}
@ -244,15 +261,17 @@ public class CreationTest
cacheFactory.setEvictionPolicy(SessionCache.NEVER_EVICT);
SessionDataStoreFactory storeFactory = new TestSessionDataStoreFactory();
_server1 = new TestServer(0, inactivePeriod, scavengePeriod, cacheFactory, storeFactory);
ServletHolder holder = new ServletHolder(_servlet);
ServletContextHandler contextHandler = _server1.addContext(contextPath);
TestServer server1 = new TestServer(0, inactivePeriod, scavengePeriod, cacheFactory, storeFactory);
TestServlet servlet = new TestServlet();
ServletHolder holder = new ServletHolder(servlet);
ServletContextHandler contextHandler = server1.addContext(contextPath);
TestContextScopeListener scopeListener = new TestContextScopeListener();
contextHandler.addEventListener(scopeListener);
contextHandler.addServlet(holder, servletMapping);
ServletContextHandler ctxB = _server1.addContext(contextB);
ServletContextHandler ctxB = server1.addContext(contextB);
ctxB.addServlet(TestServletB.class, servletMapping);
_server1.start();
int port1 = _server1.getPort();
server1.start();
int port1 = server1.getPort();
try (StacklessLogging stackless = new StacklessLogging(Log.getLogger("org.eclipse.jetty.server.session")))
{
@ -261,19 +280,21 @@ public class CreationTest
String url = "http://localhost:" + port1 + contextPath + servletMapping;
//make a request to set up a session on the server
CountDownLatch synchronizer = new CountDownLatch(1);
scopeListener.setExitSynchronizer(synchronizer);
ContentResponse response = client.GET(url+"?action=forward");
assertEquals(HttpServletResponse.SC_OK,response.getStatus());
//ensure work has finished on the server side
_synchronizer.await(2*inactivePeriod, TimeUnit.SECONDS);
//wait for request to have exited server completely
synchronizer.await(5, TimeUnit.SECONDS);
//check that the sessions exist persisted
assertTrue(contextHandler.getSessionHandler().getSessionCache().getSessionDataStore().exists(_servlet._id));
assertTrue(ctxB.getSessionHandler().getSessionCache().getSessionDataStore().exists(_servlet._id));
assertTrue(contextHandler.getSessionHandler().getSessionCache().getSessionDataStore().exists(servlet._id));
assertTrue(ctxB.getSessionHandler().getSessionCache().getSessionDataStore().exists(servlet._id));
}
finally
{
_server1.stop();
server1.stop();
}
}
@ -297,15 +318,17 @@ public class CreationTest
cacheFactory.setEvictionPolicy(SessionCache.NEVER_EVICT);
SessionDataStoreFactory storeFactory = new TestSessionDataStoreFactory();
_server1 = new TestServer (0, inactivePeriod, scavengePeriod, cacheFactory, storeFactory);
ServletHolder holder = new ServletHolder(_servlet);
ServletContextHandler contextHandler = _server1.addContext(contextPath);
TestServer server1 = new TestServer (0, inactivePeriod, scavengePeriod, cacheFactory, storeFactory);
TestServlet servlet = new TestServlet();
ServletHolder holder = new ServletHolder(servlet);
ServletContextHandler contextHandler = server1.addContext(contextPath);
TestContextScopeListener scopeListener = new TestContextScopeListener();
contextHandler.addEventListener(scopeListener);
contextHandler.addServlet(holder, servletMapping);
ServletContextHandler ctxB = _server1.addContext(contextB);
ServletContextHandler ctxB = server1.addContext(contextB);
ctxB.addServlet(TestServletB.class, servletMapping);
_server1.start();
int port1 = _server1.getPort();
server1.start();
int port1 = server1.getPort();
try (StacklessLogging stackless = new StacklessLogging(Log.getLogger("org.eclipse.jetty.server.session")))
{
@ -314,19 +337,21 @@ public class CreationTest
String url = "http://localhost:" + port1 + contextPath + servletMapping;
//make a request to set up a session on the server
CountDownLatch synchronizer = new CountDownLatch(1);
scopeListener.setExitSynchronizer(synchronizer);
ContentResponse response = client.GET(url+"?action=forwardinv");
assertEquals(HttpServletResponse.SC_OK,response.getStatus());
//wait for the request to have finished before checking session
_synchronizer.await(10, TimeUnit.SECONDS);
//wait for request to have exited server completely
synchronizer.await(5, TimeUnit.SECONDS);
//check that the session does not exist
assertFalse(contextHandler.getSessionHandler().getSessionCache().getSessionDataStore().exists(_servlet._id));
assertFalse(ctxB.getSessionHandler().getSessionCache().getSessionDataStore().exists(_servlet._id));
assertFalse(contextHandler.getSessionHandler().getSessionCache().getSessionDataStore().exists(servlet._id));
assertFalse(ctxB.getSessionHandler().getSessionCache().getSessionDataStore().exists(servlet._id));
}
finally
{
_server1.stop();
server1.stop();
}
}
@ -337,13 +362,9 @@ public class CreationTest
{
private static final long serialVersionUID = 1L;
public String _id = null;
public CountDownLatch _synchronizer;
public SessionDataStore _store;
public TestServlet (CountDownLatch latch)
{
_synchronizer = latch;
}
public void setStore (SessionDataStore store)
{
@ -376,7 +397,6 @@ public class CreationTest
assertNull(session.getAttribute("B")); //check we don't see stuff from other context
}
_synchronizer.countDown();
return;
}
else if (action!=null && "test".equals(action))

View File

@ -25,6 +25,8 @@ import static org.junit.Assert.assertNull;
import java.io.IOException;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
@ -183,6 +185,9 @@ public class DeleteUnloadableSessionTest
TestServer server = new TestServer(0, inactivePeriod, scavengePeriod, cacheFactory, storeFactory);
ServletContextHandler context = server.addContext(contextPath);
TestContextScopeListener scopeListener = new TestContextScopeListener();
context.addEventListener(scopeListener);
TestServlet servlet = new TestServlet();
ServletHolder holder = new ServletHolder(servlet);
context.addServlet(holder, servletMapping);
@ -195,12 +200,17 @@ public class DeleteUnloadableSessionTest
client.start();
try
{
CountDownLatch latch = new CountDownLatch(1);
scopeListener.setExitSynchronizer(latch);
String sessionCookie = "JSESSIONID=w0rm3zxpa6h1zg1mevtv76b3te00.w0;$Path=/";
Request request = client.newRequest("http://localhost:" + port + contextPath + servletMapping+ "?action=test");
request.header("Cookie", sessionCookie);
ContentResponse response = request.send();
assertEquals(HttpServletResponse.SC_OK,response.getStatus());
//ensure request fully finished handlers
latch.await(5, TimeUnit.SECONDS);
assertFalse(context.getSessionHandler().getSessionCache().getSessionDataStore().exists(TestServer.extractSessionId(sessionCookie)));
}

View File

@ -23,6 +23,8 @@ import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.io.Serializable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
@ -97,6 +99,9 @@ public class DirtyAttributeTest
ServletContextHandler ctxA = server.addContext("/mod");
ctxA.addServlet(TestDirtyServlet.class, "/test");
TestContextScopeListener scopeListener = new TestContextScopeListener();
ctxA.addEventListener(scopeListener);
server.start();
int port=server.getPort();
try
@ -105,36 +110,50 @@ public class DirtyAttributeTest
client.start();
try
{
// Perform a request to create a session
// Perform a request to create a session
ContentResponse response = client.GET("http://localhost:" + port + "/mod/test?action=create");
assertEquals(HttpServletResponse.SC_OK,response.getStatus());
String sessionCookie = response.getHeaders().get("Set-Cookie");
assertTrue(sessionCookie != null);
//do another request to change the session attribute
CountDownLatch latch = new CountDownLatch(1);
scopeListener.setExitSynchronizer(latch);
Request request = client.newRequest("http://localhost:" + port + "/mod/test?action=setA");
response = request.send();
assertEquals(HttpServletResponse.SC_OK,response.getStatus());
//ensure request fully finished processing
latch.await(5, TimeUnit.SECONDS);
A_VALUE.assertPassivatesEquals(1);
A_VALUE.assertActivatesEquals(1);
A_VALUE.assertBindsEquals(1);
A_VALUE.assertUnbindsEquals(0);
//do another request using the cookie to try changing the session attribute to the same value again
//do another request using the cookie to try changing the session attribute to the same value again
latch = new CountDownLatch(1);
scopeListener.setExitSynchronizer(latch);
request= client.newRequest("http://localhost:" + port + "/mod/test?action=setA");
response = request.send();
assertEquals(HttpServletResponse.SC_OK,response.getStatus());
//ensure request fully finished processing
latch.await(5, TimeUnit.SECONDS);
A_VALUE.assertPassivatesEquals(2);
A_VALUE.assertActivatesEquals(2);
A_VALUE.assertBindsEquals(1);
A_VALUE.assertUnbindsEquals(0);
//do another request using the cookie and change to a different value
//do another request using the cookie and change to a different value
latch = new CountDownLatch(1);
scopeListener.setExitSynchronizer(latch);
request= client.newRequest("http://localhost:" + port + "/mod/test?action=setB");
response = request.send();
assertEquals(HttpServletResponse.SC_OK,response.getStatus());
latch.await(5, TimeUnit.SECONDS);
B_VALUE.assertPassivatesEquals(1);
B_VALUE.assertActivatesEquals(1);
B_VALUE.assertBindsEquals(1);

View File

@ -509,7 +509,6 @@ public class ModifyMaxInactiveIntervalTest extends AbstractTestBase
try
{
// Perform a request to create a session
ContentResponse response = client.GET("http://localhost:" + port + "/mod/test?action=create");
assertEquals(HttpServletResponse.SC_OK,response.getStatus());

View File

@ -27,6 +27,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import javax.servlet.ServletException;
@ -78,7 +79,9 @@ public class NonClusteredSessionScavengingTest extends AbstractTestBase
context1.addServlet(TestServlet.class, servletMapping);
TestHttpSessionListener listener = new TestHttpSessionListener();
context1.getSessionHandler().addEventListener(listener);
TestContextScopeListener scopeListener = new TestContextScopeListener();
context1.addEventListener(scopeListener);
try
{
@ -93,14 +96,20 @@ public class NonClusteredSessionScavengingTest extends AbstractTestBase
// Create the session
CountDownLatch latch = new CountDownLatch(1);
scopeListener.setExitSynchronizer(latch);
ContentResponse response1 = client.GET(url + "?action=create");
assertEquals(HttpServletResponse.SC_OK,response1.getStatus());
String sessionCookie = response1.getHeaders().get("Set-Cookie");
assertTrue(sessionCookie != null);
//ensure request fully finished processing
latch.await(5, TimeUnit.SECONDS);
//test session was created
SessionHandler m1 = context1.getSessionHandler();
assertEquals(1, m1.getSessionsCreated());
// Wait a while to ensure that the session should have expired, if the
//scavenger was running
pause(2*inactivePeriod);
@ -146,6 +155,8 @@ public class NonClusteredSessionScavengingTest extends AbstractTestBase
TestServer server = new TestServer(0, maxInactivePeriod, scavengePeriod,
cacheFactory, storeFactory);
ServletContextHandler context = server.addContext("/");
TestContextScopeListener scopeListener = new TestContextScopeListener();
context.addEventListener(scopeListener);
_dataStore = context.getSessionHandler().getSessionCache().getSessionDataStore();
context.addServlet(TestServlet.class, servletMapping);
@ -159,11 +170,16 @@ public class NonClusteredSessionScavengingTest extends AbstractTestBase
client.start();
try
{
CountDownLatch latch = new CountDownLatch(1);
scopeListener.setExitSynchronizer(latch);
ContentResponse response = client.GET("http://localhost:" + port + contextPath + servletMapping.substring(1) + "?action=create");
assertEquals(HttpServletResponse.SC_OK,response.getStatus());
String sessionCookie = response.getHeaders().get("Set-Cookie");
assertTrue(sessionCookie != null);
//ensure request fully finished processing
latch.await(5, TimeUnit.SECONDS);
// Let's wait for the scavenger to run
pause(maxInactivePeriod + scavengePeriod);
@ -203,6 +219,8 @@ public class NonClusteredSessionScavengingTest extends AbstractTestBase
TestServer server = new TestServer(0, maxInactivePeriod, scavengePeriod,
cacheFactory, storeFactory);
ServletContextHandler context = server.addContext("/");
TestContextScopeListener scopeListener = new TestContextScopeListener();
context.addEventListener(scopeListener);
_dataStore = context.getSessionHandler().getSessionCache().getSessionDataStore();
context.addServlet(TestServlet.class, servletMapping);
String contextPath = "/";
@ -216,11 +234,16 @@ public class NonClusteredSessionScavengingTest extends AbstractTestBase
try
{
//create an immortal session
CountDownLatch latch = new CountDownLatch(1);
scopeListener.setExitSynchronizer(latch);
ContentResponse response = client.GET("http://localhost:" + port + contextPath + servletMapping.substring(1) + "?action=create");
assertEquals(HttpServletResponse.SC_OK,response.getStatus());
String sessionCookie = response.getHeaders().get("Set-Cookie");
assertTrue(sessionCookie != null);
//ensure request fully finished processing
latch.await(5, TimeUnit.SECONDS);
// Let's wait for the scavenger to run
pause(2*scavengePeriod);

View File

@ -22,6 +22,8 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
@ -32,6 +34,7 @@ import javax.servlet.http.HttpSession;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.junit.Test;
@ -56,7 +59,12 @@ public class ReentrantRequestSessionTest
TestServer server = new TestServer(0, -1, 60, cacheFactory, storeFactory);
server.addContext(contextPath).addServlet(TestServlet.class, servletMapping);
ServletContextHandler context = server.addContext(contextPath);
context.addServlet(TestServlet.class, servletMapping);
TestContextScopeListener scopeListener = new TestContextScopeListener();
context.addEventListener(scopeListener);
try
{
server.start();
@ -67,19 +75,22 @@ public class ReentrantRequestSessionTest
try
{
//create the session
ContentResponse response = client.GET("http://localhost:" + port + contextPath + servletMapping + "?action=create&port=" + port + "&path=" + contextPath + servletMapping);
CountDownLatch latch = new CountDownLatch(1);
scopeListener.setExitSynchronizer(latch);
ContentResponse response = client.GET("http://localhost:" + port + contextPath + servletMapping + "?action=create");
assertEquals(HttpServletResponse.SC_OK,response.getStatus());
String sessionCookie = response.getHeaders().get("Set-Cookie");
assertTrue(sessionCookie != null);
//ensure request fully finished processing
latch.await(5, TimeUnit.SECONDS);
//make a request that will make a simultaneous request for the same session
Request request = client.newRequest("http://localhost:" + port + contextPath + servletMapping + "?action=reenter&port=" + port + "&path=" + contextPath + servletMapping);
request.header("Cookie", sessionCookie);
response = request.send();
assertEquals(HttpServletResponse.SC_OK,response.getStatus());
}
finally
{

View File

@ -25,6 +25,7 @@ import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
@ -50,16 +51,6 @@ import org.junit.Test;
*/
public class SameContextForwardedSessionTest
{
protected CountDownLatch _synchronizer;
protected Servlet1 _one;
@Before
public void setUp ()
{
_synchronizer = new CountDownLatch(1);
_one = new Servlet1();
_one.setSynchronizer(_synchronizer);
}
@Test
@ -71,7 +62,9 @@ public class SameContextForwardedSessionTest
TestServer testServer = new TestServer(0, -1, -1, cacheFactory, storeFactory);
ServletContextHandler testServletContextHandler = testServer.addContext("/context");
ServletHolder holder = new ServletHolder(_one);
TestContextScopeListener scopeListener = new TestContextScopeListener();
testServletContextHandler.addEventListener(scopeListener);
ServletHolder holder = new ServletHolder(new Servlet1());
testServletContextHandler.addServlet(holder, "/one");
testServletContextHandler.addServlet(Servlet2.class, "/two");
testServletContextHandler.addServlet(Servlet3.class, "/three");
@ -86,13 +79,15 @@ public class SameContextForwardedSessionTest
try
{
//make a request to the first servlet, which will forward it to other servlets
CountDownLatch latch = new CountDownLatch(1);
scopeListener.setExitSynchronizer(latch);
ContentResponse response = client.GET("http://localhost:" + serverPort + "/context/one");
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
String sessionCookie = response.getHeaders().get("Set-Cookie");
assertTrue(sessionCookie != null);
//wait until all of the request handling has finished
_synchronizer.await();
latch.await(5, TimeUnit.SECONDS);
//test that the session was created, and that it contains the attributes from servlet3 and servlet1
testServletContextHandler.getSessionHandler().getSessionCache().contains(TestServer.extractSessionId(sessionCookie));
@ -120,12 +115,7 @@ public class SameContextForwardedSessionTest
public static class Servlet1 extends HttpServlet
{
private static final long serialVersionUID = 1L;
CountDownLatch _synchronizer;
public void setSynchronizer(CountDownLatch sync)
{
_synchronizer = sync;
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
@ -141,9 +131,6 @@ public class SameContextForwardedSessionTest
assertNotNull(sess);
assertNotNull(sess.getAttribute("servlet3"));
sess.setAttribute("servlet1", "servlet1");
if (_synchronizer != null)
_synchronizer.countDown();
}
}

View File

@ -26,6 +26,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import javax.servlet.ServletException;
@ -436,6 +437,8 @@ public class SaveOptimizeTest
_servlet = new TestServlet();
ServletHolder holder = new ServletHolder(_servlet);
ServletContextHandler contextHandler = _server1.addContext(contextPath);
TestContextScopeListener scopeListener = new TestContextScopeListener();
contextHandler.addEventListener(scopeListener);
contextHandler.addServlet(holder, servletMapping);
_servlet.setStore(contextHandler.getSessionHandler().getSessionCache().getSessionDataStore());
_server1.start();
@ -450,11 +453,16 @@ public class SaveOptimizeTest
String url = "http://localhost:" + port1 + contextPath + servletMapping+"?action=create&check=true";
//make a request to set up a session on the server
CountDownLatch latch = new CountDownLatch(1);
scopeListener.setExitSynchronizer(latch);
ContentResponse response = client.GET(url);
assertEquals(HttpServletResponse.SC_OK,response.getStatus());
String sessionCookie = response.getHeaders().get("Set-Cookie");
assertTrue(sessionCookie != null);
String sessionId = TestServer.extractSessionId(sessionCookie);
//ensure request fully finished processing
latch.await(5, TimeUnit.SECONDS);
SessionData data = contextHandler.getSessionHandler().getSessionCache().getSessionDataStore().load(sessionId);
assertNotNull(data);
@ -462,8 +470,13 @@ public class SaveOptimizeTest
assertTrue(lastSaved > 0); //check session created was saved
// Perform a request to change maxInactive on session
latch = new CountDownLatch(1);
scopeListener.setExitSynchronizer(latch);
Request request = client.newRequest("http://localhost:" + port1 + contextPath + servletMapping+"?action=max&value=60");
response = request.send();
//ensure request fully finished processing
latch.await(5, TimeUnit.SECONDS);
//check session is saved, even though the save optimisation interval has not passed
SessionData d = contextHandler.getSessionHandler().getSessionCache().getSessionDataStore().load(sessionId);