Merge remote-tracking branch 'origin/jetty-9.4.x' into jetty-10.0.x
This commit is contained in:
commit
4abc4f8dd4
|
@ -59,7 +59,6 @@ pipeline {
|
|||
}
|
||||
*/
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -681,6 +681,21 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
if (context == null)
|
||||
throw new IllegalArgumentException("WebAppContext null");
|
||||
|
||||
//if we don't know where its from it can't be excluded
|
||||
if (sciResource == null)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("!Excluded {} null resource", sci);
|
||||
return false;
|
||||
}
|
||||
|
||||
//A ServletContainerInitialier that came from WEB-INF/classes or equivalent cannot be excluded by an ordering
|
||||
if (isFromWebInfClasses(context, sciResource))
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("!Excluded {} from web-inf/classes", sci);
|
||||
return false;
|
||||
}
|
||||
|
||||
//A ServletContainerInitializer that came from the container's classpath cannot be excluded by an ordering
|
||||
//of WEB-INF/lib jars
|
||||
|
@ -709,14 +724,7 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
return true;
|
||||
}
|
||||
|
||||
if (sciResource == null)
|
||||
{
|
||||
//not from a jar therefore not from WEB-INF so not excludable
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("!Excluded {} not from jar", sci);
|
||||
return false;
|
||||
}
|
||||
|
||||
//Check if it is excluded by an ordering
|
||||
URI loadingJarURI = sciResource.getURI();
|
||||
boolean found = false;
|
||||
Iterator<Resource> itor = orderedJars.iterator();
|
||||
|
@ -762,7 +770,46 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
{
|
||||
if (sci == null)
|
||||
return false;
|
||||
return sci.getClass().getClassLoader()==context.getClassLoader().getParent();
|
||||
|
||||
ClassLoader sciLoader = sci.getClass().getClassLoader();
|
||||
|
||||
//if loaded by bootstrap loader, then its the container classpath
|
||||
if ( sciLoader == null)
|
||||
return true;
|
||||
|
||||
//if there is no context classloader, then its the container classpath
|
||||
if (context.getClassLoader() == null)
|
||||
return true;
|
||||
|
||||
ClassLoader loader = sciLoader;
|
||||
while (loader != null)
|
||||
{
|
||||
if (loader == context.getClassLoader())
|
||||
return false; //the webapp classloader is in the ancestry of the classloader for the sci
|
||||
else
|
||||
loader = loader.getParent();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the ServletContainerInitializer is from WEB-INF/classes
|
||||
*
|
||||
* @param context the webapp to test
|
||||
* @param sci a Resource representing the SCI
|
||||
* @return true if the sci Resource is inside a WEB-INF/classes directory, false otherwise
|
||||
*/
|
||||
public boolean isFromWebInfClasses (WebAppContext context, Resource sci)
|
||||
{
|
||||
for (Resource dir : context.getMetaData().getWebInfClassesDirs())
|
||||
{
|
||||
if (dir.equals(sci))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -849,27 +896,47 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
//No jetty-specific ordering specified, or just the wildcard value "*" specified.
|
||||
//Fallback to ordering the ServletContainerInitializers according to:
|
||||
//container classpath first, WEB-INF/classes then WEB-INF/lib (obeying any web.xml jar ordering)
|
||||
|
||||
//no web.xml ordering defined, add SCIs in any order
|
||||
|
||||
//First add in all SCIs that can't be excluded
|
||||
int lastContainerSCI = -1;
|
||||
for (Map.Entry<ServletContainerInitializer, Resource> entry:sciResourceMap.entrySet())
|
||||
{
|
||||
if (entry.getKey().getClass().getClassLoader()==context.getClassLoader().getParent())
|
||||
{
|
||||
nonExcludedInitializers.add(++lastContainerSCI, entry.getKey()); //add all container SCIs before any webapp SCIs
|
||||
}
|
||||
else if (entry.getValue() == null) //can't work out provenance of SCI, so can't be ordered/excluded
|
||||
{
|
||||
nonExcludedInitializers.add(entry.getKey()); //add at end of list
|
||||
}
|
||||
else
|
||||
{
|
||||
for (Resource dir : context.getMetaData().getWebInfClassesDirs())
|
||||
{
|
||||
if (dir.equals(entry.getValue()))//from WEB-INF/classes so can't be ordered/excluded
|
||||
{
|
||||
nonExcludedInitializers.add(entry.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//throw out the ones we've already accounted for
|
||||
for (ServletContainerInitializer s:nonExcludedInitializers)
|
||||
sciResourceMap.remove(s);
|
||||
|
||||
if (context.getMetaData().getOrdering() == null)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("No web.xml ordering, ServletContainerInitializers in random order");
|
||||
//add the rest of the scis
|
||||
nonExcludedInitializers.addAll(sciResourceMap.keySet());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Ordering ServletContainerInitializers with ordering {}",context.getMetaData().getOrdering());
|
||||
for (Map.Entry<ServletContainerInitializer, Resource> entry:sciResourceMap.entrySet())
|
||||
{
|
||||
//add in SCIs from the container classpath
|
||||
if (entry.getKey().getClass().getClassLoader()==context.getClassLoader().getParent())
|
||||
nonExcludedInitializers.add(entry.getKey());
|
||||
else if (entry.getValue() == null) //add in SCIs not in a jar, as they must be from WEB-INF/classes and can't be ordered
|
||||
nonExcludedInitializers.add(entry.getKey());
|
||||
}
|
||||
|
||||
|
||||
//add SCIs according to the ordering of its containing jar
|
||||
for (Resource webInfJar:context.getMetaData().getOrderedWebInfJars())
|
||||
{
|
||||
|
@ -889,7 +956,7 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
ListIterator<ServletContainerInitializer> it = nonExcludedInitializers.listIterator();
|
||||
while (it.hasNext())
|
||||
{
|
||||
ServletContainerInitializer sci = it.next();
|
||||
ServletContainerInitializer sci = it.next();
|
||||
if (!isFromContainerClassPath(context, sci))
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -23,6 +23,7 @@ import java.net.URL;
|
|||
import java.net.URLClassLoader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
@ -32,6 +33,7 @@ import javax.servlet.ServletContainerInitializer;
|
|||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.webapp.FragmentDescriptor;
|
||||
import org.eclipse.jetty.webapp.RelativeOrdering;
|
||||
import org.eclipse.jetty.webapp.WebAppContext;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
@ -119,56 +121,123 @@ public class TestAnnotationConfiguration
|
|||
File web25 = MavenTestingUtils.getTestResourceFile("web25.xml");
|
||||
File web31false = MavenTestingUtils.getTestResourceFile("web31false.xml");
|
||||
File web31true = MavenTestingUtils.getTestResourceFile("web31true.xml");
|
||||
Set<String> sciNames = new HashSet<>(Arrays.asList("org.eclipse.jetty.annotations.ServerServletContainerInitializer", "com.acme.initializer.FooInitializer"));
|
||||
|
||||
//prepare an sci that will be on the webapp's classpath
|
||||
File jarDir = new File(MavenTestingUtils.getTestResourcesDir().getParentFile(), "jar");
|
||||
File testSciJar = new File(jarDir, "test-sci.jar");
|
||||
assertTrue(testSciJar.exists());
|
||||
URLClassLoader webAppLoader = new URLClassLoader(new URL[]{testSciJar.toURI().toURL()}, Thread.currentThread().getContextClassLoader());
|
||||
|
||||
ClassLoader orig = Thread.currentThread().getContextClassLoader();
|
||||
File testContainerSciJar = new File(jarDir, "test-sci-for-container-path.jar");
|
||||
URLClassLoader containerLoader = new URLClassLoader(new URL[] {testContainerSciJar.toURI().toURL()}, Thread.currentThread().getContextClassLoader());
|
||||
URLClassLoader webAppLoader = new URLClassLoader(new URL[] {testSciJar.toURI().toURL()}, containerLoader);
|
||||
Resource targetClasses = Resource.newResource(MavenTestingUtils.getTargetDir().toURI()).addPath("/test-classes");
|
||||
|
||||
ClassLoader old = Thread.currentThread().getContextClassLoader();
|
||||
Thread.currentThread().setContextClassLoader(containerLoader);
|
||||
try
|
||||
{
|
||||
//test 3.1 webapp loads both server and app scis
|
||||
|
||||
AnnotationConfiguration config = new AnnotationConfiguration();
|
||||
WebAppContext context = new WebAppContext();
|
||||
List<ServletContainerInitializer> scis;
|
||||
|
||||
//test 3.1 webapp loads both server and app scis
|
||||
context.setClassLoader(webAppLoader);
|
||||
context.getMetaData().addWebInfJar(Resource.newResource(testSciJar.toURI().toURL()));
|
||||
context.getMetaData().setWebXml(Resource.newResource(web31true));
|
||||
context.getMetaData().setWebInfClassesDirs(Collections.singletonList(targetClasses));
|
||||
context.getServletContext().setEffectiveMajorVersion(3);
|
||||
context.getServletContext().setEffectiveMinorVersion(1);
|
||||
Thread.currentThread().setContextClassLoader(webAppLoader);
|
||||
List<ServletContainerInitializer> scis = config.getNonExcludedInitializers(context);
|
||||
|
||||
scis = config.getNonExcludedInitializers(context);
|
||||
assertNotNull(scis);
|
||||
assertEquals(2, scis.size());
|
||||
assertTrue(sciNames.contains(scis.get(0).getClass().getName()));
|
||||
assertTrue(sciNames.contains(scis.get(1).getClass().getName()));
|
||||
assertEquals(3, scis.size());
|
||||
assertEquals("com.acme.ServerServletContainerInitializer", scis.get(0).getClass().getName()); //container path
|
||||
assertEquals("org.eclipse.jetty.annotations.WebInfClassServletContainerInitializer", scis.get(1).getClass().getName()); //web-inf classes
|
||||
assertEquals("com.acme.initializer.FooInitializer", scis.get(2).getClass().getName()); //web-inf jar no web-fragment
|
||||
|
||||
//test a 3.1 webapp with metadata-complete=false loads both server and webapp scis
|
||||
config = new AnnotationConfiguration();
|
||||
context = new WebAppContext();
|
||||
context.setClassLoader(webAppLoader);
|
||||
context.getMetaData().setWebXml(Resource.newResource(web31false));
|
||||
context.getMetaData().setWebInfClassesDirs(Collections.singletonList(targetClasses));
|
||||
context.getMetaData().addWebInfJar(Resource.newResource(testSciJar.toURI().toURL()));
|
||||
context.getServletContext().setEffectiveMajorVersion(3);
|
||||
context.getServletContext().setEffectiveMinorVersion(1);
|
||||
scis = config.getNonExcludedInitializers(context);
|
||||
assertNotNull(scis);
|
||||
assertEquals(2, scis.size());
|
||||
assertTrue(sciNames.contains(scis.get(0).getClass().getName()));
|
||||
assertTrue(sciNames.contains(scis.get(1).getClass().getName()));
|
||||
assertEquals(3, scis.size());
|
||||
assertEquals("com.acme.ServerServletContainerInitializer", scis.get(0).getClass().getName()); //container path
|
||||
assertEquals("org.eclipse.jetty.annotations.WebInfClassServletContainerInitializer", scis.get(1).getClass().getName()); //web-inf classes
|
||||
assertEquals("com.acme.initializer.FooInitializer", scis.get(2).getClass().getName()); //web-inf jar no web-fragment
|
||||
|
||||
|
||||
//test a 3.1 webapp with RELATIVE ORDERING loads sci from equivalent of WEB-INF/classes as well as container path
|
||||
File orderedFragmentJar = new File(jarDir, "test-sci-with-ordering.jar");
|
||||
assertTrue(orderedFragmentJar.exists());
|
||||
URLClassLoader orderedLoader = new URLClassLoader(new URL[] {orderedFragmentJar.toURI().toURL(), testSciJar.toURI().toURL()}, Thread.currentThread().getContextClassLoader());
|
||||
config = new AnnotationConfiguration();
|
||||
context = new WebAppContext();
|
||||
context.setClassLoader(orderedLoader);
|
||||
context.getMetaData().setWebXml(Resource.newResource(web31true));
|
||||
RelativeOrdering ordering = new RelativeOrdering(context.getMetaData());
|
||||
context.getMetaData().setOrdering(ordering);
|
||||
context.getMetaData().addWebInfJar(Resource.newResource(orderedFragmentJar.toURI().toURL()));
|
||||
context.getMetaData().addWebInfJar(Resource.newResource(testSciJar.toURI().toURL()));
|
||||
context.getMetaData().setWebInfClassesDirs(Collections.singletonList(targetClasses));
|
||||
context.getMetaData().orderFragments();
|
||||
context.getServletContext().setEffectiveMajorVersion(3);
|
||||
context.getServletContext().setEffectiveMinorVersion(1);
|
||||
scis = config.getNonExcludedInitializers(context);
|
||||
assertNotNull(scis);
|
||||
assertEquals(4, scis.size());
|
||||
assertEquals("com.acme.ServerServletContainerInitializer", scis.get(0).getClass().getName()); //container path
|
||||
assertEquals("org.eclipse.jetty.annotations.WebInfClassServletContainerInitializer", scis.get(1).getClass().getName()); //web-inf classes
|
||||
assertEquals("com.acme.AcmeServletContainerInitializer", scis.get(2).getClass().getName()); //first in ordering
|
||||
assertEquals("com.acme.initializer.FooInitializer", scis.get(3).getClass().getName()); //other in ordering
|
||||
|
||||
|
||||
//test 3.1 webapp with a specific SCI ordering
|
||||
config = new AnnotationConfiguration();
|
||||
context = new WebAppContext();
|
||||
context.setClassLoader(webAppLoader);
|
||||
context.getMetaData().setWebXml(Resource.newResource(web31false));
|
||||
context.getMetaData().setWebInfClassesDirs(Collections.singletonList(targetClasses));
|
||||
context.getMetaData().addWebInfJar(Resource.newResource(testSciJar.toURI().toURL()));
|
||||
context.getServletContext().setEffectiveMajorVersion(3);
|
||||
context.getServletContext().setEffectiveMinorVersion(1);
|
||||
context.setAttribute("org.eclipse.jetty.containerInitializerOrder", "com.acme.initializer.FooInitializer,com.acme.ServerServletContainerInitializer, *");
|
||||
scis = config.getNonExcludedInitializers(context);
|
||||
assertNotNull(scis);
|
||||
assertEquals(3, scis.size());
|
||||
assertEquals("com.acme.initializer.FooInitializer", scis.get(0).getClass().getName()); //web-inf jar no web-fragment
|
||||
assertEquals("com.acme.ServerServletContainerInitializer", scis.get(1).getClass().getName()); //container path
|
||||
assertEquals("org.eclipse.jetty.annotations.WebInfClassServletContainerInitializer", scis.get(2).getClass().getName()); //web-inf classes
|
||||
|
||||
|
||||
|
||||
|
||||
//test 2.5 webapp with configurationDiscovered=false loads only server scis
|
||||
config = new AnnotationConfiguration();
|
||||
context = new WebAppContext();
|
||||
context.setClassLoader(webAppLoader);
|
||||
context.getMetaData().setWebXml(Resource.newResource(web25));
|
||||
context.getMetaData().setWebInfClassesDirs(Collections.singletonList(targetClasses));
|
||||
context.getMetaData().addWebInfJar(Resource.newResource(testSciJar.toURI().toURL()));
|
||||
context.getServletContext().setEffectiveMajorVersion(2);
|
||||
context.getServletContext().setEffectiveMinorVersion(5);
|
||||
scis = config.getNonExcludedInitializers(context);
|
||||
assertNotNull(scis);
|
||||
assertEquals(1, scis.size());
|
||||
assertTrue("org.eclipse.jetty.annotations.ServerServletContainerInitializer".equals(scis.get(0).getClass().getName()));
|
||||
for (ServletContainerInitializer s:scis)
|
||||
{
|
||||
//should not have any of the web-inf lib scis in here
|
||||
assertFalse(s.getClass().getName().equals("com.acme.AcmeServletContainerInitializer"));
|
||||
assertFalse(s.getClass().getName().equals("com.acme.initializer.FooInitializer"));
|
||||
//NOTE: should also not have the web-inf classes scis in here either, but due to the
|
||||
//way the test is set up, the sci we're pretending is in web-inf classes will actually
|
||||
//NOT be loaded by the webapp's classloader, but rather by the junit classloader, so
|
||||
//it looks as if it is a container class.
|
||||
}
|
||||
|
||||
//test 2.5 webapp with configurationDiscovered=true loads both server and webapp scis
|
||||
config = new AnnotationConfiguration();
|
||||
|
@ -176,20 +245,25 @@ public class TestAnnotationConfiguration
|
|||
context.setConfigurationDiscovered(true);
|
||||
context.setClassLoader(webAppLoader);
|
||||
context.getMetaData().setWebXml(Resource.newResource(web25));
|
||||
context.getMetaData().setWebInfClassesDirs(Collections.singletonList(targetClasses));
|
||||
context.getMetaData().addWebInfJar(Resource.newResource(testSciJar.toURI().toURL()));
|
||||
context.getServletContext().setEffectiveMajorVersion(2);
|
||||
context.getServletContext().setEffectiveMinorVersion(5);
|
||||
scis = config.getNonExcludedInitializers(context);
|
||||
assertNotNull(scis);
|
||||
assertEquals(2, scis.size());
|
||||
assertTrue(sciNames.contains(scis.get(0).getClass().getName()));
|
||||
assertTrue(sciNames.contains(scis.get(1).getClass().getName()));
|
||||
assertEquals(3, scis.size());
|
||||
assertEquals("com.acme.ServerServletContainerInitializer", scis.get(0).getClass().getName()); //container path
|
||||
assertEquals("org.eclipse.jetty.annotations.WebInfClassServletContainerInitializer", scis.get(1).getClass().getName()); //web-inf classes
|
||||
assertEquals("com.acme.initializer.FooInitializer", scis.get(2).getClass().getName()); //web-inf jar no web-fragment
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
Thread.currentThread().setContextClassLoader(orig);
|
||||
Thread.currentThread().setContextClassLoader(old);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testGetFragmentFromJar() throws Exception
|
||||
{
|
||||
|
|
|
@ -30,13 +30,13 @@ import javax.servlet.ServletException;
|
|||
*
|
||||
*
|
||||
*/
|
||||
public class ServerServletContainerInitializer implements ServletContainerInitializer
|
||||
public class WebInfClassServletContainerInitializer implements ServletContainerInitializer
|
||||
{
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public ServerServletContainerInitializer()
|
||||
public WebInfClassServletContainerInitializer()
|
||||
{
|
||||
// TODO Auto-generated constructor stub
|
||||
}
|
|
@ -1 +1 @@
|
|||
org.eclipse.jetty.annotations.ServerServletContainerInitializer
|
||||
org.eclipse.jetty.annotations.WebInfClassServletContainerInitializer
|
|
@ -91,7 +91,29 @@ By default, log files are kept for 90 days before being deleted.
|
|||
The value for `retainDays` (xml) or `setRetainDays` (Java) should be configured as _1 + n_ days.
|
||||
For example, if you wanted to keep the logs for the current day and the day prior you would set the `retainDays` (or `setRetainDays`) value to 2.
|
||||
|
||||
To examine more configuration options, see link:{JDURL}/org/eclipse/jetty/server/NCSARequestLog.html[NCSARequestLog.java].
|
||||
|
||||
[[request-log-custom-writer]]
|
||||
==== Introducing RequestLog.Writer
|
||||
|
||||
The concept of a `RequestLog.Writer`, introduced in Jetty 9.4.15, manages the writing to a log the string generated by the `RequestLog`.
|
||||
This allows the `CustomRequestLog` to match the functionality of other `RequestLogger` implementations by plugging in any `RequestLog.Writer` and a custom format string.
|
||||
Jetty currently has implementations of `RequestLog.Writer`, `RequestLogWriter`, `AsyncRequestLogWriter`, and `Slf4jRequestLogWriter`.
|
||||
|
||||
So, the way to create an asynchronous `RequestLog` using the extended NCSA format has been changed from:
|
||||
|
||||
`new AsyncNcsaRequestLog(filename)`
|
||||
|
||||
to:
|
||||
|
||||
`new CustomRequestLog(new AsyncRequestLogWriter(filename), CustomRequestLog.EXTENDED_NCSA_FORMAT)`
|
||||
|
||||
Additionally, there are now two settings for the log timezone to be configured.
|
||||
There is the configuration for logging the request time, which is set in the `timeZone` parameter in the `%t` format code of the string, given in the format `%{format|timeZone|locale}t`.
|
||||
|
||||
The other `timeZone` parameter relates to the generation of the log file name (both at creation and roll over).
|
||||
This is configured in the `requestlog` module file, or can be used as a setter on `RequestLogWriter` via XML.
|
||||
|
||||
Both timezones are set to GMT by default.
|
||||
|
||||
[[configuring-separate-request-log-for-web-application]]
|
||||
==== Configuring a Separate Request Log For a Web Application
|
||||
|
|
|
@ -18,23 +18,23 @@
|
|||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>findbugs-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<onlyAnalyze>org.eclipse.jetty.jndi.*</onlyAnalyze>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>maven-bundle-plugin</artifactId>
|
||||
<extensions>true</extensions>
|
||||
<configuration>
|
||||
<instructions>
|
||||
<Import-Package>javax.mail.*;resolution:=optional,*</Import-Package>
|
||||
<Import-Package>javax.mail.*;resolution:=optional,*</Import-Package>
|
||||
</instructions>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>findbugs-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<onlyAnalyze>org.eclipse.jetty.jndi.*</onlyAnalyze>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
|
|
@ -17,6 +17,8 @@ Running single test
|
|||
You can run single or set of test as well using the command line argument: ```-Dinvoker.test=jetty-run-mojo-it,jetty-run-war*-it,!jetty-run-distro*```
|
||||
The parameter supports pattern and exclusion with !
|
||||
|
||||
NOTE: if you use ```clean``` arg to maven, you will also need to add the test ```it-parent-pom``` first for invoker.test, eg ```-Dinvoker.test=it-parent-pom,jetty-run-mojo-it```.
|
||||
|
||||
Running Logs
|
||||
--------------------
|
||||
The output of each Maven build will be located in /target/it/${project-name}/build.log
|
||||
|
|
|
@ -3,13 +3,6 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<!--parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-parent</artifactId>
|
||||
<version>2.1.1.RELEASE</version>
|
||||
</parent-->
|
||||
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty.its</groupId>
|
||||
<artifactId>it-parent-pom</artifactId>
|
||||
|
@ -20,7 +13,6 @@
|
|||
<version>1.0.0-SNAPSHOT</version>
|
||||
<packaging>war</packaging>
|
||||
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
|
|
|
@ -58,32 +58,7 @@ public class AsyncContextState implements AsyncContext
|
|||
@Override
|
||||
public void addListener(final AsyncListener listener, final ServletRequest request, final ServletResponse response)
|
||||
{
|
||||
AsyncListener wrap = new AsyncListener()
|
||||
{
|
||||
@Override
|
||||
public void onTimeout(AsyncEvent event) throws IOException
|
||||
{
|
||||
listener.onTimeout(new AsyncEvent(event.getAsyncContext(),request,response,event.getThrowable()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartAsync(AsyncEvent event) throws IOException
|
||||
{
|
||||
listener.onStartAsync(new AsyncEvent(event.getAsyncContext(),request,response,event.getThrowable()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(AsyncEvent event) throws IOException
|
||||
{
|
||||
listener.onError(new AsyncEvent(event.getAsyncContext(),request,response,event.getThrowable()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete(AsyncEvent event) throws IOException
|
||||
{
|
||||
listener.onComplete(new AsyncEvent(event.getAsyncContext(),request,response,event.getThrowable()));
|
||||
}
|
||||
};
|
||||
AsyncListener wrap = new WrappedAsyncListener(listener, request, response);
|
||||
state().addListener(wrap);
|
||||
}
|
||||
|
||||
|
@ -188,6 +163,46 @@ public class AsyncContextState implements AsyncContext
|
|||
return state();
|
||||
}
|
||||
|
||||
|
||||
public static class WrappedAsyncListener implements AsyncListener
|
||||
{
|
||||
private final AsyncListener _listener;
|
||||
private final ServletRequest _request;
|
||||
private final ServletResponse _response;
|
||||
|
||||
public WrappedAsyncListener(AsyncListener listener, ServletRequest request, ServletResponse response)
|
||||
{
|
||||
_listener = listener;
|
||||
_request = request;
|
||||
_response = response;
|
||||
}
|
||||
|
||||
public AsyncListener getListener()
|
||||
{
|
||||
return _listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimeout(AsyncEvent event) throws IOException
|
||||
{
|
||||
_listener.onTimeout(new AsyncEvent(event.getAsyncContext(), _request, _response,event.getThrowable()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartAsync(AsyncEvent event) throws IOException
|
||||
{
|
||||
_listener.onStartAsync(new AsyncEvent(event.getAsyncContext(), _request, _response,event.getThrowable()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(AsyncEvent event) throws IOException
|
||||
{
|
||||
_listener.onError(new AsyncEvent(event.getAsyncContext(), _request, _response,event.getThrowable()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete(AsyncEvent event) throws IOException
|
||||
{
|
||||
_listener.onComplete(new AsyncEvent(event.getAsyncContext(), _request, _response,event.getThrowable()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -149,6 +149,25 @@ public class HttpChannelState
|
|||
}
|
||||
}
|
||||
|
||||
public boolean hasListener(AsyncListener listener)
|
||||
{
|
||||
try(Locker.Lock lock= _locker.lock())
|
||||
{
|
||||
if (_asyncListeners==null)
|
||||
return false;
|
||||
for (AsyncListener l : _asyncListeners)
|
||||
{
|
||||
if (l==listener)
|
||||
return true;
|
||||
|
||||
if (l instanceof AsyncContextState.WrappedAsyncListener && ((AsyncContextState.WrappedAsyncListener)l).getListener()==listener)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void setTimeout(long ms)
|
||||
{
|
||||
try(Locker.Lock lock= _locker.lock())
|
||||
|
|
|
@ -442,7 +442,7 @@ public class LowResourceMonitor extends ContainerLifeCycle
|
|||
{
|
||||
}
|
||||
|
||||
interface LowResourceCheck
|
||||
public interface LowResourceCheck
|
||||
{
|
||||
boolean isLowOnResources();
|
||||
|
||||
|
|
|
@ -72,6 +72,7 @@ import org.eclipse.jetty.http.HttpCookie;
|
|||
import org.eclipse.jetty.http.HttpField;
|
||||
import org.eclipse.jetty.http.HttpFields;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpHeaderValue;
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.eclipse.jetty.http.HttpScheme;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
|
@ -465,8 +466,12 @@ public class Request implements HttpServletRequest
|
|||
if (MimeTypes.Type.FORM_ENCODED.is(contentType) &&
|
||||
_channel.getHttpConfiguration().isFormEncodedMethod(getMethod()))
|
||||
{
|
||||
if (_metaData!=null && getHttpFields().contains(HttpHeader.CONTENT_ENCODING))
|
||||
throw new BadMessageException(HttpStatus.NOT_IMPLEMENTED_501,"Unsupported Content-Encoding");
|
||||
if (_metaData!=null)
|
||||
{
|
||||
String contentEncoding = getHttpFields().get(HttpHeader.CONTENT_ENCODING);
|
||||
if (contentEncoding!=null && !HttpHeaderValue.IDENTITY.is(contentEncoding))
|
||||
throw new BadMessageException(HttpStatus.NOT_IMPLEMENTED_501, "Unsupported Content-Encoding");
|
||||
}
|
||||
extractFormParameters(_contentParameters);
|
||||
}
|
||||
else if (MimeTypes.Type.MULTIPART_FORM_DATA.is(contentType) &&
|
||||
|
|
|
@ -55,7 +55,7 @@ public class DefaultSessionIdManager extends ContainerLifeCycle implements Sessi
|
|||
{
|
||||
private final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session");
|
||||
|
||||
private final static String __NEW_SESSION_ID="org.eclipse.jetty.server.newSessionId";
|
||||
public final static String __NEW_SESSION_ID="org.eclipse.jetty.server.newSessionId";
|
||||
|
||||
protected static final AtomicLong COUNTER = new AtomicLong();
|
||||
|
||||
|
|
|
@ -1148,4 +1148,18 @@ public class Session implements SessionHandler.SessionIf
|
|||
return _resident;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
try (Lock lock = _lock.lock())
|
||||
{
|
||||
return String.format("%s@%x{id=%s,x=%s,req=%d,res=%b}",
|
||||
getClass().getSimpleName(),
|
||||
hashCode(),
|
||||
_sessionData.getId(),
|
||||
_extendedId,
|
||||
_requests,
|
||||
_resident);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -162,8 +162,22 @@ public class SessionHandler extends ScopedHandler
|
|||
@Override
|
||||
public void onComplete(AsyncEvent event) throws IOException
|
||||
{
|
||||
//An async request has completed, so we can complete the session
|
||||
complete(Request.getBaseRequest(event.getAsyncContext().getRequest()).getSession(false));
|
||||
// An async request has completed, so we can complete the session,
|
||||
// but we must locate the session instance for this context
|
||||
Request request = Request.getBaseRequest(event.getAsyncContext().getRequest());
|
||||
HttpSession session = request.getSession(false);
|
||||
String id;
|
||||
if (session!=null)
|
||||
id = session.getId();
|
||||
else
|
||||
{
|
||||
id = (String)request.getAttribute(DefaultSessionIdManager.__NEW_SESSION_ID);
|
||||
if (id==null)
|
||||
id = request.getRequestedSessionId();
|
||||
}
|
||||
|
||||
if (id!=null)
|
||||
complete(getSession(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -407,9 +421,12 @@ public class SessionHandler extends ScopedHandler
|
|||
*/
|
||||
public void complete(HttpSession session)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Complete called with session {}", session);
|
||||
|
||||
if (session == null)
|
||||
return;
|
||||
|
||||
|
||||
Session s = ((SessionIf)session).getSession();
|
||||
|
||||
try
|
||||
|
@ -422,23 +439,26 @@ public class SessionHandler extends ScopedHandler
|
|||
LOG.warn(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void complete (Session session, Request request)
|
||||
|
||||
@Deprecated
|
||||
public void complete(Session session, Request baseRequest)
|
||||
{
|
||||
if (request.isAsyncStarted() && request.getDispatcherType() == DispatcherType.REQUEST)
|
||||
ensureCompletion(baseRequest);
|
||||
}
|
||||
|
||||
private void ensureCompletion(Request baseRequest)
|
||||
{
|
||||
if (baseRequest.isAsyncStarted())
|
||||
{
|
||||
request.getAsyncContext().addListener(_sessionAsyncListener);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Adding AsyncListener for {}", baseRequest);
|
||||
if (!baseRequest.getHttpChannelState().hasListener(_sessionAsyncListener))
|
||||
baseRequest.getAsyncContext().addListener(_sessionAsyncListener);
|
||||
}
|
||||
else
|
||||
{
|
||||
complete(session);
|
||||
complete(baseRequest.getSession(false));
|
||||
}
|
||||
//if dispatcher type is not async and not request, complete immediately (its a forward or an include)
|
||||
|
||||
//else if dispatcher type is request and not async, complete immediately
|
||||
|
||||
//else register an async callback completion listener that will complete the session
|
||||
}
|
||||
|
||||
|
||||
|
@ -455,7 +475,6 @@ public class SessionHandler extends ScopedHandler
|
|||
_context=ContextHandler.getCurrentContext();
|
||||
_loader=Thread.currentThread().getContextClassLoader();
|
||||
|
||||
|
||||
synchronized (server)
|
||||
{
|
||||
//Get a SessionDataStore and a SessionDataStore, falling back to in-memory sessions only
|
||||
|
@ -472,7 +491,6 @@ public class SessionHandler extends ScopedHandler
|
|||
|
||||
_sessionCache.setSessionDataStore(sds);
|
||||
}
|
||||
|
||||
|
||||
if (_sessionIdManager==null)
|
||||
{
|
||||
|
@ -1593,16 +1611,19 @@ public class SessionHandler extends ScopedHandler
|
|||
@Override
|
||||
public void doScope(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
SessionHandler old_session_manager = null;
|
||||
SessionHandler old_session_handler = null;
|
||||
HttpSession old_session = null;
|
||||
HttpSession existingSession = null;
|
||||
|
||||
try
|
||||
{
|
||||
old_session_manager = baseRequest.getSessionHandler();
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("SessionHandler.doScope");
|
||||
|
||||
old_session_handler = baseRequest.getSessionHandler();
|
||||
old_session = baseRequest.getSession(false);
|
||||
|
||||
if (old_session_manager != this)
|
||||
if (old_session_handler != this)
|
||||
{
|
||||
// new session context
|
||||
baseRequest.setSessionHandler(this);
|
||||
|
@ -1613,7 +1634,7 @@ public class SessionHandler extends ScopedHandler
|
|||
// access any existing session for this context
|
||||
existingSession = baseRequest.getSession(false);
|
||||
|
||||
if ((existingSession != null) && (old_session_manager != this))
|
||||
if ((existingSession != null) && (old_session_handler != this))
|
||||
{
|
||||
HttpCookie cookie = access(existingSession,request.isSecure());
|
||||
// Handle changed ID or max-age refresh, but only if this is not a redispatched request
|
||||
|
@ -1622,10 +1643,7 @@ public class SessionHandler extends ScopedHandler
|
|||
}
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
LOG.debug("sessionHandler=" + this);
|
||||
LOG.debug("session=" + existingSession);
|
||||
}
|
||||
LOG.debug("sessionHandler={} session={}",this, existingSession);
|
||||
|
||||
if (_nextScope != null)
|
||||
_nextScope.doScope(target,baseRequest,request,response);
|
||||
|
@ -1637,16 +1655,18 @@ public class SessionHandler extends ScopedHandler
|
|||
finally
|
||||
{
|
||||
//if there is a session that was created during handling this context, then complete it
|
||||
HttpSession finalSession = baseRequest.getSession(false);
|
||||
if (LOG.isDebugEnabled()) LOG.debug("FinalSession="+finalSession+" old_session_manager="+old_session_manager+" this="+this);
|
||||
if ((finalSession != null) && (old_session_manager != this))
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("FinalSession={}, old_session_handler={}, this={}, calling complete={}", baseRequest.getSession(false), old_session_handler, this, (old_session_handler != this));
|
||||
|
||||
// If we are leaving the scope of this session handler, ensure the session is completed
|
||||
if (old_session_handler != this)
|
||||
ensureCompletion(baseRequest);
|
||||
|
||||
// revert the session handler to the previous, unless it was null, in which case remember it as
|
||||
// the first session handler encountered.
|
||||
if (old_session_handler != null && old_session_handler != this)
|
||||
{
|
||||
complete((Session)finalSession, baseRequest);
|
||||
}
|
||||
|
||||
if (old_session_manager != null && old_session_manager != this)
|
||||
{
|
||||
baseRequest.setSessionHandler(old_session_manager);
|
||||
baseRequest.setSessionHandler(old_session_handler);
|
||||
baseRequest.setSession(old_session);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -648,18 +648,29 @@ public class RequestTest
|
|||
assertThat(responses,startsWith("HTTP/1.1 200"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIdentityParamExtraction() throws Exception
|
||||
{
|
||||
_handler._checker = (request, response) -> "bar".equals(request.getParameter("foo"));
|
||||
|
||||
//Send a request with encoded form content
|
||||
String request="POST / HTTP/1.1\r\n"+
|
||||
"Host: whatever\r\n"+
|
||||
"Content-Type: application/x-www-form-urlencoded; charset=utf-8\n"+
|
||||
"Content-Length: 7\n"+
|
||||
"Content-Encoding: identity\n"+
|
||||
"Connection: close\n"+
|
||||
"\n"+
|
||||
"foo=bar\n";
|
||||
|
||||
String responses=_connector.getResponse(request);
|
||||
assertThat(responses,startsWith("HTTP/1.1 200"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodedNotParams() throws Exception
|
||||
{
|
||||
_handler._checker = new RequestTester()
|
||||
{
|
||||
@Override
|
||||
public boolean check(HttpServletRequest request,HttpServletResponse response)
|
||||
{
|
||||
return request.getParameter("param")==null;
|
||||
}
|
||||
};
|
||||
_handler._checker = (request, response) -> request.getParameter("param")==null;
|
||||
|
||||
//Send a request with encoded form content
|
||||
String request="POST / HTTP/1.1\r\n"+
|
||||
|
@ -675,7 +686,6 @@ public class RequestTest
|
|||
assertThat(responses,startsWith("HTTP/1.1 200"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testInvalidHostHeader() throws Exception
|
||||
{
|
||||
|
|
2
pom.xml
2
pom.xml
|
@ -50,7 +50,7 @@
|
|||
<jetty-test-policy.version>1.2</jetty-test-policy.version>
|
||||
<servlet.api.version>4.0.1</servlet.api.version>
|
||||
<servlet.schema.version>4.0.3</servlet.schema.version>
|
||||
<jsp.version>8.5.33</jsp.version>
|
||||
<jsp.version>8.5.35.1</jsp.version>
|
||||
<!-- default values are unsupported, but required to be defined for reactor sanity reasons -->
|
||||
<alpn.version>undefined</alpn.version>
|
||||
<conscrypt.version>1.4.1</conscrypt.version>
|
||||
|
|
|
@ -117,7 +117,7 @@ public class AttributeNameTest
|
|||
//Mangle the cookie, replacing Path with $Path, etc.
|
||||
sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=","$1\\$Path=");
|
||||
|
||||
//Make a request to the 2nd server which will do a refresh, use TestFooServlet to ensure that the
|
||||
//Make a request to the 2nd server which will do a refresh, use TestServlet to ensure that the
|
||||
//session attribute with dotted name is not removed
|
||||
Request request2 = client.newRequest("http://localhost:" + port2 + contextPath + servletMapping + "?action=get");
|
||||
request2.header("Cookie", sessionCookie);
|
||||
|
|
|
@ -1,59 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.io.IOException;
|
||||
import java.lang.reflect.Proxy;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
|
||||
|
||||
public class TestFooServlet extends HttpServlet
|
||||
{
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||
{
|
||||
String action = request.getParameter("action");
|
||||
|
||||
if ("create".equals(action))
|
||||
{
|
||||
HttpSession session = request.getSession(true);
|
||||
TestFoo testFoo = new TestFoo();
|
||||
testFoo.setInt(33);
|
||||
FooInvocationHandler handler = new FooInvocationHandler(testFoo);
|
||||
Foo foo = (Foo)Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[] {Foo.class}, handler);
|
||||
session.setAttribute("foo", foo);
|
||||
}
|
||||
else if ("test".equals(action))
|
||||
{
|
||||
HttpSession session = request.getSession(false);
|
||||
if (session == null)
|
||||
response.sendError(500, "Session not activated");
|
||||
Foo foo = (Foo)session.getAttribute("foo");
|
||||
if (foo == null || foo.getInt() != 33)
|
||||
response.sendError(500, "Foo not deserialized");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,358 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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 static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Proxy;
|
||||
|
||||
import javax.servlet.AsyncContext;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.WriteListener;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.StacklessLogging;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* AsyncTest
|
||||
*
|
||||
* Tests async handling wrt sessions.
|
||||
*/
|
||||
public class AsyncTest
|
||||
{
|
||||
public static class LatchServlet extends HttpServlet
|
||||
{
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
|
||||
{
|
||||
resp.getWriter().println("Latched");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSessionWithAsyncDispatch() throws Exception
|
||||
{
|
||||
// Test async dispatch back to same context, which then creates a session.
|
||||
|
||||
DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory();
|
||||
cacheFactory.setEvictionPolicy(SessionCache.EVICT_ON_SESSION_EXIT);
|
||||
SessionDataStoreFactory storeFactory = new TestSessionDataStoreFactory();
|
||||
TestServer server = new TestServer(0, -1, -1, cacheFactory, storeFactory);
|
||||
|
||||
String contextPath = "";
|
||||
String mapping = "/server";
|
||||
|
||||
ServletContextHandler contextHandler = server.addContext(contextPath);
|
||||
TestServlet servlet = new TestServlet();
|
||||
ServletHolder holder = new ServletHolder(servlet);
|
||||
contextHandler.addServlet(holder, mapping);
|
||||
LatchServlet latchServlet = new LatchServlet();
|
||||
ServletHolder latchHolder = new ServletHolder(latchServlet);
|
||||
contextHandler.addServlet(latchHolder, "/latch");
|
||||
|
||||
server.start();
|
||||
int port = server.getPort();
|
||||
|
||||
try (StacklessLogging stackless = new StacklessLogging(Log.getLogger("org.eclipse.jetty.server.session")))
|
||||
{
|
||||
HttpClient client = new HttpClient();
|
||||
client.start();
|
||||
String url = "http://localhost:" + port + contextPath + mapping+"?action=async";
|
||||
|
||||
//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);
|
||||
|
||||
//make another request, when this is handled, the first request is definitely finished being handled
|
||||
response = client.GET("http://localhost:" + port + contextPath + "/latch");
|
||||
assertEquals(HttpServletResponse.SC_OK,response.getStatus());
|
||||
|
||||
//session should now be evicted from the cache after request exited
|
||||
String id = TestServer.extractSessionId(sessionCookie);
|
||||
assertFalse(contextHandler.getSessionHandler().getSessionCache().contains(id));
|
||||
assertTrue(contextHandler.getSessionHandler().getSessionCache().getSessionDataStore().exists(id));
|
||||
}
|
||||
finally
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSessionWithAsyncComplete() throws Exception
|
||||
{
|
||||
// Test async write, which creates a session and completes outside of a dispatch
|
||||
|
||||
DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory();
|
||||
cacheFactory.setEvictionPolicy(SessionCache.EVICT_ON_SESSION_EXIT);
|
||||
SessionDataStoreFactory storeFactory = new TestSessionDataStoreFactory();
|
||||
TestServer server = new TestServer(0, -1, -1, cacheFactory, storeFactory);
|
||||
|
||||
String contextPath = "";
|
||||
String mapping = "/server";
|
||||
|
||||
ServletContextHandler contextHandler = server.addContext(contextPath);
|
||||
TestServlet servlet = new TestServlet();
|
||||
ServletHolder holder = new ServletHolder(servlet);
|
||||
contextHandler.addServlet(holder, mapping);
|
||||
LatchServlet latchServlet = new LatchServlet();
|
||||
ServletHolder latchHolder = new ServletHolder(latchServlet);
|
||||
contextHandler.addServlet(latchHolder, "/latch");
|
||||
|
||||
server.start();
|
||||
int port = server.getPort();
|
||||
|
||||
try (StacklessLogging stackless = new StacklessLogging(Log.getLogger("org.eclipse.jetty.server.session")))
|
||||
{
|
||||
HttpClient client = new HttpClient();
|
||||
client.start();
|
||||
String url = "http://localhost:" + port + contextPath + mapping+"?action=asyncComplete";
|
||||
|
||||
//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);
|
||||
|
||||
//make another request, when this is handled, the first request is definitely finished being handled
|
||||
response = client.GET("http://localhost:" + port + contextPath + "/latch");
|
||||
assertEquals(HttpServletResponse.SC_OK,response.getStatus());
|
||||
|
||||
//session should now be evicted from the cache after request exited
|
||||
String id = TestServer.extractSessionId(sessionCookie);
|
||||
assertFalse(contextHandler.getSessionHandler().getSessionCache().contains(id));
|
||||
assertTrue(contextHandler.getSessionHandler().getSessionCache().getSessionDataStore().exists(id));
|
||||
}
|
||||
finally
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSessionWithCrossContextAsync() throws Exception
|
||||
{
|
||||
// Test async dispatch from context A to context B then
|
||||
// async dispatch back to context B, which then creates a session (in context B).
|
||||
|
||||
DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory();
|
||||
cacheFactory.setEvictionPolicy(SessionCache.EVICT_ON_SESSION_EXIT);
|
||||
SessionDataStoreFactory storeFactory = new TestSessionDataStoreFactory();
|
||||
TestServer server = new TestServer(0, -1, -1, cacheFactory, storeFactory);
|
||||
|
||||
ServletContextHandler contextA = server.addContext("/ctxA");
|
||||
CrossContextServlet ccServlet = new CrossContextServlet();
|
||||
ServletHolder ccHolder = new ServletHolder(ccServlet);
|
||||
contextA.addServlet(ccHolder, "/*");
|
||||
|
||||
ServletContextHandler contextB = server.addContext("/ctxB");
|
||||
TestServlet testServlet = new TestServlet();
|
||||
ServletHolder testHolder = new ServletHolder(testServlet);
|
||||
contextB.addServlet(testHolder, "/*");
|
||||
LatchServlet latchServlet = new LatchServlet();
|
||||
ServletHolder latchHolder = new ServletHolder(latchServlet);
|
||||
contextB.addServlet(latchHolder, "/latch");
|
||||
|
||||
|
||||
server.start();
|
||||
int port = server.getPort();
|
||||
|
||||
try (StacklessLogging stackless = new StacklessLogging(Log.getLogger("org.eclipse.jetty.server.session")))
|
||||
{
|
||||
HttpClient client = new HttpClient();
|
||||
client.start();
|
||||
String url = "http://localhost:" + port + "/ctxA/test?action=async";
|
||||
|
||||
//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);
|
||||
|
||||
//make another request, when this is handled, the first request is definitely finished being handled
|
||||
response = client.GET("http://localhost:" + port + "/ctxB/latch");
|
||||
assertEquals(HttpServletResponse.SC_OK,response.getStatus());
|
||||
|
||||
//session should now be evicted from the cache after request exited
|
||||
String id = TestServer.extractSessionId(sessionCookie);
|
||||
assertFalse(contextB.getSessionHandler().getSessionCache().contains(id));
|
||||
assertTrue(contextB.getSessionHandler().getSessionCache().getSessionDataStore().exists(id));
|
||||
}
|
||||
finally
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSessionWithCrossContextAsyncComplete() throws Exception
|
||||
{
|
||||
// Test async dispatch from context A to context B, which then does an
|
||||
// async write, which creates a session (in context A) and completes outside of a
|
||||
// dispatch
|
||||
|
||||
DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory();
|
||||
cacheFactory.setEvictionPolicy(SessionCache.EVICT_ON_SESSION_EXIT);
|
||||
SessionDataStoreFactory storeFactory = new TestSessionDataStoreFactory();
|
||||
TestServer server = new TestServer(0, -1, -1, cacheFactory, storeFactory);
|
||||
|
||||
ServletContextHandler contextA = server.addContext("/ctxA");
|
||||
CrossContextServlet ccServlet = new CrossContextServlet();
|
||||
ServletHolder ccHolder = new ServletHolder(ccServlet);
|
||||
contextA.addServlet(ccHolder, "/*");
|
||||
|
||||
ServletContextHandler contextB = server.addContext("/ctxB");
|
||||
TestServlet testServlet = new TestServlet();
|
||||
ServletHolder testHolder = new ServletHolder(testServlet);
|
||||
contextB.addServlet(testHolder, "/*");
|
||||
LatchServlet latchServlet = new LatchServlet();
|
||||
ServletHolder latchHolder = new ServletHolder(latchServlet);
|
||||
contextB.addServlet(latchHolder, "/latch");
|
||||
|
||||
server.start();
|
||||
int port = server.getPort();
|
||||
|
||||
try (StacklessLogging stackless = new StacklessLogging(Log.getLogger("org.eclipse.jetty.server.session")))
|
||||
{
|
||||
HttpClient client = new HttpClient();
|
||||
client.start();
|
||||
String url = "http://localhost:" + port + "/ctxA/test?action=asyncComplete";
|
||||
|
||||
//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);
|
||||
|
||||
//make another request, when this is handled, the first request is definitely finished being handled
|
||||
response = client.GET("http://localhost:" + port + "/ctxB/latch");
|
||||
assertEquals(HttpServletResponse.SC_OK,response.getStatus());
|
||||
|
||||
//session should now be evicted from the cache A after request exited
|
||||
String id = TestServer.extractSessionId(sessionCookie);
|
||||
assertFalse(contextA.getSessionHandler().getSessionCache().contains(id));
|
||||
assertTrue(contextA.getSessionHandler().getSessionCache().getSessionDataStore().exists(id));
|
||||
}
|
||||
finally
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
}
|
||||
|
||||
public static class TestServlet extends HttpServlet
|
||||
{
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||
{
|
||||
String action = request.getParameter("action");
|
||||
|
||||
if ("create".equals(action))
|
||||
{
|
||||
HttpSession session = request.getSession(true);
|
||||
TestFoo testFoo = new TestFoo();
|
||||
testFoo.setInt(33);
|
||||
FooInvocationHandler handler = new FooInvocationHandler(testFoo);
|
||||
Foo foo = (Foo)Proxy
|
||||
.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[] {Foo.class}, handler);
|
||||
session.setAttribute("foo", foo);
|
||||
}
|
||||
else if ("test".equals(action))
|
||||
{
|
||||
HttpSession session = request.getSession(false);
|
||||
if (session == null)
|
||||
response.sendError(500, "Session not activated");
|
||||
Foo foo = (Foo)session.getAttribute("foo");
|
||||
if (foo == null || foo.getInt() != 33)
|
||||
response.sendError(500, "Foo not deserialized");
|
||||
}
|
||||
else if ("async".equals(action))
|
||||
{
|
||||
if (request.getAttribute("async-test") == null)
|
||||
{
|
||||
request.setAttribute("async-test", Boolean.TRUE);
|
||||
AsyncContext acontext = request.startAsync();
|
||||
acontext.dispatch();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
HttpSession session = request.getSession(true);
|
||||
response.getWriter().println("OK");
|
||||
}
|
||||
}
|
||||
else if ("asyncComplete".equals(action))
|
||||
{
|
||||
AsyncContext acontext = request.startAsync();
|
||||
ServletOutputStream out = response.getOutputStream();
|
||||
out.setWriteListener(new WriteListener()
|
||||
{
|
||||
@Override
|
||||
public void onWritePossible() throws IOException
|
||||
{
|
||||
if (out.isReady())
|
||||
{
|
||||
request.getSession(true);
|
||||
out.print("OK\n");
|
||||
acontext.complete();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable t)
|
||||
{
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class CrossContextServlet extends HttpServlet
|
||||
{
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||
{
|
||||
AsyncContext acontext = request.startAsync();
|
||||
|
||||
acontext.dispatch(request.getServletContext().getContext("/ctxB"),"/test");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -128,12 +128,7 @@ public class DeleteUnloadableSessionTest
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* TestFooServlet
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
public static class TestServlet extends HttpServlet
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
|
|
@ -136,16 +136,9 @@ public class SessionEvictionFailureTest
|
|||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* TestFooServlet
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
public static class TestServlet extends HttpServlet
|
||||
{
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse httpServletResponse) throws ServletException, IOException
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue