Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AbstractDiscoverableAnnotationHandler.java24
-rw-r--r--jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationConfiguration.java228
-rw-r--r--jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationParser.java305
-rw-r--r--jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ClassInheritanceHandler.java8
-rw-r--r--jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ContainerInitializerAnnotationHandler.java11
-rw-r--r--jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ServletContainerInitializerListener.java14
-rw-r--r--jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ServletSecurityAnnotationHandler.java127
-rw-r--r--jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebFilterAnnotation.java6
-rw-r--r--jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebFilterAnnotationHandler.java19
-rw-r--r--jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebListenerAnnotation.java6
-rw-r--r--jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebListenerAnnotationHandler.java14
-rw-r--r--jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebServletAnnotation.java9
-rw-r--r--jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebServletAnnotationHandler.java18
-rw-r--r--jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationInheritance.java8
-rw-r--r--jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationParser.java21
-rw-r--r--jetty-client-old/src/main/java/org/eclipse/jetty/client/AbstractHttpConnection.java16
-rw-r--r--jetty-client-old/src/main/java/org/eclipse/jetty/client/AsyncHttpConnection.java6
-rw-r--r--jetty-client-old/src/main/java/org/eclipse/jetty/client/BlockingHttpConnection.java2
-rw-r--r--jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java25
-rw-r--r--jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java24
-rw-r--r--jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldsTest.java59
-rw-r--r--jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/JaspiAuthenticator.java9
-rw-r--r--jetty-jndi/src/main/java/org/eclipse/jetty/jndi/ContextFactory.java69
-rw-r--r--jetty-jndi/src/test/java/org/eclipse/jetty/jndi/java/TestJNDI.java97
-rw-r--r--jetty-osgi/jetty-osgi-boot-jsp/src/main/java/org/eclipse/jetty/osgi/boot/jasper/WebappRegistrationCustomizerImpl.java24
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationConfiguration.java29
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/jsp/TldLocatableURLClassloader.java14
-rw-r--r--jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintSecurityHandler.java435
-rw-r--r--jetty-security/src/main/java/org/eclipse/jetty/security/authentication/SessionAuthentication.java7
-rw-r--r--jetty-security/src/test/java/org/eclipse/jetty/security/ConstraintTest.java80
-rw-r--r--jetty-security/src/test/java/org/eclipse/jetty/security/SpecExampleConstraintTest.java315
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java7
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java3
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/Request.java70
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/Response.java29
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/Server.java2
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/handler/ShutdownHandler.java2
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java150
-rw-r--r--jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java87
-rw-r--r--jetty-server/src/test/java/org/eclipse/jetty/server/HttpURITest.java103
-rw-r--r--jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java116
-rw-r--r--jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java40
-rw-r--r--jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SelectChannelServerSslTest.java11
-rw-r--r--jetty-server/src/test/resources/jetty-logging.properties3
-rw-r--r--jetty-servlet/src/main/java/org/eclipse/jetty/servlet/DefaultServlet.java22
-rw-r--r--jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletContextHandler.java15
-rw-r--r--jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java21
-rw-r--r--jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DispatcherTest.java70
-rw-r--r--jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ResponseHeadersTest.java3
-rw-r--r--jetty-servlets/src/main/java/org/eclipse/jetty/servlets/GzipFilter.java90
-rw-r--r--jetty-servlets/src/main/java/org/eclipse/jetty/servlets/MultiPartFilter.java44
-rw-r--r--jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterDefaultNoRecompressTest.java4
-rw-r--r--jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterDefaultTest.java68
-rw-r--r--jetty-servlets/src/test/java/org/eclipse/jetty/servlets/MultipartFilterTest.java2
-rw-r--r--jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/GzipTester.java11
-rw-r--r--jetty-start/src/main/java/org/eclipse/jetty/start/CommandLineBuilder.java12
-rw-r--r--jetty-start/src/main/java/org/eclipse/jetty/start/Main.java27
-rw-r--r--jetty-start/src/main/java/org/eclipse/jetty/start/Monitor.java11
-rw-r--r--jetty-start/src/main/resources/org/eclipse/jetty/start/usage.txt5
-rw-r--r--jetty-start/src/test/java/org/eclipse/jetty/start/CommandLineBuilderTest.java22
-rw-r--r--jetty-start/src/test/java/org/eclipse/jetty/start/MainTest.java30
-rw-r--r--jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartInputStream.java111
-rw-r--r--jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartOutputStream.java3
-rw-r--r--jetty-util/src/main/java/org/eclipse/jetty/util/StringUtil.java2
-rw-r--r--jetty-util/src/test/java/org/eclipse/jetty/util/MultiPartInputStreamTest.java154
-rw-r--r--jetty-webapp/src/main/java/org/eclipse/jetty/webapp/DiscoveredAnnotation.java15
-rw-r--r--jetty-webapp/src/main/java/org/eclipse/jetty/webapp/MetaData.java45
-rw-r--r--jetty-webapp/src/main/java/org/eclipse/jetty/webapp/Origin.java2
-rw-r--r--jetty-webapp/src/main/java/org/eclipse/jetty/webapp/StandardDescriptorProcessor.java49
-rw-r--r--jetty-webapp/src/main/java/org/eclipse/jetty/webapp/TagLibConfiguration.java8
-rw-r--r--jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java87
-rw-r--r--jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/UnitGenerator.java1
-rw-r--r--test-jetty-webapp/src/main/config/contexts/test.d/override-web.xml9
-rw-r--r--test-jetty-webapp/src/main/java/com/acme/RegTest.java194
-rw-r--r--test-jetty-webapp/src/main/java/com/acme/TestListener.java36
-rw-r--r--test-jetty-webapp/src/main/webapp/WEB-INF/web.xml27
-rw-r--r--test-jetty-webapp/src/main/webapp/auth.html2
-rw-r--r--tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/ImmortalSessionTest.java30
-rw-r--r--tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/SessionCookieTest.java30
-rw-r--r--tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/SessionValueSharedSaving.java30
-rw-r--r--tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionCookieTest.java164
81 files changed, 3440 insertions, 668 deletions
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AbstractDiscoverableAnnotationHandler.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AbstractDiscoverableAnnotationHandler.java
index 3f44b743f3..56150a23c4 100644
--- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AbstractDiscoverableAnnotationHandler.java
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AbstractDiscoverableAnnotationHandler.java
@@ -22,6 +22,8 @@ import java.util.ArrayList;
import java.util.List;
import org.eclipse.jetty.annotations.AnnotationParser.DiscoverableAnnotationHandler;
+import org.eclipse.jetty.annotations.AnnotationParser.Value;
+import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.webapp.DiscoveredAnnotation;
import org.eclipse.jetty.webapp.WebAppContext;
@@ -33,13 +35,32 @@ import org.eclipse.jetty.webapp.WebAppContext;
public abstract class AbstractDiscoverableAnnotationHandler implements DiscoverableAnnotationHandler
{
protected WebAppContext _context;
- protected List<DiscoveredAnnotation> _annotations = new ArrayList<DiscoveredAnnotation>();
+ protected List<DiscoveredAnnotation> _annotations;
+ protected Resource _resource;
public AbstractDiscoverableAnnotationHandler(WebAppContext context)
{
+ this(context, null);
+ }
+
+ public AbstractDiscoverableAnnotationHandler(WebAppContext context, List<DiscoveredAnnotation> list)
+ {
_context = context;
+ if (list == null)
+ _annotations = new ArrayList<DiscoveredAnnotation>();
+ else
+ _annotations = list;
}
+ public Resource getResource()
+ {
+ return _resource;
+ }
+
+ public void setResource(Resource resource)
+ {
+ _resource = resource;
+ }
public List<DiscoveredAnnotation> getAnnotationList ()
{
@@ -51,7 +72,6 @@ public abstract class AbstractDiscoverableAnnotationHandler implements Discovera
_annotations.clear();
}
-
public void addAnnotation (DiscoveredAnnotation a)
{
_annotations.add(a);
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationConfiguration.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationConfiguration.java
index 91a41a1e48..7af711a2af 100644
--- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationConfiguration.java
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationConfiguration.java
@@ -28,6 +28,7 @@ import javax.servlet.annotation.HandlesTypes;
import org.eclipse.jetty.annotations.AnnotationParser.DiscoverableAnnotationHandler;
import org.eclipse.jetty.plus.annotation.ContainerInitializer;
+import org.eclipse.jetty.util.MultiMap;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.Resource;
@@ -50,10 +51,20 @@ public class AnnotationConfiguration extends AbstractConfiguration
public static final String CONTAINER_INITIALIZERS = "org.eclipse.jetty.containerInitializers";
+ protected List<DiscoverableAnnotationHandler> _discoverableAnnotationHandlers = new ArrayList<DiscoverableAnnotationHandler>();
+ protected ClassInheritanceHandler _classInheritanceHandler;
+ protected List<ContainerInitializerAnnotationHandler> _containerInitializerAnnotationHandlers = new ArrayList<ContainerInitializerAnnotationHandler>();
+
+
public void preConfigure(final WebAppContext context) throws Exception
{
}
+
+
+ /**
+ * @see org.eclipse.jetty.webapp.AbstractConfiguration#configure(org.eclipse.jetty.webapp.WebAppContext)
+ */
@Override
public void configure(WebAppContext context) throws Exception
{
@@ -68,10 +79,9 @@ public class AnnotationConfiguration extends AbstractConfiguration
//If metadata isn't complete, if this is a servlet 3 webapp or isConfigDiscovered is true, we need to search for annotations
if (context.getServletContext().getEffectiveMajorVersion() >= 3 || context.isConfigurationDiscovered())
{
- parser = createAnnotationParser();
- parser.registerAnnotationHandler("javax.servlet.annotation.WebServlet", new WebServletAnnotationHandler(context));
- parser.registerAnnotationHandler("javax.servlet.annotation.WebFilter", new WebFilterAnnotationHandler(context));
- parser.registerAnnotationHandler("javax.servlet.annotation.WebListener", new WebListenerAnnotationHandler(context));
+ _discoverableAnnotationHandlers.add(new WebServletAnnotationHandler(context));
+ _discoverableAnnotationHandlers.add(new WebFilterAnnotationHandler(context));
+ _discoverableAnnotationHandlers.add(new WebListenerAnnotationHandler(context));
}
}
else
@@ -81,11 +91,11 @@ public class AnnotationConfiguration extends AbstractConfiguration
//Regardless of metadata, if there are any ServletContainerInitializers with @HandlesTypes, then we need to scan all the
//classes so we can call their onStartup() methods correctly
- List<ServletContainerInitializer> nonExcludedInitializers = getNonExcludedInitializers(context);
- parser = registerServletContainerInitializerAnnotationHandlers(context, parser, nonExcludedInitializers);
+ createServletContainerInitializerAnnotationHandlers(context, getNonExcludedInitializers(context));
- if (parser != null)
+ if (!_discoverableAnnotationHandlers.isEmpty() || _classInheritanceHandler != null || !_containerInitializerAnnotationHandlers.isEmpty())
{
+ parser = createAnnotationParser();
if (LOG.isDebugEnabled()) LOG.debug("Scanning all classses for annotations: webxmlVersion="+context.getServletContext().getEffectiveMajorVersion()+" configurationDiscovered="+context.isConfigurationDiscovered());
parseContainerPath(context, parser);
//email from Rajiv Mordani jsrs 315 7 April 2010
@@ -95,11 +105,39 @@ public class AnnotationConfiguration extends AbstractConfiguration
// WEB-INF/classes + order of the elements.
parseWebInfClasses(context, parser);
parseWebInfLib (context, parser);
+
+ for (DiscoverableAnnotationHandler h:_discoverableAnnotationHandlers)
+ context.getMetaData().addDiscoveredAnnotations(((AbstractDiscoverableAnnotationHandler)h).getAnnotationList());
}
}
+ /**
+ * @see org.eclipse.jetty.webapp.AbstractConfiguration#postConfigure(org.eclipse.jetty.webapp.WebAppContext)
+ */
+ @Override
+ public void postConfigure(WebAppContext context) throws Exception
+ {
+ MultiMap map = (MultiMap)context.getAttribute(CLASS_INHERITANCE_MAP);
+ if (map != null)
+ map.clear();
+
+ context.removeAttribute(CLASS_INHERITANCE_MAP);
+
+ List<ContainerInitializer> initializers = (List<ContainerInitializer>)context.getAttribute(CONTAINER_INITIALIZERS);
+ if (initializers != null)
+ initializers.clear();
+ if (_discoverableAnnotationHandlers != null)
+ _discoverableAnnotationHandlers.clear();
+
+ _classInheritanceHandler = null;
+ if (_containerInitializerAnnotationHandlers != null)
+ _containerInitializerAnnotationHandlers.clear();
+
+ super.postConfigure(context);
+ }
+
/**
* @return a new AnnotationParser. This method can be overridden to use a different impleemntation of
* the AnnotationParser. Note that this is considered internal API.
@@ -109,6 +147,9 @@ public class AnnotationConfiguration extends AbstractConfiguration
return new AnnotationParser();
}
+ /**
+ * @see org.eclipse.jetty.webapp.AbstractConfiguration#cloneConfigure(org.eclipse.jetty.webapp.WebAppContext, org.eclipse.jetty.webapp.WebAppContext)
+ */
@Override
public void cloneConfigure(WebAppContext template, WebAppContext context) throws Exception
{
@@ -116,32 +157,23 @@ public class AnnotationConfiguration extends AbstractConfiguration
}
-
-
- public AnnotationParser registerServletContainerInitializerAnnotationHandlers (WebAppContext context, AnnotationParser parser, List<ServletContainerInitializer> scis)
+
+ /**
+ * @param context
+ * @param scis
+ * @throws Exception
+ */
+ public void createServletContainerInitializerAnnotationHandlers (WebAppContext context, List<ServletContainerInitializer> scis)
throws Exception
{
- //TODO verify my interpretation of the spec. That is, that metadata-complete has nothing
- //to do with finding the ServletContainerInitializers, classes designated to be of interest to them,
- //or even calling them on startup.
-
- //Get all ServletContainerInitializers, and check them for HandlesTypes annotations.
- //For each class in the HandlesTypes value, if it IS an annotation, register a handler
- //that will record the classes that have that annotation.
- //If it is NOT an annotation, then we will interrogate the type hierarchy discovered during
- //parsing later on to find the applicable classes.
if (scis == null || scis.isEmpty())
- return parser; // nothing to do
-
- ServletContainerInitializerListener listener = new ServletContainerInitializerListener();
- listener.setWebAppContext(context);
- context.addEventListener(listener);
+ return; // nothing to do
+
- //may need to add a listener
- ArrayList<ContainerInitializer> initializers = new ArrayList<ContainerInitializer>();
+ List<ContainerInitializer> initializers = new ArrayList<ContainerInitializer>();
context.setAttribute(CONTAINER_INITIALIZERS, initializers);
for (ServletContainerInitializer service : scis)
@@ -158,17 +190,14 @@ public class AnnotationConfiguration extends AbstractConfiguration
{
initializer.setInterestedTypes(classes);
- //We need to create a parser if we haven't already
- if (parser == null)
- parser = createAnnotationParser();
//If we haven't already done so, we need to register a handler that will
- //process the whole class hierarchy
+ //process the whole class hierarchy to satisfy the ServletContainerInitializer
if (context.getAttribute(CLASS_INHERITANCE_MAP) == null)
{
- ClassInheritanceHandler classHandler = new ClassInheritanceHandler();
- context.setAttribute(CLASS_INHERITANCE_MAP, classHandler.getMap());
- parser.registerClassHandler(classHandler);
+ MultiMap map = new MultiMap();
+ context.setAttribute(CLASS_INHERITANCE_MAP, map);
+ _classInheritanceHandler = new ClassInheritanceHandler(map);
}
for (Class c: classes)
@@ -179,7 +208,7 @@ public class AnnotationConfiguration extends AbstractConfiguration
{
if (LOG.isDebugEnabled()) LOG.debug("Registering annotation handler for "+c.getName());
- parser.registerAnnotationHandler(c.getName(), new ContainerInitializerAnnotationHandler(initializer, c));
+ _containerInitializerAnnotationHandlers.add(new ContainerInitializerAnnotationHandler(initializer, c));
}
}
}
@@ -190,12 +219,13 @@ public class AnnotationConfiguration extends AbstractConfiguration
if (LOG.isDebugEnabled()) LOG.debug("No annotation on initializer "+service.getClass());
}
- //return the parser in case we lazily created it
- return parser;
- }
-
+ //add a listener which will call the servletcontainerinitializers when appropriate
+ ServletContainerInitializerListener listener = new ServletContainerInitializerListener();
+ listener.setWebAppContext(context);
+ context.addEventListener(listener);
+ }
@@ -241,7 +271,12 @@ public class AnnotationConfiguration extends AbstractConfiguration
- public List<ServletContainerInitializer> getNonExcludedInitializers (WebAppContext context)
+ /**
+ * @param context
+ * @return
+ * @throws Exception
+ */
+ public List<ServletContainerInitializer> getNonExcludedInitializers (WebAppContext context)
throws Exception
{
List<ServletContainerInitializer> nonExcludedInitializers = new ArrayList<ServletContainerInitializer>();
@@ -263,14 +298,29 @@ public class AnnotationConfiguration extends AbstractConfiguration
+ /**
+ * Scan jars on container path.
+ *
+ * @param context
+ * @param parser
+ * @throws Exception
+ */
public void parseContainerPath (final WebAppContext context, final AnnotationParser parser)
throws Exception
{
//if no pattern for the container path is defined, then by default scan NOTHING
LOG.debug("Scanning container jars");
- //clear any previously discovered annotations
- clearAnnotationList(parser.getAnnotationHandlers());
+ //always parse for discoverable annotations as well as class hierarchy and servletcontainerinitializer related annotations
+ parser.clearHandlers();
+ for (DiscoverableAnnotationHandler h:_discoverableAnnotationHandlers)
+ {
+ if (h instanceof AbstractDiscoverableAnnotationHandler)
+ ((AbstractDiscoverableAnnotationHandler)h).setResource(null); //
+ }
+ parser.registerHandlers(_discoverableAnnotationHandlers);
+ parser.registerHandler(_classInheritanceHandler);
+ parser.registerHandlers(_containerInitializerAnnotationHandlers);
//Convert from Resource to URI
ArrayList<URI> containerUris = new ArrayList<URI>();
@@ -297,16 +347,19 @@ public class AnnotationConfiguration extends AbstractConfiguration
return true;
return false;
}
- });
-
- //gather together all annotations discovered
- List<DiscoveredAnnotation> annotations = new ArrayList<DiscoveredAnnotation>();
- gatherAnnotations(annotations, parser.getAnnotationHandlers());
+ });
- context.getMetaData().addDiscoveredAnnotations(annotations);
+
}
+ /**
+ * Scan jars in WEB-INF/lib
+ *
+ * @param context
+ * @param parser
+ * @throws Exception
+ */
public void parseWebInfLib (final WebAppContext context, final AnnotationParser parser)
throws Exception
{
@@ -325,17 +378,35 @@ public class AnnotationConfiguration extends AbstractConfiguration
for (Resource r : jars)
{
- //clear any previously discovered annotations from handlers
- clearAnnotationList(parser.getAnnotationHandlers());
-
+ //for each jar, we decide which set of annotations we need to parse for
+ parser.clearHandlers();
URI uri = r.getURI();
FragmentDescriptor f = getFragmentFromJar(r, frags);
- //if a jar has no web-fragment.xml we scan it (because it is not exluded by the ordering)
+ //if its from a fragment jar that is metadata complete, we should skip scanning for @webservlet etc
+ // but yet we still need to do the scanning for the classes on behalf of the servletcontainerinitializers
+ //if a jar has no web-fragment.xml we scan it (because it is not excluded by the ordering)
//or if it has a fragment we scan it if it is not metadata complete
- if (f == null || !isMetaDataComplete(f))
+ if (f == null || !isMetaDataComplete(f) || _classInheritanceHandler != null || !_containerInitializerAnnotationHandlers.isEmpty())
{
+ //register the classinheritance handler if there is one
+ parser.registerHandler(_classInheritanceHandler);
+
+ //register the handlers for the @HandlesTypes values that are themselves annotations if there are any
+ parser.registerHandlers(_containerInitializerAnnotationHandlers);
+
+ //only register the discoverable annotation handlers if this fragment is not metadata complete, or has no fragment descriptor
+ if (f == null || !isMetaDataComplete(f))
+ {
+ for (DiscoverableAnnotationHandler h:_discoverableAnnotationHandlers)
+ {
+ if (h instanceof AbstractDiscoverableAnnotationHandler)
+ ((AbstractDiscoverableAnnotationHandler)h).setResource(r);
+ }
+ parser.registerHandlers(_discoverableAnnotationHandlers);
+ }
+
parser.parse(uri,
new ClassNameResolver()
{
@@ -354,13 +425,17 @@ public class AnnotationConfiguration extends AbstractConfiguration
return true;
}
});
- List<DiscoveredAnnotation> annotations = new ArrayList<DiscoveredAnnotation>();
- gatherAnnotations(annotations, parser.getAnnotationHandlers());
- context.getMetaData().addDiscoveredAnnotations(r, annotations);
}
}
}
+ /**
+ * Scan classes in WEB-INF/classes
+ *
+ * @param context
+ * @param parser
+ * @throws Exception
+ */
public void parseWebInfClasses (final WebAppContext context, final AnnotationParser parser)
throws Exception
{
@@ -370,7 +445,16 @@ public class AnnotationConfiguration extends AbstractConfiguration
Resource classesDir = context.getWebInf().addPath("classes/");
if (classesDir.exists())
{
- clearAnnotationList(parser.getAnnotationHandlers());
+ parser.clearHandlers();
+ for (DiscoverableAnnotationHandler h:_discoverableAnnotationHandlers)
+ {
+ if (h instanceof AbstractDiscoverableAnnotationHandler)
+ ((AbstractDiscoverableAnnotationHandler)h).setResource(null); //
+ }
+ parser.registerHandlers(_discoverableAnnotationHandlers);
+ parser.registerHandler(_classInheritanceHandler);
+ parser.registerHandlers(_containerInitializerAnnotationHandlers);
+
parser.parse(classesDir,
new ClassNameResolver()
{
@@ -389,17 +473,20 @@ public class AnnotationConfiguration extends AbstractConfiguration
return true;
}
});
-
- //TODO - where to set the annotations discovered from WEB-INF/classes?
- List<DiscoveredAnnotation> annotations = new ArrayList<DiscoveredAnnotation>();
- gatherAnnotations(annotations, parser.getAnnotationHandlers());
- context.getMetaData().addDiscoveredAnnotations (annotations);
}
}
}
+ /**
+ * Get the web-fragment.xml from a jar
+ *
+ * @param jar
+ * @param frags
+ * @return
+ * @throws Exception
+ */
public FragmentDescriptor getFragmentFromJar (Resource jar, List<FragmentDescriptor> frags)
throws Exception
{
@@ -422,24 +509,5 @@ public class AnnotationConfiguration extends AbstractConfiguration
return (d!=null && d.getMetaDataComplete() == MetaDataComplete.True);
}
- protected void clearAnnotationList (List<DiscoverableAnnotationHandler> handlers)
- {
- if (handlers == null)
- return;
-
- for (DiscoverableAnnotationHandler h:handlers)
- {
- if (h instanceof AbstractDiscoverableAnnotationHandler)
- ((AbstractDiscoverableAnnotationHandler)h).resetList();
- }
- }
- protected void gatherAnnotations (List<DiscoveredAnnotation> annotations, List<DiscoverableAnnotationHandler> handlers)
- {
- for (DiscoverableAnnotationHandler h:handlers)
- {
- if (h instanceof AbstractDiscoverableAnnotationHandler)
- annotations.addAll(((AbstractDiscoverableAnnotationHandler)h).getAnnotationList());
- }
- }
}
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationParser.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationParser.java
index e253f0850b..fc79225ead 100644
--- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationParser.java
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationParser.java
@@ -52,11 +52,8 @@ public class AnnotationParser
{
private static final Logger LOG = Log.getLogger(AnnotationParser.class);
- protected List<String> _parsedClassNames = new ArrayList<String>();
- protected Map<String, List<DiscoverableAnnotationHandler>> _annotationHandlers = new HashMap<String, List<DiscoverableAnnotationHandler>>();
- protected List<ClassHandler> _classHandlers = new ArrayList<ClassHandler>();
- protected List<MethodHandler> _methodHandlers = new ArrayList<MethodHandler>();
- protected List<FieldHandler> _fieldHandlers = new ArrayList<FieldHandler>();
+ protected List<String> _parsedClassNames = new ArrayList<String>();
+ protected List<Handler> _handlers = new ArrayList<Handler>();
public static String normalize (String name)
{
@@ -167,37 +164,122 @@ public class AnnotationParser
- public interface DiscoverableAnnotationHandler
+ /**
+ * Handler
+ *
+ * Signature for all handlers that respond to parsing class files.
+ */
+ public interface Handler
+ {
+
+ }
+
+
+
+ /**
+ * DiscoverableAnnotationHandler
+ *
+ * Processes an annotation when it is discovered on a class.
+ */
+ public interface DiscoverableAnnotationHandler extends Handler
{
+ /**
+ * Process an annotation that was discovered on a class
+ * @param className
+ * @param version
+ * @param access
+ * @param signature
+ * @param superName
+ * @param interfaces
+ * @param annotation
+ * @param values
+ */
public void handleClass (String className, int version, int access,
String signature, String superName, String[] interfaces,
String annotation, List<Value>values);
+ /**
+ * Process an annotation that was discovered on a method
+ * @param className
+ * @param methodName
+ * @param access
+ * @param desc
+ * @param signature
+ * @param exceptions
+ * @param annotation
+ * @param values
+ */
public void handleMethod (String className, String methodName, int access,
String desc, String signature,String[] exceptions,
String annotation, List<Value>values);
+
+ /**
+ * Process an annotation that was discovered on a field
+ * @param className
+ * @param fieldName
+ * @param access
+ * @param fieldType
+ * @param signature
+ * @param value
+ * @param annotation
+ * @param values
+ */
public void handleField (String className, String fieldName, int access,
String fieldType, String signature, Object value,
String annotation, List<Value>values);
+
+
+ /**
+ * Get the name of the annotation processed by this handler. Can be null
+ *
+ * @return
+ */
+ public String getAnnotationName();
}
- public interface ClassHandler
+
+ /**
+ * ClassHandler
+ *
+ * Responds to finding a Class
+ */
+ public interface ClassHandler extends Handler
{
public void handle (String className, int version, int access, String signature, String superName, String[] interfaces);
}
- public interface MethodHandler
+
+
+ /**
+ * MethodHandler
+ *
+ * Responds to finding a Method
+ */
+ public interface MethodHandler extends Handler
{
public void handle (String className, String methodName, int access, String desc, String signature,String[] exceptions);
}
- public interface FieldHandler
+
+ /**
+ * FieldHandler
+ *
+ * Responds to finding a Field
+ */
+ public interface FieldHandler extends Handler
{
public void handle (String className, String fieldName, int access, String fieldType, String signature, Object value);
}
+
+
+ /**
+ * MyAnnotationVisitor
+ *
+ * ASM Visitor for Annotations
+ */
public class MyAnnotationVisitor implements AnnotationVisitor
{
List<Value> _annotationValues;
@@ -307,9 +389,12 @@ public class AnnotationParser
normalizedInterfaces[i++] = normalize(s);
}
- for (ClassHandler h : AnnotationParser.this._classHandlers)
+ for (Handler h : AnnotationParser.this._handlers)
{
- h.handle(_className, _version, _access, _signature, normalize(_superName), normalizedInterfaces);
+ if (h instanceof ClassHandler)
+ {
+ ((ClassHandler)h).handle(_className, _version, _access, _signature, normalize(_superName), normalizedInterfaces);
+ }
}
}
@@ -322,12 +407,13 @@ public class AnnotationParser
super.visitEnd();
//call all AnnotationHandlers with classname, annotation name + values
- List<DiscoverableAnnotationHandler> handlers = AnnotationParser.this._annotationHandlers.get(_annotationName);
- if (handlers != null)
+ for (Handler h : AnnotationParser.this._handlers)
{
- for (DiscoverableAnnotationHandler h:handlers)
+ if (h instanceof DiscoverableAnnotationHandler)
{
- h.handleClass(_className, _version, _access, _signature, _superName, _interfaces, _annotationName, _annotationValues);
+ DiscoverableAnnotationHandler dah = (DiscoverableAnnotationHandler)h;
+ if (_annotationName.equalsIgnoreCase(dah.getAnnotationName()))
+ dah.handleClass(_className, _version, _access, _signature, _superName, _interfaces, _annotationName, _annotationValues);
}
}
}
@@ -353,12 +439,13 @@ public class AnnotationParser
{
super.visitEnd();
//call all AnnotationHandlers with classname, method, annotation name + values
- List<DiscoverableAnnotationHandler> handlers = AnnotationParser.this._annotationHandlers.get(_annotationName);
- if (handlers != null)
+ for (Handler h : AnnotationParser.this._handlers)
{
- for (DiscoverableAnnotationHandler h:handlers)
+ if (h instanceof DiscoverableAnnotationHandler)
{
- h.handleMethod(_className, name, access, methodDesc, signature, exceptions, _annotationName, _annotationValues);
+ DiscoverableAnnotationHandler dah = (DiscoverableAnnotationHandler)h;
+ if (_annotationName.equalsIgnoreCase(dah.getAnnotationName()))
+ dah.handleMethod(_className, name, access, methodDesc, signature, exceptions, _annotationName, _annotationValues);
}
}
}
@@ -385,12 +472,13 @@ public class AnnotationParser
public void visitEnd()
{
super.visitEnd();
- List<DiscoverableAnnotationHandler> handlers = AnnotationParser.this._annotationHandlers.get(_annotationName);
- if (handlers != null)
+ for (Handler h : AnnotationParser.this._handlers)
{
- for (DiscoverableAnnotationHandler h:handlers)
+ if (h instanceof DiscoverableAnnotationHandler)
{
- h.handleField(_className, fieldName, access, fieldType, signature, value, _annotationName, _annotationValues);
+ DiscoverableAnnotationHandler dah = (DiscoverableAnnotationHandler)h;
+ if (_annotationName.equalsIgnoreCase(dah.getAnnotationName()))
+ dah.handleField(_className, fieldName, access, fieldType, signature, value, _annotationName, _annotationValues);
}
}
}
@@ -406,46 +494,130 @@ public class AnnotationParser
* Register a handler that will be called back when the named annotation is
* encountered on a class.
*
+ * @deprecated see registerHandler(Handler)
* @param annotationName
* @param handler
*/
public void registerAnnotationHandler (String annotationName, DiscoverableAnnotationHandler handler)
{
- List<DiscoverableAnnotationHandler> handlers = _annotationHandlers.get(annotationName);
- if (handlers == null)
- {
- handlers = new ArrayList<DiscoverableAnnotationHandler>();
- _annotationHandlers.put(annotationName, handlers);
- }
- handlers.add(handler);
+ _handlers.add(handler);
}
+
+ /**
+ * @deprecated
+ * @param annotationName
+ * @return
+ */
public List<DiscoverableAnnotationHandler> getAnnotationHandlers(String annotationName)
{
- List<DiscoverableAnnotationHandler> handlers = _annotationHandlers.get(annotationName);
- if (handlers == null)
- return Collections.emptyList();
- return new ArrayList<DiscoverableAnnotationHandler>();
+ List<DiscoverableAnnotationHandler> handlers = new ArrayList<DiscoverableAnnotationHandler>();
+ for (Handler h:_handlers)
+ {
+ if (h instanceof DiscoverableAnnotationHandler)
+ {
+ DiscoverableAnnotationHandler dah = (DiscoverableAnnotationHandler)h;
+ if (annotationName.equals(dah.getAnnotationName()))
+ handlers.add(dah);
+ }
+ }
+
+ return handlers;
}
+ /**
+ * @deprecated
+ * @return
+ */
public List<DiscoverableAnnotationHandler> getAnnotationHandlers()
{
- List<DiscoverableAnnotationHandler> allHandlers = new ArrayList<DiscoverableAnnotationHandler>();
- for (List<DiscoverableAnnotationHandler> list:_annotationHandlers.values())
- allHandlers.addAll(list);
- return allHandlers;
+ List<DiscoverableAnnotationHandler> allAnnotationHandlers = new ArrayList<DiscoverableAnnotationHandler>();
+ for (Handler h:_handlers)
+ {
+ if (h instanceof DiscoverableAnnotationHandler)
+ allAnnotationHandlers.add((DiscoverableAnnotationHandler)h);
+ }
+ return allAnnotationHandlers;
}
+ /**
+ * @deprecated see registerHandler(Handler)
+ * @param handler
+ */
public void registerClassHandler (ClassHandler handler)
{
- _classHandlers.add(handler);
+ _handlers.add(handler);
}
+
+
+
+ /**
+ * Add a particular handler
+ *
+ * @param h
+ */
+ public void registerHandler(Handler h)
+ {
+ if (h == null)
+ return;
+
+ _handlers.add(h);
+ }
+
+
+ /**
+ * Add a list of handlers
+ *
+ * @param handlers
+ */
+ public void registerHandlers(List<? extends Handler> handlers)
+ {
+ if (handlers == null)
+ return;
+ _handlers.addAll(handlers);
+ }
+
+
+ /**
+ * Remove a particular handler
+ *
+ * @param h
+ * @return
+ */
+ public boolean deregisterHandler(Handler h)
+ {
+ return _handlers.remove(h);
+ }
+
+
+ /**
+ * Remove all registered handlers
+ */
+ public void clearHandlers()
+ {
+ _handlers.clear();
+ }
+
+ /**
+ * True if the class has already been processed, false otherwise
+ * @param className
+ * @return
+ */
public boolean isParsed (String className)
{
return _parsedClassNames.contains(className);
}
+
+
+ /**
+ * Parse a given class
+ *
+ * @param className
+ * @param resolver
+ * @throws Exception
+ */
public void parse (String className, ClassNameResolver resolver)
throws Exception
{
@@ -467,6 +639,16 @@ public class AnnotationParser
}
}
+
+
+ /**
+ * Parse the given class, optionally walking its inheritance hierarchy
+ *
+ * @param clazz
+ * @param resolver
+ * @param visitSuperClasses
+ * @throws Exception
+ */
public void parse (Class clazz, ClassNameResolver resolver, boolean visitSuperClasses)
throws Exception
{
@@ -493,6 +675,15 @@ public class AnnotationParser
}
}
+
+
+ /**
+ * Parse the given classes
+ *
+ * @param classNames
+ * @param resolver
+ * @throws Exception
+ */
public void parse (String[] classNames, ClassNameResolver resolver)
throws Exception
{
@@ -502,6 +693,14 @@ public class AnnotationParser
parse(Arrays.asList(classNames), resolver);
}
+
+ /**
+ * Parse the given classes
+ *
+ * @param classNames
+ * @param resolver
+ * @throws Exception
+ */
public void parse (List<String> classNames, ClassNameResolver resolver)
throws Exception
{
@@ -520,6 +719,14 @@ public class AnnotationParser
}
}
+
+ /**
+ * Parse all classes in a directory
+ *
+ * @param dir
+ * @param resolver
+ * @throws Exception
+ */
public void parse (Resource dir, ClassNameResolver resolver)
throws Exception
{
@@ -555,8 +762,9 @@ public class AnnotationParser
/**
- * Find annotations on classes in the supplied classloader.
+ * Parse classes in the supplied classloader.
* Only class files in jar files will be scanned.
+ *
* @param loader
* @param visitParents
* @param nullInclusive
@@ -605,7 +813,8 @@ public class AnnotationParser
/**
- * Find annotations in classes in the supplied url of jar files.
+ * Parse classes in the supplied url of jar files.
+ *
* @param uris
* @param resolver
* @throws Exception
@@ -647,6 +856,12 @@ public class AnnotationParser
scanner.scan(null, uris, true);
}
+ /**
+ * Parse a particular resource
+ * @param uri
+ * @param resolver
+ * @throws Exception
+ */
public void parse (URI uri, final ClassNameResolver resolver)
throws Exception
{
@@ -656,6 +871,14 @@ public class AnnotationParser
parse(uris, resolver);
}
+
+
+ /**
+ * Use ASM on a class
+ *
+ * @param is
+ * @throws IOException
+ */
protected void scanClass (InputStream is)
throws IOException
{
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ClassInheritanceHandler.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ClassInheritanceHandler.java
index 79f111a0ab..8242b08d74 100644
--- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ClassInheritanceHandler.java
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ClassInheritanceHandler.java
@@ -35,10 +35,16 @@ public class ClassInheritanceHandler implements ClassHandler
private static final Logger LOG = Log.getLogger(ClassInheritanceHandler.class);
- MultiMap _inheritanceMap = new MultiMap();
+ MultiMap _inheritanceMap;
public ClassInheritanceHandler()
{
+ _inheritanceMap = new MultiMap();
+ }
+
+ public ClassInheritanceHandler(MultiMap map)
+ {
+ _inheritanceMap = map;
}
public void handle(String className, int version, int access, String signature, String superName, String[] interfaces)
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ContainerInitializerAnnotationHandler.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ContainerInitializerAnnotationHandler.java
index 77beb0534d..6a8e7c2dff 100644
--- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ContainerInitializerAnnotationHandler.java
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ContainerInitializerAnnotationHandler.java
@@ -65,4 +65,15 @@ public class ContainerInitializerAnnotationHandler implements DiscoverableAnnota
_initializer.addAnnotatedTypeName(className);
}
+ @Override
+ public String getAnnotationName()
+ {
+ return _annotation.getName();
+ }
+
+ public ContainerInitializer getContainerInitializer()
+ {
+ return _initializer;
+ }
+
}
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ServletContainerInitializerListener.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ServletContainerInitializerListener.java
index e3d90a471a..54d861f405 100644
--- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ServletContainerInitializerListener.java
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ServletContainerInitializerListener.java
@@ -26,6 +26,8 @@ import javax.servlet.ServletContextListener;
import org.eclipse.jetty.plus.annotation.ContainerInitializer;
import org.eclipse.jetty.util.MultiMap;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.webapp.WebAppContext;
/**
@@ -35,7 +37,8 @@ import org.eclipse.jetty.webapp.WebAppContext;
*/
public class ServletContainerInitializerListener implements ServletContextListener
{
- WebAppContext _context = null;
+ private static final Logger LOG = Log.getLogger(ServletContainerInitializerListener.class);
+ protected WebAppContext _context = null;
public void setWebAppContext (WebAppContext context)
@@ -103,14 +106,10 @@ public class ServletContainerInitializerListener implements ServletContextListen
}
catch (Exception e)
{
- //OK, how do I throw an exception such that it really stops the startup sequence?
- e.printStackTrace();
+ LOG.warn(e);
+ throw new RuntimeException(e);
}
}
-
- //Email from Jan Luehe 18 August: after all ServletContainerInitializers have been
- //called, need to check to see if there are any ServletRegistrations remaining
- //that are "preliminary" and fail the deployment if so. Implemented in ServletHolder.doStart().
}
}
@@ -136,7 +135,6 @@ public class ServletContainerInitializerListener implements ServletContextListen
*/
public void contextDestroyed(ServletContextEvent sce)
{
- // TODO Auto-generated method stub
}
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ServletSecurityAnnotationHandler.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ServletSecurityAnnotationHandler.java
index a09579109d..7c09e85f0d 100644
--- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ServletSecurityAnnotationHandler.java
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ServletSecurityAnnotationHandler.java
@@ -20,6 +20,7 @@ package org.eclipse.jetty.annotations;
import java.util.ArrayList;
import java.util.List;
+import javax.servlet.ServletSecurityElement;
import javax.servlet.annotation.HttpConstraint;
import javax.servlet.annotation.HttpMethodConstraint;
import javax.servlet.annotation.ServletSecurity;
@@ -29,11 +30,13 @@ import javax.servlet.annotation.ServletSecurity.TransportGuarantee;
import org.eclipse.jetty.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler;
import org.eclipse.jetty.security.ConstraintAware;
import org.eclipse.jetty.security.ConstraintMapping;
+import org.eclipse.jetty.security.ConstraintSecurityHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.servlet.ServletMapping;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.security.Constraint;
+import org.eclipse.jetty.webapp.Origin;
import org.eclipse.jetty.webapp.WebAppContext;
/**
@@ -80,7 +83,7 @@ public class ServletSecurityAnnotationHandler extends AbstractIntrospectableAnno
if (servletSecurity == null)
return;
- //If there are already constraints defined (ie from web.xml or programmatically(?)) that match any
+ //If there are already constraints defined (ie from web.xml) that match any
//of the url patterns defined for this servlet, then skip the security annotation.
List<ServletMapping> servletMappings = getServletMappings(clazz.getCanonicalName());
@@ -95,19 +98,15 @@ public class ServletSecurityAnnotationHandler extends AbstractIntrospectableAnno
//Make a fresh list
constraintMappings = new ArrayList<ConstraintMapping>();
- //Get the values that form the constraints that will apply unless there are HttpMethodConstraints to augment them
- HttpConstraint defaults = servletSecurity.value();
-
- //Make a Constraint for the <auth-constraint> and <user-data-constraint> specified by the HttpConstraint
- Constraint defaultConstraint = makeConstraint (clazz,
- defaults.rolesAllowed(),
- defaults.value(),
- defaults.transportGuarantee());
-
- constraintMappings.addAll(makeMethodMappings(clazz,
- defaultConstraint,
- servletMappings,
- servletSecurity.httpMethodConstraints()));
+ ServletSecurityElement securityElement = new ServletSecurityElement(servletSecurity);
+ for (ServletMapping sm : servletMappings)
+ {
+ for (String url : sm.getPathSpecs())
+ {
+ _context.getMetaData().setOrigin("constraint.url."+url, Origin.Annotation);
+ constraintMappings.addAll(ConstraintSecurityHandler.createConstraintsWithMappingsForPath(clazz.getName(), url, securityElement));
+ }
+ }
//set up the security constraints produced by the annotation
ConstraintAware securityHandler = (ConstraintAware)_context.getSecurityHandler();
@@ -129,108 +128,13 @@ public class ServletSecurityAnnotationHandler extends AbstractIntrospectableAnno
*/
protected Constraint makeConstraint (Class servlet, String[] rolesAllowed, EmptyRoleSemantic permitOrDeny, TransportGuarantee transport)
{
- Constraint constraint = new Constraint();
- if (rolesAllowed == null || rolesAllowed.length==0)
- {
- if (permitOrDeny.equals(EmptyRoleSemantic.DENY))
- {
- //Equivalent to <auth-constraint> with no roles
- constraint.setName(servlet.getName()+"-Deny");
- constraint.setAuthenticate(true);
- }
- else
- {
- //Equivalent to no <auth-constraint>
- constraint.setAuthenticate(false);
- constraint.setName(servlet.getName()+"-Permit");
- }
- }
- else
- {
- //Equivalent to <auth-constraint> with list of <security-role-name>s
- constraint.setAuthenticate(true);
- constraint.setRoles(rolesAllowed);
- constraint.setName(servlet.getName()+"-RolesAllowed");
- }
+ return ConstraintSecurityHandler.createConstraint(servlet.getName(), rolesAllowed, permitOrDeny, transport);
+
- //Equivalent to //<user-data-constraint><transport-guarantee>CONFIDENTIAL</transport-guarantee></user-data-constraint>
- constraint.setDataConstraint((transport.equals(TransportGuarantee.CONFIDENTIAL)?Constraint.DC_CONFIDENTIAL:Constraint.DC_NONE));
- return constraint;
- }
- /**
- * Make a ConstraintMapping which captures the <http-method> or <http-method-omission> elements for a particular url pattern,
- * and relates it to a Constraint object (<auth-constraint> and <user-data-constraint>).
- * @param constraint
- * @param url
- * @param method
- * @param omissions
- * @return
- */
- protected ConstraintMapping makeConstraintMapping (Constraint constraint, String url, String method, String[] omissions)
- {
- ConstraintMapping mapping = new ConstraintMapping();
- mapping.setConstraint(constraint);
- mapping.setPathSpec(url);
- if (method != null)
- mapping.setMethod(method);
- if (omissions != null)
- mapping.setMethodOmissions(omissions);
- return mapping;
- }
- /**
- * Make the Jetty Constraints and ConstraintMapping objects that correspond to the HttpMethodConstraint
- * annotations for each url pattern for the servlet.
- * @param servlet
- * @param defaultConstraint
- * @param servletMappings
- * @param annotations
- * @return
- */
- protected List<ConstraintMapping> makeMethodMappings (Class servlet, Constraint defaultConstraint, List<ServletMapping> servletMappings, HttpMethodConstraint[] annotations)
- {
- List<ConstraintMapping> mappings = new ArrayList<ConstraintMapping>();
- //for each url-pattern existing for the servlet make a ConstraintMapping for the HttpConstraint, and ConstraintMappings for
- //each HttpMethodConstraint
- for (ServletMapping sm : servletMappings)
- {
- for (String url : sm.getPathSpecs())
- {
- //Make a ConstraintMapping that matches the defaultConstraint
- ConstraintMapping defaultMapping = makeConstraintMapping(defaultConstraint, url, null, null);
-
- //If there are HttpMethodConstraint annotations, make a Constraint and a ConstraintMapping for it
- if (annotations != null && annotations.length>0)
- {
- List<String> omissions = new ArrayList<String>();
-
- //for each HttpMethodConstraint annotation, make a new Constraint and ConstraintMappings for this url
- for (int i=0; i < annotations.length;i++)
- {
- //Make a Constraint that captures the <auth-constraint> and <user-data-constraint> elements
- Constraint methodConstraint = makeConstraint(servlet,
- annotations[i].rolesAllowed(),
- annotations[i].emptyRoleSemantic(),
- annotations[i].transportGuarantee());
-
- //Make ConstraintMapping that captures the <http-method> elements
- ConstraintMapping methodConstraintMapping = makeConstraintMapping (methodConstraint,
- url,annotations[i].value(),
- null);
- mappings.add(methodConstraintMapping);
- omissions.add(annotations[i].value());
- }
- defaultMapping.setMethodOmissions(omissions.toArray(new String[0]));
- }
-
- //add the constraint mapping containing the http-method-omissions, if there are any
- mappings.add(defaultMapping);
- }
- }
- return mappings;
}
@@ -282,6 +186,7 @@ public class ServletSecurityAnnotationHandler extends AbstractIntrospectableAnno
{
for (int j=0; j < pathSpecs.length; j++)
{
+ //TODO decide if we need to check the origin
if (pathSpecs[j].equals(constraintMappings.get(i).getPathSpec()))
{
exists = true;
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebFilterAnnotation.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebFilterAnnotation.java
index 2eb1844757..176b4d0368 100644
--- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebFilterAnnotation.java
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebFilterAnnotation.java
@@ -30,6 +30,7 @@ import org.eclipse.jetty.servlet.FilterMapping;
import org.eclipse.jetty.servlet.Holder;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.webapp.DiscoveredAnnotation;
import org.eclipse.jetty.webapp.MetaData;
import org.eclipse.jetty.webapp.Origin;
@@ -52,6 +53,11 @@ public class WebFilterAnnotation extends DiscoveredAnnotation
{
super(context, className);
}
+
+ public WebFilterAnnotation(WebAppContext context, String className, Resource resource)
+ {
+ super(context, className, resource);
+ }
/**
* @see org.eclipse.jetty.annotations.ClassAnnotation#apply()
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebFilterAnnotationHandler.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebFilterAnnotationHandler.java
index 11f16ed0ea..88403dbe97 100644
--- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebFilterAnnotationHandler.java
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebFilterAnnotationHandler.java
@@ -23,6 +23,7 @@ import java.util.List;
import org.eclipse.jetty.annotations.AnnotationParser.Value;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.webapp.DiscoveredAnnotation;
import org.eclipse.jetty.webapp.WebAppContext;
/**
@@ -38,24 +39,38 @@ public class WebFilterAnnotationHandler extends AbstractDiscoverableAnnotationHa
{
super(context);
}
-
+
+ public WebFilterAnnotationHandler (WebAppContext context, List<DiscoveredAnnotation> list)
+ {
+ super(context, list);
+ }
+
+ @Override
public void handleClass(String className, int version, int access, String signature, String superName, String[] interfaces, String annotation,
List<Value> values)
{
- WebFilterAnnotation wfAnnotation = new WebFilterAnnotation(_context, className);
+ WebFilterAnnotation wfAnnotation = new WebFilterAnnotation(_context, className, _resource);
addAnnotation(wfAnnotation);
}
+ @Override
public void handleField(String className, String fieldName, int access, String fieldType, String signature, Object value, String annotation,
List<Value> values)
{
LOG.warn ("@WebFilter not applicable for fields: "+className+"."+fieldName);
}
+ @Override
public void handleMethod(String className, String methodName, int access, String params, String signature, String[] exceptions, String annotation,
List<Value> values)
{
LOG.warn ("@WebFilter not applicable for methods: "+className+"."+methodName+" "+signature);
}
+ @Override
+ public String getAnnotationName()
+ {
+ return "javax.servlet.annotation.WebFilter";
+ }
+
}
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebListenerAnnotation.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebListenerAnnotation.java
index 905d43570f..0efcb2078b 100644
--- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebListenerAnnotation.java
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebListenerAnnotation.java
@@ -27,6 +27,7 @@ import javax.servlet.http.HttpSessionListener;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.webapp.DiscoveredAnnotation;
import org.eclipse.jetty.webapp.MetaData;
import org.eclipse.jetty.webapp.Origin;
@@ -49,6 +50,11 @@ public class WebListenerAnnotation extends DiscoveredAnnotation
{
super(context, className);
}
+
+ public WebListenerAnnotation(WebAppContext context, String className, Resource resource)
+ {
+ super(context, className, resource);
+ }
/**
* @see org.eclipse.jetty.annotations.ClassAnnotation#apply()
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebListenerAnnotationHandler.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebListenerAnnotationHandler.java
index 91fdd9d471..2dd1c16bd1 100644
--- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebListenerAnnotationHandler.java
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebListenerAnnotationHandler.java
@@ -23,6 +23,7 @@ import java.util.List;
import org.eclipse.jetty.annotations.AnnotationParser.Value;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.webapp.DiscoveredAnnotation;
import org.eclipse.jetty.webapp.WebAppContext;
public class WebListenerAnnotationHandler extends AbstractDiscoverableAnnotationHandler
@@ -34,13 +35,18 @@ public class WebListenerAnnotationHandler extends AbstractDiscoverableAnnotation
super(context);
}
+ public WebListenerAnnotationHandler (WebAppContext context, List<DiscoveredAnnotation> list)
+ {
+ super(context, list);
+ }
+
/**
* @see org.eclipse.jetty.annotations.AnnotationParser.DiscoverableAnnotationHandler#handleClass(java.lang.String, int, int, java.lang.String, java.lang.String, java.lang.String[], java.lang.String, java.util.List)
*/
public void handleClass(String className, int version, int access, String signature, String superName, String[] interfaces, String annotation,
List<Value> values)
{
- WebListenerAnnotation wlAnnotation = new WebListenerAnnotation(_context, className);
+ WebListenerAnnotation wlAnnotation = new WebListenerAnnotation(_context, className, _resource);
addAnnotation(wlAnnotation);
}
@@ -56,4 +62,10 @@ public class WebListenerAnnotationHandler extends AbstractDiscoverableAnnotation
LOG.warn ("@WebListener is not applicable to methods: "+className+"."+methodName+" "+signature);
}
+ @Override
+ public String getAnnotationName()
+ {
+ return "javax.servlet.annotation.WebListener";
+ }
+
}
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebServletAnnotation.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebServletAnnotation.java
index 352eb5ec17..88cdfc12d2 100644
--- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebServletAnnotation.java
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebServletAnnotation.java
@@ -29,6 +29,7 @@ import org.eclipse.jetty.servlet.ServletMapping;
import org.eclipse.jetty.util.LazyList;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.webapp.DiscoveredAnnotation;
import org.eclipse.jetty.webapp.MetaData;
import org.eclipse.jetty.webapp.Origin;
@@ -47,7 +48,13 @@ public class WebServletAnnotation extends DiscoveredAnnotation
{
super(context, className);
}
-
+
+
+ public WebServletAnnotation (WebAppContext context, String className, Resource resource)
+ {
+ super(context, className, resource);
+ }
+
/**
* @see org.eclipse.jetty.annotations.ClassAnnotation#apply()
*/
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebServletAnnotationHandler.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebServletAnnotationHandler.java
index c9c31cb45e..2a9019f641 100644
--- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebServletAnnotationHandler.java
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebServletAnnotationHandler.java
@@ -23,6 +23,7 @@ import java.util.List;
import org.eclipse.jetty.annotations.AnnotationParser.Value;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.webapp.DiscoveredAnnotation;
import org.eclipse.jetty.webapp.WebAppContext;
/**
@@ -40,6 +41,11 @@ public class WebServletAnnotationHandler extends AbstractDiscoverableAnnotationH
super(context);
}
+ public WebServletAnnotationHandler (WebAppContext context, List<DiscoveredAnnotation> list)
+ {
+ super(context, list);
+ }
+
/**
* Handle discovering a WebServlet annotation.
@@ -47,25 +53,35 @@ public class WebServletAnnotationHandler extends AbstractDiscoverableAnnotationH
*
* @see org.eclipse.jetty.annotations.AnnotationParser.DiscoverableAnnotationHandler#handleClass(java.lang.String, int, int, java.lang.String, java.lang.String, java.lang.String[], java.lang.String, java.util.List)
*/
+ @Override
public void handleClass(String className, int version, int access, String signature, String superName, String[] interfaces, String annotationName,
List<Value> values)
{
if (!"javax.servlet.annotation.WebServlet".equals(annotationName))
return;
- WebServletAnnotation annotation = new WebServletAnnotation (_context, className);
+ WebServletAnnotation annotation = new WebServletAnnotation (_context, className, _resource);
addAnnotation(annotation);
}
+ @Override
public void handleField(String className, String fieldName, int access, String fieldType, String signature, Object value, String annotation,
List<Value> values)
{
LOG.warn ("@WebServlet annotation not supported for fields");
}
+ @Override
public void handleMethod(String className, String methodName, int access, String params, String signature, String[] exceptions, String annotation,
List<Value> values)
{
LOG.warn ("@WebServlet annotation not supported for methods");
}
+
+
+ @Override
+ public String getAnnotationName()
+ {
+ return "javax.servlet.annotation.WebServlet";
+ }
}
diff --git a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationInheritance.java b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationInheritance.java
index cd8c4b791b..4deb5408f5 100644
--- a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationInheritance.java
+++ b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationInheritance.java
@@ -66,6 +66,12 @@ public class TestAnnotationInheritance
{
annotatedMethods.add(className+"."+methodName);
}
+
+ @Override
+ public String getAnnotationName()
+ {
+ return "org.eclipse.jetty.annotations.Sample";
+ }
}
@After
@@ -85,7 +91,7 @@ public class TestAnnotationInheritance
SampleHandler handler = new SampleHandler();
AnnotationParser parser = new AnnotationParser();
- parser.registerAnnotationHandler("org.eclipse.jetty.annotations.Sample", handler);
+ parser.registerHandler(handler);
parser.parse(classNames, new ClassNameResolver ()
{
public boolean isExcluded(String name)
diff --git a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationParser.java b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationParser.java
index ec74005170..ec00c9b905 100644
--- a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationParser.java
+++ b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationParser.java
@@ -41,6 +41,9 @@ public class TestAnnotationParser
{
private List<String> methods = Arrays.asList("a", "b", "c", "d", "l");
+
+
+
public void handleClass(String className, int version, int access, String signature, String superName, String[] interfaces, String annotation,
List<Value> values)
{
@@ -81,9 +84,15 @@ public class TestAnnotationParser
assertTrue(methods.contains(methodName));
assertEquals("org.eclipse.jetty.annotations.Sample", annotation);
}
+
+ @Override
+ public String getAnnotationName()
+ {
+ return "org.eclipse.jetty.annotations.Sample";
+ }
}
- parser.registerAnnotationHandler("org.eclipse.jetty.annotations.Sample", new SampleAnnotationHandler());
+ parser.registerHandler(new SampleAnnotationHandler());
long start = System.currentTimeMillis();
parser.parse(classNames, new ClassNameResolver ()
@@ -140,9 +149,17 @@ public class TestAnnotationParser
System.err.println(anv.toString());
}
}
+
+ @Override
+ public String getAnnotationName()
+ {
+ return "org.eclipse.jetty.annotations.Multi";
+ }
+
+
}
- parser.registerAnnotationHandler("org.eclipse.jetty.annotations.Multi", new MultiAnnotationHandler());
+ parser.registerHandler(new MultiAnnotationHandler());
parser.parse(classNames, null);
}
}
diff --git a/jetty-client-old/src/main/java/org/eclipse/jetty/client/AbstractHttpConnection.java b/jetty-client-old/src/main/java/org/eclipse/jetty/client/AbstractHttpConnection.java
index 6bba537b2e..83960b87a3 100644
--- a/jetty-client-old/src/main/java/org/eclipse/jetty/client/AbstractHttpConnection.java
+++ b/jetty-client-old/src/main/java/org/eclipse/jetty/client/AbstractHttpConnection.java
@@ -172,6 +172,9 @@ public abstract class AbstractHttpConnection extends AbstractConnection implemen
{
}
+ /**
+ * @throws IOException
+ */
protected void commitRequest() throws IOException
{
synchronized (this)
@@ -223,6 +226,7 @@ public abstract class AbstractHttpConnection extends AbstractConnection implemen
requestHeaders.putLongField(HttpHeader.CONTENT_LENGTH, requestContent.length());
_generator.completeHeader(requestHeaders,false);
_generator.addContent(new View(requestContent),true);
+ _exchange.setStatus(HttpExchange.STATUS_WAITING_FOR_RESPONSE);
}
else
{
@@ -230,24 +234,14 @@ public abstract class AbstractHttpConnection extends AbstractConnection implemen
if (requestContentStream != null)
{
_generator.completeHeader(requestHeaders, false);
- int available = requestContentStream.available();
- if (available > 0)
- {
- // TODO deal with any known content length
- // TODO reuse this buffer!
- byte[] buf = new byte[available];
- int length = requestContentStream.read(buf);
- _generator.addContent(new ByteArrayBuffer(buf, 0, length), false);
- }
}
else
{
requestHeaders.remove(HttpHeader.CONTENT_LENGTH);
_generator.completeHeader(requestHeaders, true);
+ _exchange.setStatus(HttpExchange.STATUS_WAITING_FOR_RESPONSE);
}
}
-
- _exchange.setStatus(HttpExchange.STATUS_WAITING_FOR_RESPONSE);
}
}
diff --git a/jetty-client-old/src/main/java/org/eclipse/jetty/client/AsyncHttpConnection.java b/jetty-client-old/src/main/java/org/eclipse/jetty/client/AsyncHttpConnection.java
index 388bf399d4..8c95ceab57 100644
--- a/jetty-client-old/src/main/java/org/eclipse/jetty/client/AsyncHttpConnection.java
+++ b/jetty-client-old/src/main/java/org/eclipse/jetty/client/AsyncHttpConnection.java
@@ -114,6 +114,8 @@ public class AsyncHttpConnection extends AbstractHttpConnection implements Async
ByteBuffer chunk=_requestContentChunk;
_requestContentChunk=exchange.getRequestContentChunk(null);
_generator.addContent(chunk,_requestContentChunk==null);
+ if (_requestContentChunk==null)
+ exchange.setStatus(HttpExchange.STATUS_WAITING_FOR_RESPONSE);
}
}
}
@@ -208,12 +210,12 @@ public class AsyncHttpConnection extends AbstractHttpConnection implements Async
{
Connection switched=exchange.onSwitchProtocol(_endp);
if (switched!=null)
- connection=switched;
{
// switched protocol!
- _pipeline = null;
if (_pipeline!=null)
+ {
_destination.send(_pipeline);
+ }
_pipeline = null;
connection=switched;
diff --git a/jetty-client-old/src/main/java/org/eclipse/jetty/client/BlockingHttpConnection.java b/jetty-client-old/src/main/java/org/eclipse/jetty/client/BlockingHttpConnection.java
index 99db6bfcf1..22f61346ef 100644
--- a/jetty-client-old/src/main/java/org/eclipse/jetty/client/BlockingHttpConnection.java
+++ b/jetty-client-old/src/main/java/org/eclipse/jetty/client/BlockingHttpConnection.java
@@ -122,6 +122,8 @@ public class BlockingHttpConnection extends AbstractHttpConnection
ByteBuffer chunk=_requestContentChunk;
_requestContentChunk=exchange.getRequestContentChunk(null);
_generator.addContent(chunk,_requestContentChunk==null);
+ if (_requestContentChunk==null)
+ exchange.setStatus(HttpExchange.STATUS_WAITING_FOR_RESPONSE);
}
}
}
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java
index 3200d0edb3..d221c1d3de 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java
@@ -792,12 +792,16 @@ public class HttpFields implements Iterable<HttpFields.Field>
QuotedStringTokenizer.quoteIfNeeded(buf, name, delim);
buf.append('=');
String start=buf.toString();
+ boolean hasDomain = false;
+ boolean hasPath = false;
+
if (value != null && value.length() > 0)
QuotedStringTokenizer.quoteIfNeeded(buf, value, delim);
if (path != null && path.length() > 0)
{
+ hasPath = true;
buf.append(";Path=");
if (path.trim().startsWith("\""))
buf.append(path);
@@ -806,6 +810,7 @@ public class HttpFields implements Iterable<HttpFields.Field>
}
if (domain != null && domain.length() > 0)
{
+ hasDomain = true;
buf.append(";Domain=");
QuotedStringTokenizer.quoteIfNeeded(buf,domain.toLowerCase(),delim);
}
@@ -841,14 +846,20 @@ public class HttpFields implements Iterable<HttpFields.Field>
Field last=null;
while (field!=null)
{
- if (field._value!=null && field._value.toString().startsWith(start))
+ String val = (field._value == null ? null : field._value.toString());
+ if (val!=null && val.startsWith(start))
{
- _fields.remove(field);
- if (last==null)
- _names.put(HttpHeader.SET_COOKIE.toString(),field._next);
- else
- last._next=field._next;
- break;
+ //existing cookie has same name, does it also match domain and path?
+ if (((!hasDomain && !val.contains("Domain")) || (hasDomain && val.contains("Domain="+domain))) &&
+ ((!hasPath && !val.contains("Path")) || (hasPath && val.contains("Path="+path))))
+ {
+ _fields.remove(field);
+ if (last==null)
+ _names.put(HttpHeader.SET_COOKIE.toString(),field._next);
+ else
+ last._next=field._next;
+ break;
+ }
}
last=field;
field=field._next;
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java
index 63e1478b41..963ec23718 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java
@@ -100,7 +100,15 @@ public class HttpURI
public HttpURI(String raw)
{
_rawString=raw;
- byte[] b = raw.getBytes();
+ byte[] b;
+ try
+ {
+ b = raw.getBytes(StringUtil.__UTF8);
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ throw new RuntimeException(e.getMessage());
+ }
parse(b,0,b.length);
_charset = URIUtil.__CHARSET;
}
@@ -693,20 +701,22 @@ public class HttpURI
{
if (_query==_fragment)
return;
- UrlEncoded.decodeUtf8To(_raw,_query+1,_fragment-_query-1,parameters);
+ if (_charset==StringUtil.__UTF8_CHARSET)
+ UrlEncoded.decodeUtf8To(_raw,_query+1,_fragment-_query-1,parameters);
+ else
+ UrlEncoded.decodeTo(StringUtil.toString(_raw,_query+1,_fragment-_query-1,_charset.toString()),parameters,_charset.toString());
}
- public void decodeQueryTo(MultiMap parameters, String encoding)
- throws UnsupportedEncodingException
- {
+ public void decodeQueryTo(MultiMap parameters, String encoding) throws UnsupportedEncodingException
+ {
if (_query==_fragment)
return;
if (encoding==null || StringUtil.isUTF8(encoding))
UrlEncoded.decodeUtf8To(_raw,_query+1,_fragment-_query-1,parameters);
else
- UrlEncoded.decodeTo(new String(_raw,_query+1,_fragment-_query-1,_charset),parameters,encoding);
- }
+ UrlEncoded.decodeTo(StringUtil.toString(_raw,_query+1,_fragment-_query-1,encoding),parameters,encoding);
+ }
public void clear()
{
diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldsTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldsTest.java
index 89f5bd4c29..3eba7713e9 100644
--- a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldsTest.java
+++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldsTest.java
@@ -277,7 +277,8 @@ public class HttpFieldsTest
assertEquals("minimal=value",fields.getStringField("Set-Cookie"));
fields.clear();
- fields.addSetCookie("everything","wrong","wrong","wrong",0,"to be replaced",true,true,0);
+ //test cookies with same name, domain and path, only 1 allowed
+ fields.addSetCookie("everything","wrong","domain","path",0,"to be replaced",true,true,0);
fields.addSetCookie("everything","value","domain","path",0,"comment",true,true,0);
assertEquals("everything=value;Path=path;Domain=domain;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",fields.getStringField("Set-Cookie"));
Enumeration<String> e =fields.getValues("Set-Cookie");
@@ -285,7 +286,61 @@ public class HttpFieldsTest
assertEquals("everything=value;Path=path;Domain=domain;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement());
assertFalse(e.hasMoreElements());
assertEquals("Thu, 01 Jan 1970 00:00:00 GMT",fields.getStringField("Expires"));
-
+ assertFalse(e.hasMoreElements());
+
+ //test cookies with same name, different domain
+ fields.clear();
+ fields.addSetCookie("everything","other","domain1","path",0,"blah",true,true,0);
+ fields.addSetCookie("everything","value","domain2","path",0,"comment",true,true,0);
+ e =fields.getValues("Set-Cookie");
+ assertTrue(e.hasMoreElements());
+ assertEquals("everything=other;Path=path;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=blah",e.nextElement());
+ assertTrue(e.hasMoreElements());
+ assertEquals("everything=value;Path=path;Domain=domain2;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement());
+ assertFalse(e.hasMoreElements());
+
+ //test cookies with same name, same path, one with domain, one without
+ fields.clear();
+ fields.addSetCookie("everything","other","domain1","path",0,"blah",true,true,0);
+ fields.addSetCookie("everything","value","","path",0,"comment",true,true,0);
+ e =fields.getValues("Set-Cookie");
+ assertTrue(e.hasMoreElements());
+ assertEquals("everything=other;Path=path;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=blah",e.nextElement());
+ assertTrue(e.hasMoreElements());
+ assertEquals("everything=value;Path=path;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement());
+ assertFalse(e.hasMoreElements());
+
+
+ //test cookies with same name, different path
+ fields.clear();
+ fields.addSetCookie("everything","other","domain1","path1",0,"blah",true,true,0);
+ fields.addSetCookie("everything","value","domain1","path2",0,"comment",true,true,0);
+ e =fields.getValues("Set-Cookie");
+ assertTrue(e.hasMoreElements());
+ assertEquals("everything=other;Path=path1;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=blah",e.nextElement());
+ assertTrue(e.hasMoreElements());
+ assertEquals("everything=value;Path=path2;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement());
+ assertFalse(e.hasMoreElements());
+
+ //test cookies with same name, same domain, one with path, one without
+ fields.clear();
+ fields.addSetCookie("everything","other","domain1","path1",0,"blah",true,true,0);
+ fields.addSetCookie("everything","value","domain1","",0,"comment",true,true,0);
+ e =fields.getValues("Set-Cookie");
+ assertTrue(e.hasMoreElements());
+ assertEquals("everything=other;Path=path1;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=blah",e.nextElement());
+ assertTrue(e.hasMoreElements());
+ assertEquals("everything=value;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement());
+ assertFalse(e.hasMoreElements());
+
+ //test cookies same name only, no path, no domain
+ fields.clear();
+ fields.addSetCookie("everything","other","","",0,"blah",true,true,0);
+ fields.addSetCookie("everything","value","","",0,"comment",true,true,0);
+ e =fields.getValues("Set-Cookie");
+ assertTrue(e.hasMoreElements());
+ assertEquals("everything=value;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement());
+ assertFalse(e.hasMoreElements());
fields.clear();
fields.addSetCookie("ev erything","va lue","do main","pa th",1,"co mment",true,true,2);
diff --git a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/JaspiAuthenticator.java b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/JaspiAuthenticator.java
index c69e190416..c93bafa770 100644
--- a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/JaspiAuthenticator.java
+++ b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/JaspiAuthenticator.java
@@ -37,6 +37,7 @@ import org.eclipse.jetty.security.IdentityService;
import org.eclipse.jetty.security.ServerAuthException;
import org.eclipse.jetty.security.UserAuthentication;
import org.eclipse.jetty.security.authentication.DeferredAuthentication;
+import org.eclipse.jetty.security.authentication.LoginAuthenticator;
import org.eclipse.jetty.server.Authentication;
import org.eclipse.jetty.server.UserIdentity;
import org.eclipse.jetty.server.Authentication.User;
@@ -44,7 +45,7 @@ import org.eclipse.jetty.server.Authentication.User;
/**
* @version $Rev: 4793 $ $Date: 2009-03-19 00:00:01 +0100 (Thu, 19 Mar 2009) $
*/
-public class JaspiAuthenticator implements Authenticator
+public class JaspiAuthenticator extends LoginAuthenticator
{
private final ServerAuthConfig _authConfig;
@@ -58,7 +59,7 @@ public class JaspiAuthenticator implements Authenticator
private final IdentityService _identityService;
- private final DeferredAuthentication _deferred;
+
public JaspiAuthenticator(ServerAuthConfig authConfig, Map authProperties, ServletCallbackHandler callbackHandler, Subject serviceSubject,
boolean allowLazyAuthentication, IdentityService identityService)
@@ -72,11 +73,11 @@ public class JaspiAuthenticator implements Authenticator
this._serviceSubject = serviceSubject;
this._allowLazyAuthentication = allowLazyAuthentication;
this._identityService = identityService;
- this._deferred = new DeferredAuthentication(this);
}
public void setConfiguration(AuthConfiguration configuration)
{
+ super.setConfiguration(configuration);
}
public String getAuthMethod()
@@ -93,7 +94,7 @@ public class JaspiAuthenticator implements Authenticator
//if its not mandatory to authenticate, and the authenticator returned UNAUTHENTICATED, we treat it as authentication deferred
if (_allowLazyAuthentication && !info.isAuthMandatory() && a == Authentication.UNAUTHENTICATED)
- a =_deferred;
+ a = new DeferredAuthentication(this);
return a;
}
diff --git a/jetty-jndi/src/main/java/org/eclipse/jetty/jndi/ContextFactory.java b/jetty-jndi/src/main/java/org/eclipse/jetty/jndi/ContextFactory.java
index 68452d755a..5b54fa1f76 100644
--- a/jetty-jndi/src/main/java/org/eclipse/jetty/jndi/ContextFactory.java
+++ b/jetty-jndi/src/main/java/org/eclipse/jetty/jndi/ContextFactory.java
@@ -97,25 +97,15 @@ public class ContextFactory implements ObjectFactory
return ctx;
}
- // Next, see if we are in a webapp context, if we are, use
- // the classloader of the webapp to find the right jndi comp context
ClassLoader loader = null;
- if (ContextHandler.getCurrentContext() != null)
- {
- loader = ContextHandler.getCurrentContext().getContextHandler().getClassLoader();
- }
+ loader = Thread.currentThread().getContextClassLoader();
+ if (__log.isDebugEnabled() && loader != null) __log.debug("Using thread context classloader");
- if (loader != null)
+ if (loader == null && ContextHandler.getCurrentContext() != null)
{
- if (__log.isDebugEnabled()) __log.debug("Using classloader of current org.eclipse.jetty.server.handler.ContextHandler");
- }
- else
- {
- //Not already in a webapp context, in that case, we try the
- //curren't thread's classloader instead
- loader = Thread.currentThread().getContextClassLoader();
- if (__log.isDebugEnabled()) __log.debug("Using thread context classloader");
+ loader = ContextHandler.getCurrentContext().getContextHandler().getClassLoader();
+ if (__log.isDebugEnabled() && loader != null) __log.debug("Using classloader of current org.eclipse.jetty.server.handler.ContextHandler");
}
//Get the context matching the classloader
@@ -124,47 +114,24 @@ public class ContextFactory implements ObjectFactory
//The map does not contain an entry for this classloader
if (ctx == null)
{
- //Check if a parent classloader has created the context
- ctx = getParentClassLoaderContext(loader);
-
- //Didn't find a context to match any of the ancestors
- //of the classloader, so make a context
- if (ctx == null)
- {
- Reference ref = (Reference)obj;
- StringRefAddr parserAddr = (StringRefAddr)ref.get("parser");
- String parserClassName = (parserAddr==null?null:(String)parserAddr.getContent());
- NameParser parser = (NameParser)(parserClassName==null?null:loader.loadClass(parserClassName).newInstance());
-
- ctx = new NamingContext (env,
- name.get(0),
- (NamingContext)nameCtx,
- parser);
- if(__log.isDebugEnabled())__log.debug("No entry for classloader: "+loader);
- __contextMap.put (loader, ctx);
- }
+ //Didn't find a context to match, make one
+ Reference ref = (Reference)obj;
+ StringRefAddr parserAddr = (StringRefAddr)ref.get("parser");
+ String parserClassName = (parserAddr==null?null:(String)parserAddr.getContent());
+ NameParser parser = (NameParser)(parserClassName==null?null:loader.loadClass(parserClassName).newInstance());
+
+ ctx = new NamingContext (env,
+ name.get(0),
+ (NamingContext)nameCtx,
+ parser);
+ if(__log.isDebugEnabled())__log.debug("Made context "+name.get(0)+" for classloader: "+loader);
+ __contextMap.put (loader, ctx);
}
return ctx;
}
- /**
- * Keep trying ancestors of the given classloader to find one to which
- * the context is bound.
- * @param loader
- * @return the context from the parent class loader
- */
- public Context getParentClassLoaderContext (ClassLoader loader)
- {
- Context ctx = null;
- ClassLoader cl = loader;
- for (cl = cl.getParent(); (cl != null) && (ctx == null); cl = cl.getParent())
- {
- ctx = (Context)__contextMap.get(cl);
- }
-
- return ctx;
- }
+
/**
diff --git a/jetty-jndi/src/test/java/org/eclipse/jetty/jndi/java/TestJNDI.java b/jetty-jndi/src/test/java/org/eclipse/jetty/jndi/java/TestJNDI.java
index 405c188d52..9b7ee67606 100644
--- a/jetty-jndi/src/test/java/org/eclipse/jetty/jndi/java/TestJNDI.java
+++ b/jetty-jndi/src/test/java/org/eclipse/jetty/jndi/java/TestJNDI.java
@@ -35,6 +35,7 @@ import javax.naming.Reference;
import javax.naming.StringRefAddr;
import javax.naming.spi.ObjectFactory;
+import org.eclipse.jetty.jndi.ContextFactory;
import org.eclipse.jetty.jndi.NamingContext;
import org.eclipse.jetty.jndi.local.localContextRoot;
import org.eclipse.jetty.util.log.Log;
@@ -44,7 +45,8 @@ import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
/**
*
*/
@@ -71,14 +73,16 @@ public class TestJNDI
@Test
public void testIt() throws Exception
{
+ //set up some classloaders
+ Thread currentThread = Thread.currentThread();
+ ClassLoader currentLoader = currentThread.getContextClassLoader();
+ ClassLoader childLoader1 = new URLClassLoader(new URL[0], currentLoader);
+ ClassLoader childLoader2 = new URLClassLoader(new URL[0], currentLoader);
+
try
{
- //set up some classloaders
- Thread currentThread = Thread.currentThread();
- ClassLoader currentLoader = currentThread.getContextClassLoader();
- ClassLoader childLoader1 = new URLClassLoader(new URL[0], currentLoader);
- ClassLoader childLoader2 = new URLClassLoader(new URL[0], currentLoader);
+ //Uncomment to aid with debug
/*
javaRootURLContext.getRoot().addListener(new NamingContext.Listener()
{
@@ -116,7 +120,19 @@ public class TestJNDI
InitialContext initCtxA = new InitialContext();
initCtxA.bind ("blah", "123");
assertEquals ("123", initCtxA.lookup("blah"));
-
+
+ initCtxA.destroySubcontext("blah");
+ try
+ {
+ initCtxA.lookup("blah");
+ fail("context blah was not destroyed");
+ }
+ catch (NameNotFoundException e)
+ {
+ //expected
+ }
+
+
InitialContext initCtx = new InitialContext();
Context sub0 = (Context)initCtx.lookup("java:");
@@ -216,6 +232,7 @@ public class TestJNDI
try
{
initCtx.lookup("java:comp/env/rubbish");
+ fail("env should not exist for this classloader");
}
catch (NameNotFoundException e)
{
@@ -284,18 +301,78 @@ public class TestJNDI
{
//expected failure to modify immutable context
}
-
- System.err.println("java:"+javaRootURLContext.getRoot().dump());
- System.err.println("local:"+localContextRoot.getRoot().dump());
+
//test what happens when you close an initial context that was used
initCtx.close();
}
finally
{
+ //make some effort to clean up
InitialContext ic = new InitialContext();
+ Context java = (Context)ic.lookup("java:");
+ java.destroySubcontext("zero");
+ java.destroySubcontext("fee");
+ currentThread.setContextClassLoader(childLoader1);
Context comp = (Context)ic.lookup("java:comp");
comp.destroySubcontext("env");
+ comp.unbind("crud");
+ comp.unbind("crud2");
+ }
+ }
+
+
+ @Test
+ public void testParent()
+ throws Exception
+ {
+ //set up some classloaders
+ Thread currentThread = Thread.currentThread();
+ ClassLoader parentLoader = currentThread.getContextClassLoader();
+ ClassLoader childLoader1 = new URLClassLoader(new URL[0], parentLoader);
+
+ try
+ {
+ //Test creating a comp for the parent loader does not leak to child
+ InitialContext initCtx = new InitialContext();
+ Context comp = (Context)initCtx.lookup("java:comp");
+ assertNotNull(comp);
+
+ Context env = (Context)comp.createSubcontext("env");
+ assertNotNull(env);
+
+ env.bind("foo", "aaabbbcccddd");
+ assertEquals("aaabbbcccddd", (String)initCtx.lookup("java:comp/env/foo"));
+
+ //Change to child loader
+ currentThread.setContextClassLoader(childLoader1);
+ comp = (Context)initCtx.lookup("java:comp");
+
+ Context childEnv = (Context)comp.createSubcontext("env");
+ assertNotSame(env, childEnv);
+
+ childEnv.bind("foo", "eeefffggghhh");
+ assertEquals("eeefffggghhh", (String)initCtx.lookup("java:comp/env/foo"));
+
+ //Change back to parent
+ currentThread.setContextClassLoader(parentLoader);
+ assertEquals("aaabbbcccddd", (String)initCtx.lookup("java:comp/env/foo"));
+
+
+ }
+ finally
+ {
+ //make some effort to clean up
+ InitialContext ic = new InitialContext();
+ currentThread.setContextClassLoader(parentLoader);
+ Context comp = (Context)ic.lookup("java:comp");
+ comp.destroySubcontext("env");
+
+ currentThread.setContextClassLoader(childLoader1);
+ comp = (Context)ic.lookup("java:comp");
+ comp.destroySubcontext("env");
+
+
}
}
}
diff --git a/jetty-osgi/jetty-osgi-boot-jsp/src/main/java/org/eclipse/jetty/osgi/boot/jasper/WebappRegistrationCustomizerImpl.java b/jetty-osgi/jetty-osgi-boot-jsp/src/main/java/org/eclipse/jetty/osgi/boot/jasper/WebappRegistrationCustomizerImpl.java
index 529d49cabb..4b0b07b712 100644
--- a/jetty-osgi/jetty-osgi-boot-jsp/src/main/java/org/eclipse/jetty/osgi/boot/jasper/WebappRegistrationCustomizerImpl.java
+++ b/jetty-osgi/jetty-osgi-boot-jsp/src/main/java/org/eclipse/jetty/osgi/boot/jasper/WebappRegistrationCustomizerImpl.java
@@ -35,6 +35,8 @@ import org.eclipse.jetty.osgi.boot.JettyBootstrapActivator;
import org.eclipse.jetty.osgi.boot.OSGiAppProvider;
import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelper;
import org.eclipse.jetty.osgi.boot.utils.WebappRegistrationCustomizer;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;
import org.xml.sax.EntityResolver;
@@ -52,6 +54,7 @@ import org.xml.sax.SAXException;
*/
public class WebappRegistrationCustomizerImpl implements WebappRegistrationCustomizer
{
+ private static final Logger LOG = Log.getLogger(WebappRegistrationCustomizerImpl.class);
/**
* Default name of a class that belongs to the jstl bundle. From that class
@@ -112,8 +115,7 @@ public class WebappRegistrationCustomizerImpl implements WebappRegistrationCusto
}
catch (Exception e)
{
- System.err.println("Unable to set the JspFactory: jsp support incomplete.");
- e.printStackTrace();
+ LOG.warn("Unable to set the JspFactory: jsp support incomplete.",e);
}
}
@@ -138,17 +140,23 @@ public class WebappRegistrationCustomizerImpl implements WebappRegistrationCusto
public URL[] getJarsWithTlds(OSGiAppProvider provider, BundleFileLocatorHelper locatorHelper) throws Exception
{
- HashSet<Class<?>> classesToAddToTheTldBundles = new HashSet<Class<?>>();
+ ArrayList<URL> urls = new ArrayList<URL>();
+ HashSet<Class<?>> classesToAddToTheTldBundles = new HashSet<Class<?>>();
// Look for the jstl bundle
// We assume the jstl's tlds are defined there.
// We assume that the jstl bundle is imported by this bundle
// So we can look for this class using this bundle's classloader:
- Class<?> jstlClass = WebappRegistrationCustomizerImpl.class.getClassLoader().loadClass(DEFAULT_JSTL_BUNDLE_CLASS);
-
- classesToAddToTheTldBundles.add(jstlClass);
+ try
+ {
+ Class<?> jstlClass = WebappRegistrationCustomizerImpl.class.getClassLoader().loadClass(DEFAULT_JSTL_BUNDLE_CLASS);
- ArrayList<URL> urls = new ArrayList<URL>();
+ classesToAddToTheTldBundles.add(jstlClass);
+ }
+ catch (ClassNotFoundException e)
+ {
+ LOG.info("jstl not on classpath", e);
+ }
for (Class<?> cl : classesToAddToTheTldBundles)
{
Bundle tldBundle = FrameworkUtil.getBundle(cl);
@@ -208,7 +216,7 @@ public class WebappRegistrationCustomizerImpl implements WebappRegistrationCusto
}
catch (Exception e)
{
- e.printStackTrace();
+ LOG.warn(e);
}
}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationConfiguration.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationConfiguration.java
index 55be1dbf55..4005f9a397 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationConfiguration.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationConfiguration.java
@@ -21,6 +21,8 @@ package org.eclipse.jetty.osgi.annotations;
import java.util.ArrayList;
import java.util.List;
+import org.eclipse.jetty.annotations.AbstractDiscoverableAnnotationHandler;
+import org.eclipse.jetty.annotations.AnnotationParser.DiscoverableAnnotationHandler;
import org.eclipse.jetty.annotations.ClassNameResolver;
import org.eclipse.jetty.osgi.boot.OSGiWebappConstants;
import org.eclipse.jetty.osgi.boot.utils.internal.PackageAdminServiceTracker;
@@ -151,20 +153,25 @@ public class AnnotationConfiguration extends org.eclipse.jetty.annotations.Annot
protected void parseBundle(WebAppContext context, AnnotationParser parser,
Bundle webbundle, Bundle bundle) throws Exception
{
+
Resource bundleRes = parser.getResource(bundle);
- parser.parse(bundle,createClassNameResolver(context));
- List<DiscoveredAnnotation> annotations = new ArrayList<DiscoveredAnnotation>();
- gatherAnnotations(annotations, parser.getAnnotationHandlers());
- if (webbundle == bundle)
- {
- //just like the super with its question about annotations in WEB-INF/classes:
- //"TODO - where to set the annotations discovered from WEB-INF/classes?"
- context.getMetaData().addDiscoveredAnnotations(annotations);
- }
- else
+
+ parser.clearHandlers();
+ for (DiscoverableAnnotationHandler h:_discoverableAnnotationHandlers)
{
- context.getMetaData().addDiscoveredAnnotations(bundleRes, annotations);
+ if (h instanceof AbstractDiscoverableAnnotationHandler)
+ {
+ if (webbundle == bundle)
+ ((AbstractDiscoverableAnnotationHandler)h).setResource(null);
+ else
+ ((AbstractDiscoverableAnnotationHandler)h).setResource(bundleRes);
+ }
}
+ parser.registerHandlers(_discoverableAnnotationHandlers);
+ parser.registerHandler(_classInheritanceHandler);
+ parser.registerHandlers(_containerInitializerAnnotationHandlers);
+
+ parser.parse(bundle,createClassNameResolver(context));
}
/**
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/jsp/TldLocatableURLClassloader.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/jsp/TldLocatableURLClassloader.java
index ad23673df3..7f6c6288ac 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/jsp/TldLocatableURLClassloader.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/jsp/TldLocatableURLClassloader.java
@@ -48,4 +48,18 @@ public class TldLocatableURLClassloader extends URLClassLoader
{
return _jarsWithTldsInside;
}
+
+ public String toString()
+ {
+ StringBuilder builder = new StringBuilder();
+
+ if (_jarsWithTldsInside != null)
+ {
+ for (URL u:_jarsWithTldsInside)
+ builder.append(" "+u.toString());
+ return builder.toString();
+ }
+ else
+ return super.toString();
+ }
}
diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintSecurityHandler.java b/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintSecurityHandler.java
index ec998dd131..eb9235ab42 100644
--- a/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintSecurityHandler.java
+++ b/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintSecurityHandler.java
@@ -19,15 +19,24 @@
package org.eclipse.jetty.security;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
+import javax.servlet.HttpConstraintElement;
+import javax.servlet.HttpMethodConstraintElement;
+import javax.servlet.ServletSecurityElement;
+import javax.servlet.annotation.ServletSecurity.EmptyRoleSemantic;
+import javax.servlet.annotation.ServletSecurity.TransportGuarantee;
+
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.PathMap;
import org.eclipse.jetty.server.HttpChannel;
@@ -36,18 +45,18 @@ import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.server.UserIdentity;
import org.eclipse.jetty.util.StringMap;
-import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.security.Constraint;
/* ------------------------------------------------------------ */
/**
* Handler to enforce SecurityConstraints. This implementation is servlet spec
- * 2.4 compliant and pre-computes the constraint combinations for runtime
+ * 3.0 compliant and pre-computes the constraint combinations for runtime
* efficiency.
*
*/
public class ConstraintSecurityHandler extends SecurityHandler implements ConstraintAware
{
+ private static final String OMISSION_SUFFIX = ".omission";
private static final String ALL_METHODS = "*";
private final List<ConstraintMapping> _constraintMappings= new CopyOnWriteArrayList<>();
private final Set<String> _roles = new CopyOnWriteArraySet<>();
@@ -55,6 +64,212 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
private boolean _strict = true;
/* ------------------------------------------------------------ */
+ /**
+ * @return
+ */
+ public static Constraint createConstraint()
+ {
+ return new Constraint();
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @param constraint
+ * @return
+ */
+ public static Constraint createConstraint(Constraint constraint)
+ {
+ try
+ {
+ return (Constraint)constraint.clone();
+ }
+ catch (CloneNotSupportedException e)
+ {
+ throw new IllegalStateException (e);
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Create a security constraint
+ *
+ * @param name
+ * @param authenticate
+ * @param roles
+ * @param dataConstraint
+ * @return
+ */
+ public static Constraint createConstraint (String name, boolean authenticate, String[] roles, int dataConstraint)
+ {
+ Constraint constraint = createConstraint();
+ if (name != null)
+ constraint.setName(name);
+ constraint.setAuthenticate(authenticate);
+ constraint.setRoles(roles);
+ constraint.setDataConstraint(dataConstraint);
+ return constraint;
+ }
+
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @param name
+ * @param element
+ * @return
+ */
+ public static Constraint createConstraint (String name, HttpConstraintElement element)
+ {
+ return createConstraint(name, element.getRolesAllowed(), element.getEmptyRoleSemantic(), element.getTransportGuarantee());
+ }
+
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @param name
+ * @param rolesAllowed
+ * @param permitOrDeny
+ * @param transport
+ * @return
+ */
+ public static Constraint createConstraint (String name, String[] rolesAllowed, EmptyRoleSemantic permitOrDeny, TransportGuarantee transport)
+ {
+ Constraint constraint = createConstraint();
+
+ if (rolesAllowed == null || rolesAllowed.length==0)
+ {
+ if (permitOrDeny.equals(EmptyRoleSemantic.DENY))
+ {
+ //Equivalent to <auth-constraint> with no roles
+ constraint.setName(name+"-Deny");
+ constraint.setAuthenticate(true);
+ }
+ else
+ {
+ //Equivalent to no <auth-constraint>
+ constraint.setName(name+"-Permit");
+ constraint.setAuthenticate(false);
+ }
+ }
+ else
+ {
+ //Equivalent to <auth-constraint> with list of <security-role-name>s
+ constraint.setAuthenticate(true);
+ constraint.setRoles(rolesAllowed);
+ constraint.setName(name+"-RolesAllowed");
+ }
+
+ //Equivalent to //<user-data-constraint><transport-guarantee>CONFIDENTIAL</transport-guarantee></user-data-constraint>
+ constraint.setDataConstraint((transport.equals(TransportGuarantee.CONFIDENTIAL)?Constraint.DC_CONFIDENTIAL:Constraint.DC_NONE));
+ return constraint;
+ }
+
+
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @param pathSpec
+ * @param constraintMappings
+ * @return
+ */
+ public static List<ConstraintMapping> getConstraintMappingsForPath(String pathSpec, List<ConstraintMapping> constraintMappings)
+ {
+ if (pathSpec == null || "".equals(pathSpec.trim()) || constraintMappings == null || constraintMappings.size() == 0)
+ return Collections.emptyList();
+
+ List<ConstraintMapping> mappings = new ArrayList<ConstraintMapping>();
+ for (ConstraintMapping mapping:constraintMappings)
+ {
+ if (pathSpec.equals(mapping.getPathSpec()))
+ {
+ mappings.add(mapping);
+ }
+ }
+ return mappings;
+ }
+
+
+ /* ------------------------------------------------------------ */
+ /** Take out of the constraint mappings those that match the
+ * given path.
+ *
+ * @param pathSpec
+ * @param constraintMappings a new list minus the matching constraints
+ * @return
+ */
+ public static List<ConstraintMapping> removeConstraintMappingsForPath(String pathSpec, List<ConstraintMapping> constraintMappings)
+ {
+ if (pathSpec == null || "".equals(pathSpec.trim()) || constraintMappings == null || constraintMappings.size() == 0)
+ return Collections.emptyList();
+
+ List<ConstraintMapping> mappings = new ArrayList<ConstraintMapping>();
+ for (ConstraintMapping mapping:constraintMappings)
+ {
+ //Remove the matching mappings by only copying in non-matching mappings
+ if (!pathSpec.equals(mapping.getPathSpec()))
+ {
+ mappings.add(mapping);
+ }
+ }
+ return mappings;
+ }
+
+
+
+ /* ------------------------------------------------------------ */
+ /** Generate Constraints and ContraintMappings for the given url pattern and ServletSecurityElement
+ *
+ * @param name
+ * @param pathSpec
+ * @param securityElement
+ * @return
+ */
+ public static List<ConstraintMapping> createConstraintsWithMappingsForPath (String name, String pathSpec, ServletSecurityElement securityElement)
+ {
+ List<ConstraintMapping> mappings = new ArrayList<ConstraintMapping>();
+
+ //Create a constraint that will describe the default case (ie if not overridden by specific HttpMethodConstraints)
+ Constraint constraint = ConstraintSecurityHandler.createConstraint(name, securityElement);
+
+ //Create a mapping for the pathSpec for the default case
+ ConstraintMapping defaultMapping = new ConstraintMapping();
+ defaultMapping.setPathSpec(pathSpec);
+ defaultMapping.setConstraint(constraint);
+ mappings.add(defaultMapping);
+
+
+ //See Spec 13.4.1.2 p127
+ List<String> methodOmissions = new ArrayList<String>();
+
+ //make constraint mappings for this url for each of the HttpMethodConstraintElements
+ Collection<HttpMethodConstraintElement> methodConstraints = securityElement.getHttpMethodConstraints();
+ if (methodConstraints != null)
+ {
+ for (HttpMethodConstraintElement methodConstraint:methodConstraints)
+ {
+ //Make a Constraint that captures the <auth-constraint> and <user-data-constraint> elements supplied for the HttpMethodConstraintElement
+ Constraint mconstraint = ConstraintSecurityHandler.createConstraint(name, methodConstraint);
+ ConstraintMapping mapping = new ConstraintMapping();
+ mapping.setConstraint(mconstraint);
+ mapping.setPathSpec(pathSpec);
+ if (methodConstraint.getMethodName() != null)
+ {
+ mapping.setMethod(methodConstraint.getMethodName());
+ //See spec 13.4.1.2 p127 - add an omission for every method name to the default constraint
+ methodOmissions.add(methodConstraint.getMethodName());
+ }
+ mappings.add(mapping);
+ }
+ }
+ //See spec 13.4.1.2 p127 - add an omission for every method name to the default constraint
+ if (methodOmissions.size() > 0)
+ defaultMapping.setMethodOmissions(methodOmissions.toArray(new String[methodOmissions.size()]));
+
+ return mappings;
+ }
+
+
+
+ /* ------------------------------------------------------------ */
/** Get the strict mode.
* @return true if the security handler is running in strict mode.
*/
@@ -140,8 +355,6 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
@Override
public void setConstraintMappings(List<ConstraintMapping> constraintMappings, Set<String> roles)
{
- if (isStarted())
- throw new IllegalStateException("Started");
_constraintMappings.clear();
_constraintMappings.addAll(constraintMappings);
@@ -160,6 +373,14 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
}
}
setRoles(roles);
+
+ if (isStarted())
+ {
+ for (ConstraintMapping mapping : _constraintMappings)
+ {
+ processConstraintMapping(mapping);
+ }
+ }
}
/* ------------------------------------------------------------ */
@@ -172,9 +393,6 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
*/
public void setRoles(Set<String> roles)
{
- if (isStarted())
- throw new IllegalStateException("Started");
-
_roles.clear();
_roles.addAll(roles);
}
@@ -238,14 +456,24 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
}
super.doStart();
}
-
+
+
+ /* ------------------------------------------------------------ */
@Override
protected void doStop() throws Exception
{
super.doStop();
_constraintMap.clear();
}
-
+
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Create and combine the constraint with the existing processed
+ * constraints.
+ *
+ * @param mapping
+ */
protected void processConstraintMapping(ConstraintMapping mapping)
{
Map<String, RoleInfo> mappings = _constraintMap.get(mapping.getPathSpec());
@@ -258,6 +486,12 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
if (allMethodsRoleInfo != null && allMethodsRoleInfo.isForbidden())
return;
+ if (mapping.getMethodOmissions() != null && mapping.getMethodOmissions().length > 0)
+ {
+ processConstraintMappingWithMethodOmissions(mapping, mappings);
+ return;
+ }
+
String httpMethod = mapping.getMethod();
if (httpMethod==null)
httpMethod=ALL_METHODS;
@@ -274,10 +508,10 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
if (roleInfo.isForbidden())
return;
- Constraint constraint = mapping.getConstraint();
- boolean forbidden = constraint.isForbidden();
- roleInfo.setForbidden(forbidden);
- if (forbidden)
+ //add in info from the constraint
+ configureRoleInfo(roleInfo, mapping);
+
+ if (roleInfo.isForbidden())
{
if (httpMethod.equals(ALL_METHODS))
{
@@ -287,41 +521,12 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
}
else
{
- UserDataConstraint userDataConstraint = UserDataConstraint.get(constraint.getDataConstraint());
- roleInfo.setUserDataConstraint(userDataConstraint);
-
- boolean checked = constraint.getAuthenticate();
- roleInfo.setChecked(checked);
- if (roleInfo.isChecked())
- {
- if (constraint.isAnyRole())
- {
- if (_strict)
- {
- // * means "all defined roles"
- for (String role : _roles)
- roleInfo.addRole(role);
- }
- else
- // * means any role
- roleInfo.setAnyRole(true);
- }
- else
- {
- String[] newRoles = constraint.getRoles();
- for (String role : newRoles)
- {
- if (_strict &&!_roles.contains(role))
- throw new IllegalArgumentException("Attempt to use undeclared role: " + role + ", known roles: " + _roles);
- roleInfo.addRole(role);
- }
- }
- }
- if (httpMethod.equals(ALL_METHODS))
+ //combine with any entry that covers all methods
+ if (httpMethod == null)
{
for (Map.Entry<String, RoleInfo> entry : mappings.entrySet())
{
- if (!entry.getKey().equals(ALL_METHODS))
+ if (entry.getKey() != null)
{
RoleInfo specific = entry.getValue();
specific.combine(roleInfo);
@@ -331,17 +536,145 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
}
}
+ /* ------------------------------------------------------------ */
+ /** Constraints that name method omissions are dealt with differently.
+ * We create an entry in the mappings with key "method.omission". This entry
+ * is only ever combined with other omissions for the same method to produce a
+ * consolidated RoleInfo. Then, when we wish to find the relevant constraints for
+ * a given Request (in prepareConstraintInfo()), we consult 3 types of entries in
+ * the mappings: an entry that names the method of the Request specifically, an
+ * entry that names constraints that apply to all methods, entries of the form
+ * method.omission, where the method of the Request is not named in the omission.
+ * @param mapping
+ * @param mappings
+ */
+ protected void processConstraintMappingWithMethodOmissions (ConstraintMapping mapping, Map<String, RoleInfo> mappings)
+ {
+ String[] omissions = mapping.getMethodOmissions();
+
+ for (String omission:omissions)
+ {
+ //for each method omission, see if there is already a RoleInfo for it in mappings
+ RoleInfo ri = mappings.get(omission+OMISSION_SUFFIX);
+ if (ri == null)
+ {
+ //if not, make one
+ ri = new RoleInfo();
+ mappings.put(omission+OMISSION_SUFFIX, ri);
+ }
+
+ //initialize RoleInfo or combine from ConstraintMapping
+ configureRoleInfo(ri, mapping);
+ }
+ }
+
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Initialize or update the RoleInfo from the constraint
+ * @param ri
+ * @param mapping
+ */
+ protected void configureRoleInfo (RoleInfo ri, ConstraintMapping mapping)
+ {
+ Constraint constraint = mapping.getConstraint();
+ boolean forbidden = constraint.isForbidden();
+ ri.setForbidden(forbidden);
+
+ //set up the data constraint (NOTE: must be done after setForbidden, as it nulls out the data constraint
+ //which we need in order to do combining of omissions in prepareConstraintInfo
+ UserDataConstraint userDataConstraint = UserDataConstraint.get(mapping.getConstraint().getDataConstraint());
+ ri.setUserDataConstraint(userDataConstraint);
+
+
+ //if forbidden, no point setting up roles
+ if (!ri.isForbidden())
+ {
+ //add in the roles
+ boolean checked = mapping.getConstraint().getAuthenticate();
+ ri.setChecked(checked);
+ if (ri.isChecked())
+ {
+ if (mapping.getConstraint().isAnyRole())
+ {
+ if (_strict)
+ {
+ // * means "all defined roles"
+ for (String role : _roles)
+ ri.addRole(role);
+ }
+ else
+ // * means any role
+ ri.setAnyRole(true);
+ }
+ else
+ {
+ String[] newRoles = mapping.getConstraint().getRoles();
+ for (String role : newRoles)
+ {
+ if (_strict &&!_roles.contains(role))
+ throw new IllegalArgumentException("Attempt to use undeclared role: " + role + ", known roles: " + _roles);
+ ri.addRole(role);
+ }
+ }
+ }
+ }
+ }
+
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Find constraints that apply to the given path.
+ * In order to do this, we consult 3 different types of information stored in the mappings for each path - each mapping
+ * represents a merged set of user data constraints, roles etc -:
+ * <ol>
+ * <li>A mapping of an exact method name </li>
+ * <li>A mapping will null key that matches every method name</li>
+ * <li>Mappings with keys of the form "method.omission" that indicates it will match every method name EXCEPT that given</li>
+ * </ol>
+ *
+ * @see org.eclipse.jetty.security.SecurityHandler#prepareConstraintInfo(java.lang.String, org.eclipse.jetty.server.Request)
+ */
@Override
protected RoleInfo prepareConstraintInfo(String pathInContext, Request request)
{
- Map<String, RoleInfo> mappings = _constraintMap.match(pathInContext);
+ Map<String, RoleInfo> mappings = (Map<String, RoleInfo>)_constraintMap.match(pathInContext);
if (mappings != null)
{
String httpMethod = request.getMethod();
RoleInfo roleInfo = mappings.get(httpMethod);
if (roleInfo == null)
- roleInfo = mappings.get(ALL_METHODS);
+ {
+ //No specific http-method names matched
+ List<RoleInfo> applicableConstraints = new ArrayList<RoleInfo>();
+
+ //Get info for constraint that matches all methods if it exists
+ RoleInfo all = mappings.get(ALL_METHODS);
+ if (all != null)
+ applicableConstraints.add(all);
+
+
+ //Get info for constraints that name method omissions where target method name is not omitted
+ //(ie matches because target method is not omitted, hence considered covered by the constraint)
+ for (Entry<String, RoleInfo> entry: mappings.entrySet())
+ {
+ if (entry.getKey() != null && entry.getKey().contains(OMISSION_SUFFIX) && !(httpMethod+OMISSION_SUFFIX).equals(entry.getKey()))
+ applicableConstraints.add(entry.getValue());
+ }
+
+ if (applicableConstraints.size() == 1)
+ roleInfo = applicableConstraints.get(0);
+ else
+ {
+ roleInfo = new RoleInfo();
+ roleInfo.setUserDataConstraint(UserDataConstraint.None);
+
+ for (RoleInfo r:applicableConstraints)
+ roleInfo.combine(r);
+ }
+
+ }
return roleInfo;
}
@@ -396,7 +729,12 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
{
return constraintInfo != null && ((RoleInfo)constraintInfo).isChecked();
}
-
+
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @see org.eclipse.jetty.security.SecurityHandler#checkWebResourcePermissions(java.lang.String, org.eclipse.jetty.server.Request, org.eclipse.jetty.server.Response, java.lang.Object, org.eclipse.jetty.server.UserIdentity)
+ */
@Override
protected boolean checkWebResourcePermissions(String pathInContext, Request request, Response response, Object constraintInfo, UserIdentity userIdentity)
throws IOException
@@ -435,4 +773,5 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
Collections.singleton(_roles),
_constraintMap.entrySet());
}
+
}
diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/SessionAuthentication.java b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/SessionAuthentication.java
index 7e7ec72d49..9e94282364 100644
--- a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/SessionAuthentication.java
+++ b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/SessionAuthentication.java
@@ -119,18 +119,25 @@ public class SessionAuthentication implements Authentication.User, Serializable,
@Override
public void sessionWillPassivate(HttpSessionEvent se)
{
+
}
@Override
public void sessionDidActivate(HttpSessionEvent se)
{
if (_session==null)
+ {
_session=se.getSession();
+ }
}
@Override
public void valueBound(HttpSessionBindingEvent event)
{
+ if (_session==null)
+ {
+ _session=event.getSession();
+ }
}
@Override
diff --git a/jetty-security/src/test/java/org/eclipse/jetty/security/ConstraintTest.java b/jetty-security/src/test/java/org/eclipse/jetty/security/ConstraintTest.java
index 1d903c4ad0..75f0e62554 100644
--- a/jetty-security/src/test/java/org/eclipse/jetty/security/ConstraintTest.java
+++ b/jetty-security/src/test/java/org/eclipse/jetty/security/ConstraintTest.java
@@ -79,6 +79,8 @@ public class ConstraintTest
_loginService.putUser("user",new Password("password"));
_loginService.putUser("user2",new Password("password"), new String[] {"user"});
_loginService.putUser("admin",new Password("password"), new String[] {"user","administrator"});
+ _loginService.putUser("user3", new Password("password"), new String[] {"foo"});
+
_context.setContextPath("/ctx");
_server.setHandler(_context);
@@ -190,17 +192,59 @@ public class ConstraintTest
@Test
public void testBasic() throws Exception
{
+
+ List<ConstraintMapping> list = new ArrayList<ConstraintMapping>(_security.getConstraintMappings());
+
+ Constraint constraint6 = new Constraint();
+ constraint6.setAuthenticate(true);
+ constraint6.setName("omit POST and GET");
+ constraint6.setRoles(new String[]{"user"});
+ ConstraintMapping mapping6 = new ConstraintMapping();
+ mapping6.setPathSpec("/omit/*");
+ mapping6.setConstraint(constraint6);
+ mapping6.setMethodOmissions(new String[]{"GET", "HEAD"}); //requests for every method except GET and HEAD must be in role "user"
+ list.add(mapping6);
+
+ Constraint constraint7 = new Constraint();
+ constraint7.setAuthenticate(true);
+ constraint7.setName("non-omitted GET");
+ constraint7.setRoles(new String[]{"administrator"});
+ ConstraintMapping mapping7 = new ConstraintMapping();
+ mapping7.setPathSpec("/omit/*");
+ mapping7.setConstraint(constraint7);
+ mapping7.setMethod("GET"); //requests for GET must be in role "admin"
+ list.add(mapping7);
+
+ Constraint constraint8 = new Constraint();
+ constraint8.setAuthenticate(true);
+ constraint8.setName("non specific");
+ constraint8.setRoles(new String[]{"foo"});
+ ConstraintMapping mapping8 = new ConstraintMapping();
+ mapping8.setPathSpec("/omit/*");
+ mapping8.setConstraint(constraint8);//requests for all methods must be in role "foo"
+ list.add(mapping8);
+
+ Set<String> knownRoles=new HashSet<String>();
+ knownRoles.add("user");
+ knownRoles.add("administrator");
+ knownRoles.add("foo");
+
+ _security.setConstraintMappings(list, knownRoles);
+
+
_security.setAuthenticator(new BasicAuthenticator());
_security.setStrict(false);
_server.start();
String response;
+ /*
response = _connector.getResponses("GET /ctx/noauth/info HTTP/1.0\r\n\r\n");
assertThat(response,startsWith("HTTP/1.1 200 OK"));
-
+*/
+
response = _connector.getResponses("GET /ctx/forbid/info HTTP/1.0\r\n\r\n");
assertThat(response,startsWith("HTTP/1.1 403 Forbidden"));
-
+ /*
response = _connector.getResponses("GET /ctx/auth/info HTTP/1.0\r\n\r\n");
assertThat(response,startsWith("HTTP/1.1 401 Unauthorized"));
assertThat(response,containsString("WWW-Authenticate: basic realm=\"TestRealm\""));
@@ -215,8 +259,8 @@ public class ConstraintTest
"Authorization: Basic " + B64Code.encode("user:password") + "\r\n" +
"\r\n");
assertThat(response,startsWith("HTTP/1.1 200 OK"));
-
-
+*/
+/*
// test admin
response = _connector.getResponses("GET /ctx/admin/info HTTP/1.0\r\n\r\n");
assertThat(response,startsWith("HTTP/1.1 401 Unauthorized"));
@@ -242,7 +286,33 @@ public class ConstraintTest
response = _connector.getResponses("GET /ctx/admin/relax/info HTTP/1.0\r\n\r\n");
assertThat(response,startsWith("HTTP/1.1 200 OK"));
+
+ //check GET is in role administrator
+ response = _connector.getResponses("GET /ctx/omit/x HTTP/1.0\r\n" +
+ "Authorization: Basic " + B64Code.encode("admin:password") + "\r\n" +
+ "\r\n");
+ assertTrue(response.startsWith("HTTP/1.1 200 OK"));
+
+ //check POST is in role user
+ response = _connector.getResponses("POST /ctx/omit/x HTTP/1.0\r\n" +
+ "Authorization: Basic " + B64Code.encode("user2:password") + "\r\n" +
+ "\r\n");
+ assertTrue(response.startsWith("HTTP/1.1 200 OK"));
+
+ //check POST can be in role foo too
+ response = _connector.getResponses("POST /ctx/omit/x HTTP/1.0\r\n" +
+ "Authorization: Basic " + B64Code.encode("user3:password") + "\r\n" +
+ "\r\n");
+ assertTrue(response.startsWith("HTTP/1.1 200 OK"));
+
+ //check HEAD cannot be in role user
+ response = _connector.getResponses("HEAD /ctx/omit/x HTTP/1.0\r\n" +
+ "Authorization: Basic " + B64Code.encode("user2:password") + "\r\n" +
+ "\r\n");
+ assertTrue(response.startsWith("HTTP/1.1 200 OK"));*/
}
+
+
@Test
public void testFormDispatch() throws Exception
@@ -853,7 +923,7 @@ public class ConstraintTest
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response ) throws IOException, ServletException
{
baseRequest.setHandled(true);
- if (request.getAuthType()==null || "user".equals(request.getRemoteUser()) || request.isUserInRole("user"))
+ if (request.getAuthType()==null || "user".equals(request.getRemoteUser()) || request.isUserInRole("user") || request.isUserInRole("foo"))
{
response.setStatus(200);
response.setContentType("text/plain; charset=UTF-8");
diff --git a/jetty-security/src/test/java/org/eclipse/jetty/security/SpecExampleConstraintTest.java b/jetty-security/src/test/java/org/eclipse/jetty/security/SpecExampleConstraintTest.java
new file mode 100644
index 0000000000..ba12f1af57
--- /dev/null
+++ b/jetty-security/src/test/java/org/eclipse/jetty/security/SpecExampleConstraintTest.java
@@ -0,0 +1,315 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2012 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.security;
+
+import static org.hamcrest.Matchers.startsWith;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.security.authentication.BasicAuthenticator;
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.LocalConnector;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.server.session.SessionHandler;
+import org.eclipse.jetty.util.B64Code;
+import org.eclipse.jetty.util.security.Constraint;
+import org.eclipse.jetty.util.security.Password;
+import org.hamcrest.Matchers;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * @version $Revision: 1441 $ $Date: 2010-04-02 12:28:17 +0200 (Fri, 02 Apr 2010) $
+ */
+public class SpecExampleConstraintTest
+{
+ private static final String TEST_REALM = "TestRealm";
+ private static Server _server;
+ private static LocalConnector _connector;
+ private static SessionHandler _session;
+ private ConstraintSecurityHandler _security;
+
+ @BeforeClass
+ public static void startServer()
+ {
+ _server = new Server();
+ _connector = new LocalConnector(_server);
+ _server.setConnectors(new Connector[]{_connector});
+
+ ContextHandler _context = new ContextHandler();
+ _session = new SessionHandler();
+
+ HashLoginService _loginService = new HashLoginService(TEST_REALM);
+ _loginService.putUser("fred",new Password("password"));
+ _loginService.putUser("harry",new Password("password"), new String[] {"HOMEOWNER"});
+ _loginService.putUser("chris",new Password("password"), new String[] {"CONTRACTOR"});
+ _loginService.putUser("steven", new Password("password"), new String[] {"SALESCLERK"});
+
+
+ _context.setContextPath("/ctx");
+ _server.setHandler(_context);
+ _context.setHandler(_session);
+
+ _server.addBean(_loginService);
+ }
+
+ @Before
+ public void setupSecurity()
+ {
+ _security = new ConstraintSecurityHandler();
+ _session.setHandler(_security);
+ RequestHandler _handler = new RequestHandler();
+ _security.setHandler(_handler);
+
+
+ /*
+
+ <security-constraint>
+ <web-resource-collection>
+ <web-resource-name>precluded methods</web-resource-name>
+ <url-pattern>/*</url-pattern>
+ <url-pattern>/acme/wholesale/*</url-pattern>
+ <url-pattern>/acme/retail/*</url-pattern>
+ <http-method-exception>GET</http-method-exception>
+ <http-method-exception>POST</http-method-exception>
+ </web-resource-collection>
+ <auth-constraint/>
+ </security-constraint>
+ */
+
+ Constraint constraint0 = new Constraint();
+ constraint0.setAuthenticate(true);
+ constraint0.setName("precluded methods");
+ ConstraintMapping mapping0 = new ConstraintMapping();
+ mapping0.setPathSpec("/*");
+ mapping0.setConstraint(constraint0);
+ mapping0.setMethodOmissions(new String[]{"GET", "POST"});
+
+ ConstraintMapping mapping1 = new ConstraintMapping();
+ mapping1.setPathSpec("/acme/wholesale/*");
+ mapping1.setConstraint(constraint0);
+ mapping1.setMethodOmissions(new String[]{"GET", "POST"});
+
+ ConstraintMapping mapping2 = new ConstraintMapping();
+ mapping2.setPathSpec("/acme/retail/*");
+ mapping2.setConstraint(constraint0);
+ mapping2.setMethodOmissions(new String[]{"GET", "POST"});
+
+ /*
+
+ <security-constraint>
+ <web-resource-collection>
+ <web-resource-name>wholesale</web-resource-name>
+ <url-pattern>/acme/wholesale/*</url-pattern>
+ <http-method>GET</http-method>
+ <http-method>PUT</http-method>
+ </web-resource-collection>
+ <auth-constraint>
+ <role-name>SALESCLERK</role-name>
+ </auth-constraint>
+ </security-constraint>
+ */
+ Constraint constraint1 = new Constraint();
+ constraint1.setAuthenticate(true);
+ constraint1.setName("wholesale");
+ constraint1.setRoles(new String[]{"SALESCLERK"});
+ ConstraintMapping mapping3 = new ConstraintMapping();
+ mapping3.setPathSpec("/acme/wholesale/*");
+ mapping3.setConstraint(constraint1);
+ mapping3.setMethod("GET");
+ ConstraintMapping mapping4 = new ConstraintMapping();
+ mapping4.setPathSpec("/acme/wholesale/*");
+ mapping4.setConstraint(constraint1);
+ mapping4.setMethod("PUT");
+
+ /*
+ <security-constraint>
+ <web-resource-collection>
+ <web-resource-name>wholesale 2</web-resource-name>
+ <url-pattern>/acme/wholesale/*</url-pattern>
+ <http-method>GET</http-method>
+ <http-method>POST</http-method>
+ </web-resource-collection>
+ <auth-constraint>
+ <role-name>CONTRACTOR</role-name>
+ </auth-constraint>
+ <user-data-constraint>
+ <transport-guarantee>CONFIDENTIAL</transport-guarantee>
+ </user-data-constraint>
+ </security-constraint>
+ */
+ Constraint constraint2 = new Constraint();
+ constraint2.setAuthenticate(true);
+ constraint2.setName("wholesale 2");
+ constraint2.setRoles(new String[]{"CONTRACTOR"});
+ constraint2.setDataConstraint(Constraint.DC_CONFIDENTIAL);
+ ConstraintMapping mapping5 = new ConstraintMapping();
+ mapping5.setPathSpec("/acme/wholesale/*");
+ mapping5.setMethod("GET");
+ mapping5.setConstraint(constraint2);
+ ConstraintMapping mapping6 = new ConstraintMapping();
+ mapping6.setPathSpec("/acme/wholesale/*");
+ mapping6.setMethod("POST");
+ mapping6.setConstraint(constraint2);
+
+ /*
+<security-constraint>
+<web-resource-collection>
+<web-resource-name>retail</web-resource-name>
+<url-pattern>/acme/retail/*</url-pattern>
+<http-method>GET</http-method>
+<http-method>POST</http-method>
+</web-resource-collection>
+<auth-constraint>
+<role-name>CONTRACTOR</role-name>
+<role-name>HOMEOWNER</role-name>
+</auth-constraint>
+</security-constraint>
+*/
+ Constraint constraint4 = new Constraint();
+ constraint4.setName("retail");
+ constraint4.setAuthenticate(true);
+ constraint4.setRoles(new String[]{"CONTRACTOR", "HOMEOWNER"});
+ ConstraintMapping mapping7 = new ConstraintMapping();
+ mapping7.setPathSpec("/acme/retail/*");
+ mapping7.setMethod("GET");
+ mapping7.setConstraint(constraint4);
+ ConstraintMapping mapping8 = new ConstraintMapping();
+ mapping8.setPathSpec("/acme/retail/*");
+ mapping8.setMethod("POST");
+ mapping8.setConstraint(constraint4);
+
+
+
+
+ Set<String> knownRoles=new HashSet<String>();
+ knownRoles.add("CONTRACTOR");
+ knownRoles.add("HOMEOWNER");
+ knownRoles.add("SALESCLERK");
+
+ _security.setConstraintMappings(Arrays.asList(new ConstraintMapping[]
+ {
+ mapping0, mapping1, mapping2, mapping3, mapping4, mapping5, mapping6, mapping7, mapping8
+ }), knownRoles);
+ }
+
+ @After
+ public void stopServer() throws Exception
+ {
+ if (_server.isRunning())
+ {
+ _server.stop();
+ _server.join();
+ }
+ }
+
+
+
+ @Test
+ public void testBasic() throws Exception
+ {
+
+ _security.setAuthenticator(new BasicAuthenticator());
+ _security.setStrict(false);
+ _server.start();
+
+ String response;
+ /*
+ /star all methods except GET/POST forbidden
+ /acme/wholesale/star all methods except GET/POST forbidden
+ /acme/retail/star all methods except GET/POST forbidden
+ /acme/wholesale/star GET must be in role CONTRACTOR or SALESCLERK
+ /acme/wholesale/star POST must be in role CONTRACTOR and confidential transport
+ /acme/retail/star GET must be in role CONTRACTOR or HOMEOWNER
+ /acme/retail/star POST must be in role CONTRACTOR or HOMEOWNER
+ */
+
+ //a user in role HOMEOWNER is forbidden HEAD request
+ response = _connector.getResponses("HEAD /ctx/index.html HTTP/1.0\r\n\r\n");
+ assertTrue(response.startsWith("HTTP/1.1 403 Forbidden"));
+
+ response = _connector.getResponses("HEAD /ctx/index.html HTTP/1.0\r\n" +
+ "Authorization: Basic " + B64Code.encode("harry:password") + "\r\n" +
+ "\r\n");
+ assertThat(response,startsWith("HTTP/1.1 403 Forbidden"));
+
+ response = _connector.getResponses("HEAD /ctx/acme/wholesale/index.html HTTP/1.0\r\n" +
+ "Authorization: Basic " + B64Code.encode("harry:password") + "\r\n" +
+ "\r\n");
+ assertThat(response,startsWith("HTTP/1.1 403 Forbidden"));
+
+ response = _connector.getResponses("HEAD /ctx/acme/retail/index.html HTTP/1.0\r\n" +
+ "Authorization: Basic " + B64Code.encode("harry:password") + "\r\n" +
+ "\r\n");
+ assertThat(response,startsWith("HTTP/1.1 403 Forbidden"));
+
+ //a user in role CONTRACTOR can do a GET
+ response = _connector.getResponses("GET /ctx/acme/wholesale/index.html HTTP/1.0\r\n" +
+ "Authorization: Basic " + B64Code.encode("chris:password") + "\r\n" +
+ "\r\n");
+
+ assertThat(response,startsWith("HTTP/1.1 200 OK"));
+
+ //a user in role CONTRACTOR can only do a post if confidential
+ response = _connector.getResponses("POST /ctx/acme/wholesale/index.html HTTP/1.0\r\n" +
+ "Authorization: Basic " + B64Code.encode("chris:password") + "\r\n" +
+ "\r\n");
+ assertThat(response,startsWith("HTTP/1.1 403 !"));
+
+ //a user in role HOMEOWNER can do a GET
+ response = _connector.getResponses("GET /ctx/acme/retail/index.html HTTP/1.0\r\n" +
+ "Authorization: Basic " + B64Code.encode("harry:password") + "\r\n" +
+ "\r\n");
+ assertThat(response,startsWith("HTTP/1.1 200 OK"));
+ }
+
+
+ private class RequestHandler extends AbstractHandler
+ {
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response ) throws IOException, ServletException
+ {
+ baseRequest.setHandled(true);
+
+ response.setStatus(200);
+ response.setContentType("text/plain; charset=UTF-8");
+ response.getWriter().println("URI="+request.getRequestURI());
+ String user = request.getRemoteUser();
+ response.getWriter().println("user="+user);
+ if (request.getParameter("test_parameter")!=null)
+ response.getWriter().println(request.getParameter("test_parameter"));
+ }
+ }
+
+}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java
index 98a86b8a01..5484a62253 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java
@@ -73,9 +73,9 @@ public abstract class HttpInput<T> extends ServletInputStream
@Override
public int read() throws IOException
{
- byte[] oneByte = new byte[1];
- int len = read(oneByte, 0, 1);
- return len < 0 ? len : 0xFF & oneByte[0];
+ byte[] bytes = new byte[1];
+ int read = read(bytes, 0, 1);
+ return read < 0 ? -1 : 0xff & bytes[0];
}
@Override
@@ -127,7 +127,6 @@ public abstract class HttpInput<T> extends ServletInputStream
}
return get(item, b, off, len);
}
-
protected abstract int remaining(T item);
protected abstract int get(T item, byte[] buffer, int offset, int length);
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java
index 7c6c19fa63..8cbc9d0f6f 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java
@@ -173,6 +173,7 @@ public class HttpOutput extends ServletOutputStream
_channel.write(_aggregate, false);
}
+
@Override
public void write(int b) throws IOException
{
@@ -181,7 +182,7 @@ public class HttpOutput extends ServletOutputStream
if (_aggregate == null)
_aggregate = _channel.getByteBufferPool().acquire(getBufferSize(), false);
-
+
BufferUtil.append(_aggregate, (byte)b);
_written++;
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java
index 4975a2c600..e203a3f4d4 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java
@@ -49,6 +49,8 @@ import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletRequestAttributeEvent;
import javax.servlet.ServletRequestAttributeListener;
+import javax.servlet.ServletRequestEvent;
+import javax.servlet.ServletRequestListener;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
@@ -71,6 +73,7 @@ import org.eclipse.jetty.server.handler.ContextHandler.Context;
import org.eclipse.jetty.server.session.AbstractSessionManager;
import org.eclipse.jetty.util.Attributes;
import org.eclipse.jetty.util.AttributesMap;
+import org.eclipse.jetty.util.MultiException;
import org.eclipse.jetty.util.MultiMap;
import org.eclipse.jetty.util.MultiPartInputStream;
import org.eclipse.jetty.util.StringUtil;
@@ -114,6 +117,8 @@ import org.eclipse.jetty.util.log.Logger;
public class Request implements HttpServletRequest
{
public static final String __MULTIPART_CONFIG_ELEMENT = "org.eclipse.multipartConfig";
+ public static final String __MULTIPART_INPUT_STREAM = "org.eclipse.multiPartInputStream";
+ public static final String __MULTIPART_CONTEXT = "org.eclipse.multiPartContext";
private static final Logger LOG = Log.getLogger(Request.class);
private static final Collection<Locale> __defaultLocale = Collections.singleton(Locale.getDefault());
@@ -123,6 +128,42 @@ public class Request implements HttpServletRequest
private final HttpFields _fields=new HttpFields();
private final List<ServletRequestAttributeListener> _requestAttributeListeners=new ArrayList<>();
private final HttpInput<?> _input;
+
+ public static class MultiPartCleanerListener implements ServletRequestListener
+ {
+ @Override
+ public void requestDestroyed(ServletRequestEvent sre)
+ {
+ //Clean up any tmp files created by MultiPartInputStream
+ MultiPartInputStream mpis = (MultiPartInputStream)sre.getServletRequest().getAttribute(__MULTIPART_INPUT_STREAM);
+ if (mpis != null)
+ {
+ ContextHandler.Context context = (ContextHandler.Context)sre.getServletRequest().getAttribute(__MULTIPART_CONTEXT);
+
+ //Only do the cleanup if we are exiting from the context in which a servlet parsed the multipart files
+ if (context == sre.getServletContext())
+ {
+ try
+ {
+ mpis.deleteParts();
+ }
+ catch (MultiException e)
+ {
+ sre.getServletContext().log("Errors deleting multipart tmp files", e);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void requestInitialized(ServletRequestEvent sre)
+ {
+ //nothing to do, multipart config set up by ServletHolder.handle()
+ }
+
+ }
+
+
private boolean _secure;
private boolean _asyncSupported = true;
@@ -1763,6 +1804,7 @@ public class Request implements HttpServletRequest
public void setQueryString(String queryString)
{
_queryString = queryString;
+ _queryEncoding = null; //assume utf-8
}
/* ------------------------------------------------------------ */
@@ -1950,9 +1992,16 @@ public class Request implements HttpServletRequest
if (_multiPartInputStream == null)
{
+ MultipartConfigElement config = (MultipartConfigElement)getAttribute(__MULTIPART_CONFIG_ELEMENT);
+
+ if (config == null)
+ throw new IllegalStateException("No multipart config for servlet");
+
_multiPartInputStream = new MultiPartInputStream(getInputStream(),
- getContentType(),(MultipartConfigElement)getAttribute(__MULTIPART_CONFIG_ELEMENT),
+ getContentType(),config,
(_context != null?(File)_context.getAttribute("javax.servlet.context.tempdir"):null));
+ setAttribute(__MULTIPART_INPUT_STREAM, _multiPartInputStream);
+ setAttribute(__MULTIPART_CONTEXT, _context);
Collection<Part> parts = _multiPartInputStream.getParts(); //causes parsing
for (Part p:parts)
{
@@ -1982,9 +2031,17 @@ public class Request implements HttpServletRequest
if (_multiPartInputStream == null)
{
+ MultipartConfigElement config = (MultipartConfigElement)getAttribute(__MULTIPART_CONFIG_ELEMENT);
+
+ if (config == null)
+ throw new IllegalStateException("No multipart config for servlet");
+
_multiPartInputStream = new MultiPartInputStream(getInputStream(),
- getContentType(),(MultipartConfigElement)getAttribute(__MULTIPART_CONFIG_ELEMENT),
+ getContentType(), config,
(_context != null?(File)_context.getAttribute("javax.servlet.context.tempdir"):null));
+
+ setAttribute(__MULTIPART_INPUT_STREAM, _multiPartInputStream);
+ setAttribute(__MULTIPART_CONTEXT, _context);
Collection<Part> parts = _multiPartInputStream.getParts(); //causes parsing
for (Part p:parts)
{
@@ -2042,7 +2099,7 @@ public class Request implements HttpServletRequest
{
// extract parameters from dispatch query
MultiMap<String> parameters = new MultiMap<>();
- UrlEncoded.decodeTo(query,parameters,getCharacterEncoding());
+ UrlEncoded.decodeTo(query,parameters, StringUtil.__UTF8); //have to assume UTF-8 because we can't know otherwise
boolean merge_old_query = false;
@@ -2063,10 +2120,11 @@ public class Request implements HttpServletRequest
{
StringBuilder overridden_query_string = new StringBuilder();
MultiMap<String> overridden_old_query = new MultiMap<>();
- UrlEncoded.decodeTo(_queryString,overridden_old_query,getCharacterEncoding());
-
+ UrlEncoded.decodeTo(_queryString,overridden_old_query,getQueryEncoding());//decode using any queryencoding set for the request
+
+
MultiMap<String> overridden_new_query = new MultiMap<>();
- UrlEncoded.decodeTo(query,overridden_new_query,getCharacterEncoding());
+ UrlEncoded.decodeTo(query,overridden_new_query,StringUtil.__UTF8); //have to assume utf8 as we cannot know otherwise
for(String name: overridden_old_query.keySet())
{
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java
index 6294d4a3d1..4cc8a885cc 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java
@@ -22,7 +22,9 @@ import java.io.IOException;
import java.io.PrintWriter;
import java.nio.channels.IllegalSelectorException;
import java.util.Collection;
+import java.util.ArrayList;
import java.util.Collections;
+import java.util.Enumeration;
import java.util.Locale;
import java.util.concurrent.atomic.AtomicInteger;
import javax.servlet.RequestDispatcher;
@@ -58,6 +60,15 @@ public class Response implements HttpServletResponse
{
private static final Logger LOG = Log.getLogger(Response.class);
+ /* ------------------------------------------------------------ */
+ public static Response getResponse(HttpServletResponse response)
+ {
+ if (response instanceof Response)
+ return (Response)response;
+ return HttpChannel.getCurrentHttpChannel().getResponse();
+ }
+
+
public enum OutputType
{
NONE, STREAM, WRITER
@@ -76,7 +87,7 @@ public class Response implements HttpServletResponse
*/
public final static String HTTP_ONLY_COMMENT = "__HTTP_ONLY__";
- private final HttpChannel _channel;
+ private final HttpChannel<?> _channel;
private final HttpOutput _out;
private final HttpFields _fields = new HttpFields();
private final AtomicInteger _include = new AtomicInteger();
@@ -905,6 +916,22 @@ public class Response implements HttpServletResponse
}
}
+ public void reset(boolean preserveCookies)
+ {
+ if (!preserveCookies)
+ reset();
+ else
+ {
+ ArrayList<String> cookieValues = new ArrayList<String>(5);
+ Enumeration<String> vals = _fields.getValues(HttpHeader.SET_COOKIE.asString());
+ while (vals.hasMoreElements())
+ cookieValues.add(vals.nextElement());
+ reset();
+ for (String v:cookieValues)
+ _fields.add(HttpHeader.SET_COOKIE, v);
+ }
+ }
+
public void resetForForward()
{
resetBuffer();
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java
index 767ea8fa03..564e01b1dc 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java
@@ -443,7 +443,7 @@ public class Server extends HandlerWrapper implements Attributes
baseRequest.setRequestURI(null);
baseRequest.setPathInfo(baseRequest.getRequestURI());
if (uri.getQuery()!=null)
- baseRequest.mergeQueryString(uri.getQuery());
+ baseRequest.mergeQueryString(uri.getQuery()); //we have to assume dispatch path and query are UTF8
}
final String target=baseRequest.getPathInfo();
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ShutdownHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ShutdownHandler.java
index 3ddfad8b2f..c4f07a5d52 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ShutdownHandler.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ShutdownHandler.java
@@ -50,7 +50,7 @@ import org.eclipse.jetty.util.log.Logger;
<pre>
public static void attemptShutdown(int port, String shutdownCookie) {
try {
- URL url = new URL("http://localhost:" + port + "/shutdown?cookie=" + shutdownCookie);
+ URL url = new URL("http://localhost:" + port + "/shutdown?token=" + shutdownCookie);
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
connection.setRequestMethod("POST");
connection.getResponseCode();
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java
index c277db35ab..4d34ef78b9 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java
@@ -21,7 +21,6 @@ package org.eclipse.jetty.server.session;
import java.io.IOException;
import java.util.EnumSet;
import java.util.EventListener;
-
import javax.servlet.DispatcherType;
import javax.servlet.ServletException;
import javax.servlet.SessionTrackingMode;
@@ -39,7 +38,8 @@ import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
/* ------------------------------------------------------------ */
-/** SessionHandler.
+/**
+ * SessionHandler.
*/
public class SessionHandler extends ScopedHandler
{
@@ -52,9 +52,8 @@ public class SessionHandler extends ScopedHandler
private SessionManager _sessionManager;
/* ------------------------------------------------------------ */
- /** Constructor.
- * Construct a SessionHandler witha a HashSessionManager with a standard
- * java.util.Random generator is created.
+ /**
+ * Constructor. Construct a SessionHandler witha a HashSessionManager with a standard java.util.Random generator is created.
*/
public SessionHandler()
{
@@ -63,7 +62,8 @@ public class SessionHandler extends ScopedHandler
/* ------------------------------------------------------------ */
/**
- * @param manager The session manager
+ * @param manager
+ * The session manager
*/
public SessionHandler(SessionManager manager)
{
@@ -81,13 +81,14 @@ public class SessionHandler extends ScopedHandler
/* ------------------------------------------------------------ */
/**
- * @param sessionManager The sessionManager to set.
+ * @param sessionManager
+ * The sessionManager to set.
*/
public void setSessionManager(SessionManager sessionManager)
{
if (isStarted())
throw new IllegalStateException();
- if (sessionManager!=null)
+ if (sessionManager != null)
sessionManager.setSessionHandler(this);
updateBean(_sessionManager,sessionManager);
_sessionManager=sessionManager;
@@ -104,6 +105,7 @@ public class SessionHandler extends ScopedHandler
setSessionManager(new HashSessionManager());
super.doStart();
}
+
/* ------------------------------------------------------------ */
/*
* @see org.eclipse.thread.AbstractLifeCycle#doStop()
@@ -121,12 +123,11 @@ public class SessionHandler extends ScopedHandler
* @see org.eclipse.jetty.server.Handler#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, int)
*/
@Override
- public void doScope(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
- throws IOException, ServletException
+ public void doScope(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
- SessionManager old_session_manager=null;
- HttpSession old_session=null;
- HttpSession access=null;
+ SessionManager old_session_manager = null;
+ HttpSession old_session = null;
+ HttpSession access = null;
try
{
old_session_manager = baseRequest.getSessionManager();
@@ -141,54 +142,54 @@ public class SessionHandler extends ScopedHandler
}
// access any existing session
- HttpSession session=null;
- if (_sessionManager!=null)
+ HttpSession session = null;
+ if (_sessionManager != null)
{
- session=baseRequest.getSession(false);
- if (session!=null)
+ session = baseRequest.getSession(false);
+ if (session != null)
{
- if(session!=old_session)
+ if (session != old_session)
{
- access=session;
+ access = session;
HttpCookie cookie = _sessionManager.access(session,request.isSecure());
- if (cookie!=null ) // Handle changed ID or max-age refresh
+ if (cookie != null) // Handle changed ID or max-age refresh
baseRequest.getResponse().addCookie(cookie);
}
}
else
{
- session=baseRequest.recoverNewSession(_sessionManager);
- if (session!=null)
+ session = baseRequest.recoverNewSession(_sessionManager);
+ if (session != null)
baseRequest.setSession(session);
}
}
- if(LOG.isDebugEnabled())
+ if (LOG.isDebugEnabled())
{
- LOG.debug("sessionManager="+_sessionManager);
- LOG.debug("session="+session);
+ LOG.debug("sessionManager=" + _sessionManager);
+ LOG.debug("session=" + session);
}
// start manual inline of nextScope(target,baseRequest,request,response);
- if (_nextScope!=null)
- _nextScope.doScope(target,baseRequest,request, response);
- else if (_outerScope!=null)
- _outerScope.doHandle(target,baseRequest,request, response);
+ if (_nextScope != null)
+ _nextScope.doScope(target,baseRequest,request,response);
+ else if (_outerScope != null)
+ _outerScope.doHandle(target,baseRequest,request,response);
else
- doHandle(target,baseRequest,request, response);
+ doHandle(target,baseRequest,request,response);
// end manual inline (pathentic attempt to reduce stack depth)
}
finally
{
- if (access!=null)
+ if (access != null)
_sessionManager.complete(access);
HttpSession session = baseRequest.getSession(false);
- if (session!=null && old_session==null && session!=access)
+ if (session != null && old_session == null && session != access)
_sessionManager.complete(session);
- if (old_session_manager!=null && old_session_manager != _sessionManager)
+ if (old_session_manager != null && old_session_manager != _sessionManager)
{
baseRequest.setSessionManager(old_session_manager);
baseRequest.setSession(old_session);
@@ -201,91 +202,102 @@ public class SessionHandler extends ScopedHandler
* @see org.eclipse.jetty.server.Handler#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, int)
*/
@Override
- public void doHandle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
- throws IOException, ServletException
+ public void doHandle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
// start manual inline of nextHandle(target,baseRequest,request,response);
if (never())
nextHandle(target,baseRequest,request,response);
- else if (_nextScope!=null && _nextScope==_handler)
- _nextScope.doHandle(target,baseRequest,request, response);
- else if (_handler!=null)
- _handler.handle(target,baseRequest, request, response);
+ else if (_nextScope != null && _nextScope == _handler)
+ _nextScope.doHandle(target,baseRequest,request,response);
+ else if (_handler != null)
+ _handler.handle(target,baseRequest,request,response);
// end manual inline
}
/* ------------------------------------------------------------ */
- /** Look for a requested session ID in cookies and URI parameters
+ /**
+ * Look for a requested session ID in cookies and URI parameters
+ *
* @param baseRequest
* @param request
*/
protected void checkRequestedSessionId(Request baseRequest, HttpServletRequest request)
{
- String requested_session_id=request.getRequestedSessionId();
+ String requested_session_id = request.getRequestedSessionId();
SessionManager sessionManager = getSessionManager();
- if (requested_session_id!=null && sessionManager!=null)
+ if (requested_session_id != null && sessionManager != null)
{
- HttpSession session=sessionManager.getHttpSession(requested_session_id);
- if (session!=null && sessionManager.isValid(session))
+ HttpSession session = sessionManager.getHttpSession(requested_session_id);
+ if (session != null && sessionManager.isValid(session))
baseRequest.setSession(session);
return;
}
else if (!DispatcherType.REQUEST.equals(baseRequest.getDispatcherType()))
return;
- boolean requested_session_id_from_cookie=false;
- HttpSession session=null;
+ boolean requested_session_id_from_cookie = false;
+ HttpSession session = null;
// Look for session id cookie
if (_sessionManager.isUsingCookies())
{
- Cookie[] cookies=request.getCookies();
- if (cookies!=null && cookies.length>0)
+ Cookie[] cookies = request.getCookies();
+ if (cookies != null && cookies.length > 0)
{
final String sessionCookie=sessionManager.getSessionCookieConfig().getName();
- for (int i=0;i<cookies.length;i++)
+ for (int i = 0; i < cookies.length; i++)
{
if (sessionCookie.equalsIgnoreCase(cookies[i].getName()))
{
- requested_session_id=cookies[i].getValue();
+ requested_session_id = cookies[i].getValue();
requested_session_id_from_cookie = true;
- if(LOG.isDebugEnabled())
- LOG.debug("Got Session ID {} from cookie",requested_session_id);
- session=sessionManager.getHttpSession(requested_session_id);
- if (session!=null && sessionManager.isValid(session))
- break;
+ LOG.debug("Got Session ID {} from cookie",requested_session_id);
+
+ if (requested_session_id != null)
+ {
+ session = sessionManager.getHttpSession(requested_session_id);
+
+ if (session != null && sessionManager.isValid(session))
+ {
+ break;
+ }
+ }
+ else
+ {
+ LOG.warn("null session id from cookie");
+ }
}
}
}
}
- if (requested_session_id==null || session==null)
+ if (requested_session_id == null || session == null)
{
String uri = request.getRequestURI();
- String prefix=sessionManager.getSessionIdPathParameterNamePrefix();
- if (prefix!=null)
+ String prefix = sessionManager.getSessionIdPathParameterNamePrefix();
+ if (prefix != null)
{
int s = uri.indexOf(prefix);
- if (s>=0)
+ if (s >= 0)
{
- s+=prefix.length();
- int i=s;
- while (i<uri.length())
+ s += prefix.length();
+ int i = s;
+ while (i < uri.length())
{
- char c=uri.charAt(i);
- if (c==';'||c=='#'||c=='?'||c=='/')
+ char c = uri.charAt(i);
+ if (c == ';' || c == '#' || c == '?' || c == '/')
break;
i++;
}
requested_session_id = uri.substring(s,i);
requested_session_id_from_cookie = false;
- session=sessionManager.getHttpSession(requested_session_id);
- if(LOG.isDebugEnabled())
+ session = sessionManager.getHttpSession(requested_session_id);
+ if (LOG.isDebugEnabled())
LOG.debug("Got Session ID {} from URL",requested_session_id);
}
}
@@ -293,7 +305,7 @@ public class SessionHandler extends ScopedHandler
baseRequest.setRequestedSessionId(requested_session_id);
baseRequest.setRequestedSessionIdFromCookie(requested_session_id!=null && requested_session_id_from_cookie);
- if (session!=null && sessionManager.isValid(session))
+ if (session != null && sessionManager.isValid(session))
baseRequest.setSession(session);
}
@@ -303,14 +315,14 @@ public class SessionHandler extends ScopedHandler
*/
public void addEventListener(EventListener listener)
{
- if(_sessionManager!=null)
+ if (_sessionManager != null)
_sessionManager.addEventListener(listener);
}
/* ------------------------------------------------------------ */
public void clearEventListeners()
{
- if(_sessionManager!=null)
+ if (_sessionManager != null)
_sessionManager.clearEventListeners();
}
}
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java
index fbf0bc4a0f..c98c22be75 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java
@@ -36,13 +36,15 @@ import java.net.URL;
import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.Exchanger;
-
+import java.util.concurrent.atomic.AtomicBoolean;
import javax.servlet.ServletException;
+import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.EofException;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.StringUtil;
@@ -53,6 +55,8 @@ import org.junit.Assert;
import org.junit.Test;
import org.junit.matchers.JUnitMatchers;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertTrue;
/**
*
*/
@@ -174,9 +178,83 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
}
}
+ @Test
+ public void testExceptionThrownInHandler() throws Exception
+ {
+ configureServer(new AbstractHandler()
+ {
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ throw new ServletException("handler exception");
+ }
+ });
+
+ StringBuffer request = new StringBuffer("GET / HTTP/1.0\r\n");
+ request.append("Host: localhost\r\n\r\n");
+
+ Socket client = newSocket(HOST, _connector.getLocalPort());
+ OutputStream os = client.getOutputStream();
+
+ os.write(request.toString().getBytes());
+ os.flush();
+
+ String response = readResponse(client);
+ assertThat("response code is 500", response.contains("500"), is(true));
+ }
+
+ @Test
+ public void testInterruptedRequest() throws Exception
+ {
+ final AtomicBoolean fourBytesRead = new AtomicBoolean(false);
+ final AtomicBoolean earlyEOFException = new AtomicBoolean(false);
+ configureServer(new AbstractHandler()
+ {
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ baseRequest.setHandled(true);
+ int contentLength = request.getContentLength();
+ ServletInputStream inputStream = request.getInputStream();
+ for (int i = 0; i < contentLength; i++)
+ {
+ try
+ {
+ inputStream.read();
+ }
+ catch (EofException e)
+ {
+ earlyEOFException.set(true);
+ throw e;
+ }
+ if (i == 3)
+ fourBytesRead.set(true);
+ }
+ }
+ });
+
+ StringBuffer request = new StringBuffer("GET / HTTP/1.0\n");
+ request.append("Host: localhost\n");
+ request.append("Content-length: 6\n\n");
+ request.append("foo");
+
+ Socket client = newSocket(HOST, _connector.getLocalPort());
+ OutputStream os = client.getOutputStream();
+
+ os.write(request.toString().getBytes());
+ os.flush();
+ client.shutdownOutput();
+ String response = readResponse(client);
+ client.close();
+
+ assertThat("response contains 500", response, Matchers.containsString(" 500 "));
+ assertThat("The 4th byte (-1) has not been passed to the handler", fourBytesRead.get(), is(false));
+ assertThat("EofException has been caught", earlyEOFException.get(), is(true));
+ }
+
/*
- * Feed a full header method
- */
+ * Feed a full header method
+ */
@Test
public void testFullHeader() throws Exception
{
@@ -672,6 +750,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
// Handler that sends big blocks of data in each of 10 writes, and then sends the time it took for each big block.
protected static class BigBlockHandler extends AbstractHandler
{
+ @Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
byte[] buf = new byte[128 * 1024];
@@ -1262,6 +1341,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
new Thread()
{
+ @Override
public void run()
{
try
@@ -1295,6 +1375,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
public class NoopHandler extends AbstractHandler
{
+ @Override
public void handle(String target, Request baseRequest,
HttpServletRequest request, HttpServletResponse response) throws IOException,
ServletException
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpURITest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpURITest.java
index 9f3d523688..def37c10ad 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpURITest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpURITest.java
@@ -18,14 +18,18 @@
package org.eclipse.jetty.server;
+import static org.junit.Assert.assertNotNull;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.Charset;
+import java.util.Iterator;
+import java.util.Set;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.util.MultiMap;
import org.eclipse.jetty.util.StringUtil;
+import org.eclipse.jetty.util.TypeUtil;
import org.junit.Assert;
import org.junit.Test;
@@ -205,6 +209,105 @@ public class HttpURITest
}
}
+
+
+ @Test
+ public void testNoPercentEncodingOfQueryUsingNonUTF8() throws Exception
+ {
+ byte[] utf8_bytes = "/%D0%A1%D1%82%D1%80%D0%BE%D0%BD%D0%B3-%D1%84%D0%B8%D0%BB%D1%8C%D1%82%D1%80/%D0%BA%D0%B0%D1%82%D0%B0%D0%BB%D0%BE%D0%B3?".getBytes("UTF-8");
+ byte[] cp1251_bytes = TypeUtil.fromHexString("e2fbe1f0e0edee3dd2e5ecefe5f0e0f2f3f0e0");
+ String expectedCP1251String = new String(cp1251_bytes, "cp1251");
+ String expectedCP1251Key = new String(cp1251_bytes, 0, 7, "cp1251");
+ String expectedCP1251Value = new String(cp1251_bytes, 8, cp1251_bytes.length-8, "cp1251");
+
+ //paste both byte arrays together to form the uri
+ byte[] allbytes = new byte[utf8_bytes.length+cp1251_bytes.length];
+ int i=0;
+ for (;i<utf8_bytes.length;i++) {
+ allbytes[i] = utf8_bytes[i];
+ }
+ for (int j=0; j< cp1251_bytes.length;j++)
+ allbytes[i+j] = cp1251_bytes[j];
+
+ //Test using a HttpUri that expects a particular charset encoding. See URIUtil.__CHARSET
+ HttpURI uri = new HttpURI(Charset.forName("cp1251"));
+ uri.parse(allbytes, 0, allbytes.length);
+ assertEquals(expectedCP1251String, uri.getQuery("cp1251"));
+
+ //Test params decoded correctly
+ MultiMap params = new MultiMap();
+ uri.decodeQueryTo(params);
+ String val = params.getString(expectedCP1251Key);
+ assertNotNull(val);
+ assertEquals(expectedCP1251Value, val);
+
+ //Test using HttpURI where you pass in the charset encoding.
+ HttpURI httpuri = new HttpURI();
+ httpuri.parse(allbytes,0,allbytes.length);
+ assertNotNull(httpuri.getQuery("UTF-8")); //still get back a query string, just incorrectly encoded
+ assertEquals(expectedCP1251String, httpuri.getQuery("cp1251"));
+
+ //Test params decoded correctly
+ params.clear();
+ httpuri.decodeQueryTo(params, "cp1251");
+ val = params.getString(expectedCP1251Key);
+ assertNotNull(val);
+ assertEquals(expectedCP1251Value, val);
+
+ //test able to set the query encoding and call getQueryString multiple times
+ Request request = new Request(null,null);
+ request.setUri(httpuri);
+ request.setAttribute("org.eclipse.jetty.server.Request.queryEncoding", "ISO-8859-1");
+ assertNotNull (request.getQueryString()); //will be incorrect encoding but not null
+ request.setAttribute("org.eclipse.jetty.server.Request.queryEncoding", "cp1251");
+ assertEquals(expectedCP1251String, request.getQueryString());
+ }
+
+ @Test
+ public void testPercentEncodingOfQueryStringUsingNonUTF8() throws UnsupportedEncodingException
+ {
+
+ byte[] utf8_bytes = "/%D0%A1%D1%82%D1%80%D0%BE%D0%BD%D0%B3-%D1%84%D0%B8%D0%BB%D1%8C%D1%82%D1%80/%D0%BA%D0%B0%D1%82%D0%B0%D0%BB%D0%BE%D0%B3?".getBytes("UTF-8");
+ byte[] cp1251_bytes = "%e2%fb%e1%f0%e0%ed%ee=%d2%e5%ec%ef%e5%f0%e0%f2%f3%f0%e0".getBytes("cp1251");
+
+ byte[] key_bytes = TypeUtil.fromHexString("e2fbe1f0e0edee");
+ byte[] val_bytes = TypeUtil.fromHexString("d2e5ecefe5f0e0f2f3f0e0");
+ String expectedCP1251String = new String(cp1251_bytes, "cp1251");
+ String expectedCP1251Key = new String(key_bytes, "cp1251");
+ String expectedCP1251Value = new String(val_bytes, "cp1251");
+
+ byte[] allbytes = new byte[utf8_bytes.length+cp1251_bytes.length];
+
+ //stick both arrays together to form uri
+ int i=0;
+ for (;i<utf8_bytes.length;i++) {
+ allbytes[i] = utf8_bytes[i];
+ }
+ for (int j=0; j< cp1251_bytes.length;j++)
+ allbytes[i+j] = cp1251_bytes[j];
+
+
+ HttpURI httpuri = new HttpURI();
+ httpuri.parse(allbytes,0,allbytes.length);
+ assertNotNull(httpuri.getQuery("UTF-8")); //will be incorrectly encoded, but no errors
+ assertEquals(expectedCP1251String, httpuri.getQuery("cp1251"));
+
+ //test params decoded correctly
+ MultiMap params = new MultiMap();
+ httpuri.decodeQueryTo(params, "cp1251");
+ String val = params.getString(expectedCP1251Key);
+ assertNotNull(val);
+ assertEquals(expectedCP1251Value, val);
+
+ //test able to set the query encoding and call getQueryString multiple times
+ Request request = new Request(null,null);
+ request.setUri(httpuri);
+ request.setAttribute("org.eclipse.jetty.server.Request.queryEncoding", "ISO-8859-1");
+ assertNotNull (request.getQueryString()); //will be incorrect encoding but not null
+ request.setAttribute("org.eclipse.jetty.server.Request.queryEncoding", "cp1251");
+ assertEquals(expectedCP1251String, request.getQueryString());
+
+ }
@Test
public void testUnicodeErrors() throws UnsupportedEncodingException
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java
index 926a6ecfed..5b1756e1d1 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java
@@ -38,7 +38,9 @@ import java.util.Enumeration;
import java.util.Map;
import java.util.concurrent.TimeUnit;
+import javax.servlet.MultipartConfigElement;
import javax.servlet.ServletException;
+import javax.servlet.ServletRequestEvent;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -48,6 +50,7 @@ import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.MultiPartInputStream;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@@ -133,7 +136,7 @@ public class RequestTest
}
@Test
- public void testMultiPart() throws Exception
+ public void testMultiPartNoConfig() throws Exception
{
_handler._checker = new RequestTester()
{
@@ -143,14 +146,16 @@ public class RequestTest
try
{
Part foo = request.getPart("stuff");
- assertNotNull(foo);
- String value = request.getParameter("stuff");
- byte[] expected = "000000000000000000000000000000000000000000000000000".getBytes("ISO-8859-1");
- return value.equals(new String(expected, "ISO-8859-1"));
+ return false;
+ }
+ catch (IllegalStateException e)
+ {
+ //expected exception because no multipart config is set up
+ assertTrue(e.getMessage().startsWith("No multipart config"));
+ return true;
}
catch (Exception e)
{
- e.printStackTrace();
return false;
}
}
@@ -178,6 +183,66 @@ public class RequestTest
String responses=_connector.getResponses(request);
assertTrue(responses.startsWith("HTTP/1.1 200"));
}
+
+
+ @Test
+ public void testMultiPart() throws Exception
+ {
+ final File tmpDir = new File (System.getProperty("java.io.tmpdir"));
+ final File testTmpDir = new File (tmpDir, "reqtest");
+ testTmpDir.deleteOnExit();
+ assertTrue(testTmpDir.mkdirs());
+ assertTrue(testTmpDir.list().length == 0);
+
+ ContextHandler contextHandler = new ContextHandler();
+ contextHandler.setContextPath("/foo");
+ contextHandler.setResourceBase(".");
+ contextHandler.setHandler(new MultiPartRequestHandler(testTmpDir));
+ contextHandler.addEventListener(new Request.MultiPartCleanerListener()
+ {
+
+ @Override
+ public void requestDestroyed(ServletRequestEvent sre)
+ {
+ MultiPartInputStream m = (MultiPartInputStream)sre.getServletRequest().getAttribute(Request.__MULTIPART_INPUT_STREAM);
+ ContextHandler.Context c = (ContextHandler.Context)sre.getServletRequest().getAttribute(Request.__MULTIPART_CONTEXT);
+ assertNotNull (m);
+ assertNotNull (c);
+ assertTrue(c == sre.getServletContext());
+ assertTrue(!m.getParsedParts().isEmpty());
+ assertTrue(testTmpDir.list().length == 2);
+ super.requestDestroyed(sre);
+ String[] files = testTmpDir.list();
+ assertTrue(files.length == 0);
+ }
+
+ });
+ _server.stop();
+ _server.setHandler(contextHandler);
+ _server.start();
+
+ String multipart = "--AaB03x\r\n"+
+ "content-disposition: form-data; name=\"field1\"\r\n"+
+ "\r\n"+
+ "Joe Blow\r\n"+
+ "--AaB03x\r\n"+
+ "content-disposition: form-data; name=\"stuff\"; filename=\"foo.upload\"\r\n"+
+ "Content-Type: text/plain;charset=ISO-8859-1\r\n"+
+ "\r\n"+
+ "000000000000000000000000000000000000000000000000000\r\n"+
+ "--AaB03x--\r\n";
+
+ String request="GET /foo/x.html HTTP/1.1\r\n"+
+ "Host: whatever\r\n"+
+ "Content-Type: multipart/form-data; boundary=\"AaB03x\"\r\n"+
+ "Content-Length: "+multipart.getBytes().length+"\r\n"+
+ "\r\n"+
+ multipart;
+
+ String responses=_connector.getResponses(request);
+ System.err.println(responses);
+ assertTrue(responses.startsWith("HTTP/1.1 200"));
+ }
@Test
public void testBadUtf8ParamExtraction() throws Exception
@@ -945,4 +1010,43 @@ public class RequestTest
response.sendError(500);
}
}
+
+ private class MultiPartRequestHandler extends AbstractHandler
+ {
+ File tmpDir;
+
+ public MultiPartRequestHandler(File tmpDir)
+ {
+ this.tmpDir = tmpDir;
+ }
+
+
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ ((Request)request).setHandled(true);
+ try
+ {
+
+ MultipartConfigElement mpce = new MultipartConfigElement(tmpDir.getAbsolutePath(),-1, -1, 2);
+ request.setAttribute(Request.__MULTIPART_CONFIG_ELEMENT, mpce);
+
+ Part foo = request.getPart("stuff");
+ assertNotNull(foo);
+ assertTrue(foo.getSize() > 0);
+
+ response.setStatus(200);
+ }
+ catch (IllegalStateException e)
+ {
+ //expected exception because no multipart config is set up
+ assertTrue(e.getMessage().startsWith("No multipart config"));
+ response.setStatus(200);
+ }
+ catch (Exception e)
+ {
+ response.sendError(500);
+ }
+ }
+ }
}
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java
index 23edfc2a65..2c791abbcb 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java
@@ -20,6 +20,7 @@ package org.eclipse.jetty.server;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -29,6 +30,9 @@ import java.io.LineNumberReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
import java.util.Iterator;
import java.util.Locale;
@@ -564,6 +568,42 @@ public class ResponseTest
assertEquals("name=value;Path=/path;Domain=domain;Secure;HttpOnly;Comment=comment", set);
}
+
+
+ @Test
+ public void testCookiesWithReset() throws Exception
+ {
+ Response response = newResponse();
+
+ Cookie cookie=new Cookie("name","value");
+ cookie.setDomain("domain");
+ cookie.setPath("/path");
+ cookie.setSecure(true);
+ cookie.setComment("comment__HTTP_ONLY__");
+ response.addCookie(cookie);
+
+ Cookie cookie2=new Cookie("name2", "value2");
+ cookie2.setDomain("domain");
+ cookie2.setPath("/path");
+ response.addCookie(cookie2);
+
+ //keep the cookies
+ response.reset(true);
+
+ Enumeration<String> set = response.getHttpFields().getValues("Set-Cookie");
+
+ assertNotNull(set);
+ ArrayList<String> list = Collections.list(set);
+ assertEquals(2, list.size());
+ assertTrue(list.contains("name=value;Path=/path;Domain=domain;Secure;HttpOnly;Comment=comment"));
+ assertTrue(list.contains("name2=value2;Path=/path;Domain=domain"));
+
+ //get rid of the cookies
+ response.reset();
+
+ set = response.getHttpFields().getValues("Set-Cookie");
+ assertFalse(set.hasMoreElements());
+ }
private Response newResponse()
{
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SelectChannelServerSslTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SelectChannelServerSslTest.java
index 9fea2e8bdd..fe77943884 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SelectChannelServerSslTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SelectChannelServerSslTest.java
@@ -18,15 +18,12 @@
package org.eclipse.jetty.server.ssl;
-import static org.junit.Assert.assertEquals;
-
import java.io.FileInputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketException;
import java.security.KeyStore;
import java.util.Arrays;
-
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
@@ -39,6 +36,8 @@ import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
/**
* HttpServer Tester.
@@ -190,6 +189,12 @@ public class SelectChannelServerSslTest extends HttpServerTestBase
}
@Override
+ @Test
+ @Ignore("Override and ignore this test as SSLSocket.shutdownOutput() is not supported, " +
+ "but shutdownOutput() is needed by the test.")
+ public void testInterruptedRequest(){}
+
+ @Override
@Ignore
public void testAvailable() throws Exception
{
diff --git a/jetty-server/src/test/resources/jetty-logging.properties b/jetty-server/src/test/resources/jetty-logging.properties
deleted file mode 100644
index 459fe40840..0000000000
--- a/jetty-server/src/test/resources/jetty-logging.properties
+++ /dev/null
@@ -1,3 +0,0 @@
-org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
-#org.eclipse.jetty.LEVEL=DEBUG
-org.eclipse.jetty.server.Server.LEVEL=WARN
diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/DefaultServlet.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/DefaultServlet.java
index 74cdf50819..c8bbe277b6 100644
--- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/DefaultServlet.java
+++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/DefaultServlet.java
@@ -25,6 +25,7 @@ import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.ByteBuffer;
+import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
@@ -667,6 +668,9 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
String ifms=request.getHeader(HttpHeader.IF_MODIFIED_SINCE.asString());
if (ifms!=null)
{
+ //Get jetty's Response impl
+ Response r = Response.getResponse(response);
+
if (content!=null)
{
String mdlm=content.getLastModified();
@@ -674,9 +678,9 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
{
if (ifms.equals(mdlm))
{
- response.reset();
- response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
- response.flushBuffer();
+ r.reset(true);
+ r.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
+ r.flushBuffer();
return false;
}
}
@@ -686,10 +690,10 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
if (ifmsl!=-1)
{
if (resource.lastModified()/1000 <= ifmsl/1000)
- {
- response.reset();
- response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
- response.flushBuffer();
+ {
+ r.reset(true);
+ r.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
+ r.flushBuffer();
return false;
}
}
@@ -942,8 +946,6 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
pos=start;
}
- System.err.println("PART "+ibr);
-
IO.copy(in,multi,size);
pos+=size;
}
@@ -1022,6 +1024,8 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
if (_cacheControl!=null)
response.setHeader(HttpHeader.CACHE_CONTROL.asString(),_cacheControl);
}
+
+
/* ------------------------------------------------------------ */
/*
diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletContextHandler.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletContextHandler.java
index 0a86f76d51..e0f2730b10 100644
--- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletContextHandler.java
+++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletContextHandler.java
@@ -47,6 +47,7 @@ import javax.servlet.descriptor.JspPropertyGroupDescriptor;
import javax.servlet.descriptor.TaglibDescriptor;
import org.eclipse.jetty.security.ConstraintAware;
+import org.eclipse.jetty.security.ConstraintMapping;
import org.eclipse.jetty.security.ConstraintSecurityHandler;
import org.eclipse.jetty.security.SecurityHandler;
import org.eclipse.jetty.server.Dispatcher;
@@ -59,6 +60,7 @@ import org.eclipse.jetty.server.handler.HandlerWrapper;
import org.eclipse.jetty.server.session.SessionHandler;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
+import org.eclipse.jetty.util.security.Constraint;
/* ------------------------------------------------------------ */
@@ -393,10 +395,21 @@ public class ServletContextHandler extends ContextHandler
* @param registration ServletRegistration.Dynamic instance that setServletSecurity was called on
* @param servletSecurityElement new security info
* @return the set of exact URL mappings currently associated with the registration that are also present in the web.xml
- * security constratins and thus will be unaffected by this call.
+ * security constraints and thus will be unaffected by this call.
*/
public Set<String> setServletSecurity(ServletRegistration.Dynamic registration, ServletSecurityElement servletSecurityElement)
{
+ //Default implementation is to just accept them all. If using a webapp, then this behaviour is overridden in WebAppContext.setServletSecurity
+ Collection<String> pathSpecs = registration.getMappings();
+ if (pathSpecs != null)
+ {
+ for (String pathSpec:pathSpecs)
+ {
+ List<ConstraintMapping> mappings = ConstraintSecurityHandler.createConstraintsWithMappingsForPath(registration.getName(), pathSpec, servletSecurityElement);
+ for (ConstraintMapping m:mappings)
+ ((ConstraintAware)getSecurityHandler()).addConstraintMapping(m);
+ }
+ }
return Collections.emptySet();
}
diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java
index 4d0d206c6b..d5bef18aaf 100644
--- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java
+++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java
@@ -509,6 +509,8 @@ public class ServletHolder extends Holder<Servlet> implements UserIdentity.Scope
initJspServlet();
}
+ initMultiPart();
+
_servlet.init(_config);
}
catch (UnavailableException e)
@@ -565,6 +567,25 @@ public class ServletHolder extends Holder<Servlet> implements UserIdentity.Scope
}
}
+ /* ------------------------------------------------------------ */
+ /**
+ * Register a ServletRequestListener that will ensure tmp multipart
+ * files are deleted when the request goes out of scope.
+ *
+ * @throws Exception
+ */
+ protected void initMultiPart () throws Exception
+ {
+ //if this servlet can handle multipart requests, ensure tmp files will be
+ //cleaned up correctly
+ if (((Registration)getRegistration()).getMultipartConfig() != null)
+ {
+ //Register a listener to delete tmp files that are created as a result of this
+ //servlet calling Request.getPart() or Request.getParts()
+ ContextHandler ch = ((ContextHandler.Context)getServletHandler().getServletContext()).getContextHandler();
+ ch.addEventListener(new Request.MultiPartCleanerListener());
+ }
+ }
/* ------------------------------------------------------------ */
/**
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DispatcherTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DispatcherTest.java
index 235bea6886..34a8cd84b5 100644
--- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DispatcherTest.java
+++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DispatcherTest.java
@@ -19,6 +19,7 @@
package org.eclipse.jetty.servlet;
import java.io.IOException;
+import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
@@ -49,6 +50,7 @@ import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.server.handler.ResourceHandler;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+import org.eclipse.jetty.util.TypeUtil;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
@@ -111,6 +113,22 @@ public class DispatcherTest
assertEquals(expected, responses);
}
+ @Test
+ public void testForwardNonUTF8() throws Exception
+ {
+ _contextHandler.addServlet(ForwardNonUTF8Servlet.class, "/ForwardServlet/*");
+ _contextHandler.addServlet(AssertNonUTF8ForwardServlet.class, "/AssertForwardServlet/*");
+
+ String expected=
+ "HTTP/1.1 200 OK\r\n"+
+ "Content-Type: text/html\r\n"+
+ "Content-Length: 0\r\n"+
+ "\r\n";
+ String responses = _connector.getResponses("GET /context/ForwardServlet?do=assertforward&foreign=%d2%e5%ec%ef%e5%f0%e0%f2%f3%f0%e0&test=1 HTTP/1.0\n\n");
+
+ assertEquals(expected, responses);
+ }
+
@Test
public void testForwardWithParam() throws Exception
{
@@ -299,6 +317,7 @@ public class DispatcherTest
dispatcher = getServletContext().getRequestDispatcher("/IncludeServlet/includepath?do=assertforwardinclude");
else if(request.getParameter("do").equals("assertincludeforward"))
dispatcher = getServletContext().getRequestDispatcher("/AssertIncludeForwardServlet/assertpath?do=end");
+
else if(request.getParameter("do").equals("assertforward"))
dispatcher = getServletContext().getRequestDispatcher("/AssertForwardServlet?do=end&do=the");
else if(request.getParameter("do").equals("ctx.echo"))
@@ -309,6 +328,18 @@ public class DispatcherTest
}
}
+
+ public static class ForwardNonUTF8Servlet extends HttpServlet implements Servlet
+ {
+ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+ RequestDispatcher dispatcher = null;
+ request.setAttribute("org.eclipse.jetty.server.Request.queryEncoding", "cp1251");
+ dispatcher = getServletContext().getRequestDispatcher("/AssertForwardServlet?do=end&else=%D0%B2%D1%8B%D0%B1%D1%80%D0%B0%D0%BD%D0%BE%3D%D0%A2%D0%B5%D0%BC%D0%BF%D0%B5%D1%80%D0%B0%D1%82%D1%83%D1%80%D0%B0");
+ dispatcher.forward(request, response);
+ }
+ }
+
/*
* Forward filter works with roger, echo and reverse echo servlets to test various
* forwarding bits using filters.
@@ -527,6 +558,45 @@ public class DispatcherTest
response.setStatus(HttpServletResponse.SC_OK);
}
}
+
+
+
+ public static class AssertNonUTF8ForwardServlet extends HttpServlet implements Servlet
+ {
+ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+ byte[] cp1251_bytes = TypeUtil.fromHexString("d2e5ecefe5f0e0f2f3f0e0");
+ String expectedCP1251String = new String(cp1251_bytes, "cp1251");
+
+ assertEquals( "/context/ForwardServlet", request.getAttribute(Dispatcher.FORWARD_REQUEST_URI));
+ assertEquals( "/context", request.getAttribute(Dispatcher.FORWARD_CONTEXT_PATH) );
+ assertEquals( "/ForwardServlet", request.getAttribute(Dispatcher.FORWARD_SERVLET_PATH));
+ assertEquals( null, request.getAttribute(Dispatcher.FORWARD_PATH_INFO));
+ assertEquals( "do=assertforward&foreign=%d2%e5%ec%ef%e5%f0%e0%f2%f3%f0%e0&test=1", request.getAttribute(Dispatcher.FORWARD_QUERY_STRING) );
+
+ List<String> expectedAttributeNames = Arrays.asList(Dispatcher.FORWARD_REQUEST_URI, Dispatcher.FORWARD_CONTEXT_PATH,
+ Dispatcher.FORWARD_SERVLET_PATH, Dispatcher.FORWARD_QUERY_STRING);
+ List<String> requestAttributeNames = Collections.list(request.getAttributeNames());
+ assertTrue(requestAttributeNames.containsAll(expectedAttributeNames));
+
+ assertEquals(null, request.getPathInfo());
+ assertEquals(null, request.getPathTranslated());
+ assertTrue(request.getQueryString().startsWith("do=end&else=%D0%B2%D1%8B%D0%B1%D1%80%D0%B0%D0%BD%D0%BE%3D%D0%A2%D0%B5%D0%BC%D0%BF%D0%B5%D1%80%D0%B0%D1%82%D1%83%D1%80%D0%B0&test=1&foreign="));
+
+ String[] vals = request.getParameterValues("foreign");
+ assertTrue(vals!=null);
+ assertEquals(1, vals.length);
+ assertEquals(expectedCP1251String, vals[0]);
+
+ assertEquals("/context/AssertForwardServlet", request.getRequestURI());
+ assertEquals("/context", request.getContextPath());
+ assertEquals("/AssertForwardServlet", request.getServletPath());
+
+ response.setContentType("text/html");
+ response.setStatus(HttpServletResponse.SC_OK);
+ }
+ }
+
public static class AssertIncludeServlet extends HttpServlet implements Servlet
{
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ResponseHeadersTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ResponseHeadersTest.java
index b652acf8c3..2a6b275eba 100644
--- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ResponseHeadersTest.java
+++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ResponseHeadersTest.java
@@ -89,7 +89,6 @@ public class ResponseHeadersTest
}
int port = connector.getLocalPort();
serverUri = new URI(String.format("http://%s:%d/",host,port));
- System.out.printf("Server URI: %s%n",serverUri);
}
@AfterClass
@@ -130,10 +129,8 @@ public class ResponseHeadersTest
// Read response
String respHeader = readResponseHeader(in);
- System.out.println("RESPONSE: " + respHeader);
// Now test for properly formatted HTTP Response Headers.
-
Assert.assertThat("Response Code",respHeader,startsWith("HTTP/1.1 101 Switching Protocols"));
Assert.assertThat("Response Header Upgrade",respHeader,containsString("Upgrade: WebSocket\r\n"));
Assert.assertThat("Response Header Connection",respHeader,containsString("Connection: Upgrade\r\n"));
diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/GzipFilter.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/GzipFilter.java
index da85e5dee6..81a05fcbf8 100644
--- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/GzipFilter.java
+++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/GzipFilter.java
@@ -105,6 +105,7 @@ public class GzipFilter extends UserAgentFilter
private static final Logger LOG = Log.getLogger(GzipFilter.class);
public final static String GZIP="gzip";
public final static String DEFLATE="deflate";
+
protected Set<String> _mimeTypes;
protected int _bufferSize=8192;
@@ -115,6 +116,11 @@ public class GzipFilter extends UserAgentFilter
protected Set<Pattern> _excludedAgentPatterns;
protected Set<String> _excludedPaths;
protected Set<Pattern> _excludedPathPatterns;
+
+ private static final int STATE_SEPARATOR = 0;
+ private static final int STATE_Q = 1;
+ private static final int STATE_QVALUE = 2;
+ private static final int STATE_DEFAULT = 3;
/* ------------------------------------------------------------ */
@@ -262,15 +268,89 @@ public class GzipFilter extends UserAgentFilter
{
// TODO, this could be a little more robust.
// prefer gzip over deflate
+ String compression = null;
if (encodingHeader!=null)
{
- if (encodingHeader.toLowerCase().contains(GZIP))
- return GZIP;
- else if (encodingHeader.toLowerCase().contains(DEFLATE))
- return DEFLATE;
+
+ String[] encodings = getEncodings(encodingHeader);
+ if (encodings != null)
+ {
+ for (int i=0; i< encodings.length; i++)
+ {
+ if (encodings[i].toLowerCase().contains(GZIP))
+ {
+ if (isEncodingAcceptable(encodings[i]))
+ {
+ compression = GZIP;
+ break; //prefer Gzip over deflate
+ }
+ }
+
+ if (encodings[i].toLowerCase().contains(DEFLATE))
+ {
+ if (isEncodingAcceptable(encodings[i]))
+ {
+ compression = DEFLATE; //Keep checking in case gzip is acceptable
+ }
+ }
+ }
+ }
}
- return null;
+ return compression;
+ }
+
+
+ private String[] getEncodings (String encodingHeader)
+ {
+ if (encodingHeader == null)
+ return null;
+ return encodingHeader.split(",");
+ }
+
+ private boolean isEncodingAcceptable(String encoding)
+ {
+ int state = STATE_DEFAULT;
+ int qvalueIdx = -1;
+ for (int i=0;i<encoding.length();i++)
+ {
+ char c = encoding.charAt(i);
+ switch (state)
+ {
+ case STATE_DEFAULT:
+ {
+ if (';' == c)
+ state = STATE_SEPARATOR;
+ break;
+ }
+ case STATE_SEPARATOR:
+ {
+ if ('q' == c || 'Q' == c)
+ state = STATE_Q;
+ break;
+ }
+ case STATE_Q:
+ {
+ if ('=' == c)
+ state = STATE_QVALUE;
+ break;
+ }
+ case STATE_QVALUE:
+ {
+ if (qvalueIdx < 0 && '0' == c || '1' == c)
+ qvalueIdx = i;
+ break;
+ }
+ }
+ }
+
+ if (qvalueIdx < 0)
+ return true;
+
+ if ("0".equals(encoding.substring(qvalueIdx).trim()))
+ return false;
+ return true;
}
+
protected CompressedResponseWrapper createWrappedResponse(HttpServletRequest request, HttpServletResponse response, final String compressionType)
{
diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/MultiPartFilter.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/MultiPartFilter.java
index f14b7b6935..13a749ca83 100644
--- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/MultiPartFilter.java
+++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/MultiPartFilter.java
@@ -77,7 +77,7 @@ import org.eclipse.jetty.util.StringUtil;
public class MultiPartFilter implements Filter
{
public final static String CONTENT_TYPE_SUFFIX=".org.eclipse.jetty.servlet.contentType";
- private final static String FILES ="org.eclipse.jetty.servlet.MultiPartFilter.files";
+ private final static String MULTIPART = "org.eclipse.jetty.servlet.MultiPartFile.multiPartInputStream";
private File tempdir;
private boolean _deleteFiles;
private ServletContext _context;
@@ -141,7 +141,8 @@ public class MultiPartFilter implements Filter
MultipartConfigElement config = new MultipartConfigElement(tempdir.getCanonicalPath(), _maxFileSize, _maxRequestSize, _fileOutputBuffer);
MultiPartInputStream mpis = new MultiPartInputStream(in, content_type, config, tempdir);
-
+ mpis.setDeleteOnExit(_deleteFiles);
+ request.setAttribute(MULTIPART, mpis);
try
{
Collection<Part> parts = mpis.getParts();
@@ -161,18 +162,6 @@ public class MultiPartFilter implements Filter
if (mp.getContentType() != null)
params.add(mp.getName()+CONTENT_TYPE_SUFFIX, mp.getContentType());
}
- if (_deleteFiles)
- {
- mp.getFile().deleteOnExit();
-
- ArrayList<File> files = (ArrayList<File>)request.getAttribute(FILES);
- if (files==null)
- {
- files=new ArrayList<>();
- request.setAttribute(FILES,files);
- }
- files.add(mp.getFile());
- }
}
else
{
@@ -193,24 +182,27 @@ public class MultiPartFilter implements Filter
deleteFiles(request);
}
}
-
+
+
+ /* ------------------------------------------------------------ */
private void deleteFiles(ServletRequest request)
{
- ArrayList<File> files = (ArrayList<File>)request.getAttribute(FILES);
- if (files!=null)
+ if (!_deleteFiles)
+ return;
+
+ MultiPartInputStream mpis = (MultiPartInputStream)request.getAttribute(MULTIPART);
+ if (mpis != null)
{
- for (File file : files)
+ try
{
- try
- {
- file.delete();
- }
- catch (Exception e)
- {
- _context.log("failed to delete " + file, e);
- }
+ mpis.deleteParts();
+ }
+ catch (Exception e)
+ {
+ _context.log("Error deleting multipart tmp files", e);
}
}
+ request.removeAttribute(MULTIPART);
}
/* ------------------------------------------------------------------------------- */
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterDefaultNoRecompressTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterDefaultNoRecompressTest.java
index 0cebfd6c78..9cadebba23 100644
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterDefaultNoRecompressTest.java
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterDefaultNoRecompressTest.java
@@ -65,6 +65,10 @@ public class GzipFilterDefaultNoRecompressTest
{ "jetty_logo.tiff", "image/tiff", GzipFilter.GZIP },
{ "jetty_logo.xcf", "image/xcf", GzipFilter.GZIP },
{ "jetty_logo.jp2", "image/jpeg2000", GzipFilter.GZIP },
+ //qvalue disables compression
+ { "test_quotes.txt", "text/plain", GzipFilter.GZIP+";q=0"},
+ { "test_quotes.txt", "text/plain", GzipFilter.GZIP+"; q = 0 "},
+
// Same tests again for deflate
// Some already compressed files
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterDefaultTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterDefaultTest.java
index e3d1fa3b41..8911b54361 100644
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterDefaultTest.java
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterDefaultTest.java
@@ -123,6 +123,52 @@ public class GzipFilterDefaultTest
}
@Test
+ public void testIsGzipCompressedTinyWithQ() throws Exception
+ {
+ GzipTester tester = new GzipTester(testingdir, compressionType+";q=0.5");
+
+ // Test content that is smaller than the buffer.
+ int filesize = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE / 4;
+ tester.prepareServerFile("file.txt",filesize);
+
+ FilterHolder holder = tester.setContentServlet(org.eclipse.jetty.servlet.DefaultServlet.class);
+ holder.setInitParameter("mimeTypes","text/plain");
+
+ try
+ {
+ tester.start();
+ tester.assertIsResponseGzipCompressed("file.txt");
+ }
+ finally
+ {
+ tester.stop();
+ }
+ }
+
+ @Test
+ public void testIsGzipCompressedTinyWithBadQ() throws Exception
+ {
+ GzipTester tester = new GzipTester(testingdir, compressionType+";q=");
+
+ // Test content that is smaller than the buffer.
+ int filesize = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE / 4;
+ tester.prepareServerFile("file.txt",filesize);
+
+ FilterHolder holder = tester.setContentServlet(org.eclipse.jetty.servlet.DefaultServlet.class);
+ holder.setInitParameter("mimeTypes","text/plain");
+
+ try
+ {
+ tester.start();
+ tester.assertIsResponseGzipCompressed("file.txt");
+ }
+ finally
+ {
+ tester.stop();
+ }
+ }
+
+ @Test
public void testIsGzipCompressedLarge() throws Exception
{
GzipTester tester = new GzipTester(testingdir, compressionType);
@@ -146,6 +192,28 @@ public class GzipFilterDefaultTest
}
@Test
+ public void testIsNotGzipCompressedWithQ() throws Exception
+ {
+ GzipTester tester = new GzipTester(testingdir, compressionType+"; q = 0");
+
+ int filesize = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE / 4;
+ tester.prepareServerFile("file.txt",filesize);
+
+ FilterHolder holder = tester.setContentServlet(org.eclipse.jetty.servlet.DefaultServlet.class);
+ holder.setInitParameter("mimeTypes","text/plain");
+
+ try
+ {
+ tester.start();
+ tester.assertIsResponseNotGzipCompressed("file.txt", filesize, HttpStatus.OK_200);
+ }
+ finally
+ {
+ tester.stop();
+ }
+ }
+
+ @Test
public void testIsNotGzipCompressed() throws Exception
{
GzipTester tester = new GzipTester(testingdir, compressionType);
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/MultipartFilterTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/MultipartFilterTest.java
index d6d7936064..d592ceaf78 100644
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/MultipartFilterTest.java
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/MultipartFilterTest.java
@@ -113,7 +113,7 @@ public class MultipartFilterTest
response = HttpTester.parseResponse(tester.getResponses(request.generate()));
- assertEquals(HttpServletResponse.SC_OK,response.getStatus());
+ assertEquals(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,response.getStatus());
}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/GzipTester.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/GzipTester.java
index 125b51280a..a643e366f4 100644
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/GzipTester.java
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/GzipTester.java
@@ -98,7 +98,11 @@ public class GzipTester
// Assert the response headers
// Assert.assertThat("Response.status",response.getStatus(),is(HttpServletResponse.SC_OK));
Assert.assertThat("Response.header[Content-Length]",response.get("Content-Length"),notNullValue());
- Assert.assertThat("Response.header[Content-Encoding]",response.get("Content-Encoding"),containsString(compressionType));
+ int qindex = compressionType.indexOf(";");
+ if (qindex < 0)
+ Assert.assertThat("Response.header[Content-Encoding]",response.get("Content-Encoding"),containsString(compressionType));
+ else
+ Assert.assertThat("Response.header[Content-Encoding]", response.get("Content-Encoding"),containsString(compressionType.substring(0,qindex)));
// Assert that the decompressed contents are what we expect.
File serverFile = testdir.getFile(serverFilename);
@@ -111,11 +115,11 @@ public class GzipTester
try
{
bais = new ByteArrayInputStream(response.getContentBytes());
- if (compressionType.equals(GzipFilter.GZIP))
+ if (compressionType.startsWith(GzipFilter.GZIP))
{
in = new GZIPInputStream(bais);
}
- else if (compressionType.equals(GzipFilter.DEFLATE))
+ else if (compressionType.startsWith(GzipFilter.DEFLATE))
{
in = new InflaterInputStream(bais, new Inflater(true));
}
@@ -247,6 +251,7 @@ public class GzipTester
Assert.assertEquals("Expected response equals actual response",expectedResponse,actual);
}
}
+
/**
* Asserts that the request results in a properly structured GzipFilter response, where the content is
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/CommandLineBuilder.java b/jetty-start/src/main/java/org/eclipse/jetty/start/CommandLineBuilder.java
index b92fa35553..7a7c91c728 100644
--- a/jetty-start/src/main/java/org/eclipse/jetty/start/CommandLineBuilder.java
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/CommandLineBuilder.java
@@ -41,7 +41,8 @@ public class CommandLineBuilder
*/
public void addArg(String arg)
{
- args.add(quote(arg));
+ if (arg != null)
+ args.add(quote(arg));
}
/**
@@ -84,7 +85,8 @@ public class CommandLineBuilder
*/
public void addRawArg(String arg)
{
- args.add(arg);
+ if (arg != null)
+ args.add(arg);
}
public List<String> getArgs()
@@ -106,7 +108,7 @@ public class CommandLineBuilder
return arg;
}
StringBuilder buf = new StringBuilder();
-// buf.append('"');
+ // buf.append('"');
boolean escaped = false;
for (char c : arg.toCharArray())
{
@@ -117,7 +119,7 @@ public class CommandLineBuilder
escaped = (c == '\\');
buf.append(c);
}
-// buf.append('"');
+ // buf.append('"');
return buf.toString();
}
@@ -133,7 +135,7 @@ public class CommandLineBuilder
{
buf.append(' ');
}
- buf.append(arg);
+ buf.append(quote(arg));
delim = true;
}
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/Main.java b/jetty-start/src/main/java/org/eclipse/jetty/start/Main.java
index 53593187fe..7766503522 100644
--- a/jetty-start/src/main/java/org/eclipse/jetty/start/Main.java
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/Main.java
@@ -30,6 +30,7 @@ import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
+import java.io.LineNumberReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.lang.reflect.InvocationTargetException;
@@ -167,9 +168,17 @@ public class Main
{
int port = Integer.parseInt(Config.getProperty("STOP.PORT","-1"));
String key = Config.getProperty("STOP.KEY",null);
- stop(port,key);
+ stop(port,key, false);
return null;
}
+
+ if ("--stop-wait".equals(arg))
+ {
+ int port = Integer.parseInt(Config.getProperty("STOP.PORT","-1"));
+ String key = Config.getProperty("STOP.KEY",null);
+ stop(port,key, true);
+ return null;
+ }
if ("--version".equals(arg) || "-v".equals(arg) || "--info".equals(arg))
{
@@ -268,7 +277,7 @@ public class Main
{
String opts[] = assign[1].split(",");
for (String opt : opts)
- _config.addActiveOption(opt);
+ _config.addActiveOption(opt.trim());
}
else
{
@@ -983,6 +992,12 @@ public class Main
*/
public void stop(int port, String key)
{
+ stop (port,key,false);
+ }
+
+
+ public void stop (int port, String key, boolean wait)
+ {
int _port = port;
String _key = key;
@@ -1005,6 +1020,14 @@ public class Main
OutputStream out = s.getOutputStream();
out.write((_key + "\r\nstop\r\n").getBytes());
out.flush();
+
+ if (wait)
+ {
+ LineNumberReader lin = new LineNumberReader(new InputStreamReader(s.getInputStream()));
+ String response=lin.readLine();
+ if ("Stopped".equals(response))
+ System.err.println("Stopped");
+ }
}
finally
{
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/Monitor.java b/jetty-start/src/main/java/org/eclipse/jetty/start/Monitor.java
index 3bc79cb893..ff29f11df9 100644
--- a/jetty-start/src/main/java/org/eclipse/jetty/start/Monitor.java
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/Monitor.java
@@ -17,6 +17,7 @@
//
package org.eclipse.jetty.start;
+import java.io.IOException;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.net.InetAddress;
@@ -113,21 +114,23 @@ public class Monitor extends Thread
Config.debug("command=" + cmd);
if ("stop".equals(cmd))
{
- try {socket.close();}catch(Exception e){e.printStackTrace();}
- try {_socket.close();}catch(Exception e){e.printStackTrace();}
if (_process!=null)
{
//if we have a child process, wait for it to finish before we stop
try
{
- _process.destroy();
- _process.waitFor();
+ _process.destroy();
+ _process.waitFor();
+
}
catch (InterruptedException e)
{
System.err.println("Interrupted waiting for child to terminate");
}
}
+ socket.getOutputStream().write("Stopped\r\n".getBytes());
+ try {socket.close();}catch(Exception e){e.printStackTrace();}
+ try {_socket.close();}catch(Exception e){e.printStackTrace();}
System.exit(0);
}
else if ("status".equals(cmd))
diff --git a/jetty-start/src/main/resources/org/eclipse/jetty/start/usage.txt b/jetty-start/src/main/resources/org/eclipse/jetty/start/usage.txt
index 5848974e70..7b4c991083 100644
--- a/jetty-start/src/main/resources/org/eclipse/jetty/start/usage.txt
+++ b/jetty-start/src/main/resources/org/eclipse/jetty/start/usage.txt
@@ -24,7 +24,10 @@ Command Line Options:
contains -X or -D arguments, but creates an extra
JVM instance.
- --stop Stop the running Jetty instance.
+ --stop Send a stop signal to the running Jetty instance.
+
+ --stop-wait Send a stop signal to the running Jetty instance, waiting for
+ confirmation that it is stopping.
--daemon Start in daemon mode with stderr and stdout
redirected to ${jetty.log}/start.log
diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/CommandLineBuilderTest.java b/jetty-start/src/test/java/org/eclipse/jetty/start/CommandLineBuilderTest.java
index 9f6fe07fd4..1d3b8afae9 100644
--- a/jetty-start/src/test/java/org/eclipse/jetty/start/CommandLineBuilderTest.java
+++ b/jetty-start/src/test/java/org/eclipse/jetty/start/CommandLineBuilderTest.java
@@ -18,21 +18,29 @@
package org.eclipse.jetty.start;
+import static org.hamcrest.Matchers.is;
+
import org.junit.Assert;
+import org.junit.Before;
import org.junit.Test;
import static org.hamcrest.Matchers.is;
public class CommandLineBuilderTest
{
- @Test
- public void testSimpleCommandline()
+ private CommandLineBuilder cmd = new CommandLineBuilder("java");
+
+ @Before
+ public void setUp()
{
- CommandLineBuilder cmd = new CommandLineBuilder("java");
cmd.addEqualsArg("-Djava.io.tmpdir","/home/java/temp dir/");
cmd.addArg("--version");
+ }
- Assert.assertThat(cmd.toString(), is("java -Djava.io.tmpdir=/home/java/temp\\ dir/ --version"));
+ @Test
+ public void testSimpleCommandline()
+ {
+ Assert.assertThat(cmd.toString(),is("java -Djava.io.tmpdir=/home/java/temp\\ dir/ --version"));
}
@Test
@@ -53,6 +61,12 @@ public class CommandLineBuilderTest
assertQuoting("/opt/jetty 7 \"special\"/home","/opt/jetty\\ 7\\ \\\"special\\\"/home");
}
+ @Test
+ public void testToStringIsQuotedEvenIfArgsAreNotQuotedForProcessBuilder()
+ {
+ System.out.println(cmd.toString());
+ }
+
private void assertQuoting(String raw, String expected)
{
String actual = CommandLineBuilder.quote(raw);
diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/MainTest.java b/jetty-start/src/test/java/org/eclipse/jetty/start/MainTest.java
index 0b70111d33..a19f292dbe 100644
--- a/jetty-start/src/test/java/org/eclipse/jetty/start/MainTest.java
+++ b/jetty-start/src/test/java/org/eclipse/jetty/start/MainTest.java
@@ -26,13 +26,17 @@ import java.util.List;
import java.util.Vector;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import static org.hamcrest.Matchers.containsString;
+import static org.junit.Assert.assertEquals;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.hasItems;
import static org.hamcrest.Matchers.notNullValue;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
/* ------------------------------------------------------------ */
/**
@@ -106,14 +110,24 @@ public class MainTest
Classpath classpath = nastyWayToCreateAClasspathObject("/jetty/home with spaces/");
CommandLineBuilder cmd = main.buildCommandLine(classpath,xmls);
- Assert.assertThat("CommandLineBuilder shouldn't be null",cmd,notNullValue());
- String commandLine = cmd.toString();
- Assert.assertThat("CommandLine shouldn't be null",commandLine,notNullValue());
- Assert.assertThat("Classpath should be correctly quoted and match expected value",commandLine,
- containsString("-cp /jetty/home with spaces/somejar.jar:/jetty/home with spaces/someotherjar.jar"));
- Assert.assertThat("CommandLine should contain jvmArgs",commandLine,containsString("--exec -Xms1024m -Xmx1024m"));
- Assert.assertThat("CommandLine should contain xmls",commandLine,containsString("jetty.xml jetty-jmx.xml jetty-logging.xml"));
+ assertThat("CommandLineBuilder shouldn't be null",cmd,notNullValue());
+
+ List<String> commandArgs = cmd.getArgs();
+ assertThat("commandArgs should contain 11 elements",commandArgs.size(),equalTo(11));
+ assertThat("args does not contain -cp",commandArgs,hasItems("-cp"));
+ assertThat("Classpath should be correctly quoted and match expected value",commandArgs,
+ hasItems("/jetty/home with spaces/somejar.jar:/jetty/home with spaces/someotherjar.jar"));
+ assertThat("args does not contain --exec",commandArgs,hasItems("--exec"));
+ assertThat("CommandLine should contain jvmArgs",commandArgs,hasItems("-Xms1024m"));
+ assertThat("CommandLine should contain jvmArgs", commandArgs, hasItems("-Xmx1024m"));
+ assertThat("CommandLine should contain xmls",commandArgs,hasItems("jetty.xml"));
+ assertThat("CommandLine should contain xmls",commandArgs,hasItems("jetty-jmx.xml"));
+ assertThat("CommandLine should contain xmls", commandArgs, hasItems("jetty-logging.xml"));
+ String commandLine = cmd.toString();
+ assertThat("cmd.toString() should be properly escaped",commandLine,containsString("-cp /jetty/home\\ with\\ " +
+ "spaces/somejar.jar:/jetty/home\\ with\\ spaces/someotherjar.jar"));
+ assertThat("cmd.toString() doesn't contain xml config files",commandLine,containsString(" jetty.xml jetty-jmx.xml jetty-logging.xml"));
}
private Classpath nastyWayToCreateAClasspathObject(String jettyHome) throws NoSuchFieldException, IllegalAccessException
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartInputStream.java b/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartInputStream.java
index f22d0dcc38..6d6efa788d 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartInputStream.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartInputStream.java
@@ -32,6 +32,7 @@ import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import javax.servlet.MultipartConfigElement;
import javax.servlet.ServletException;
@@ -53,7 +54,7 @@ public class MultiPartInputStream
protected MultiMap _parts;
protected File _tmpDir;
protected File _contextTmpDir;
-
+ protected boolean _deleteOnExit;
@@ -67,6 +68,7 @@ public class MultiPartInputStream
protected String _contentType;
protected MultiMap _headers;
protected long _size = 0;
+ protected boolean _temporary = true;
public MultiPart (String name, String filename)
throws IOException
@@ -135,6 +137,8 @@ public class MultiPartInputStream
throws IOException
{
_file = File.createTempFile("MultiPart", "", MultiPartInputStream.this._tmpDir);
+ if (_deleteOnExit)
+ _file.deleteOnExit();
FileOutputStream fos = new FileOutputStream(_file);
BufferedOutputStream bos = new BufferedOutputStream(fos);
@@ -197,11 +201,12 @@ public class MultiPartInputStream
{
if (_file != null)
{
+ //written to a file, whether temporary or not
return new BufferedInputStream (new FileInputStream(_file));
}
else
{
- //part content is in a ByteArrayOutputStream
+ //part content is in memory
return new ByteArrayInputStream(_bout.getBuf(),0,_bout.size());
}
}
@@ -226,7 +231,7 @@ public class MultiPartInputStream
*/
public long getSize()
{
- return _size;
+ return _size;
}
/**
@@ -236,8 +241,11 @@ public class MultiPartInputStream
{
if (_file == null)
{
+ _temporary = false;
+
//part data is only in the ByteArrayOutputStream and never been written to disk
_file = new File (_tmpDir, fileName);
+
BufferedOutputStream bos = null;
try
{
@@ -255,6 +263,8 @@ public class MultiPartInputStream
else
{
//the part data is already written to a temporary file, just rename it
+ _temporary = false;
+
File f = new File(_tmpDir, fileName);
if (_file.renameTo(f))
_file = f;
@@ -262,11 +272,24 @@ public class MultiPartInputStream
}
/**
+ * Remove the file, whether or not Part.write() was called on it
+ * (ie no longer temporary)
* @see javax.servlet.http.Part#delete()
*/
public void delete() throws IOException
{
- if (_file != null)
+ if (_file != null && _file.exists())
+ _file.delete();
+ }
+
+ /**
+ * Only remove tmp files.
+ *
+ * @throws IOException
+ */
+ public void cleanUp() throws IOException
+ {
+ if (_temporary && _file != null && _file.exists())
_file.delete();
}
@@ -308,12 +331,65 @@ public class MultiPartInputStream
_contextTmpDir = contextTmpDir;
if (_contextTmpDir == null)
_contextTmpDir = new File (System.getProperty("java.io.tmpdir"));
+
if (_config == null)
_config = new MultipartConfigElement(_contextTmpDir.getAbsolutePath());
}
+ /**
+ * Get the already parsed parts.
+ *
+ * @return
+ */
+ public Collection<Part> getParsedParts()
+ {
+ if (_parts == null)
+ return Collections.emptyList();
+
+ Collection<Object> values = _parts.values();
+ List<Part> parts = new ArrayList<Part>();
+ for (Object o: values)
+ {
+ List<Part> asList = LazyList.getList(o, false);
+ parts.addAll(asList);
+ }
+ return parts;
+ }
+ /**
+ * Delete any tmp storage for parts, and clear out the parts list.
+ *
+ * @throws MultiException
+ */
+ public void deleteParts ()
+ throws MultiException
+ {
+ Collection<Part> parts = getParsedParts();
+ MultiException err = new MultiException();
+ for (Part p:parts)
+ {
+ try
+ {
+ ((MultiPartInputStream.MultiPart)p).cleanUp();
+ }
+ catch(Exception e)
+ {
+ err.add(e);
+ }
+ }
+ _parts.clear();
+
+ err.ifExceptionThrowMulti();
+ }
+
+ /**
+ * Parse, if necessary, the multipart data and return the list of Parts.
+ *
+ * @return
+ * @throws IOException
+ * @throws ServletException
+ */
public Collection<Part> getParts()
throws IOException, ServletException
{
@@ -329,6 +405,14 @@ public class MultiPartInputStream
}
+ /**
+ * Get the named Part.
+ *
+ * @param name
+ * @return
+ * @throws IOException
+ * @throws ServletException
+ */
public Part getPart(String name)
throws IOException, ServletException
{
@@ -337,6 +421,12 @@ public class MultiPartInputStream
}
+ /**
+ * Parse, if necessary, the multipart stream.
+ *
+ * @throws IOException
+ * @throws ServletException
+ */
protected void parse ()
throws IOException, ServletException
{
@@ -583,6 +673,19 @@ public class MultiPartInputStream
part.close();
}
}
+ if (!lastPart)
+ throw new IOException("Incomplete parts");
+ }
+
+ public void setDeleteOnExit(boolean deleteOnExit)
+ {
+ _deleteOnExit = deleteOnExit;
+ }
+
+
+ public boolean isDeleteOnExit()
+ {
+ return _deleteOnExit;
}
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartOutputStream.java b/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartOutputStream.java
index a872d28615..3cb100e884 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartOutputStream.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartOutputStream.java
@@ -129,9 +129,6 @@ public class MultiPartOutputStream extends FilterOutputStream
{
out.write(b,off,len);
}
-
-
-
}
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/StringUtil.java b/jetty-util/src/main/java/org/eclipse/jetty/util/StringUtil.java
index 07b1be5aff..b86794899e 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/StringUtil.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/StringUtil.java
@@ -102,7 +102,7 @@ public class StringUtil
ByteBuffer slice = b.slice();
slice.position(position);
slice.limit(position+length);
- return BufferUtil.toString(slice);
+ return BufferUtil.toString(slice,__UTF8_CHARSET);
}
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/MultiPartInputStreamTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/MultiPartInputStreamTest.java
index e282ce5ea5..307af34066 100644
--- a/jetty-util/src/test/java/org/eclipse/jetty/util/MultiPartInputStreamTest.java
+++ b/jetty-util/src/test/java/org/eclipse/jetty/util/MultiPartInputStreamTest.java
@@ -21,6 +21,7 @@ package org.eclipse.jetty.util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
+import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
@@ -54,6 +55,40 @@ public class MultiPartInputStreamTest
protected String _contentType = "multipart/form-data, boundary=AaB03x";
protected String _multi = createMultipartRequestString(FILENAME);
protected String _dirname = System.getProperty("java.io.tmpdir")+File.separator+"myfiles-"+System.currentTimeMillis();
+ protected File _tmpDir = new File(_dirname);
+
+ public MultiPartInputStreamTest ()
+ {
+ _tmpDir.deleteOnExit();
+ }
+
+ public void testBadMultiPartRequest()
+ throws Exception
+ {
+ String boundary = "X0Y0";
+ String str = "--" + boundary + "\r\n"+
+ "Content-Disposition: form-data; name=\"fileup\"; filename=\"test.upload\"\r\n"+
+ "Content-Type: application/octet-stream\r\n\r\n"+
+ "How now brown cow."+
+ "\r\n--" + boundary + "-\r\n\r\n";
+
+ MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
+ MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(str.getBytes()),
+ "multipart/form-data, boundary="+boundary,
+ config,
+ _tmpDir);
+ mpis.setDeleteOnExit(true);
+ try
+ {
+ mpis.getParts();
+ fail ("Multipart incomplete");
+ }
+ catch (IOException e)
+ {
+ assertTrue(e.getMessage().startsWith("Incomplete"));
+ }
+ }
+
@Test
public void testNonMultiPartRequest()
@@ -63,7 +98,8 @@ public class MultiPartInputStreamTest
MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(_multi.getBytes()),
"Content-type: text/plain",
config,
- new File(_dirname));
+ _tmpDir);
+ mpis.setDeleteOnExit(true);
assertTrue(mpis.getParts().isEmpty());
}
@@ -75,7 +111,8 @@ public class MultiPartInputStreamTest
MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(_multi.getBytes()),
_contentType,
config,
- new File(_dirname));
+ _tmpDir);
+ mpis.setDeleteOnExit(true);
Collection<Part> parts = mpis.getParts();
assertFalse(parts.isEmpty());
}
@@ -88,11 +125,12 @@ public class MultiPartInputStreamTest
MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(_multi.getBytes()),
_contentType,
config,
- new File(_dirname));
-
+ _tmpDir);
+ mpis.setDeleteOnExit(true);
+ Collection<Part> parts = null;
try
{
- mpis.getParts();
+ parts = mpis.getParts();
fail("Request should have exceeded maxRequestSize");
}
catch (IllegalStateException e)
@@ -109,11 +147,12 @@ public class MultiPartInputStreamTest
MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(_multi.getBytes()),
_contentType,
config,
- new File(_dirname));
-
+ _tmpDir);
+ mpis.setDeleteOnExit(true);
+ Collection<Part> parts = null;
try
{
- mpis.getParts();
+ parts = mpis.getParts();
fail("stuff.txt should have been larger than maxFileSize");
}
catch (IllegalStateException e)
@@ -123,6 +162,48 @@ public class MultiPartInputStreamTest
}
@Test
+ public void testPartFileNotDeleted () throws Exception
+ {
+ MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
+ MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(createMultipartRequestString("tptfd").getBytes()),
+ _contentType,
+ config,
+ _tmpDir);
+ mpis.setDeleteOnExit(true);
+ Collection<Part> parts = mpis.getParts();
+
+ MultiPart part = (MultiPart)mpis.getPart("stuff");
+ File stuff = ((MultiPartInputStream.MultiPart)part).getFile();
+ assertThat(stuff,notNullValue()); // longer than 100 bytes, should already be a tmp file
+ part.write("tptfd.txt");
+ File tptfd = new File (_dirname+File.separator+"tptfd.txt");
+ assertThat(tptfd.exists(), is(true));
+ assertThat(stuff.exists(), is(false)); //got renamed
+ part.cleanUp();
+ assertThat(tptfd.exists(), is(true)); //explicitly written file did not get removed after cleanup
+ tptfd.deleteOnExit(); //clean up test
+ }
+
+
+ public void testPartTmpFileDeletion () throws Exception
+ {
+ MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
+ MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(createMultipartRequestString("tptfd").getBytes()),
+ _contentType,
+ config,
+ _tmpDir);
+ mpis.setDeleteOnExit(true);
+ Collection<Part> parts = mpis.getParts();
+
+ MultiPart part = (MultiPart)mpis.getPart("stuff");
+ File stuff = ((MultiPartInputStream.MultiPart)part).getFile();
+ assertThat(stuff,notNullValue()); // longer than 100 bytes, should already be a tmp file
+ assertThat (stuff.exists(), is(true));
+ part.cleanUp();
+ assertThat(stuff.exists(), is(false)); //tmp file was removed after cleanup
+ }
+
+
public void testMulti ()
throws Exception
{
@@ -141,11 +222,11 @@ public class MultiPartInputStreamTest
MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(createMultipartRequestString(filename).getBytes()),
_contentType,
config,
- new File(_dirname));
-
+ _tmpDir);
+ mpis.setDeleteOnExit(true);
Collection<Part> parts = mpis.getParts();
assertThat(parts.size(), is(2));
- Part field1 = mpis.getPart("field1");
+ Part field1 = mpis.getPart("field1"); //field 1 too small to go into tmp file, should be in internal buffer
assertThat(field1,notNullValue());
assertThat(field1.getName(),is("field1"));
InputStream is = field1.getInputStream();
@@ -154,17 +235,18 @@ public class MultiPartInputStreamTest
assertEquals("Joe Blow", new String(os.toByteArray()));
assertEquals(8, field1.getSize());
- assertNotNull(((MultiPartInputStream.MultiPart)field1).getBytes()); //in internal buffer
+ assertNotNull(((MultiPartInputStream.MultiPart)field1).getBytes());//in internal buffer
field1.write("field1.txt");
- assertNull(((MultiPartInputStream.MultiPart)field1).getBytes()); //no longer in internal buffer
+ assertNull(((MultiPartInputStream.MultiPart)field1).getBytes());//no longer in internal buffer
File f = new File (_dirname+File.separator+"field1.txt");
assertTrue(f.exists());
- field1.write("another_field1.txt");
+ field1.write("another_field1.txt"); //write after having already written
File f2 = new File(_dirname+File.separator+"another_field1.txt");
assertTrue(f2.exists());
assertFalse(f.exists()); //should have been renamed
field1.delete(); //file should be deleted
- assertFalse(f2.exists());
+ assertFalse(f.exists()); //original file was renamed
+ assertFalse(f2.exists()); //2nd written file was explicitly deleted
MultiPart stuff = (MultiPart)mpis.getPart("stuff");
assertThat(stuff.getContentDispositionFilename(), is(filename));
@@ -174,14 +256,24 @@ public class MultiPartInputStreamTest
assertThat(stuff.getHeader("content-disposition"),is("form-data; name=\"stuff\"; filename=\"" + filename + "\""));
assertThat(stuff.getHeaderNames().size(),is(2));
assertThat(stuff.getSize(),is(51L));
- f = stuff.getFile();
- assertThat(f,notNullValue()); // longer than 100 bytes, should already be a file
- assertThat(stuff.getBytes(),nullValue()); //not in internal buffer any more
- assertThat(f.exists(),is(true));
- assertThat(f.getName(),is(not("stuff with space.txt")));
+ File tmpfile = ((MultiPartInputStream.MultiPart)stuff).getFile();
+ assertThat(tmpfile,notNullValue()); // longer than 100 bytes, should already be a tmp file
+ assertThat(((MultiPartInputStream.MultiPart)stuff).getBytes(),nullValue()); //not in an internal buffer
+ assertThat(tmpfile.exists(),is(true));
+ assertThat(tmpfile.getName(),is(not("stuff with space.txt")));
stuff.write(filename);
f = new File(_dirname+File.separator+filename);
assertThat(f.exists(),is(true));
+ assertThat(tmpfile.exists(), is(false));
+ try
+ {
+ stuff.getInputStream();
+ }
+ catch (Exception e)
+ {
+ fail("Part.getInputStream() after file rename operation");
+ }
+ f.deleteOnExit(); //clean up after test
}
@Test
@@ -197,15 +289,15 @@ public class MultiPartInputStreamTest
"content-disposition: form-data; name=\"stuff\"; filename=\"stuff2.txt\"\r\n"+
"Content-Type: text/plain\r\n"+
"\r\n"+
- "000000000000000000000000000000000000000000000000000\r\n"+
+ "110000000000000000000000000000000000000000000000000\r\n"+
"--AaB03x--\r\n";
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(sameNames.getBytes()),
_contentType,
config,
- new File(_dirname));
-
+ _tmpDir);
+ mpis.setDeleteOnExit(true);
Collection<Part> parts = mpis.getParts();
assertEquals(2, parts.size());
for (Part p:parts)
@@ -219,6 +311,18 @@ public class MultiPartInputStreamTest
private String createMultipartRequestString(String filename)
{
+ int length = filename.length();
+ String name = filename;
+ if (length > 10)
+ name = filename.substring(0,10);
+ StringBuffer filler = new StringBuffer();
+ int i = name.length();
+ while (i < 51)
+ {
+ filler.append("0");
+ i++;
+ }
+
return "--AaB03x\r\n"+
"content-disposition: form-data; name=\"field1\"\r\n"+
"\r\n"+
@@ -226,8 +330,8 @@ public class MultiPartInputStreamTest
"--AaB03x\r\n"+
"content-disposition: form-data; name=\"stuff\"; filename=\"" + filename + "\"\r\n"+
"Content-Type: text/plain\r\n"+
- "\r\n"+
- "000000000000000000000000000000000000000000000000000\r\n"+
+ "\r\n"+name+
+ filler.toString()+"\r\n" +
"--AaB03x--\r\n";
}
}
diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/DiscoveredAnnotation.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/DiscoveredAnnotation.java
index 8df641817d..988414b85f 100644
--- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/DiscoveredAnnotation.java
+++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/DiscoveredAnnotation.java
@@ -21,6 +21,7 @@ package org.eclipse.jetty.webapp;
import org.eclipse.jetty.util.Loader;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.resource.Resource;
/**
* DiscoveredAnnotation
@@ -36,15 +37,27 @@ public abstract class DiscoveredAnnotation
protected WebAppContext _context;
protected String _className;
protected Class<?> _clazz;
+ protected Resource _resource; //resource it was discovered on, can be null (eg from WEB-INF/classes)
public abstract void apply();
public DiscoveredAnnotation (WebAppContext context, String className)
{
+ this(context,className, null);
+ }
+
+
+ public DiscoveredAnnotation(WebAppContext context, String className, Resource resource)
+ {
_context = context;
_className = className;
- }
+ _resource = resource;
+ }
+ public Resource getResource ()
+ {
+ return _resource;
+ }
public Class<?> getTargetClass()
{
diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/MetaData.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/MetaData.java
index af63d02859..ebc7f471a6 100644
--- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/MetaData.java
+++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/MetaData.java
@@ -200,7 +200,7 @@ public class MetaData
_metaDataComplete=true;
break;
case False:
- _metaDataComplete=true;
+ _metaDataComplete=false;
break;
case NotSet:
break;
@@ -270,12 +270,42 @@ public class MetaData
*/
public void addDiscoveredAnnotations(List<DiscoveredAnnotation> annotations)
{
- _annotations.addAll(annotations);
+ if (annotations == null)
+ return;
+ for (DiscoveredAnnotation a:annotations)
+ {
+ Resource r = a.getResource();
+ if (r == null || !_webInfJars.contains(r))
+ _annotations.add(a);
+ else
+ addDiscoveredAnnotation(a.getResource(), a);
+
+ }
+ }
+
+
+ public void addDiscoveredAnnotation(Resource resource, DiscoveredAnnotation annotation)
+ {
+ List<DiscoveredAnnotation> list = _webFragmentAnnotations.get(resource);
+ if (list == null)
+ {
+ list = new ArrayList<DiscoveredAnnotation>();
+ _webFragmentAnnotations.put(resource, list);
+ }
+ list.add(annotation);
}
+
public void addDiscoveredAnnotations(Resource resource, List<DiscoveredAnnotation> annotations)
{
- _webFragmentAnnotations.put(resource, new ArrayList<DiscoveredAnnotation>(annotations));
+ List<DiscoveredAnnotation> list = _webFragmentAnnotations.get(resource);
+ if (list == null)
+ {
+ list = new ArrayList<DiscoveredAnnotation>();
+ _webFragmentAnnotations.put(resource, list);
+ }
+
+ list.addAll(annotations);
}
public void addDescriptorProcessor(DescriptorProcessor p)
@@ -507,6 +537,15 @@ public class MetaData
OriginInfo x = new OriginInfo (name, Origin.Annotation);
_origins.put(name, x);
}
+
+ public void setOrigin(String name, Origin origin)
+ {
+ if (name == null)
+ return;
+
+ OriginInfo x = new OriginInfo (name, origin);
+ _origins.put(name, x);
+ }
public boolean isMetaDataComplete()
{
diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/Origin.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/Origin.java
index 1481b3804f..f2f941c478 100644
--- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/Origin.java
+++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/Origin.java
@@ -18,4 +18,4 @@
package org.eclipse.jetty.webapp;
-public enum Origin {NotSet, WebXml, WebDefaults, WebOverride, WebFragment, Annotation} \ No newline at end of file
+public enum Origin {NotSet, WebXml, WebDefaults, WebOverride, WebFragment, Annotation, API} \ No newline at end of file
diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/StandardDescriptorProcessor.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/StandardDescriptorProcessor.java
index 72bc262db5..4b210a6170 100644
--- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/StandardDescriptorProcessor.java
+++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/StandardDescriptorProcessor.java
@@ -617,7 +617,7 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor
{
//no servlet mappings
context.getMetaData().setOrigin(servlet_name+".servlet.mappings", descriptor);
- ServletMapping mapping = addServletMapping(servlet_name, node, context);
+ ServletMapping mapping = addServletMapping(servlet_name, node, context, descriptor);
mapping.setDefault(context.getMetaData().getOrigin(servlet_name+".servlet.mappings") == Origin.WebDefaults);
break;
}
@@ -629,14 +629,14 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor
//otherwise just ignore it
if (!(descriptor instanceof FragmentDescriptor))
{
- addServletMapping(servlet_name, node, context);
+ addServletMapping(servlet_name, node, context, descriptor);
}
break;
}
case WebFragment:
{
//mappings previously set by another web-fragment, so merge in this web-fragment's mappings
- addServletMapping(servlet_name, node, context);
+ addServletMapping(servlet_name, node, context, descriptor);
break;
}
}
@@ -1161,7 +1161,7 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor
* @param node
* @param context
*/
- protected ServletMapping addServletMapping (String servletName, XmlParser.Node node, WebAppContext context)
+ protected ServletMapping addServletMapping (String servletName, XmlParser.Node node, WebAppContext context, Descriptor descriptor)
{
ServletMapping mapping = new ServletMapping();
mapping.setServletName(servletName);
@@ -1173,6 +1173,7 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor
String p = iter.next().toString(false, true);
p = normalizePattern(p);
paths.add(p);
+ context.getMetaData().setOrigin(servletName+".servlet.mapping."+p, descriptor);
}
mapping.setPathSpecs((String[]) paths.toArray(new String[paths.size()]));
context.getServletHandler().addServletMapping(mapping);
@@ -1184,7 +1185,7 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor
* @param node
* @param context
*/
- protected void addFilterMapping (String filterName, XmlParser.Node node, WebAppContext context)
+ protected void addFilterMapping (String filterName, XmlParser.Node node, WebAppContext context, Descriptor descriptor)
{
FilterMapping mapping = new FilterMapping();
mapping.setFilterName(filterName);
@@ -1196,6 +1197,7 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor
String p = iter.next().toString(false, true);
p = normalizePattern(p);
paths.add(p);
+ context.getMetaData().setOrigin(filterName+".filter.mapping."+p, descriptor);
}
mapping.setPathSpecs((String[]) paths.toArray(new String[paths.size()]));
@@ -1299,7 +1301,7 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor
jpg.setIsXml(group.getString("is-xml", false, true));
jpg.setDeferredSyntaxAllowedAsLiteral(group.getString("deferred-syntax-allowed-as-literal", false, true));
jpg.setTrimDirectiveWhitespaces(group.getString("trim-directive-whitespaces", false, true));
- jpg.setDefaultContentType(group.getString("defaultContentType", false, true));
+ jpg.setDefaultContentType(group.getString("default-content-type", false, true));
jpg.setBuffer(group.getString("buffer", false, true));
jpg.setErrorOnUndeclaredNamespace(group.getString("error-on-undeclared-namespace", false, true));
@@ -1352,6 +1354,8 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor
//ServletSpec 3.0, p74 security-constraints, as minOccurs > 1, are additive
//across fragments
+
+ //TODO: need to remember origin of the constraints
try
{
XmlParser.Node auths = node.get("auth-constraint");
@@ -1400,29 +1404,50 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor
{
String url = iter2.next().toString(false, true);
url = normalizePattern(url);
-
+ //remember origin so we can process ServletRegistration.Dynamic.setServletSecurityElement() correctly
+ context.getMetaData().setOrigin("constraint.url."+url, descriptor);
+
Iterator<XmlParser.Node> iter3 = collection.iterator("http-method");
+ Iterator<XmlParser.Node> iter4 = collection.iterator("http-method-omission");
+
if (iter3.hasNext())
{
+ if (iter4.hasNext())
+ throw new IllegalStateException ("web-resource-collection cannot contain both http-method and http-method-omission");
+
+ //configure all the http-method elements for each url
while (iter3.hasNext())
{
String method = ((XmlParser.Node) iter3.next()).toString(false, true);
ConstraintMapping mapping = new ConstraintMapping();
mapping.setMethod(method);
mapping.setPathSpec(url);
+ mapping.setConstraint(sc);
+ ((ConstraintAware)context.getSecurityHandler()).addConstraintMapping(mapping);
+ }
+ }
+ else if (iter4.hasNext())
+ {
+ //configure all the http-method-omission elements for each url
+ while (iter4.hasNext())
+ {
+ String method = ((XmlParser.Node)iter4.next()).toString(false, true);
+ ConstraintMapping mapping = new ConstraintMapping();
+ mapping.setMethodOmissions(new String[]{method});
+ mapping.setPathSpec(url);
mapping.setConstraint(sc);
-
((ConstraintAware)context.getSecurityHandler()).addConstraintMapping(mapping);
}
}
else
{
+ //No http-methods or http-method-omissions specified, the constraint applies to all
ConstraintMapping mapping = new ConstraintMapping();
mapping.setPathSpec(url);
mapping.setConstraint(sc);
((ConstraintAware)context.getSecurityHandler()).addConstraintMapping(mapping);
}
- }
+ }
}
}
catch (CloneNotSupportedException e)
@@ -1768,7 +1793,7 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor
{
//no filtermappings for this filter yet defined
context.getMetaData().setOrigin(filter_name+".filter.mappings", descriptor);
- addFilterMapping(filter_name, node, context);
+ addFilterMapping(filter_name, node, context, descriptor);
break;
}
case WebDefaults:
@@ -1778,14 +1803,14 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor
//filter mappings defined in a web xml file. If we're processing a fragment, we ignore filter mappings.
if (!(descriptor instanceof FragmentDescriptor))
{
- addFilterMapping(filter_name, node, context);
+ addFilterMapping(filter_name, node, context, descriptor);
}
break;
}
case WebFragment:
{
//filter mappings first defined in a web-fragment, allow other fragments to add
- addFilterMapping(filter_name, node, context);
+ addFilterMapping(filter_name, node, context, descriptor);
break;
}
}
diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/TagLibConfiguration.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/TagLibConfiguration.java
index 1b050971cf..72075ddbe1 100644
--- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/TagLibConfiguration.java
+++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/TagLibConfiguration.java
@@ -110,7 +110,13 @@ public class TagLibConfiguration extends AbstractConfiguration
//Get the system classpath tlds and tell jasper about them, if jasper is on the classpath
try
{
- Class<?> clazz = getClass().getClassLoader().loadClass("org.apache.jasper.compiler.TldLocationsCache");
+
+ ClassLoader loader = _context.getClassLoader();
+ if (loader == null || loader.getParent() == null)
+ loader = getClass().getClassLoader();
+ else
+ loader = loader.getParent();
+ Class<?> clazz = loader.loadClass("org.apache.jasper.compiler.TldLocationsCache");
assert clazz!=null;
Collection<Resource> tld_resources = (Collection<Resource>)_context.getAttribute(TLD_RESOURCES);
diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java
index 0dcb7183f5..0e492dd68e 100644
--- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java
+++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java
@@ -24,17 +24,28 @@ import java.net.MalformedURLException;
import java.net.URL;
import java.security.PermissionCollection;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.EventListener;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
+import javax.servlet.HttpMethodConstraintElement;
import javax.servlet.ServletContext;
+import javax.servlet.ServletRegistration.Dynamic;
+import javax.servlet.ServletSecurityElement;
+import javax.servlet.annotation.ServletSecurity.EmptyRoleSemantic;
+import javax.servlet.annotation.ServletSecurity.TransportGuarantee;
import javax.servlet.http.HttpSessionActivationListener;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingListener;
import javax.servlet.http.HttpSessionListener;
+import org.eclipse.jetty.security.ConstraintAware;
+import org.eclipse.jetty.security.ConstraintMapping;
+import org.eclipse.jetty.security.ConstraintSecurityHandler;
import org.eclipse.jetty.security.SecurityHandler;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.HandlerContainer;
@@ -55,6 +66,7 @@ import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.resource.ResourceCollection;
+import org.eclipse.jetty.util.security.Constraint;
/* ------------------------------------------------------------ */
/** Web Application Context Handler.
@@ -1252,6 +1264,79 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
super.startContext();
}
+
+ /* ------------------------------------------------------------ */
+ @Override
+ public Set<String> setServletSecurity(Dynamic registration, ServletSecurityElement servletSecurityElement)
+ {
+
+ Set<String> unchangedURLMappings = new HashSet<String>();
+ //From javadoc for ServletSecurityElement:
+ /*
+ If a URL pattern of this ServletRegistration is an exact target of a security-constraint that
+ was established via the portable deployment descriptor, then this method does not change the
+ security-constraint for that pattern, and the pattern will be included in the return value.
+
+ If a URL pattern of this ServletRegistration is an exact target of a security constraint
+ that was established via the ServletSecurity annotation or a previous call to this method,
+ then this method replaces the security constraint for that pattern.
+
+ If a URL pattern of this ServletRegistration is neither the exact target of a security constraint
+ that was established via the ServletSecurity annotation or a previous call to this method,
+ nor the exact target of a security-constraint in the portable deployment descriptor, then
+ this method establishes the security constraint for that pattern from the argument ServletSecurityElement.
+ */
+
+ Collection<String> pathMappings = registration.getMappings();
+ if (pathMappings != null)
+ {
+ Constraint constraint = ConstraintSecurityHandler.createConstraint(registration.getName(), servletSecurityElement);
+
+ for (String pathSpec:pathMappings)
+ {
+ Origin origin = getMetaData().getOrigin("constraint.url."+pathSpec);
+
+ switch (origin)
+ {
+ case NotSet:
+ {
+ //No mapping for this url already established
+ List<ConstraintMapping> mappings = ConstraintSecurityHandler.createConstraintsWithMappingsForPath(registration.getName(), pathSpec, servletSecurityElement);
+ for (ConstraintMapping m:mappings)
+ ((ConstraintAware)getSecurityHandler()).addConstraintMapping(m);
+ getMetaData().setOrigin("constraint.url."+pathSpec, Origin.API);
+ break;
+ }
+ case WebXml:
+ case WebDefaults:
+ case WebOverride:
+ case WebFragment:
+ {
+ //a mapping for this url was created in a descriptor, which overrides everything
+ unchangedURLMappings.add(pathSpec);
+ break;
+ }
+ case Annotation:
+ case API:
+ {
+ //mapping established via an annotation or by previous call to this method,
+ //replace the security constraint for this pattern
+ List<ConstraintMapping> constraintMappings = ConstraintSecurityHandler.removeConstraintMappingsForPath(pathSpec, ((ConstraintAware)getSecurityHandler()).getConstraintMappings());
+
+ List<ConstraintMapping> freshMappings = ConstraintSecurityHandler.createConstraintsWithMappingsForPath(registration.getName(), pathSpec, servletSecurityElement);
+ constraintMappings.addAll(freshMappings);
+
+ ((ConstraintSecurityHandler)getSecurityHandler()).setConstraintMappings(constraintMappings);
+ break;
+ }
+ }
+ }
+ }
+
+ return unchangedURLMappings;
+ }
+
+
/* ------------------------------------------------------------ */
public class Context extends ServletContextHandler.Context
@@ -1301,6 +1386,8 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
return servletContext;
}
}
+
+
}
/* ------------------------------------------------------------ */
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/UnitGenerator.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/UnitGenerator.java
index b278187682..83cbfb04eb 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/UnitGenerator.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/UnitGenerator.java
@@ -31,4 +31,5 @@ public class UnitGenerator extends Generator
{
super(WebSocketPolicy.newServerPolicy(),new MappedByteBufferPool());
}
+
}
diff --git a/test-jetty-webapp/src/main/config/contexts/test.d/override-web.xml b/test-jetty-webapp/src/main/config/contexts/test.d/override-web.xml
index 9e42d6d5ef..7b7f9e4312 100644
--- a/test-jetty-webapp/src/main/config/contexts/test.d/override-web.xml
+++ b/test-jetty-webapp/src/main/config/contexts/test.d/override-web.xml
@@ -15,15 +15,6 @@
<param-value>a context value</param-value>
</context-param>
- <!-- Add or override filter init parameter -->
- <filter>
- <filter-name>TestFilter</filter-name>
- <filter-class>com.acme.TestFilter</filter-class>
- <init-param>
- <param-name>remote</param-name>
- <param-value>false</param-value>
- </init-param>
- </filter>
<!-- Add or override servlet init parameter -->
<servlet>
diff --git a/test-jetty-webapp/src/main/java/com/acme/RegTest.java b/test-jetty-webapp/src/main/java/com/acme/RegTest.java
new file mode 100644
index 0000000000..8d99cd11a5
--- /dev/null
+++ b/test-jetty-webapp/src/main/java/com/acme/RegTest.java
@@ -0,0 +1,194 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2012 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 com.acme;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.util.StringUtil;
+
+
+
+
+/* ------------------------------------------------------------ */
+/** Rego Servlet - tests being accessed from servlet 3.0 programmatic
+ * configuration.
+ *
+ */
+public class RegTest extends HttpServlet
+{
+
+ /* ------------------------------------------------------------ */
+ @Override
+ public void init(ServletConfig config) throws ServletException
+ {
+ super.init(config);
+ }
+
+ /* ------------------------------------------------------------ */
+ @Override
+ public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+ doGet(request, response);
+ }
+
+ /* ------------------------------------------------------------ */
+ @Override
+ public void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException
+ {
+ request.setCharacterEncoding("UTF-8");
+ PrintWriter pout=null;
+
+ try
+ {
+ pout =response.getWriter();
+ }
+ catch(IllegalStateException e)
+ {
+ pout=new PrintWriter(new OutputStreamWriter(response.getOutputStream(),"UTF-8"));
+ }
+
+ try
+ {
+ pout.write("<html>\n<body>\n");
+ pout.write("<h1>Rego Servlet</h1>\n");
+ pout.write("<table width=\"95%\">");
+ pout.write("<tr>\n");
+ pout.write("<th align=\"right\">getMethod:&nbsp;</th>");
+ pout.write("<td>" + notag(request.getMethod())+"</td>");
+ pout.write("</tr><tr>\n");
+ pout.write("<th align=\"right\">getContentLength:&nbsp;</th>");
+ pout.write("<td>"+Integer.toString(request.getContentLength())+"</td>");
+ pout.write("</tr><tr>\n");
+ pout.write("<th align=\"right\">getContentType:&nbsp;</th>");
+ pout.write("<td>"+notag(request.getContentType())+"</td>");
+ pout.write("</tr><tr>\n");
+ pout.write("<th align=\"right\">getRequestURI:&nbsp;</th>");
+ pout.write("<td>"+notag(request.getRequestURI())+"</td>");
+ pout.write("</tr><tr>\n");
+ pout.write("<th align=\"right\">getRequestURL:&nbsp;</th>");
+ pout.write("<td>"+notag(request.getRequestURL().toString())+"</td>");
+ pout.write("</tr><tr>\n");
+ pout.write("<th align=\"right\">getContextPath:&nbsp;</th>");
+ pout.write("<td>"+request.getContextPath()+"</td>");
+ pout.write("</tr><tr>\n");
+ pout.write("<th align=\"right\">getServletPath:&nbsp;</th>");
+ pout.write("<td>"+notag(request.getServletPath())+"</td>");
+ pout.write("</tr><tr>\n");
+ pout.write("<th align=\"right\">getPathInfo:&nbsp;</th>");
+ pout.write("<td>"+notag(request.getPathInfo())+"</td>");
+ pout.write("</tr><tr>\n");
+ pout.write("<th align=\"right\">getPathTranslated:&nbsp;</th>");
+ pout.write("<td>"+notag(request.getPathTranslated())+"</td>");
+ pout.write("</tr><tr>\n");
+ pout.write("<th align=\"right\">getQueryString:&nbsp;</th>");
+ pout.write("<td>"+notag(request.getQueryString())+"</td>");
+ pout.write("</tr><tr>\n");
+
+ pout.write("<th align=\"right\">getProtocol:&nbsp;</th>");
+ pout.write("<td>"+request.getProtocol()+"</td>");
+ pout.write("</tr><tr>\n");
+ pout.write("<th align=\"right\">getScheme:&nbsp;</th>");
+ pout.write("<td>"+request.getScheme()+"</td>");
+ pout.write("</tr><tr>\n");
+ pout.write("<th align=\"right\">getServerName:&nbsp;</th>");
+ pout.write("<td>"+notag(request.getServerName())+"</td>");
+ pout.write("</tr><tr>\n");
+ pout.write("<th align=\"right\">getServerPort:&nbsp;</th>");
+ pout.write("<td>"+Integer.toString(request.getServerPort())+"</td>");
+ pout.write("</tr><tr>\n");
+ pout.write("<th align=\"right\">getLocalName:&nbsp;</th>");
+ pout.write("<td>"+request.getLocalName()+"</td>");
+ pout.write("</tr><tr>\n");
+ pout.write("<th align=\"right\">getLocalAddr:&nbsp;</th>");
+ pout.write("<td>"+request.getLocalAddr()+"</td>");
+ pout.write("</tr><tr>\n");
+ pout.write("<th align=\"right\">getLocalPort:&nbsp;</th>");
+ pout.write("<td>"+Integer.toString(request.getLocalPort())+"</td>");
+ pout.write("</tr><tr>\n");
+ pout.write("<th align=\"right\">getRemoteUser:&nbsp;</th>");
+ pout.write("<td>"+request.getRemoteUser()+"</td>");
+ pout.write("</tr><tr>\n");
+ pout.write("<th align=\"right\">getUserPrincipal:&nbsp;</th>");
+ pout.write("<td>"+request.getUserPrincipal()+"</td>");
+ pout.write("</tr><tr>\n");
+ pout.write("<th align=\"right\">getRemoteAddr:&nbsp;</th>");
+ pout.write("<td>"+request.getRemoteAddr()+"</td>");
+ pout.write("</tr><tr>\n");
+ pout.write("<th align=\"right\">getRemoteHost:&nbsp;</th>");
+ pout.write("<td>"+request.getRemoteHost()+"</td>");
+ pout.write("</tr><tr>\n");
+ pout.write("<th align=\"right\">getRemotePort:&nbsp;</th>");
+ pout.write("<td>"+request.getRemotePort()+"</td>");
+ pout.write("</tr><tr>\n");
+ pout.write("<th align=\"right\">getRequestedSessionId:&nbsp;</th>");
+ pout.write("<td>"+request.getRequestedSessionId()+"</td>");
+ pout.write("</tr><tr>\n");
+ pout.write("<th align=\"right\">isSecure():&nbsp;</th>");
+ pout.write("<td>"+request.isSecure()+"</td>");
+
+ pout.write("</tr><tr>\n");
+ pout.write("<th align=\"right\">isUserInRole(admin):&nbsp;</th>");
+ pout.write("<td>"+request.isUserInRole("admin")+"</td>");
+
+ pout.write("</tr></table>");
+
+ }
+ catch (Exception e)
+ {
+ getServletContext().log("dump "+e);
+ }
+
+
+ pout.write("</body>\n</html>\n");
+
+ pout.close();
+ }
+
+
+ /* ------------------------------------------------------------ */
+ @Override
+ public String getServletInfo()
+ {
+ return "Rego Servlet";
+ }
+
+ /* ------------------------------------------------------------ */
+ @Override
+ public synchronized void destroy()
+ {
+ }
+
+
+ private String notag(String s)
+ {
+ if (s==null)
+ return "null";
+ s=StringUtil.replace(s,"&","&amp;");
+ s=StringUtil.replace(s,"<","&lt;");
+ s=StringUtil.replace(s,">","&gt;");
+ return s;
+ }
+}
diff --git a/test-jetty-webapp/src/main/java/com/acme/TestListener.java b/test-jetty-webapp/src/main/java/com/acme/TestListener.java
index faaa76085b..cf950edb7d 100644
--- a/test-jetty-webapp/src/main/java/com/acme/TestListener.java
+++ b/test-jetty-webapp/src/main/java/com/acme/TestListener.java
@@ -18,6 +18,7 @@
package com.acme;
+import javax.servlet.DispatcherType;
import javax.servlet.ServletContextAttributeEvent;
import javax.servlet.ServletContextAttributeListener;
import javax.servlet.ServletContextEvent;
@@ -26,6 +27,12 @@ import javax.servlet.ServletRequestAttributeEvent;
import javax.servlet.ServletRequestAttributeListener;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
+import javax.servlet.ServletRegistration;
+import javax.servlet.FilterRegistration;
+import javax.servlet.ServletSecurityElement;
+import javax.servlet.HttpConstraintElement;
+import javax.servlet.HttpMethodConstraintElement;
+import javax.servlet.annotation.ServletSecurity;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSessionActivationListener;
import javax.servlet.http.HttpSessionAttributeListener;
@@ -33,6 +40,9 @@ import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
+import java.util.EnumSet;
+import java.util.Set;
+
public class TestListener implements HttpSessionListener, HttpSessionAttributeListener, HttpSessionActivationListener, ServletContextListener, ServletContextAttributeListener, ServletRequestListener, ServletRequestAttributeListener
{
public void attributeAdded(HttpSessionBindingEvent se)
@@ -62,16 +72,30 @@ public class TestListener implements HttpSessionListener, HttpSessionAttributeL
public void contextInitialized(ServletContextEvent sce)
{
- /* TODO for servlet 3.0
- * FilterRegistration registration=context.addFilter("TestFilter",TestFilter.class.getName());
-
-
+ //configure programmatic security
+ ServletRegistration.Dynamic rego = sce.getServletContext().addServlet("RegoTest", RegTest.class.getName());
+ rego.addMapping("/rego/*");
+ HttpConstraintElement constraintElement = new HttpConstraintElement(ServletSecurity.EmptyRoleSemantic.PERMIT,
+ ServletSecurity.TransportGuarantee.NONE, new String[]{"admin"});
+ ServletSecurityElement securityElement = new ServletSecurityElement(constraintElement, null);
+ Set<String> unchanged = rego.setServletSecurity(securityElement);
+ System.err.println("Security constraints registered: "+unchanged.isEmpty());
+
+ //Test that a security constraint from web.xml can't be overridden programmatically
+ ServletRegistration.Dynamic rego2 = sce.getServletContext().addServlet("RegoTest2", RegTest.class.getName());
+ rego2.addMapping("/rego2/*");
+ securityElement = new ServletSecurityElement(constraintElement, null);
+ unchanged = rego2.setServletSecurity(securityElement);
+ System.err.println("Overridding web.xml constraints not possible:" +!unchanged.isEmpty());
+
+ /* For servlet 3.0 */
+ FilterRegistration.Dynamic registration = sce.getServletContext().addFilter("TestFilter",TestFilter.class.getName());
+ registration.setInitParameter("remote", "false");
registration.setAsyncSupported(true);
registration.addMappingForUrlPatterns(
EnumSet.of(DispatcherType.ERROR,DispatcherType.ASYNC,DispatcherType.FORWARD,DispatcherType.INCLUDE,DispatcherType.REQUEST),
true,
- new String[]{"/dump/*","/dispatch/*","*.dump"});
- */
+ new String[]{"/*"});
}
public void contextDestroyed(ServletContextEvent sce)
diff --git a/test-jetty-webapp/src/main/webapp/WEB-INF/web.xml b/test-jetty-webapp/src/main/webapp/WEB-INF/web.xml
index 669d3d3663..4b43b09038 100644
--- a/test-jetty-webapp/src/main/webapp/WEB-INF/web.xml
+++ b/test-jetty-webapp/src/main/webapp/WEB-INF/web.xml
@@ -18,20 +18,6 @@
<listener-class>com.acme.TestListener</listener-class>
</listener>
- <filter>
- <filter-name>TestFilter</filter-name>
- <filter-class>com.acme.TestFilter</filter-class>
- <async-supported>true</async-supported>
- <init-param>
- <param-name>remote</param-name>
- <param-value>false</param-value>
- </init-param>
- </filter>
- <filter-mapping>
- <filter-name>TestFilter</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
-
<filter>
<filter-name>QoSFilter</filter-name>
@@ -120,7 +106,6 @@
</filter-mapping>
-->
-
<servlet>
<servlet-name>Hello</servlet-name>
<servlet-class>com.acme.HelloWorld</servlet-class>
@@ -276,6 +261,18 @@
<location>/error404.html</location>
</error-page>
+
+
+ <security-constraint>
+ <web-resource-collection>
+ <web-resource-name>Rego2</web-resource-name>
+ <url-pattern>/rego2/*</url-pattern>
+ </web-resource-collection>
+ <auth-constraint>
+ <role-name>server-administrator</role-name>
+ </auth-constraint>
+ </security-constraint>
+
<security-constraint>
<web-resource-collection>
<web-resource-name>Auth2</web-resource-name>
diff --git a/test-jetty-webapp/src/main/webapp/auth.html b/test-jetty-webapp/src/main/webapp/auth.html
index 1b1de1157c..0bce2d555f 100644
--- a/test-jetty-webapp/src/main/webapp/auth.html
+++ b/test-jetty-webapp/src/main/webapp/auth.html
@@ -18,6 +18,8 @@ This page contains several links to test the authentication constraints:
<li><a href="dump/auth/info">dump/auth/*</a> - Authenticated any user</li>
<li><a href="dump/auth/admin/info">dump/auth/admin/*</a> - Authenticated admin role (<a href="session/?Action=Invalidate">click</a> to invalidate session)</li>
<li><a href="dump/auth/ssl/info">dump/auth/ssl/*</a> - Confidential</li>
+<li><a href="rego/info">rego/info/*</a> - Authenticated admin role from programmatic security (<a href="session/?Action=Invalidate">click</a> to invalidate session)</li>
+<li><a href="rego2/info">rego2/info/*</a> - Authenticated servlet-administrator role from programmatic security (login as admin/admin, <a href="session/?Action=Invalidate">click</a> to invalidate session)</li>
</ul>
<p/>
<p>
diff --git a/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/ImmortalSessionTest.java b/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/ImmortalSessionTest.java
new file mode 100644
index 0000000000..b2056fc130
--- /dev/null
+++ b/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/ImmortalSessionTest.java
@@ -0,0 +1,30 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2012 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;
+
+public class ImmortalSessionTest extends AbstractImmortalSessionTest
+{
+
+ @Override
+ public AbstractTestServer createServer(int port, int max, int scavenge)
+ {
+ return new HashTestServer(port,max,scavenge);
+ }
+
+}
diff --git a/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/SessionCookieTest.java b/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/SessionCookieTest.java
new file mode 100644
index 0000000000..3319bbb841
--- /dev/null
+++ b/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/SessionCookieTest.java
@@ -0,0 +1,30 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2012 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;
+
+public class SessionCookieTest extends AbstractSessionCookieTest
+{
+
+ @Override
+ public AbstractTestServer createServer(int port, int max, int scavenge)
+ {
+ return new HashTestServer(port, max, scavenge);
+ }
+
+}
diff --git a/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/SessionValueSharedSaving.java b/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/SessionValueSharedSaving.java
new file mode 100644
index 0000000000..14626095b3
--- /dev/null
+++ b/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/SessionValueSharedSaving.java
@@ -0,0 +1,30 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2012 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;
+
+public class SessionValueSharedSaving extends AbstractSessionValueSavingTest
+{
+
+ @Override
+ public AbstractTestServer createServer(int port, int max, int scavenge)
+ {
+ return new HashTestServer(port,max,scavenge);
+ }
+
+}
diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionCookieTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionCookieTest.java
new file mode 100644
index 0000000000..5246186f5a
--- /dev/null
+++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionCookieTest.java
@@ -0,0 +1,164 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2012 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.util.Random;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import junit.framework.Assert;
+
+import org.eclipse.jetty.client.Address;
+import org.eclipse.jetty.client.ContentExchange;
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.HttpDestination;
+import org.eclipse.jetty.http.HttpCookie;
+import org.eclipse.jetty.http.HttpMethods;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.util.log.Log;
+import org.junit.Ignore;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * AbstractNewSessionTest
+ */
+public abstract class AbstractSessionCookieTest
+{
+ public abstract AbstractTestServer createServer(int port, int max, int scavenge);
+
+ public void pause(int scavenge)
+ {
+ try
+ {
+ Thread.sleep(scavenge * 2500L);
+ }
+ catch (InterruptedException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ @Test
+ @Ignore("failing because an http cookie with null value is coming over as \"null\"")
+ public void testSessionCookie() throws Exception
+ {
+ String contextPath = "";
+ String servletMapping = "/server";
+ int scavengePeriod = 3;
+ AbstractTestServer server = createServer(0, 1, scavengePeriod);
+ ServletContextHandler context = server.addContext(contextPath);
+ context.addServlet(TestServlet.class, servletMapping);
+ server.start();
+ int port=server.getPort();
+ try
+ {
+ HttpClient client = new HttpClient();
+ client.setConnectorType(HttpClient.CONNECTOR_SOCKET);
+ client.start();
+ try
+ {
+ ContentExchange exchange = new ContentExchange(true);
+ exchange.setMethod(HttpMethods.GET);
+ exchange.setURL("http://localhost:" + port + contextPath + servletMapping + "?action=create");
+ client.send(exchange);
+ exchange.waitForDone();
+ assertEquals(HttpServletResponse.SC_OK,exchange.getResponseStatus());
+ String sessionCookie = exchange.getResponseFields().getStringField("Set-Cookie");
+ assertTrue(sessionCookie != null);
+ // Mangle the cookie, replacing Path with $Path, etc.
+ //sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
+
+ // Let's wait for the scavenger to run, waiting 2.5 times the scavenger period
+ //pause(scavengePeriod);
+
+ exchange = new ContentExchange(true);
+ exchange.setMethod(HttpMethods.GET);
+ exchange.setURL("http://localhost:" + port + contextPath + servletMapping + "?action=check-cookie");
+ exchange.getRequestFields().add("Cookie", sessionCookie);
+ client.send(exchange);
+ exchange.waitForDone();
+ assertEquals(HttpServletResponse.SC_OK,exchange.getResponseStatus());
+
+ exchange = new ContentExchange(true);
+ exchange.setMethod(HttpMethods.GET);
+ exchange.setURL("http://localhost:" + port + contextPath + servletMapping + "?action=null-cookie");
+ //exchange.getRequestFields().add("Cookie", "null");
+ HttpDestination dest = client.getDestination(new Address("localhost",port),false);
+
+ dest.addCookie(new HttpCookie("Cookie",null));
+
+ client.send(exchange);
+ exchange.waitForDone();
+ assertEquals(HttpServletResponse.SC_OK,exchange.getResponseStatus());
+ }
+ finally
+ {
+ client.stop();
+ }
+ }
+ 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);
+ assertTrue(session.isNew());
+ }
+ else if ("check-cookie".equals(action))
+ {
+ HttpSession session = request.getSession(false);
+
+ assertTrue(session != null);
+
+ //request.getSession(true);
+ }
+ else if ("null-cookie".equals(action))
+ {
+ HttpSession session = request.getSession(false);
+
+ assertEquals(1, request.getCookies().length);
+
+ Assert.assertFalse("null".equals(request.getCookies()[0].getValue()));
+
+ assertTrue(session == null);
+
+ }
+ else
+ {
+ assertTrue(false);
+ }
+ }
+ }
+}

Back to the top