Merge branch 'jetty-9.4.x' into jetty-9.4.x-issue-2496-skip-unsupported-packaging
This commit is contained in:
commit
a4d7670536
|
@ -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>
|
||||
|
|
|
@ -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/.
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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))
|
||||
|
|
|
@ -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)));
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue