Merge remote-tracking branch 'origin/jetty-8' into jetty-9
Conflicts: VERSION.txt example-async-rest/async-rest-jar/pom.xml example-async-rest/async-rest-webapp/pom.xml example-async-rest/pom.xml example-jetty-embedded/pom.xml jetty-aggregate/jetty-all-server/pom.xml jetty-aggregate/jetty-all/pom.xml jetty-aggregate/jetty-client/pom.xml jetty-aggregate/jetty-server/pom.xml jetty-aggregate/jetty-servlet/pom.xml jetty-aggregate/jetty-webapp/pom.xml jetty-aggregate/jetty-websocket/pom.xml jetty-aggregate/pom.xml jetty-ajp/src/main/java/org/eclipse/jetty/ajp/Ajp13Generator.java jetty-annotations/pom.xml jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AbstractDiscoverableAnnotationHandler.java jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationConfiguration.java jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationParser.java jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ServletContainerInitializerListener.java jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ServletSecurityAnnotationHandler.java jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebServletAnnotation.java jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebServletAnnotationHandler.java jetty-client/pom.xml jetty-continuation/pom.xml jetty-deploy/pom.xml jetty-distribution/pom.xml jetty-http-spi/pom.xml jetty-http/pom.xml jetty-http/src/main/java/org/eclipse/jetty/http/Generator.java jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java jetty-http/src/main/java/org/eclipse/jetty/http/MimeTypes.java jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldsTest.java jetty-io/pom.xml jetty-io/src/main/java/org/eclipse/jetty/io/AbstractBuffer.java jetty-io/src/main/java/org/eclipse/jetty/io/Buffer.java jetty-io/src/main/java/org/eclipse/jetty/io/BufferUtil.java jetty-jaspi/pom.xml jetty-jmx/pom.xml jetty-jndi/pom.xml jetty-jndi/src/main/java/org/eclipse/jetty/jndi/ContextFactory.java jetty-jndi/src/test/java/org/eclipse/jetty/jndi/java/TestJNDI.java jetty-jsp/pom.xml jetty-monitor/pom.xml jetty-nested/pom.xml jetty-nosql/pom.xml jetty-osgi/jetty-osgi-boot-jsp/pom.xml jetty-osgi/jetty-osgi-boot-warurl/pom.xml jetty-osgi/jetty-osgi-boot/pom.xml jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiDeployer.java jetty-osgi/jetty-osgi-httpservice/pom.xml jetty-osgi/pom.xml jetty-osgi/test-jetty-osgi-context/pom.xml jetty-osgi/test-jetty-osgi-webapp/pom.xml jetty-osgi/test-jetty-osgi/pom.xml jetty-overlay-deployer/pom.xml jetty-plus/pom.xml jetty-policy/pom.xml jetty-rewrite/pom.xml jetty-runner/pom.xml jetty-security/pom.xml jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintSecurityHandler.java jetty-security/src/main/java/org/eclipse/jetty/security/authentication/FormAuthenticator.java jetty-security/src/main/java/org/eclipse/jetty/security/authentication/LoginAuthenticator.java jetty-security/src/test/java/org/eclipse/jetty/security/ConstraintTest.java jetty-server/pom.xml jetty-server/src/main/java/org/eclipse/jetty/server/AbstractHttpConnection.java jetty-server/src/main/java/org/eclipse/jetty/server/AsyncHttpConnection.java jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java jetty-server/src/main/java/org/eclipse/jetty/server/Request.java jetty-server/src/main/java/org/eclipse/jetty/server/Response.java jetty-server/src/main/java/org/eclipse/jetty/server/bio/SocketConnector.java jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java jetty-server/src/test/java/org/eclipse/jetty/server/HttpURITest.java jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SelectChannelServerSslTest.java jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SslSocketServerTest.java jetty-server/src/test/resources/jetty-logging.properties jetty-servlet/pom.xml jetty-servlet/src/main/java/org/eclipse/jetty/servlet/DefaultServlet.java jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletContextHandler.java jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DispatcherTest.java jetty-servlets/pom.xml jetty-servlets/src/main/java/org/eclipse/jetty/servlets/GzipFilter.java jetty-servlets/src/main/java/org/eclipse/jetty/servlets/MultiPartFilter.java jetty-servlets/src/test/java/org/eclipse/jetty/servlets/MultipartFilterTest.java jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/GzipTester.java jetty-spdy/pom.xml jetty-spdy/spdy-client/pom.xml jetty-spdy/spdy-core/pom.xml jetty-spdy/spdy-example-webapp/pom.xml jetty-spdy/spdy-http-server/pom.xml jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYAsyncConnection.java jetty-start/pom.xml jetty-start/src/test/java/org/eclipse/jetty/start/CommandLineBuilderTest.java jetty-start/src/test/java/org/eclipse/jetty/start/MainTest.java jetty-util-ajax/pom.xml jetty-util/pom.xml jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartInputStream.java jetty-util/src/main/java/org/eclipse/jetty/util/log/StdErrLog.java jetty-util/src/test/java/org/eclipse/jetty/util/MultiPartInputStreamTest.java jetty-webapp/pom.xml jetty-webapp/src/main/java/org/eclipse/jetty/webapp/StandardDescriptorProcessor.java jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java jetty-websocket/pom.xml jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketServletRFCTest.java jetty-websocket/src/test/java/org/eclipse/jetty/websocket/helper/MessageSender.java jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/UnitGenerator.java jetty-xml/pom.xml pom.xml test-continuation/pom.xml test-jetty-nested/pom.xml test-jetty-servlet/pom.xml test-jetty-webapp/pom.xml test-jetty-webapp/src/main/webapp/WEB-INF/web.xml tests/pom.xml tests/test-integration/pom.xml tests/test-loginservice/pom.xml tests/test-sessions/pom.xml tests/test-sessions/test-hash-sessions/pom.xml tests/test-sessions/test-jdbc-sessions/pom.xml tests/test-sessions/test-mongodb-sessions/pom.xml tests/test-sessions/test-sessions-common/pom.xml tests/test-webapps/pom.xml tests/test-webapps/test-webapp-rfc2616/pom.xml
This commit is contained in:
commit
fac580c374
|
@ -22,6 +22,8 @@ import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.eclipse.jetty.annotations.AnnotationParser.DiscoverableAnnotationHandler;
|
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.DiscoveredAnnotation;
|
||||||
import org.eclipse.jetty.webapp.WebAppContext;
|
import org.eclipse.jetty.webapp.WebAppContext;
|
||||||
|
|
||||||
|
@ -33,13 +35,32 @@ import org.eclipse.jetty.webapp.WebAppContext;
|
||||||
public abstract class AbstractDiscoverableAnnotationHandler implements DiscoverableAnnotationHandler
|
public abstract class AbstractDiscoverableAnnotationHandler implements DiscoverableAnnotationHandler
|
||||||
{
|
{
|
||||||
protected WebAppContext _context;
|
protected WebAppContext _context;
|
||||||
protected List<DiscoveredAnnotation> _annotations = new ArrayList<DiscoveredAnnotation>();
|
protected List<DiscoveredAnnotation> _annotations;
|
||||||
|
protected Resource _resource;
|
||||||
|
|
||||||
public AbstractDiscoverableAnnotationHandler(WebAppContext context)
|
public AbstractDiscoverableAnnotationHandler(WebAppContext context)
|
||||||
{
|
{
|
||||||
_context = 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 ()
|
public List<DiscoveredAnnotation> getAnnotationList ()
|
||||||
{
|
{
|
||||||
|
@ -51,7 +72,6 @@ public abstract class AbstractDiscoverableAnnotationHandler implements Discovera
|
||||||
_annotations.clear();
|
_annotations.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void addAnnotation (DiscoveredAnnotation a)
|
public void addAnnotation (DiscoveredAnnotation a)
|
||||||
{
|
{
|
||||||
_annotations.add(a);
|
_annotations.add(a);
|
||||||
|
|
|
@ -28,6 +28,7 @@ import javax.servlet.annotation.HandlesTypes;
|
||||||
|
|
||||||
import org.eclipse.jetty.annotations.AnnotationParser.DiscoverableAnnotationHandler;
|
import org.eclipse.jetty.annotations.AnnotationParser.DiscoverableAnnotationHandler;
|
||||||
import org.eclipse.jetty.plus.annotation.ContainerInitializer;
|
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.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
import org.eclipse.jetty.util.resource.Resource;
|
import org.eclipse.jetty.util.resource.Resource;
|
||||||
|
@ -50,10 +51,20 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
||||||
public static final String CONTAINER_INITIALIZERS = "org.eclipse.jetty.containerInitializers";
|
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
|
public void preConfigure(final WebAppContext context) throws Exception
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see org.eclipse.jetty.webapp.AbstractConfiguration#configure(org.eclipse.jetty.webapp.WebAppContext)
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void configure(WebAppContext context) throws Exception
|
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 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())
|
if (context.getServletContext().getEffectiveMajorVersion() >= 3 || context.isConfigurationDiscovered())
|
||||||
{
|
{
|
||||||
parser = createAnnotationParser();
|
_discoverableAnnotationHandlers.add(new WebServletAnnotationHandler(context));
|
||||||
parser.registerAnnotationHandler("javax.servlet.annotation.WebServlet", new WebServletAnnotationHandler(context));
|
_discoverableAnnotationHandlers.add(new WebFilterAnnotationHandler(context));
|
||||||
parser.registerAnnotationHandler("javax.servlet.annotation.WebFilter", new WebFilterAnnotationHandler(context));
|
_discoverableAnnotationHandlers.add(new WebListenerAnnotationHandler(context));
|
||||||
parser.registerAnnotationHandler("javax.servlet.annotation.WebListener", new WebListenerAnnotationHandler(context));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
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
|
//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
|
//classes so we can call their onStartup() methods correctly
|
||||||
List<ServletContainerInitializer> nonExcludedInitializers = getNonExcludedInitializers(context);
|
createServletContainerInitializerAnnotationHandlers(context, getNonExcludedInitializers(context));
|
||||||
parser = registerServletContainerInitializerAnnotationHandlers(context, parser, nonExcludedInitializers);
|
|
||||||
|
|
||||||
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());
|
if (LOG.isDebugEnabled()) LOG.debug("Scanning all classses for annotations: webxmlVersion="+context.getServletContext().getEffectiveMajorVersion()+" configurationDiscovered="+context.isConfigurationDiscovered());
|
||||||
parseContainerPath(context, parser);
|
parseContainerPath(context, parser);
|
||||||
//email from Rajiv Mordani jsrs 315 7 April 2010
|
//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.
|
// WEB-INF/classes + order of the elements.
|
||||||
parseWebInfClasses(context, parser);
|
parseWebInfClasses(context, parser);
|
||||||
parseWebInfLib (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
|
* @return a new AnnotationParser. This method can be overridden to use a different impleemntation of
|
||||||
* the AnnotationParser. Note that this is considered internal API.
|
* the AnnotationParser. Note that this is considered internal API.
|
||||||
|
@ -109,6 +147,9 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
||||||
return new AnnotationParser();
|
return new AnnotationParser();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see org.eclipse.jetty.webapp.AbstractConfiguration#cloneConfigure(org.eclipse.jetty.webapp.WebAppContext, org.eclipse.jetty.webapp.WebAppContext)
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void cloneConfigure(WebAppContext template, WebAppContext context) throws Exception
|
public void cloneConfigure(WebAppContext template, WebAppContext context) throws Exception
|
||||||
{
|
{
|
||||||
|
@ -117,31 +158,22 @@ 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
|
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())
|
if (scis == null || scis.isEmpty())
|
||||||
return parser; // nothing to do
|
return; // nothing to do
|
||||||
|
|
||||||
ServletContainerInitializerListener listener = new ServletContainerInitializerListener();
|
|
||||||
listener.setWebAppContext(context);
|
|
||||||
context.addEventListener(listener);
|
|
||||||
|
|
||||||
//may need to add a listener
|
|
||||||
|
|
||||||
ArrayList<ContainerInitializer> initializers = new ArrayList<ContainerInitializer>();
|
List<ContainerInitializer> initializers = new ArrayList<ContainerInitializer>();
|
||||||
context.setAttribute(CONTAINER_INITIALIZERS, initializers);
|
context.setAttribute(CONTAINER_INITIALIZERS, initializers);
|
||||||
|
|
||||||
for (ServletContainerInitializer service : scis)
|
for (ServletContainerInitializer service : scis)
|
||||||
|
@ -158,17 +190,14 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
||||||
{
|
{
|
||||||
initializer.setInterestedTypes(classes);
|
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
|
//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)
|
if (context.getAttribute(CLASS_INHERITANCE_MAP) == null)
|
||||||
{
|
{
|
||||||
ClassInheritanceHandler classHandler = new ClassInheritanceHandler();
|
MultiMap map = new MultiMap();
|
||||||
context.setAttribute(CLASS_INHERITANCE_MAP, classHandler.getMap());
|
context.setAttribute(CLASS_INHERITANCE_MAP, map);
|
||||||
parser.registerClassHandler(classHandler);
|
_classInheritanceHandler = new ClassInheritanceHandler(map);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Class c: classes)
|
for (Class c: classes)
|
||||||
|
@ -179,7 +208,7 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
||||||
{
|
{
|
||||||
if (LOG.isDebugEnabled()) LOG.debug("Registering annotation handler for "+c.getName());
|
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,15 +219,16 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
||||||
if (LOG.isDebugEnabled()) LOG.debug("No annotation on initializer "+service.getClass());
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check to see if the ServletContainerIntializer loaded via the ServiceLoader came
|
* Check to see if the ServletContainerIntializer loaded via the ServiceLoader came
|
||||||
* from a jar that is excluded by the fragment ordering. See ServletSpec 3.0 p.85.
|
* from a jar that is excluded by the fragment ordering. See ServletSpec 3.0 p.85.
|
||||||
|
@ -241,6 +271,11 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param context
|
||||||
|
* @return
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
public List<ServletContainerInitializer> getNonExcludedInitializers (WebAppContext context)
|
public List<ServletContainerInitializer> getNonExcludedInitializers (WebAppContext context)
|
||||||
throws Exception
|
throws Exception
|
||||||
{
|
{
|
||||||
|
@ -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)
|
public void parseContainerPath (final WebAppContext context, final AnnotationParser parser)
|
||||||
throws Exception
|
throws Exception
|
||||||
{
|
{
|
||||||
//if no pattern for the container path is defined, then by default scan NOTHING
|
//if no pattern for the container path is defined, then by default scan NOTHING
|
||||||
LOG.debug("Scanning container jars");
|
LOG.debug("Scanning container jars");
|
||||||
|
|
||||||
//clear any previously discovered annotations
|
//always parse for discoverable annotations as well as class hierarchy and servletcontainerinitializer related annotations
|
||||||
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);
|
||||||
|
|
||||||
//Convert from Resource to URI
|
//Convert from Resource to URI
|
||||||
ArrayList<URI> containerUris = new ArrayList<URI>();
|
ArrayList<URI> containerUris = new ArrayList<URI>();
|
||||||
|
@ -299,14 +349,17 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
//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)
|
public void parseWebInfLib (final WebAppContext context, final AnnotationParser parser)
|
||||||
throws Exception
|
throws Exception
|
||||||
{
|
{
|
||||||
|
@ -325,17 +378,35 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
||||||
|
|
||||||
for (Resource r : jars)
|
for (Resource r : jars)
|
||||||
{
|
{
|
||||||
//clear any previously discovered annotations from handlers
|
//for each jar, we decide which set of annotations we need to parse for
|
||||||
clearAnnotationList(parser.getAnnotationHandlers());
|
parser.clearHandlers();
|
||||||
|
|
||||||
|
|
||||||
URI uri = r.getURI();
|
URI uri = r.getURI();
|
||||||
FragmentDescriptor f = getFragmentFromJar(r, frags);
|
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
|
//or if it has a fragment we scan it if it is not metadata complete
|
||||||
|
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))
|
if (f == null || !isMetaDataComplete(f))
|
||||||
{
|
{
|
||||||
|
for (DiscoverableAnnotationHandler h:_discoverableAnnotationHandlers)
|
||||||
|
{
|
||||||
|
if (h instanceof AbstractDiscoverableAnnotationHandler)
|
||||||
|
((AbstractDiscoverableAnnotationHandler)h).setResource(r);
|
||||||
|
}
|
||||||
|
parser.registerHandlers(_discoverableAnnotationHandlers);
|
||||||
|
}
|
||||||
|
|
||||||
parser.parse(uri,
|
parser.parse(uri,
|
||||||
new ClassNameResolver()
|
new ClassNameResolver()
|
||||||
{
|
{
|
||||||
|
@ -354,13 +425,17 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
||||||
return true;
|
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)
|
public void parseWebInfClasses (final WebAppContext context, final AnnotationParser parser)
|
||||||
throws Exception
|
throws Exception
|
||||||
{
|
{
|
||||||
|
@ -370,7 +445,16 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
||||||
Resource classesDir = context.getWebInf().addPath("classes/");
|
Resource classesDir = context.getWebInf().addPath("classes/");
|
||||||
if (classesDir.exists())
|
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,
|
parser.parse(classesDir,
|
||||||
new ClassNameResolver()
|
new ClassNameResolver()
|
||||||
{
|
{
|
||||||
|
@ -389,17 +473,20 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
||||||
return true;
|
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)
|
public FragmentDescriptor getFragmentFromJar (Resource jar, List<FragmentDescriptor> frags)
|
||||||
throws Exception
|
throws Exception
|
||||||
{
|
{
|
||||||
|
@ -422,24 +509,5 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
||||||
return (d!=null && d.getMetaDataComplete() == MetaDataComplete.True);
|
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,10 +53,7 @@ public class AnnotationParser
|
||||||
private static final Logger LOG = Log.getLogger(AnnotationParser.class);
|
private static final Logger LOG = Log.getLogger(AnnotationParser.class);
|
||||||
|
|
||||||
protected List<String> _parsedClassNames = new ArrayList<String>();
|
protected List<String> _parsedClassNames = new ArrayList<String>();
|
||||||
protected Map<String, List<DiscoverableAnnotationHandler>> _annotationHandlers = new HashMap<String, List<DiscoverableAnnotationHandler>>();
|
protected List<Handler> _handlers = new ArrayList<Handler>();
|
||||||
protected List<ClassHandler> _classHandlers = new ArrayList<ClassHandler>();
|
|
||||||
protected List<MethodHandler> _methodHandlers = new ArrayList<MethodHandler>();
|
|
||||||
protected List<FieldHandler> _fieldHandlers = new ArrayList<FieldHandler>();
|
|
||||||
|
|
||||||
public static String normalize (String name)
|
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,
|
public void handleClass (String className, int version, int access,
|
||||||
String signature, String superName, String[] interfaces,
|
String signature, String superName, String[] interfaces,
|
||||||
String annotation, List<Value>values);
|
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,
|
public void handleMethod (String className, String methodName, int access,
|
||||||
String desc, String signature,String[] exceptions,
|
String desc, String signature,String[] exceptions,
|
||||||
String annotation, List<Value>values);
|
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,
|
public void handleField (String className, String fieldName, int access,
|
||||||
String fieldType, String signature, Object value,
|
String fieldType, String signature, Object value,
|
||||||
String annotation, List<Value>values);
|
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 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 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);
|
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
|
public class MyAnnotationVisitor implements AnnotationVisitor
|
||||||
{
|
{
|
||||||
List<Value> _annotationValues;
|
List<Value> _annotationValues;
|
||||||
|
@ -307,9 +389,12 @@ public class AnnotationParser
|
||||||
normalizedInterfaces[i++] = normalize(s);
|
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();
|
super.visitEnd();
|
||||||
|
|
||||||
//call all AnnotationHandlers with classname, annotation name + values
|
//call all AnnotationHandlers with classname, annotation name + values
|
||||||
List<DiscoverableAnnotationHandler> handlers = AnnotationParser.this._annotationHandlers.get(_annotationName);
|
for (Handler h : AnnotationParser.this._handlers)
|
||||||
if (handlers != null)
|
|
||||||
{
|
{
|
||||||
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();
|
super.visitEnd();
|
||||||
//call all AnnotationHandlers with classname, method, annotation name + values
|
//call all AnnotationHandlers with classname, method, annotation name + values
|
||||||
List<DiscoverableAnnotationHandler> handlers = AnnotationParser.this._annotationHandlers.get(_annotationName);
|
for (Handler h : AnnotationParser.this._handlers)
|
||||||
if (handlers != null)
|
|
||||||
{
|
{
|
||||||
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()
|
public void visitEnd()
|
||||||
{
|
{
|
||||||
super.visitEnd();
|
super.visitEnd();
|
||||||
List<DiscoverableAnnotationHandler> handlers = AnnotationParser.this._annotationHandlers.get(_annotationName);
|
for (Handler h : AnnotationParser.this._handlers)
|
||||||
if (handlers != null)
|
|
||||||
{
|
{
|
||||||
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
|
* Register a handler that will be called back when the named annotation is
|
||||||
* encountered on a class.
|
* encountered on a class.
|
||||||
*
|
*
|
||||||
|
* @deprecated see registerHandler(Handler)
|
||||||
* @param annotationName
|
* @param annotationName
|
||||||
* @param handler
|
* @param handler
|
||||||
*/
|
*/
|
||||||
public void registerAnnotationHandler (String annotationName, DiscoverableAnnotationHandler handler)
|
public void registerAnnotationHandler (String annotationName, DiscoverableAnnotationHandler handler)
|
||||||
{
|
{
|
||||||
List<DiscoverableAnnotationHandler> handlers = _annotationHandlers.get(annotationName);
|
_handlers.add(handler);
|
||||||
if (handlers == null)
|
|
||||||
{
|
|
||||||
handlers = new ArrayList<DiscoverableAnnotationHandler>();
|
|
||||||
_annotationHandlers.put(annotationName, handlers);
|
|
||||||
}
|
|
||||||
handlers.add(handler);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
* @param annotationName
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
public List<DiscoverableAnnotationHandler> getAnnotationHandlers(String annotationName)
|
public List<DiscoverableAnnotationHandler> getAnnotationHandlers(String annotationName)
|
||||||
{
|
{
|
||||||
List<DiscoverableAnnotationHandler> handlers = _annotationHandlers.get(annotationName);
|
List<DiscoverableAnnotationHandler> handlers = new ArrayList<DiscoverableAnnotationHandler>();
|
||||||
if (handlers == null)
|
for (Handler h:_handlers)
|
||||||
return Collections.emptyList();
|
{
|
||||||
return new ArrayList<DiscoverableAnnotationHandler>();
|
if (h instanceof DiscoverableAnnotationHandler)
|
||||||
|
{
|
||||||
|
DiscoverableAnnotationHandler dah = (DiscoverableAnnotationHandler)h;
|
||||||
|
if (annotationName.equals(dah.getAnnotationName()))
|
||||||
|
handlers.add(dah);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return handlers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
public List<DiscoverableAnnotationHandler> getAnnotationHandlers()
|
public List<DiscoverableAnnotationHandler> getAnnotationHandlers()
|
||||||
{
|
{
|
||||||
List<DiscoverableAnnotationHandler> allHandlers = new ArrayList<DiscoverableAnnotationHandler>();
|
List<DiscoverableAnnotationHandler> allAnnotationHandlers = new ArrayList<DiscoverableAnnotationHandler>();
|
||||||
for (List<DiscoverableAnnotationHandler> list:_annotationHandlers.values())
|
for (Handler h:_handlers)
|
||||||
allHandlers.addAll(list);
|
{
|
||||||
return allHandlers;
|
if (h instanceof DiscoverableAnnotationHandler)
|
||||||
|
allAnnotationHandlers.add((DiscoverableAnnotationHandler)h);
|
||||||
|
}
|
||||||
|
return allAnnotationHandlers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated see registerHandler(Handler)
|
||||||
|
* @param handler
|
||||||
|
*/
|
||||||
public void registerClassHandler (ClassHandler 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)
|
public boolean isParsed (String className)
|
||||||
{
|
{
|
||||||
return _parsedClassNames.contains(className);
|
return _parsedClassNames.contains(className);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a given class
|
||||||
|
*
|
||||||
|
* @param className
|
||||||
|
* @param resolver
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
public void parse (String className, ClassNameResolver resolver)
|
public void parse (String className, ClassNameResolver resolver)
|
||||||
throws Exception
|
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)
|
public void parse (Class clazz, ClassNameResolver resolver, boolean visitSuperClasses)
|
||||||
throws Exception
|
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)
|
public void parse (String[] classNames, ClassNameResolver resolver)
|
||||||
throws Exception
|
throws Exception
|
||||||
{
|
{
|
||||||
|
@ -502,6 +693,14 @@ public class AnnotationParser
|
||||||
parse(Arrays.asList(classNames), resolver);
|
parse(Arrays.asList(classNames), resolver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the given classes
|
||||||
|
*
|
||||||
|
* @param classNames
|
||||||
|
* @param resolver
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
public void parse (List<String> classNames, ClassNameResolver resolver)
|
public void parse (List<String> classNames, ClassNameResolver resolver)
|
||||||
throws Exception
|
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)
|
public void parse (Resource dir, ClassNameResolver resolver)
|
||||||
throws Exception
|
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.
|
* Only class files in jar files will be scanned.
|
||||||
|
*
|
||||||
* @param loader
|
* @param loader
|
||||||
* @param visitParents
|
* @param visitParents
|
||||||
* @param nullInclusive
|
* @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 uris
|
||||||
* @param resolver
|
* @param resolver
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
|
@ -647,6 +856,12 @@ public class AnnotationParser
|
||||||
scanner.scan(null, uris, true);
|
scanner.scan(null, uris, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a particular resource
|
||||||
|
* @param uri
|
||||||
|
* @param resolver
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
public void parse (URI uri, final ClassNameResolver resolver)
|
public void parse (URI uri, final ClassNameResolver resolver)
|
||||||
throws Exception
|
throws Exception
|
||||||
{
|
{
|
||||||
|
@ -656,6 +871,14 @@ public class AnnotationParser
|
||||||
parse(uris, resolver);
|
parse(uris, resolver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use ASM on a class
|
||||||
|
*
|
||||||
|
* @param is
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
protected void scanClass (InputStream is)
|
protected void scanClass (InputStream is)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
|
|
|
@ -35,10 +35,16 @@ public class ClassInheritanceHandler implements ClassHandler
|
||||||
private static final Logger LOG = Log.getLogger(ClassInheritanceHandler.class);
|
private static final Logger LOG = Log.getLogger(ClassInheritanceHandler.class);
|
||||||
|
|
||||||
|
|
||||||
MultiMap _inheritanceMap = new MultiMap();
|
MultiMap _inheritanceMap;
|
||||||
|
|
||||||
public ClassInheritanceHandler()
|
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)
|
public void handle(String className, int version, int access, String signature, String superName, String[] interfaces)
|
||||||
|
|
|
@ -65,4 +65,15 @@ public class ContainerInitializerAnnotationHandler implements DiscoverableAnnota
|
||||||
_initializer.addAnnotatedTypeName(className);
|
_initializer.addAnnotatedTypeName(className);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAnnotationName()
|
||||||
|
{
|
||||||
|
return _annotation.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ContainerInitializer getContainerInitializer()
|
||||||
|
{
|
||||||
|
return _initializer;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,8 @@ import javax.servlet.ServletContextListener;
|
||||||
|
|
||||||
import org.eclipse.jetty.plus.annotation.ContainerInitializer;
|
import org.eclipse.jetty.plus.annotation.ContainerInitializer;
|
||||||
import org.eclipse.jetty.util.MultiMap;
|
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;
|
import org.eclipse.jetty.webapp.WebAppContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -35,7 +37,8 @@ import org.eclipse.jetty.webapp.WebAppContext;
|
||||||
*/
|
*/
|
||||||
public class ServletContainerInitializerListener implements ServletContextListener
|
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)
|
public void setWebAppContext (WebAppContext context)
|
||||||
|
@ -103,14 +106,10 @@ public class ServletContainerInitializerListener implements ServletContextListen
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
//OK, how do I throw an exception such that it really stops the startup sequence?
|
LOG.warn(e);
|
||||||
e.printStackTrace();
|
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)
|
public void contextDestroyed(ServletContextEvent sce)
|
||||||
{
|
{
|
||||||
// TODO Auto-generated method stub
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ package org.eclipse.jetty.annotations;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import javax.servlet.ServletSecurityElement;
|
||||||
import javax.servlet.annotation.HttpConstraint;
|
import javax.servlet.annotation.HttpConstraint;
|
||||||
import javax.servlet.annotation.HttpMethodConstraint;
|
import javax.servlet.annotation.HttpMethodConstraint;
|
||||||
import javax.servlet.annotation.ServletSecurity;
|
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.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler;
|
||||||
import org.eclipse.jetty.security.ConstraintAware;
|
import org.eclipse.jetty.security.ConstraintAware;
|
||||||
import org.eclipse.jetty.security.ConstraintMapping;
|
import org.eclipse.jetty.security.ConstraintMapping;
|
||||||
|
import org.eclipse.jetty.security.ConstraintSecurityHandler;
|
||||||
import org.eclipse.jetty.servlet.ServletHolder;
|
import org.eclipse.jetty.servlet.ServletHolder;
|
||||||
import org.eclipse.jetty.servlet.ServletMapping;
|
import org.eclipse.jetty.servlet.ServletMapping;
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
import org.eclipse.jetty.util.security.Constraint;
|
import org.eclipse.jetty.util.security.Constraint;
|
||||||
|
import org.eclipse.jetty.webapp.Origin;
|
||||||
import org.eclipse.jetty.webapp.WebAppContext;
|
import org.eclipse.jetty.webapp.WebAppContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -80,7 +83,7 @@ public class ServletSecurityAnnotationHandler extends AbstractIntrospectableAnno
|
||||||
if (servletSecurity == null)
|
if (servletSecurity == null)
|
||||||
return;
|
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.
|
//of the url patterns defined for this servlet, then skip the security annotation.
|
||||||
|
|
||||||
List<ServletMapping> servletMappings = getServletMappings(clazz.getCanonicalName());
|
List<ServletMapping> servletMappings = getServletMappings(clazz.getCanonicalName());
|
||||||
|
@ -95,19 +98,15 @@ public class ServletSecurityAnnotationHandler extends AbstractIntrospectableAnno
|
||||||
//Make a fresh list
|
//Make a fresh list
|
||||||
constraintMappings = new ArrayList<ConstraintMapping>();
|
constraintMappings = new ArrayList<ConstraintMapping>();
|
||||||
|
|
||||||
//Get the values that form the constraints that will apply unless there are HttpMethodConstraints to augment them
|
ServletSecurityElement securityElement = new ServletSecurityElement(servletSecurity);
|
||||||
HttpConstraint defaults = servletSecurity.value();
|
for (ServletMapping sm : servletMappings)
|
||||||
|
{
|
||||||
//Make a Constraint for the <auth-constraint> and <user-data-constraint> specified by the HttpConstraint
|
for (String url : sm.getPathSpecs())
|
||||||
Constraint defaultConstraint = makeConstraint (clazz,
|
{
|
||||||
defaults.rolesAllowed(),
|
_context.getMetaData().setOrigin("constraint.url."+url, Origin.Annotation);
|
||||||
defaults.value(),
|
constraintMappings.addAll(ConstraintSecurityHandler.createConstraintsWithMappingsForPath(clazz.getName(), url, securityElement));
|
||||||
defaults.transportGuarantee());
|
}
|
||||||
|
}
|
||||||
constraintMappings.addAll(makeMethodMappings(clazz,
|
|
||||||
defaultConstraint,
|
|
||||||
servletMappings,
|
|
||||||
servletSecurity.httpMethodConstraints()));
|
|
||||||
|
|
||||||
//set up the security constraints produced by the annotation
|
//set up the security constraints produced by the annotation
|
||||||
ConstraintAware securityHandler = (ConstraintAware)_context.getSecurityHandler();
|
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)
|
protected Constraint makeConstraint (Class servlet, String[] rolesAllowed, EmptyRoleSemantic permitOrDeny, TransportGuarantee transport)
|
||||||
{
|
{
|
||||||
Constraint constraint = new Constraint();
|
return ConstraintSecurityHandler.createConstraint(servlet.getName(), rolesAllowed, permitOrDeny, transport);
|
||||||
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");
|
|
||||||
}
|
|
||||||
|
|
||||||
//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++)
|
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()))
|
if (pathSpecs[j].equals(constraintMappings.get(i).getPathSpec()))
|
||||||
{
|
{
|
||||||
exists = true;
|
exists = true;
|
||||||
|
|
|
@ -30,6 +30,7 @@ import org.eclipse.jetty.servlet.FilterMapping;
|
||||||
import org.eclipse.jetty.servlet.Holder;
|
import org.eclipse.jetty.servlet.Holder;
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
import org.eclipse.jetty.util.resource.Resource;
|
||||||
import org.eclipse.jetty.webapp.DiscoveredAnnotation;
|
import org.eclipse.jetty.webapp.DiscoveredAnnotation;
|
||||||
import org.eclipse.jetty.webapp.MetaData;
|
import org.eclipse.jetty.webapp.MetaData;
|
||||||
import org.eclipse.jetty.webapp.Origin;
|
import org.eclipse.jetty.webapp.Origin;
|
||||||
|
@ -53,6 +54,11 @@ public class WebFilterAnnotation extends DiscoveredAnnotation
|
||||||
super(context, className);
|
super(context, className);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public WebFilterAnnotation(WebAppContext context, String className, Resource resource)
|
||||||
|
{
|
||||||
|
super(context, className, resource);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see org.eclipse.jetty.annotations.ClassAnnotation#apply()
|
* @see org.eclipse.jetty.annotations.ClassAnnotation#apply()
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -23,6 +23,7 @@ import java.util.List;
|
||||||
import org.eclipse.jetty.annotations.AnnotationParser.Value;
|
import org.eclipse.jetty.annotations.AnnotationParser.Value;
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
import org.eclipse.jetty.webapp.DiscoveredAnnotation;
|
||||||
import org.eclipse.jetty.webapp.WebAppContext;
|
import org.eclipse.jetty.webapp.WebAppContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -39,23 +40,37 @@ public class WebFilterAnnotationHandler extends AbstractDiscoverableAnnotationHa
|
||||||
super(context);
|
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,
|
public void handleClass(String className, int version, int access, String signature, String superName, String[] interfaces, String annotation,
|
||||||
List<Value> values)
|
List<Value> values)
|
||||||
{
|
{
|
||||||
WebFilterAnnotation wfAnnotation = new WebFilterAnnotation(_context, className);
|
WebFilterAnnotation wfAnnotation = new WebFilterAnnotation(_context, className, _resource);
|
||||||
addAnnotation(wfAnnotation);
|
addAnnotation(wfAnnotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void handleField(String className, String fieldName, int access, String fieldType, String signature, Object value, String annotation,
|
public void handleField(String className, String fieldName, int access, String fieldType, String signature, Object value, String annotation,
|
||||||
List<Value> values)
|
List<Value> values)
|
||||||
{
|
{
|
||||||
LOG.warn ("@WebFilter not applicable for fields: "+className+"."+fieldName);
|
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,
|
public void handleMethod(String className, String methodName, int access, String params, String signature, String[] exceptions, String annotation,
|
||||||
List<Value> values)
|
List<Value> values)
|
||||||
{
|
{
|
||||||
LOG.warn ("@WebFilter not applicable for methods: "+className+"."+methodName+" "+signature);
|
LOG.warn ("@WebFilter not applicable for methods: "+className+"."+methodName+" "+signature);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAnnotationName()
|
||||||
|
{
|
||||||
|
return "javax.servlet.annotation.WebFilter";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ import javax.servlet.http.HttpSessionListener;
|
||||||
|
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
import org.eclipse.jetty.util.resource.Resource;
|
||||||
import org.eclipse.jetty.webapp.DiscoveredAnnotation;
|
import org.eclipse.jetty.webapp.DiscoveredAnnotation;
|
||||||
import org.eclipse.jetty.webapp.MetaData;
|
import org.eclipse.jetty.webapp.MetaData;
|
||||||
import org.eclipse.jetty.webapp.Origin;
|
import org.eclipse.jetty.webapp.Origin;
|
||||||
|
@ -50,6 +51,11 @@ public class WebListenerAnnotation extends DiscoveredAnnotation
|
||||||
super(context, className);
|
super(context, className);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public WebListenerAnnotation(WebAppContext context, String className, Resource resource)
|
||||||
|
{
|
||||||
|
super(context, className, resource);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see org.eclipse.jetty.annotations.ClassAnnotation#apply()
|
* @see org.eclipse.jetty.annotations.ClassAnnotation#apply()
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -23,6 +23,7 @@ import java.util.List;
|
||||||
import org.eclipse.jetty.annotations.AnnotationParser.Value;
|
import org.eclipse.jetty.annotations.AnnotationParser.Value;
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
import org.eclipse.jetty.webapp.DiscoveredAnnotation;
|
||||||
import org.eclipse.jetty.webapp.WebAppContext;
|
import org.eclipse.jetty.webapp.WebAppContext;
|
||||||
|
|
||||||
public class WebListenerAnnotationHandler extends AbstractDiscoverableAnnotationHandler
|
public class WebListenerAnnotationHandler extends AbstractDiscoverableAnnotationHandler
|
||||||
|
@ -34,13 +35,18 @@ public class WebListenerAnnotationHandler extends AbstractDiscoverableAnnotation
|
||||||
super(context);
|
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)
|
* @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,
|
public void handleClass(String className, int version, int access, String signature, String superName, String[] interfaces, String annotation,
|
||||||
List<Value> values)
|
List<Value> values)
|
||||||
{
|
{
|
||||||
WebListenerAnnotation wlAnnotation = new WebListenerAnnotation(_context, className);
|
WebListenerAnnotation wlAnnotation = new WebListenerAnnotation(_context, className, _resource);
|
||||||
addAnnotation(wlAnnotation);
|
addAnnotation(wlAnnotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,4 +62,10 @@ public class WebListenerAnnotationHandler extends AbstractDiscoverableAnnotation
|
||||||
LOG.warn ("@WebListener is not applicable to methods: "+className+"."+methodName+" "+signature);
|
LOG.warn ("@WebListener is not applicable to methods: "+className+"."+methodName+" "+signature);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAnnotationName()
|
||||||
|
{
|
||||||
|
return "javax.servlet.annotation.WebListener";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ import org.eclipse.jetty.servlet.ServletMapping;
|
||||||
import org.eclipse.jetty.util.LazyList;
|
import org.eclipse.jetty.util.LazyList;
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
import org.eclipse.jetty.util.resource.Resource;
|
||||||
import org.eclipse.jetty.webapp.DiscoveredAnnotation;
|
import org.eclipse.jetty.webapp.DiscoveredAnnotation;
|
||||||
import org.eclipse.jetty.webapp.MetaData;
|
import org.eclipse.jetty.webapp.MetaData;
|
||||||
import org.eclipse.jetty.webapp.Origin;
|
import org.eclipse.jetty.webapp.Origin;
|
||||||
|
@ -48,6 +49,12 @@ public class WebServletAnnotation extends DiscoveredAnnotation
|
||||||
super(context, className);
|
super(context, className);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public WebServletAnnotation (WebAppContext context, String className, Resource resource)
|
||||||
|
{
|
||||||
|
super(context, className, resource);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see org.eclipse.jetty.annotations.ClassAnnotation#apply()
|
* @see org.eclipse.jetty.annotations.ClassAnnotation#apply()
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -23,6 +23,7 @@ import java.util.List;
|
||||||
import org.eclipse.jetty.annotations.AnnotationParser.Value;
|
import org.eclipse.jetty.annotations.AnnotationParser.Value;
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
import org.eclipse.jetty.webapp.DiscoveredAnnotation;
|
||||||
import org.eclipse.jetty.webapp.WebAppContext;
|
import org.eclipse.jetty.webapp.WebAppContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -40,6 +41,11 @@ public class WebServletAnnotationHandler extends AbstractDiscoverableAnnotationH
|
||||||
super(context);
|
super(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public WebServletAnnotationHandler (WebAppContext context, List<DiscoveredAnnotation> list)
|
||||||
|
{
|
||||||
|
super(context, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle discovering a WebServlet annotation.
|
* 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)
|
* @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,
|
public void handleClass(String className, int version, int access, String signature, String superName, String[] interfaces, String annotationName,
|
||||||
List<Value> values)
|
List<Value> values)
|
||||||
{
|
{
|
||||||
if (!"javax.servlet.annotation.WebServlet".equals(annotationName))
|
if (!"javax.servlet.annotation.WebServlet".equals(annotationName))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
WebServletAnnotation annotation = new WebServletAnnotation (_context, className);
|
WebServletAnnotation annotation = new WebServletAnnotation (_context, className, _resource);
|
||||||
addAnnotation(annotation);
|
addAnnotation(annotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void handleField(String className, String fieldName, int access, String fieldType, String signature, Object value, String annotation,
|
public void handleField(String className, String fieldName, int access, String fieldType, String signature, Object value, String annotation,
|
||||||
List<Value> values)
|
List<Value> values)
|
||||||
{
|
{
|
||||||
LOG.warn ("@WebServlet annotation not supported for fields");
|
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,
|
public void handleMethod(String className, String methodName, int access, String params, String signature, String[] exceptions, String annotation,
|
||||||
List<Value> values)
|
List<Value> values)
|
||||||
{
|
{
|
||||||
LOG.warn ("@WebServlet annotation not supported for methods");
|
LOG.warn ("@WebServlet annotation not supported for methods");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAnnotationName()
|
||||||
|
{
|
||||||
|
return "javax.servlet.annotation.WebServlet";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,6 +66,12 @@ public class TestAnnotationInheritance
|
||||||
{
|
{
|
||||||
annotatedMethods.add(className+"."+methodName);
|
annotatedMethods.add(className+"."+methodName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAnnotationName()
|
||||||
|
{
|
||||||
|
return "org.eclipse.jetty.annotations.Sample";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
|
@ -85,7 +91,7 @@ public class TestAnnotationInheritance
|
||||||
|
|
||||||
SampleHandler handler = new SampleHandler();
|
SampleHandler handler = new SampleHandler();
|
||||||
AnnotationParser parser = new AnnotationParser();
|
AnnotationParser parser = new AnnotationParser();
|
||||||
parser.registerAnnotationHandler("org.eclipse.jetty.annotations.Sample", handler);
|
parser.registerHandler(handler);
|
||||||
parser.parse(classNames, new ClassNameResolver ()
|
parser.parse(classNames, new ClassNameResolver ()
|
||||||
{
|
{
|
||||||
public boolean isExcluded(String name)
|
public boolean isExcluded(String name)
|
||||||
|
|
|
@ -41,6 +41,9 @@ public class TestAnnotationParser
|
||||||
{
|
{
|
||||||
private List<String> methods = Arrays.asList("a", "b", "c", "d", "l");
|
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,
|
public void handleClass(String className, int version, int access, String signature, String superName, String[] interfaces, String annotation,
|
||||||
List<Value> values)
|
List<Value> values)
|
||||||
{
|
{
|
||||||
|
@ -81,9 +84,15 @@ public class TestAnnotationParser
|
||||||
assertTrue(methods.contains(methodName));
|
assertTrue(methods.contains(methodName));
|
||||||
assertEquals("org.eclipse.jetty.annotations.Sample", annotation);
|
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();
|
long start = System.currentTimeMillis();
|
||||||
parser.parse(classNames, new ClassNameResolver ()
|
parser.parse(classNames, new ClassNameResolver ()
|
||||||
|
@ -140,9 +149,17 @@ public class TestAnnotationParser
|
||||||
System.err.println(anv.toString());
|
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);
|
parser.parse(classNames, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -172,6 +172,9 @@ public abstract class AbstractHttpConnection extends AbstractConnection implemen
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
protected void commitRequest() throws IOException
|
protected void commitRequest() throws IOException
|
||||||
{
|
{
|
||||||
synchronized (this)
|
synchronized (this)
|
||||||
|
@ -223,6 +226,7 @@ public abstract class AbstractHttpConnection extends AbstractConnection implemen
|
||||||
requestHeaders.putLongField(HttpHeader.CONTENT_LENGTH, requestContent.length());
|
requestHeaders.putLongField(HttpHeader.CONTENT_LENGTH, requestContent.length());
|
||||||
_generator.completeHeader(requestHeaders,false);
|
_generator.completeHeader(requestHeaders,false);
|
||||||
_generator.addContent(new View(requestContent),true);
|
_generator.addContent(new View(requestContent),true);
|
||||||
|
_exchange.setStatus(HttpExchange.STATUS_WAITING_FOR_RESPONSE);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -230,26 +234,16 @@ public abstract class AbstractHttpConnection extends AbstractConnection implemen
|
||||||
if (requestContentStream != null)
|
if (requestContentStream != null)
|
||||||
{
|
{
|
||||||
_generator.completeHeader(requestHeaders, false);
|
_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
|
else
|
||||||
{
|
{
|
||||||
requestHeaders.remove(HttpHeader.CONTENT_LENGTH);
|
requestHeaders.remove(HttpHeader.CONTENT_LENGTH);
|
||||||
_generator.completeHeader(requestHeaders, true);
|
_generator.completeHeader(requestHeaders, true);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_exchange.setStatus(HttpExchange.STATUS_WAITING_FOR_RESPONSE);
|
_exchange.setStatus(HttpExchange.STATUS_WAITING_FOR_RESPONSE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected void reset() throws IOException
|
protected void reset() throws IOException
|
||||||
{
|
{
|
||||||
|
|
|
@ -114,6 +114,8 @@ public class AsyncHttpConnection extends AbstractHttpConnection implements Async
|
||||||
ByteBuffer chunk=_requestContentChunk;
|
ByteBuffer chunk=_requestContentChunk;
|
||||||
_requestContentChunk=exchange.getRequestContentChunk(null);
|
_requestContentChunk=exchange.getRequestContentChunk(null);
|
||||||
_generator.addContent(chunk,_requestContentChunk==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);
|
Connection switched=exchange.onSwitchProtocol(_endp);
|
||||||
if (switched!=null)
|
if (switched!=null)
|
||||||
connection=switched;
|
|
||||||
{
|
{
|
||||||
// switched protocol!
|
// switched protocol!
|
||||||
_pipeline = null;
|
|
||||||
if (_pipeline!=null)
|
if (_pipeline!=null)
|
||||||
|
{
|
||||||
_destination.send(_pipeline);
|
_destination.send(_pipeline);
|
||||||
|
}
|
||||||
_pipeline = null;
|
_pipeline = null;
|
||||||
|
|
||||||
connection=switched;
|
connection=switched;
|
||||||
|
|
|
@ -122,6 +122,8 @@ public class BlockingHttpConnection extends AbstractHttpConnection
|
||||||
ByteBuffer chunk=_requestContentChunk;
|
ByteBuffer chunk=_requestContentChunk;
|
||||||
_requestContentChunk=exchange.getRequestContentChunk(null);
|
_requestContentChunk=exchange.getRequestContentChunk(null);
|
||||||
_generator.addContent(chunk,_requestContentChunk==null);
|
_generator.addContent(chunk,_requestContentChunk==null);
|
||||||
|
if (_requestContentChunk==null)
|
||||||
|
exchange.setStatus(HttpExchange.STATUS_WAITING_FOR_RESPONSE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -792,12 +792,16 @@ public class HttpFields implements Iterable<HttpFields.Field>
|
||||||
QuotedStringTokenizer.quoteIfNeeded(buf, name, delim);
|
QuotedStringTokenizer.quoteIfNeeded(buf, name, delim);
|
||||||
buf.append('=');
|
buf.append('=');
|
||||||
String start=buf.toString();
|
String start=buf.toString();
|
||||||
|
boolean hasDomain = false;
|
||||||
|
boolean hasPath = false;
|
||||||
|
|
||||||
if (value != null && value.length() > 0)
|
if (value != null && value.length() > 0)
|
||||||
QuotedStringTokenizer.quoteIfNeeded(buf, value, delim);
|
QuotedStringTokenizer.quoteIfNeeded(buf, value, delim);
|
||||||
|
|
||||||
|
|
||||||
if (path != null && path.length() > 0)
|
if (path != null && path.length() > 0)
|
||||||
{
|
{
|
||||||
|
hasPath = true;
|
||||||
buf.append(";Path=");
|
buf.append(";Path=");
|
||||||
if (path.trim().startsWith("\""))
|
if (path.trim().startsWith("\""))
|
||||||
buf.append(path);
|
buf.append(path);
|
||||||
|
@ -806,6 +810,7 @@ public class HttpFields implements Iterable<HttpFields.Field>
|
||||||
}
|
}
|
||||||
if (domain != null && domain.length() > 0)
|
if (domain != null && domain.length() > 0)
|
||||||
{
|
{
|
||||||
|
hasDomain = true;
|
||||||
buf.append(";Domain=");
|
buf.append(";Domain=");
|
||||||
QuotedStringTokenizer.quoteIfNeeded(buf,domain.toLowerCase(),delim);
|
QuotedStringTokenizer.quoteIfNeeded(buf,domain.toLowerCase(),delim);
|
||||||
}
|
}
|
||||||
|
@ -841,7 +846,12 @@ public class HttpFields implements Iterable<HttpFields.Field>
|
||||||
Field last=null;
|
Field last=null;
|
||||||
while (field!=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))
|
||||||
|
{
|
||||||
|
//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);
|
_fields.remove(field);
|
||||||
if (last==null)
|
if (last==null)
|
||||||
|
@ -850,6 +860,7 @@ public class HttpFields implements Iterable<HttpFields.Field>
|
||||||
last._next=field._next;
|
last._next=field._next;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
last=field;
|
last=field;
|
||||||
field=field._next;
|
field=field._next;
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,7 +100,15 @@ public class HttpURI
|
||||||
public HttpURI(String raw)
|
public HttpURI(String raw)
|
||||||
{
|
{
|
||||||
_rawString=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);
|
parse(b,0,b.length);
|
||||||
_charset = URIUtil.__CHARSET;
|
_charset = URIUtil.__CHARSET;
|
||||||
}
|
}
|
||||||
|
@ -693,11 +701,13 @@ public class HttpURI
|
||||||
{
|
{
|
||||||
if (_query==_fragment)
|
if (_query==_fragment)
|
||||||
return;
|
return;
|
||||||
|
if (_charset==StringUtil.__UTF8_CHARSET)
|
||||||
UrlEncoded.decodeUtf8To(_raw,_query+1,_fragment-_query-1,parameters);
|
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)
|
public void decodeQueryTo(MultiMap parameters, String encoding) throws UnsupportedEncodingException
|
||||||
throws UnsupportedEncodingException
|
|
||||||
{
|
{
|
||||||
if (_query==_fragment)
|
if (_query==_fragment)
|
||||||
return;
|
return;
|
||||||
|
@ -705,7 +715,7 @@ public class HttpURI
|
||||||
if (encoding==null || StringUtil.isUTF8(encoding))
|
if (encoding==null || StringUtil.isUTF8(encoding))
|
||||||
UrlEncoded.decodeUtf8To(_raw,_query+1,_fragment-_query-1,parameters);
|
UrlEncoded.decodeUtf8To(_raw,_query+1,_fragment-_query-1,parameters);
|
||||||
else
|
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()
|
public void clear()
|
||||||
|
|
|
@ -277,7 +277,8 @@ public class HttpFieldsTest
|
||||||
assertEquals("minimal=value",fields.getStringField("Set-Cookie"));
|
assertEquals("minimal=value",fields.getStringField("Set-Cookie"));
|
||||||
|
|
||||||
fields.clear();
|
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);
|
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"));
|
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");
|
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());
|
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());
|
assertFalse(e.hasMoreElements());
|
||||||
assertEquals("Thu, 01 Jan 1970 00:00:00 GMT",fields.getStringField("Expires"));
|
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.clear();
|
||||||
fields.addSetCookie("ev erything","va lue","do main","pa th",1,"co mment",true,true,2);
|
fields.addSetCookie("ev erything","va lue","do main","pa th",1,"co mment",true,true,2);
|
||||||
|
|
|
@ -37,6 +37,7 @@ import org.eclipse.jetty.security.IdentityService;
|
||||||
import org.eclipse.jetty.security.ServerAuthException;
|
import org.eclipse.jetty.security.ServerAuthException;
|
||||||
import org.eclipse.jetty.security.UserAuthentication;
|
import org.eclipse.jetty.security.UserAuthentication;
|
||||||
import org.eclipse.jetty.security.authentication.DeferredAuthentication;
|
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.Authentication;
|
||||||
import org.eclipse.jetty.server.UserIdentity;
|
import org.eclipse.jetty.server.UserIdentity;
|
||||||
import org.eclipse.jetty.server.Authentication.User;
|
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) $
|
* @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;
|
private final ServerAuthConfig _authConfig;
|
||||||
|
|
||||||
|
@ -58,7 +59,7 @@ public class JaspiAuthenticator implements Authenticator
|
||||||
|
|
||||||
private final IdentityService _identityService;
|
private final IdentityService _identityService;
|
||||||
|
|
||||||
private final DeferredAuthentication _deferred;
|
|
||||||
|
|
||||||
public JaspiAuthenticator(ServerAuthConfig authConfig, Map authProperties, ServletCallbackHandler callbackHandler, Subject serviceSubject,
|
public JaspiAuthenticator(ServerAuthConfig authConfig, Map authProperties, ServletCallbackHandler callbackHandler, Subject serviceSubject,
|
||||||
boolean allowLazyAuthentication, IdentityService identityService)
|
boolean allowLazyAuthentication, IdentityService identityService)
|
||||||
|
@ -72,11 +73,11 @@ public class JaspiAuthenticator implements Authenticator
|
||||||
this._serviceSubject = serviceSubject;
|
this._serviceSubject = serviceSubject;
|
||||||
this._allowLazyAuthentication = allowLazyAuthentication;
|
this._allowLazyAuthentication = allowLazyAuthentication;
|
||||||
this._identityService = identityService;
|
this._identityService = identityService;
|
||||||
this._deferred = new DeferredAuthentication(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setConfiguration(AuthConfiguration configuration)
|
public void setConfiguration(AuthConfiguration configuration)
|
||||||
{
|
{
|
||||||
|
super.setConfiguration(configuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getAuthMethod()
|
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 its not mandatory to authenticate, and the authenticator returned UNAUTHENTICATED, we treat it as authentication deferred
|
||||||
if (_allowLazyAuthentication && !info.isAuthMandatory() && a == Authentication.UNAUTHENTICATED)
|
if (_allowLazyAuthentication && !info.isAuthMandatory() && a == Authentication.UNAUTHENTICATED)
|
||||||
a =_deferred;
|
a = new DeferredAuthentication(this);
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -97,25 +97,15 @@ public class ContextFactory implements ObjectFactory
|
||||||
return ctx;
|
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;
|
ClassLoader loader = null;
|
||||||
if (ContextHandler.getCurrentContext() != null)
|
|
||||||
|
loader = Thread.currentThread().getContextClassLoader();
|
||||||
|
if (__log.isDebugEnabled() && loader != null) __log.debug("Using thread context classloader");
|
||||||
|
|
||||||
|
if (loader == null && ContextHandler.getCurrentContext() != null)
|
||||||
{
|
{
|
||||||
loader = ContextHandler.getCurrentContext().getContextHandler().getClassLoader();
|
loader = ContextHandler.getCurrentContext().getContextHandler().getClassLoader();
|
||||||
}
|
if (__log.isDebugEnabled() && loader != null) __log.debug("Using classloader of current org.eclipse.jetty.server.handler.ContextHandler");
|
||||||
|
|
||||||
|
|
||||||
if (loader != 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");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Get the context matching the classloader
|
//Get the context matching the classloader
|
||||||
|
@ -124,13 +114,7 @@ public class ContextFactory implements ObjectFactory
|
||||||
//The map does not contain an entry for this classloader
|
//The map does not contain an entry for this classloader
|
||||||
if (ctx == null)
|
if (ctx == null)
|
||||||
{
|
{
|
||||||
//Check if a parent classloader has created the context
|
//Didn't find a context to match, make one
|
||||||
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;
|
Reference ref = (Reference)obj;
|
||||||
StringRefAddr parserAddr = (StringRefAddr)ref.get("parser");
|
StringRefAddr parserAddr = (StringRefAddr)ref.get("parser");
|
||||||
String parserClassName = (parserAddr==null?null:(String)parserAddr.getContent());
|
String parserClassName = (parserAddr==null?null:(String)parserAddr.getContent());
|
||||||
|
@ -140,31 +124,14 @@ public class ContextFactory implements ObjectFactory
|
||||||
name.get(0),
|
name.get(0),
|
||||||
(NamingContext)nameCtx,
|
(NamingContext)nameCtx,
|
||||||
parser);
|
parser);
|
||||||
if(__log.isDebugEnabled())__log.debug("No entry for classloader: "+loader);
|
if(__log.isDebugEnabled())__log.debug("Made context "+name.get(0)+" for classloader: "+loader);
|
||||||
__contextMap.put (loader, ctx);
|
__contextMap.put (loader, ctx);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -35,6 +35,7 @@ import javax.naming.Reference;
|
||||||
import javax.naming.StringRefAddr;
|
import javax.naming.StringRefAddr;
|
||||||
import javax.naming.spi.ObjectFactory;
|
import javax.naming.spi.ObjectFactory;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.jndi.ContextFactory;
|
||||||
import org.eclipse.jetty.jndi.NamingContext;
|
import org.eclipse.jetty.jndi.NamingContext;
|
||||||
import org.eclipse.jetty.jndi.local.localContextRoot;
|
import org.eclipse.jetty.jndi.local.localContextRoot;
|
||||||
import org.eclipse.jetty.util.log.Log;
|
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.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertNotSame;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
@ -70,8 +72,6 @@ public class TestJNDI
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIt() throws Exception
|
public void testIt() throws Exception
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
//set up some classloaders
|
//set up some classloaders
|
||||||
Thread currentThread = Thread.currentThread();
|
Thread currentThread = Thread.currentThread();
|
||||||
|
@ -79,6 +79,10 @@ public class TestJNDI
|
||||||
ClassLoader childLoader1 = new URLClassLoader(new URL[0], currentLoader);
|
ClassLoader childLoader1 = new URLClassLoader(new URL[0], currentLoader);
|
||||||
ClassLoader childLoader2 = new URLClassLoader(new URL[0], currentLoader);
|
ClassLoader childLoader2 = new URLClassLoader(new URL[0], currentLoader);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
//Uncomment to aid with debug
|
||||||
/*
|
/*
|
||||||
javaRootURLContext.getRoot().addListener(new NamingContext.Listener()
|
javaRootURLContext.getRoot().addListener(new NamingContext.Listener()
|
||||||
{
|
{
|
||||||
|
@ -117,6 +121,18 @@ public class TestJNDI
|
||||||
initCtxA.bind ("blah", "123");
|
initCtxA.bind ("blah", "123");
|
||||||
assertEquals ("123", initCtxA.lookup("blah"));
|
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();
|
InitialContext initCtx = new InitialContext();
|
||||||
Context sub0 = (Context)initCtx.lookup("java:");
|
Context sub0 = (Context)initCtx.lookup("java:");
|
||||||
|
|
||||||
|
@ -216,6 +232,7 @@ public class TestJNDI
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
initCtx.lookup("java:comp/env/rubbish");
|
initCtx.lookup("java:comp/env/rubbish");
|
||||||
|
fail("env should not exist for this classloader");
|
||||||
}
|
}
|
||||||
catch (NameNotFoundException e)
|
catch (NameNotFoundException e)
|
||||||
{
|
{
|
||||||
|
@ -285,17 +302,77 @@ public class TestJNDI
|
||||||
//expected failure to modify immutable context
|
//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
|
//test what happens when you close an initial context that was used
|
||||||
initCtx.close();
|
initCtx.close();
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
//make some effort to clean up
|
||||||
InitialContext ic = new InitialContext();
|
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");
|
Context comp = (Context)ic.lookup("java:comp");
|
||||||
comp.destroySubcontext("env");
|
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");
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,8 @@ import org.eclipse.jetty.osgi.boot.JettyBootstrapActivator;
|
||||||
import org.eclipse.jetty.osgi.boot.OSGiAppProvider;
|
import org.eclipse.jetty.osgi.boot.OSGiAppProvider;
|
||||||
import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelper;
|
import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelper;
|
||||||
import org.eclipse.jetty.osgi.boot.utils.WebappRegistrationCustomizer;
|
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.Bundle;
|
||||||
import org.osgi.framework.FrameworkUtil;
|
import org.osgi.framework.FrameworkUtil;
|
||||||
import org.xml.sax.EntityResolver;
|
import org.xml.sax.EntityResolver;
|
||||||
|
@ -52,6 +54,7 @@ import org.xml.sax.SAXException;
|
||||||
*/
|
*/
|
||||||
public class WebappRegistrationCustomizerImpl implements WebappRegistrationCustomizer
|
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
|
* 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)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
System.err.println("Unable to set the JspFactory: jsp support incomplete.");
|
LOG.warn("Unable to set the JspFactory: jsp support incomplete.",e);
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,17 +140,23 @@ public class WebappRegistrationCustomizerImpl implements WebappRegistrationCusto
|
||||||
public URL[] getJarsWithTlds(OSGiAppProvider provider, BundleFileLocatorHelper locatorHelper) throws Exception
|
public URL[] getJarsWithTlds(OSGiAppProvider provider, BundleFileLocatorHelper locatorHelper) throws Exception
|
||||||
{
|
{
|
||||||
|
|
||||||
|
ArrayList<URL> urls = new ArrayList<URL>();
|
||||||
HashSet<Class<?>> classesToAddToTheTldBundles = new HashSet<Class<?>>();
|
HashSet<Class<?>> classesToAddToTheTldBundles = new HashSet<Class<?>>();
|
||||||
|
|
||||||
// Look for the jstl bundle
|
// Look for the jstl bundle
|
||||||
// We assume the jstl's tlds are defined there.
|
// We assume the jstl's tlds are defined there.
|
||||||
// We assume that the jstl bundle is imported by this bundle
|
// We assume that the jstl bundle is imported by this bundle
|
||||||
// So we can look for this class using this bundle's classloader:
|
// So we can look for this class using this bundle's classloader:
|
||||||
|
try
|
||||||
|
{
|
||||||
Class<?> jstlClass = WebappRegistrationCustomizerImpl.class.getClassLoader().loadClass(DEFAULT_JSTL_BUNDLE_CLASS);
|
Class<?> jstlClass = WebappRegistrationCustomizerImpl.class.getClassLoader().loadClass(DEFAULT_JSTL_BUNDLE_CLASS);
|
||||||
|
|
||||||
classesToAddToTheTldBundles.add(jstlClass);
|
classesToAddToTheTldBundles.add(jstlClass);
|
||||||
|
}
|
||||||
ArrayList<URL> urls = new ArrayList<URL>();
|
catch (ClassNotFoundException e)
|
||||||
|
{
|
||||||
|
LOG.info("jstl not on classpath", e);
|
||||||
|
}
|
||||||
for (Class<?> cl : classesToAddToTheTldBundles)
|
for (Class<?> cl : classesToAddToTheTldBundles)
|
||||||
{
|
{
|
||||||
Bundle tldBundle = FrameworkUtil.getBundle(cl);
|
Bundle tldBundle = FrameworkUtil.getBundle(cl);
|
||||||
|
@ -208,7 +216,7 @@ public class WebappRegistrationCustomizerImpl implements WebappRegistrationCusto
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
e.printStackTrace();
|
LOG.warn(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,8 @@ package org.eclipse.jetty.osgi.annotations;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
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.annotations.ClassNameResolver;
|
||||||
import org.eclipse.jetty.osgi.boot.OSGiWebappConstants;
|
import org.eclipse.jetty.osgi.boot.OSGiWebappConstants;
|
||||||
import org.eclipse.jetty.osgi.boot.utils.internal.PackageAdminServiceTracker;
|
import org.eclipse.jetty.osgi.boot.utils.internal.PackageAdminServiceTracker;
|
||||||
|
@ -151,21 +153,26 @@ public class AnnotationConfiguration extends org.eclipse.jetty.annotations.Annot
|
||||||
protected void parseBundle(WebAppContext context, AnnotationParser parser,
|
protected void parseBundle(WebAppContext context, AnnotationParser parser,
|
||||||
Bundle webbundle, Bundle bundle) throws Exception
|
Bundle webbundle, Bundle bundle) throws Exception
|
||||||
{
|
{
|
||||||
|
|
||||||
Resource bundleRes = parser.getResource(bundle);
|
Resource bundleRes = parser.getResource(bundle);
|
||||||
parser.parse(bundle,createClassNameResolver(context));
|
|
||||||
List<DiscoveredAnnotation> annotations = new ArrayList<DiscoveredAnnotation>();
|
parser.clearHandlers();
|
||||||
gatherAnnotations(annotations, parser.getAnnotationHandlers());
|
for (DiscoverableAnnotationHandler h:_discoverableAnnotationHandlers)
|
||||||
|
{
|
||||||
|
if (h instanceof AbstractDiscoverableAnnotationHandler)
|
||||||
|
{
|
||||||
if (webbundle == bundle)
|
if (webbundle == bundle)
|
||||||
{
|
((AbstractDiscoverableAnnotationHandler)h).setResource(null);
|
||||||
//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
|
else
|
||||||
{
|
((AbstractDiscoverableAnnotationHandler)h).setResource(bundleRes);
|
||||||
context.getMetaData().addDiscoveredAnnotations(bundleRes, annotations);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
parser.registerHandlers(_discoverableAnnotationHandlers);
|
||||||
|
parser.registerHandler(_classInheritanceHandler);
|
||||||
|
parser.registerHandlers(_containerInitializerAnnotationHandlers);
|
||||||
|
|
||||||
|
parser.parse(bundle,createClassNameResolver(context));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the same classname resolver than for the webInfjar scanner
|
* Returns the same classname resolver than for the webInfjar scanner
|
||||||
|
|
|
@ -48,4 +48,18 @@ public class TldLocatableURLClassloader extends URLClassLoader
|
||||||
{
|
{
|
||||||
return _jarsWithTldsInside;
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,15 +19,24 @@
|
||||||
package org.eclipse.jetty.security;
|
package org.eclipse.jetty.security;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import java.util.concurrent.CopyOnWriteArraySet;
|
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.HttpStatus;
|
||||||
import org.eclipse.jetty.http.PathMap;
|
import org.eclipse.jetty.http.PathMap;
|
||||||
import org.eclipse.jetty.server.HttpChannel;
|
import org.eclipse.jetty.server.HttpChannel;
|
||||||
|
@ -36,24 +45,230 @@ import org.eclipse.jetty.server.Request;
|
||||||
import org.eclipse.jetty.server.Response;
|
import org.eclipse.jetty.server.Response;
|
||||||
import org.eclipse.jetty.server.UserIdentity;
|
import org.eclipse.jetty.server.UserIdentity;
|
||||||
import org.eclipse.jetty.util.StringMap;
|
import org.eclipse.jetty.util.StringMap;
|
||||||
import org.eclipse.jetty.util.TypeUtil;
|
|
||||||
import org.eclipse.jetty.util.security.Constraint;
|
import org.eclipse.jetty.util.security.Constraint;
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/**
|
/**
|
||||||
* Handler to enforce SecurityConstraints. This implementation is servlet spec
|
* 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.
|
* efficiency.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class ConstraintSecurityHandler extends SecurityHandler implements ConstraintAware
|
public class ConstraintSecurityHandler extends SecurityHandler implements ConstraintAware
|
||||||
{
|
{
|
||||||
|
private static final String OMISSION_SUFFIX = ".omission";
|
||||||
private static final String ALL_METHODS = "*";
|
private static final String ALL_METHODS = "*";
|
||||||
private final List<ConstraintMapping> _constraintMappings= new CopyOnWriteArrayList<>();
|
private final List<ConstraintMapping> _constraintMappings= new CopyOnWriteArrayList<>();
|
||||||
private final Set<String> _roles = new CopyOnWriteArraySet<>();
|
private final Set<String> _roles = new CopyOnWriteArraySet<>();
|
||||||
private final PathMap<Map<String, RoleInfo>> _constraintMap = new PathMap<>();
|
private final PathMap<Map<String, RoleInfo>> _constraintMap = new PathMap<>();
|
||||||
private boolean _strict = true;
|
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.
|
/** Get the strict mode.
|
||||||
* @return true if the security handler is running in 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
|
@Override
|
||||||
public void setConstraintMappings(List<ConstraintMapping> constraintMappings, Set<String> roles)
|
public void setConstraintMappings(List<ConstraintMapping> constraintMappings, Set<String> roles)
|
||||||
{
|
{
|
||||||
if (isStarted())
|
|
||||||
throw new IllegalStateException("Started");
|
|
||||||
_constraintMappings.clear();
|
_constraintMappings.clear();
|
||||||
_constraintMappings.addAll(constraintMappings);
|
_constraintMappings.addAll(constraintMappings);
|
||||||
|
|
||||||
|
@ -160,6 +373,14 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setRoles(roles);
|
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)
|
public void setRoles(Set<String> roles)
|
||||||
{
|
{
|
||||||
if (isStarted())
|
|
||||||
throw new IllegalStateException("Started");
|
|
||||||
|
|
||||||
_roles.clear();
|
_roles.clear();
|
||||||
_roles.addAll(roles);
|
_roles.addAll(roles);
|
||||||
}
|
}
|
||||||
|
@ -239,6 +457,8 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
|
||||||
super.doStart();
|
super.doStart();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------ */
|
||||||
@Override
|
@Override
|
||||||
protected void doStop() throws Exception
|
protected void doStop() throws Exception
|
||||||
{
|
{
|
||||||
|
@ -246,6 +466,14 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
|
||||||
_constraintMap.clear();
|
_constraintMap.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------ */
|
||||||
|
/**
|
||||||
|
* Create and combine the constraint with the existing processed
|
||||||
|
* constraints.
|
||||||
|
*
|
||||||
|
* @param mapping
|
||||||
|
*/
|
||||||
protected void processConstraintMapping(ConstraintMapping mapping)
|
protected void processConstraintMapping(ConstraintMapping mapping)
|
||||||
{
|
{
|
||||||
Map<String, RoleInfo> mappings = _constraintMap.get(mapping.getPathSpec());
|
Map<String, RoleInfo> mappings = _constraintMap.get(mapping.getPathSpec());
|
||||||
|
@ -258,6 +486,12 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
|
||||||
if (allMethodsRoleInfo != null && allMethodsRoleInfo.isForbidden())
|
if (allMethodsRoleInfo != null && allMethodsRoleInfo.isForbidden())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (mapping.getMethodOmissions() != null && mapping.getMethodOmissions().length > 0)
|
||||||
|
{
|
||||||
|
processConstraintMappingWithMethodOmissions(mapping, mappings);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
String httpMethod = mapping.getMethod();
|
String httpMethod = mapping.getMethod();
|
||||||
if (httpMethod==null)
|
if (httpMethod==null)
|
||||||
httpMethod=ALL_METHODS;
|
httpMethod=ALL_METHODS;
|
||||||
|
@ -274,10 +508,10 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
|
||||||
if (roleInfo.isForbidden())
|
if (roleInfo.isForbidden())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Constraint constraint = mapping.getConstraint();
|
//add in info from the constraint
|
||||||
boolean forbidden = constraint.isForbidden();
|
configureRoleInfo(roleInfo, mapping);
|
||||||
roleInfo.setForbidden(forbidden);
|
|
||||||
if (forbidden)
|
if (roleInfo.isForbidden())
|
||||||
{
|
{
|
||||||
if (httpMethod.equals(ALL_METHODS))
|
if (httpMethod.equals(ALL_METHODS))
|
||||||
{
|
{
|
||||||
|
@ -287,41 +521,12 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
UserDataConstraint userDataConstraint = UserDataConstraint.get(constraint.getDataConstraint());
|
//combine with any entry that covers all methods
|
||||||
roleInfo.setUserDataConstraint(userDataConstraint);
|
if (httpMethod == null)
|
||||||
|
|
||||||
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))
|
|
||||||
{
|
{
|
||||||
for (Map.Entry<String, RoleInfo> entry : mappings.entrySet())
|
for (Map.Entry<String, RoleInfo> entry : mappings.entrySet())
|
||||||
{
|
{
|
||||||
if (!entry.getKey().equals(ALL_METHODS))
|
if (entry.getKey() != null)
|
||||||
{
|
{
|
||||||
RoleInfo specific = entry.getValue();
|
RoleInfo specific = entry.getValue();
|
||||||
specific.combine(roleInfo);
|
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
|
@Override
|
||||||
protected RoleInfo prepareConstraintInfo(String pathInContext, Request request)
|
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)
|
if (mappings != null)
|
||||||
{
|
{
|
||||||
String httpMethod = request.getMethod();
|
String httpMethod = request.getMethod();
|
||||||
RoleInfo roleInfo = mappings.get(httpMethod);
|
RoleInfo roleInfo = mappings.get(httpMethod);
|
||||||
if (roleInfo == null)
|
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;
|
return roleInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -397,6 +730,11 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
|
||||||
return constraintInfo != null && ((RoleInfo)constraintInfo).isChecked();
|
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
|
@Override
|
||||||
protected boolean checkWebResourcePermissions(String pathInContext, Request request, Response response, Object constraintInfo, UserIdentity userIdentity)
|
protected boolean checkWebResourcePermissions(String pathInContext, Request request, Response response, Object constraintInfo, UserIdentity userIdentity)
|
||||||
throws IOException
|
throws IOException
|
||||||
|
@ -435,4 +773,5 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
|
||||||
Collections.singleton(_roles),
|
Collections.singleton(_roles),
|
||||||
_constraintMap.entrySet());
|
_constraintMap.entrySet());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -119,18 +119,25 @@ public class SessionAuthentication implements Authentication.User, Serializable,
|
||||||
@Override
|
@Override
|
||||||
public void sessionWillPassivate(HttpSessionEvent se)
|
public void sessionWillPassivate(HttpSessionEvent se)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sessionDidActivate(HttpSessionEvent se)
|
public void sessionDidActivate(HttpSessionEvent se)
|
||||||
{
|
{
|
||||||
if (_session==null)
|
if (_session==null)
|
||||||
|
{
|
||||||
_session=se.getSession();
|
_session=se.getSession();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void valueBound(HttpSessionBindingEvent event)
|
public void valueBound(HttpSessionBindingEvent event)
|
||||||
{
|
{
|
||||||
|
if (_session==null)
|
||||||
|
{
|
||||||
|
_session=event.getSession();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -79,6 +79,8 @@ public class ConstraintTest
|
||||||
_loginService.putUser("user",new Password("password"));
|
_loginService.putUser("user",new Password("password"));
|
||||||
_loginService.putUser("user2",new Password("password"), new String[] {"user"});
|
_loginService.putUser("user2",new Password("password"), new String[] {"user"});
|
||||||
_loginService.putUser("admin",new Password("password"), new String[] {"user","administrator"});
|
_loginService.putUser("admin",new Password("password"), new String[] {"user","administrator"});
|
||||||
|
_loginService.putUser("user3", new Password("password"), new String[] {"foo"});
|
||||||
|
|
||||||
|
|
||||||
_context.setContextPath("/ctx");
|
_context.setContextPath("/ctx");
|
||||||
_server.setHandler(_context);
|
_server.setHandler(_context);
|
||||||
|
@ -190,17 +192,59 @@ public class ConstraintTest
|
||||||
@Test
|
@Test
|
||||||
public void testBasic() throws Exception
|
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.setAuthenticator(new BasicAuthenticator());
|
||||||
_security.setStrict(false);
|
_security.setStrict(false);
|
||||||
_server.start();
|
_server.start();
|
||||||
|
|
||||||
String response;
|
String response;
|
||||||
|
/*
|
||||||
response = _connector.getResponses("GET /ctx/noauth/info HTTP/1.0\r\n\r\n");
|
response = _connector.getResponses("GET /ctx/noauth/info HTTP/1.0\r\n\r\n");
|
||||||
assertThat(response,startsWith("HTTP/1.1 200 OK"));
|
assertThat(response,startsWith("HTTP/1.1 200 OK"));
|
||||||
|
*/
|
||||||
|
|
||||||
response = _connector.getResponses("GET /ctx/forbid/info HTTP/1.0\r\n\r\n");
|
response = _connector.getResponses("GET /ctx/forbid/info HTTP/1.0\r\n\r\n");
|
||||||
assertThat(response,startsWith("HTTP/1.1 403 Forbidden"));
|
assertThat(response,startsWith("HTTP/1.1 403 Forbidden"));
|
||||||
|
/*
|
||||||
response = _connector.getResponses("GET /ctx/auth/info HTTP/1.0\r\n\r\n");
|
response = _connector.getResponses("GET /ctx/auth/info HTTP/1.0\r\n\r\n");
|
||||||
assertThat(response,startsWith("HTTP/1.1 401 Unauthorized"));
|
assertThat(response,startsWith("HTTP/1.1 401 Unauthorized"));
|
||||||
assertThat(response,containsString("WWW-Authenticate: basic realm=\"TestRealm\""));
|
assertThat(response,containsString("WWW-Authenticate: basic realm=\"TestRealm\""));
|
||||||
|
@ -215,8 +259,8 @@ public class ConstraintTest
|
||||||
"Authorization: Basic " + B64Code.encode("user:password") + "\r\n" +
|
"Authorization: Basic " + B64Code.encode("user:password") + "\r\n" +
|
||||||
"\r\n");
|
"\r\n");
|
||||||
assertThat(response,startsWith("HTTP/1.1 200 OK"));
|
assertThat(response,startsWith("HTTP/1.1 200 OK"));
|
||||||
|
*/
|
||||||
|
/*
|
||||||
// test admin
|
// test admin
|
||||||
response = _connector.getResponses("GET /ctx/admin/info HTTP/1.0\r\n\r\n");
|
response = _connector.getResponses("GET /ctx/admin/info HTTP/1.0\r\n\r\n");
|
||||||
assertThat(response,startsWith("HTTP/1.1 401 Unauthorized"));
|
assertThat(response,startsWith("HTTP/1.1 401 Unauthorized"));
|
||||||
|
@ -242,8 +286,34 @@ public class ConstraintTest
|
||||||
|
|
||||||
response = _connector.getResponses("GET /ctx/admin/relax/info HTTP/1.0\r\n\r\n");
|
response = _connector.getResponses("GET /ctx/admin/relax/info HTTP/1.0\r\n\r\n");
|
||||||
assertThat(response,startsWith("HTTP/1.1 200 OK"));
|
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
|
@Test
|
||||||
public void testFormDispatch() throws Exception
|
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
|
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response ) throws IOException, ServletException
|
||||||
{
|
{
|
||||||
baseRequest.setHandled(true);
|
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.setStatus(200);
|
||||||
response.setContentType("text/plain; charset=UTF-8");
|
response.setContentType("text/plain; charset=UTF-8");
|
||||||
|
|
|
@ -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"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -73,9 +73,9 @@ public abstract class HttpInput<T> extends ServletInputStream
|
||||||
@Override
|
@Override
|
||||||
public int read() throws IOException
|
public int read() throws IOException
|
||||||
{
|
{
|
||||||
byte[] oneByte = new byte[1];
|
byte[] bytes = new byte[1];
|
||||||
int len = read(oneByte, 0, 1);
|
int read = read(bytes, 0, 1);
|
||||||
return len < 0 ? len : 0xFF & oneByte[0];
|
return read < 0 ? -1 : 0xff & bytes[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -127,7 +127,6 @@ public abstract class HttpInput<T> extends ServletInputStream
|
||||||
}
|
}
|
||||||
return get(item, b, off, len);
|
return get(item, b, off, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract int remaining(T item);
|
protected abstract int remaining(T item);
|
||||||
|
|
||||||
protected abstract int get(T item, byte[] buffer, int offset, int length);
|
protected abstract int get(T item, byte[] buffer, int offset, int length);
|
||||||
|
|
|
@ -173,6 +173,7 @@ public class HttpOutput extends ServletOutputStream
|
||||||
_channel.write(_aggregate, false);
|
_channel.write(_aggregate, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(int b) throws IOException
|
public void write(int b) throws IOException
|
||||||
{
|
{
|
||||||
|
|
|
@ -49,6 +49,8 @@ import javax.servlet.ServletInputStream;
|
||||||
import javax.servlet.ServletRequest;
|
import javax.servlet.ServletRequest;
|
||||||
import javax.servlet.ServletRequestAttributeEvent;
|
import javax.servlet.ServletRequestAttributeEvent;
|
||||||
import javax.servlet.ServletRequestAttributeListener;
|
import javax.servlet.ServletRequestAttributeListener;
|
||||||
|
import javax.servlet.ServletRequestEvent;
|
||||||
|
import javax.servlet.ServletRequestListener;
|
||||||
import javax.servlet.ServletResponse;
|
import javax.servlet.ServletResponse;
|
||||||
import javax.servlet.http.Cookie;
|
import javax.servlet.http.Cookie;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
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.server.session.AbstractSessionManager;
|
||||||
import org.eclipse.jetty.util.Attributes;
|
import org.eclipse.jetty.util.Attributes;
|
||||||
import org.eclipse.jetty.util.AttributesMap;
|
import org.eclipse.jetty.util.AttributesMap;
|
||||||
|
import org.eclipse.jetty.util.MultiException;
|
||||||
import org.eclipse.jetty.util.MultiMap;
|
import org.eclipse.jetty.util.MultiMap;
|
||||||
import org.eclipse.jetty.util.MultiPartInputStream;
|
import org.eclipse.jetty.util.MultiPartInputStream;
|
||||||
import org.eclipse.jetty.util.StringUtil;
|
import org.eclipse.jetty.util.StringUtil;
|
||||||
|
@ -114,6 +117,8 @@ import org.eclipse.jetty.util.log.Logger;
|
||||||
public class Request implements HttpServletRequest
|
public class Request implements HttpServletRequest
|
||||||
{
|
{
|
||||||
public static final String __MULTIPART_CONFIG_ELEMENT = "org.eclipse.multipartConfig";
|
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 Logger LOG = Log.getLogger(Request.class);
|
||||||
private static final Collection<Locale> __defaultLocale = Collections.singleton(Locale.getDefault());
|
private static final Collection<Locale> __defaultLocale = Collections.singleton(Locale.getDefault());
|
||||||
|
@ -124,6 +129,42 @@ public class Request implements HttpServletRequest
|
||||||
private final List<ServletRequestAttributeListener> _requestAttributeListeners=new ArrayList<>();
|
private final List<ServletRequestAttributeListener> _requestAttributeListeners=new ArrayList<>();
|
||||||
private final HttpInput<?> _input;
|
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 _secure;
|
||||||
private boolean _asyncSupported = true;
|
private boolean _asyncSupported = true;
|
||||||
private boolean _newContext;
|
private boolean _newContext;
|
||||||
|
@ -1763,6 +1804,7 @@ public class Request implements HttpServletRequest
|
||||||
public void setQueryString(String queryString)
|
public void setQueryString(String queryString)
|
||||||
{
|
{
|
||||||
_queryString = queryString;
|
_queryString = queryString;
|
||||||
|
_queryEncoding = null; //assume utf-8
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
|
@ -1950,9 +1992,16 @@ public class Request implements HttpServletRequest
|
||||||
|
|
||||||
if (_multiPartInputStream == null)
|
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(),
|
_multiPartInputStream = new MultiPartInputStream(getInputStream(),
|
||||||
getContentType(),(MultipartConfigElement)getAttribute(__MULTIPART_CONFIG_ELEMENT),
|
getContentType(),config,
|
||||||
(_context != null?(File)_context.getAttribute("javax.servlet.context.tempdir"):null));
|
(_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
|
Collection<Part> parts = _multiPartInputStream.getParts(); //causes parsing
|
||||||
for (Part p:parts)
|
for (Part p:parts)
|
||||||
{
|
{
|
||||||
|
@ -1982,9 +2031,17 @@ public class Request implements HttpServletRequest
|
||||||
|
|
||||||
if (_multiPartInputStream == null)
|
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(),
|
_multiPartInputStream = new MultiPartInputStream(getInputStream(),
|
||||||
getContentType(),(MultipartConfigElement)getAttribute(__MULTIPART_CONFIG_ELEMENT),
|
getContentType(), config,
|
||||||
(_context != null?(File)_context.getAttribute("javax.servlet.context.tempdir"):null));
|
(_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
|
Collection<Part> parts = _multiPartInputStream.getParts(); //causes parsing
|
||||||
for (Part p:parts)
|
for (Part p:parts)
|
||||||
{
|
{
|
||||||
|
@ -2042,7 +2099,7 @@ public class Request implements HttpServletRequest
|
||||||
{
|
{
|
||||||
// extract parameters from dispatch query
|
// extract parameters from dispatch query
|
||||||
MultiMap<String> parameters = new MultiMap<>();
|
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;
|
boolean merge_old_query = false;
|
||||||
|
|
||||||
|
@ -2063,10 +2120,11 @@ public class Request implements HttpServletRequest
|
||||||
{
|
{
|
||||||
StringBuilder overridden_query_string = new StringBuilder();
|
StringBuilder overridden_query_string = new StringBuilder();
|
||||||
MultiMap<String> overridden_old_query = new MultiMap<>();
|
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<>();
|
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())
|
for(String name: overridden_old_query.keySet())
|
||||||
{
|
{
|
||||||
|
|
|
@ -22,7 +22,9 @@ import java.io.IOException;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.nio.channels.IllegalSelectorException;
|
import java.nio.channels.IllegalSelectorException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Enumeration;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import javax.servlet.RequestDispatcher;
|
import javax.servlet.RequestDispatcher;
|
||||||
|
@ -58,6 +60,15 @@ public class Response implements HttpServletResponse
|
||||||
{
|
{
|
||||||
private static final Logger LOG = Log.getLogger(Response.class);
|
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
|
public enum OutputType
|
||||||
{
|
{
|
||||||
NONE, STREAM, WRITER
|
NONE, STREAM, WRITER
|
||||||
|
@ -76,7 +87,7 @@ public class Response implements HttpServletResponse
|
||||||
*/
|
*/
|
||||||
public final static String HTTP_ONLY_COMMENT = "__HTTP_ONLY__";
|
public final static String HTTP_ONLY_COMMENT = "__HTTP_ONLY__";
|
||||||
|
|
||||||
private final HttpChannel _channel;
|
private final HttpChannel<?> _channel;
|
||||||
private final HttpOutput _out;
|
private final HttpOutput _out;
|
||||||
private final HttpFields _fields = new HttpFields();
|
private final HttpFields _fields = new HttpFields();
|
||||||
private final AtomicInteger _include = new AtomicInteger();
|
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()
|
public void resetForForward()
|
||||||
{
|
{
|
||||||
resetBuffer();
|
resetBuffer();
|
||||||
|
|
|
@ -443,7 +443,7 @@ public class Server extends HandlerWrapper implements Attributes
|
||||||
baseRequest.setRequestURI(null);
|
baseRequest.setRequestURI(null);
|
||||||
baseRequest.setPathInfo(baseRequest.getRequestURI());
|
baseRequest.setPathInfo(baseRequest.getRequestURI());
|
||||||
if (uri.getQuery()!=null)
|
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();
|
final String target=baseRequest.getPathInfo();
|
||||||
|
|
|
@ -50,7 +50,7 @@ import org.eclipse.jetty.util.log.Logger;
|
||||||
<pre>
|
<pre>
|
||||||
public static void attemptShutdown(int port, String shutdownCookie) {
|
public static void attemptShutdown(int port, String shutdownCookie) {
|
||||||
try {
|
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();
|
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
|
||||||
connection.setRequestMethod("POST");
|
connection.setRequestMethod("POST");
|
||||||
connection.getResponseCode();
|
connection.getResponseCode();
|
||||||
|
|
|
@ -21,7 +21,6 @@ package org.eclipse.jetty.server.session;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.EventListener;
|
import java.util.EventListener;
|
||||||
|
|
||||||
import javax.servlet.DispatcherType;
|
import javax.servlet.DispatcherType;
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
import javax.servlet.SessionTrackingMode;
|
import javax.servlet.SessionTrackingMode;
|
||||||
|
@ -39,7 +38,8 @@ import org.eclipse.jetty.util.log.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/** SessionHandler.
|
/**
|
||||||
|
* SessionHandler.
|
||||||
*/
|
*/
|
||||||
public class SessionHandler extends ScopedHandler
|
public class SessionHandler extends ScopedHandler
|
||||||
{
|
{
|
||||||
|
@ -52,9 +52,8 @@ public class SessionHandler extends ScopedHandler
|
||||||
private SessionManager _sessionManager;
|
private SessionManager _sessionManager;
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/** Constructor.
|
/**
|
||||||
* Construct a SessionHandler witha a HashSessionManager with a standard
|
* Constructor. Construct a SessionHandler witha a HashSessionManager with a standard java.util.Random generator is created.
|
||||||
* java.util.Random generator is created.
|
|
||||||
*/
|
*/
|
||||||
public SessionHandler()
|
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)
|
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)
|
public void setSessionManager(SessionManager sessionManager)
|
||||||
{
|
{
|
||||||
if (isStarted())
|
if (isStarted())
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
if (sessionManager!=null)
|
if (sessionManager != null)
|
||||||
sessionManager.setSessionHandler(this);
|
sessionManager.setSessionHandler(this);
|
||||||
updateBean(_sessionManager,sessionManager);
|
updateBean(_sessionManager,sessionManager);
|
||||||
_sessionManager=sessionManager;
|
_sessionManager=sessionManager;
|
||||||
|
@ -104,6 +105,7 @@ public class SessionHandler extends ScopedHandler
|
||||||
setSessionManager(new HashSessionManager());
|
setSessionManager(new HashSessionManager());
|
||||||
super.doStart();
|
super.doStart();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/*
|
/*
|
||||||
* @see org.eclipse.thread.AbstractLifeCycle#doStop()
|
* @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)
|
* @see org.eclipse.jetty.server.Handler#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, int)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void doScope(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
|
public void doScope(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||||
throws IOException, ServletException
|
|
||||||
{
|
{
|
||||||
SessionManager old_session_manager=null;
|
SessionManager old_session_manager = null;
|
||||||
HttpSession old_session=null;
|
HttpSession old_session = null;
|
||||||
HttpSession access=null;
|
HttpSession access = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
old_session_manager = baseRequest.getSessionManager();
|
old_session_manager = baseRequest.getSessionManager();
|
||||||
|
@ -141,54 +142,54 @@ public class SessionHandler extends ScopedHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
// access any existing session
|
// access any existing session
|
||||||
HttpSession session=null;
|
HttpSession session = null;
|
||||||
if (_sessionManager!=null)
|
if (_sessionManager != null)
|
||||||
{
|
{
|
||||||
session=baseRequest.getSession(false);
|
session = baseRequest.getSession(false);
|
||||||
if (session!=null)
|
if (session != null)
|
||||||
{
|
{
|
||||||
if(session!=old_session)
|
if (session != old_session)
|
||||||
{
|
{
|
||||||
access=session;
|
access = session;
|
||||||
HttpCookie cookie = _sessionManager.access(session,request.isSecure());
|
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);
|
baseRequest.getResponse().addCookie(cookie);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
session=baseRequest.recoverNewSession(_sessionManager);
|
session = baseRequest.recoverNewSession(_sessionManager);
|
||||||
if (session!=null)
|
if (session != null)
|
||||||
baseRequest.setSession(session);
|
baseRequest.setSession(session);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
{
|
{
|
||||||
LOG.debug("sessionManager="+_sessionManager);
|
LOG.debug("sessionManager=" + _sessionManager);
|
||||||
LOG.debug("session="+session);
|
LOG.debug("session=" + session);
|
||||||
}
|
}
|
||||||
|
|
||||||
// start manual inline of nextScope(target,baseRequest,request,response);
|
// start manual inline of nextScope(target,baseRequest,request,response);
|
||||||
if (_nextScope!=null)
|
if (_nextScope != null)
|
||||||
_nextScope.doScope(target,baseRequest,request, response);
|
_nextScope.doScope(target,baseRequest,request,response);
|
||||||
else if (_outerScope!=null)
|
else if (_outerScope != null)
|
||||||
_outerScope.doHandle(target,baseRequest,request, response);
|
_outerScope.doHandle(target,baseRequest,request,response);
|
||||||
else
|
else
|
||||||
doHandle(target,baseRequest,request, response);
|
doHandle(target,baseRequest,request,response);
|
||||||
// end manual inline (pathentic attempt to reduce stack depth)
|
// end manual inline (pathentic attempt to reduce stack depth)
|
||||||
|
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
if (access!=null)
|
if (access != null)
|
||||||
_sessionManager.complete(access);
|
_sessionManager.complete(access);
|
||||||
|
|
||||||
HttpSession session = baseRequest.getSession(false);
|
HttpSession session = baseRequest.getSession(false);
|
||||||
if (session!=null && old_session==null && session!=access)
|
if (session != null && old_session == null && session != access)
|
||||||
_sessionManager.complete(session);
|
_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.setSessionManager(old_session_manager);
|
||||||
baseRequest.setSession(old_session);
|
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)
|
* @see org.eclipse.jetty.server.Handler#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, int)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void doHandle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
|
public void doHandle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||||
throws IOException, ServletException
|
|
||||||
{
|
{
|
||||||
// start manual inline of nextHandle(target,baseRequest,request,response);
|
// start manual inline of nextHandle(target,baseRequest,request,response);
|
||||||
if (never())
|
if (never())
|
||||||
nextHandle(target,baseRequest,request,response);
|
nextHandle(target,baseRequest,request,response);
|
||||||
else if (_nextScope!=null && _nextScope==_handler)
|
else if (_nextScope != null && _nextScope == _handler)
|
||||||
_nextScope.doHandle(target,baseRequest,request, response);
|
_nextScope.doHandle(target,baseRequest,request,response);
|
||||||
else if (_handler!=null)
|
else if (_handler != null)
|
||||||
_handler.handle(target,baseRequest, request, response);
|
_handler.handle(target,baseRequest,request,response);
|
||||||
// end manual inline
|
// 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 baseRequest
|
||||||
* @param request
|
* @param request
|
||||||
*/
|
*/
|
||||||
protected void checkRequestedSessionId(Request baseRequest, HttpServletRequest request)
|
protected void checkRequestedSessionId(Request baseRequest, HttpServletRequest request)
|
||||||
{
|
{
|
||||||
String requested_session_id=request.getRequestedSessionId();
|
String requested_session_id = request.getRequestedSessionId();
|
||||||
|
|
||||||
SessionManager sessionManager = getSessionManager();
|
SessionManager sessionManager = getSessionManager();
|
||||||
|
|
||||||
if (requested_session_id!=null && sessionManager!=null)
|
if (requested_session_id != null && sessionManager != null)
|
||||||
{
|
{
|
||||||
HttpSession session=sessionManager.getHttpSession(requested_session_id);
|
HttpSession session = sessionManager.getHttpSession(requested_session_id);
|
||||||
if (session!=null && sessionManager.isValid(session))
|
if (session != null && sessionManager.isValid(session))
|
||||||
baseRequest.setSession(session);
|
baseRequest.setSession(session);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (!DispatcherType.REQUEST.equals(baseRequest.getDispatcherType()))
|
else if (!DispatcherType.REQUEST.equals(baseRequest.getDispatcherType()))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
boolean requested_session_id_from_cookie=false;
|
boolean requested_session_id_from_cookie = false;
|
||||||
HttpSession session=null;
|
HttpSession session = null;
|
||||||
|
|
||||||
// Look for session id cookie
|
// Look for session id cookie
|
||||||
if (_sessionManager.isUsingCookies())
|
if (_sessionManager.isUsingCookies())
|
||||||
{
|
{
|
||||||
Cookie[] cookies=request.getCookies();
|
Cookie[] cookies = request.getCookies();
|
||||||
if (cookies!=null && cookies.length>0)
|
if (cookies != null && cookies.length > 0)
|
||||||
{
|
{
|
||||||
final String sessionCookie=sessionManager.getSessionCookieConfig().getName();
|
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()))
|
if (sessionCookie.equalsIgnoreCase(cookies[i].getName()))
|
||||||
{
|
{
|
||||||
requested_session_id=cookies[i].getValue();
|
requested_session_id = cookies[i].getValue();
|
||||||
requested_session_id_from_cookie = true;
|
requested_session_id_from_cookie = true;
|
||||||
if(LOG.isDebugEnabled())
|
|
||||||
LOG.debug("Got Session ID {} from cookie",requested_session_id);
|
LOG.debug("Got Session ID {} from cookie",requested_session_id);
|
||||||
|
|
||||||
session=sessionManager.getHttpSession(requested_session_id);
|
if (requested_session_id != null)
|
||||||
if (session!=null && sessionManager.isValid(session))
|
{
|
||||||
|
session = sessionManager.getHttpSession(requested_session_id);
|
||||||
|
|
||||||
|
if (session != null && sessionManager.isValid(session))
|
||||||
|
{
|
||||||
break;
|
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 uri = request.getRequestURI();
|
||||||
|
|
||||||
String prefix=sessionManager.getSessionIdPathParameterNamePrefix();
|
String prefix = sessionManager.getSessionIdPathParameterNamePrefix();
|
||||||
if (prefix!=null)
|
if (prefix != null)
|
||||||
{
|
{
|
||||||
int s = uri.indexOf(prefix);
|
int s = uri.indexOf(prefix);
|
||||||
if (s>=0)
|
if (s >= 0)
|
||||||
{
|
{
|
||||||
s+=prefix.length();
|
s += prefix.length();
|
||||||
int i=s;
|
int i = s;
|
||||||
while (i<uri.length())
|
while (i < uri.length())
|
||||||
{
|
{
|
||||||
char c=uri.charAt(i);
|
char c = uri.charAt(i);
|
||||||
if (c==';'||c=='#'||c=='?'||c=='/')
|
if (c == ';' || c == '#' || c == '?' || c == '/')
|
||||||
break;
|
break;
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
requested_session_id = uri.substring(s,i);
|
requested_session_id = uri.substring(s,i);
|
||||||
requested_session_id_from_cookie = false;
|
requested_session_id_from_cookie = false;
|
||||||
session=sessionManager.getHttpSession(requested_session_id);
|
session = sessionManager.getHttpSession(requested_session_id);
|
||||||
if(LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("Got Session ID {} from URL",requested_session_id);
|
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.setRequestedSessionId(requested_session_id);
|
||||||
baseRequest.setRequestedSessionIdFromCookie(requested_session_id!=null && requested_session_id_from_cookie);
|
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);
|
baseRequest.setSession(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,14 +315,14 @@ public class SessionHandler extends ScopedHandler
|
||||||
*/
|
*/
|
||||||
public void addEventListener(EventListener listener)
|
public void addEventListener(EventListener listener)
|
||||||
{
|
{
|
||||||
if(_sessionManager!=null)
|
if (_sessionManager != null)
|
||||||
_sessionManager.addEventListener(listener);
|
_sessionManager.addEventListener(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
public void clearEventListeners()
|
public void clearEventListeners()
|
||||||
{
|
{
|
||||||
if(_sessionManager!=null)
|
if (_sessionManager != null)
|
||||||
_sessionManager.clearEventListeners();
|
_sessionManager.clearEventListeners();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,13 +36,15 @@ import java.net.URL;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.concurrent.Exchanger;
|
import java.util.concurrent.Exchanger;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.ServletInputStream;
|
||||||
import javax.servlet.ServletOutputStream;
|
import javax.servlet.ServletOutputStream;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
import org.eclipse.jetty.io.EndPoint;
|
import org.eclipse.jetty.io.EndPoint;
|
||||||
|
import org.eclipse.jetty.io.EofException;
|
||||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||||
import org.eclipse.jetty.util.IO;
|
import org.eclipse.jetty.util.IO;
|
||||||
import org.eclipse.jetty.util.StringUtil;
|
import org.eclipse.jetty.util.StringUtil;
|
||||||
|
@ -53,6 +55,8 @@ import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.matchers.JUnitMatchers;
|
import org.junit.matchers.JUnitMatchers;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.is;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
@ -174,6 +178,80 @@ 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
|
||||||
*/
|
*/
|
||||||
|
@ -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.
|
// 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
|
protected static class BigBlockHandler extends AbstractHandler
|
||||||
{
|
{
|
||||||
|
@Override
|
||||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||||
{
|
{
|
||||||
byte[] buf = new byte[128 * 1024];
|
byte[] buf = new byte[128 * 1024];
|
||||||
|
@ -1262,6 +1341,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
|
||||||
|
|
||||||
new Thread()
|
new Thread()
|
||||||
{
|
{
|
||||||
|
@Override
|
||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@ -1295,6 +1375,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
|
||||||
|
|
||||||
public class NoopHandler extends AbstractHandler
|
public class NoopHandler extends AbstractHandler
|
||||||
{
|
{
|
||||||
|
@Override
|
||||||
public void handle(String target, Request baseRequest,
|
public void handle(String target, Request baseRequest,
|
||||||
HttpServletRequest request, HttpServletResponse response) throws IOException,
|
HttpServletRequest request, HttpServletResponse response) throws IOException,
|
||||||
ServletException
|
ServletException
|
||||||
|
|
|
@ -18,14 +18,18 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.server;
|
package org.eclipse.jetty.server;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.net.URLDecoder;
|
import java.net.URLDecoder;
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import org.eclipse.jetty.http.HttpURI;
|
import org.eclipse.jetty.http.HttpURI;
|
||||||
import org.eclipse.jetty.util.MultiMap;
|
import org.eclipse.jetty.util.MultiMap;
|
||||||
import org.eclipse.jetty.util.StringUtil;
|
import org.eclipse.jetty.util.StringUtil;
|
||||||
|
import org.eclipse.jetty.util.TypeUtil;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
@ -206,6 +210,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
|
@Test
|
||||||
public void testUnicodeErrors() throws UnsupportedEncodingException
|
public void testUnicodeErrors() throws UnsupportedEncodingException
|
||||||
{
|
{
|
||||||
|
|
|
@ -38,7 +38,9 @@ import java.util.Enumeration;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import javax.servlet.MultipartConfigElement;
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.ServletRequestEvent;
|
||||||
import javax.servlet.http.Cookie;
|
import javax.servlet.http.Cookie;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
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.AbstractHandler;
|
||||||
import org.eclipse.jetty.server.handler.ContextHandler;
|
import org.eclipse.jetty.server.handler.ContextHandler;
|
||||||
import org.eclipse.jetty.util.IO;
|
import org.eclipse.jetty.util.IO;
|
||||||
|
import org.eclipse.jetty.util.MultiPartInputStream;
|
||||||
import org.eclipse.jetty.util.StringUtil;
|
import org.eclipse.jetty.util.StringUtil;
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
@ -133,7 +136,7 @@ public class RequestTest
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMultiPart() throws Exception
|
public void testMultiPartNoConfig() throws Exception
|
||||||
{
|
{
|
||||||
_handler._checker = new RequestTester()
|
_handler._checker = new RequestTester()
|
||||||
{
|
{
|
||||||
|
@ -143,14 +146,16 @@ public class RequestTest
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Part foo = request.getPart("stuff");
|
Part foo = request.getPart("stuff");
|
||||||
assertNotNull(foo);
|
return false;
|
||||||
String value = request.getParameter("stuff");
|
}
|
||||||
byte[] expected = "000000000000000000000000000000000000000000000000000".getBytes("ISO-8859-1");
|
catch (IllegalStateException e)
|
||||||
return value.equals(new String(expected, "ISO-8859-1"));
|
{
|
||||||
|
//expected exception because no multipart config is set up
|
||||||
|
assertTrue(e.getMessage().startsWith("No multipart config"));
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
e.printStackTrace();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -179,6 +184,66 @@ public class RequestTest
|
||||||
assertTrue(responses.startsWith("HTTP/1.1 200"));
|
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
|
@Test
|
||||||
public void testBadUtf8ParamExtraction() throws Exception
|
public void testBadUtf8ParamExtraction() throws Exception
|
||||||
{
|
{
|
||||||
|
@ -945,4 +1010,43 @@ public class RequestTest
|
||||||
response.sendError(500);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ package org.eclipse.jetty.server;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
@ -29,6 +30,9 @@ import java.io.LineNumberReader;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Enumeration;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
|
@ -565,6 +569,42 @@ public class ResponseTest
|
||||||
assertEquals("name=value;Path=/path;Domain=domain;Secure;HttpOnly;Comment=comment", set);
|
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()
|
private Response newResponse()
|
||||||
{
|
{
|
||||||
_channel.reset();
|
_channel.reset();
|
||||||
|
|
|
@ -18,15 +18,12 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.server.ssl;
|
package org.eclipse.jetty.server.ssl;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
|
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.net.SocketException;
|
import java.net.SocketException;
|
||||||
import java.security.KeyStore;
|
import java.security.KeyStore;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import javax.net.ssl.HttpsURLConnection;
|
import javax.net.ssl.HttpsURLConnection;
|
||||||
import javax.net.ssl.SSLContext;
|
import javax.net.ssl.SSLContext;
|
||||||
import javax.net.ssl.TrustManagerFactory;
|
import javax.net.ssl.TrustManagerFactory;
|
||||||
|
@ -39,6 +36,8 @@ import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HttpServer Tester.
|
* HttpServer Tester.
|
||||||
|
@ -189,6 +188,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
|
@Override
|
||||||
@Ignore
|
@Ignore
|
||||||
public void testAvailable() throws Exception
|
public void testAvailable() throws Exception
|
||||||
|
|
|
@ -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
|
|
|
@ -25,6 +25,7 @@ import java.io.OutputStream;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -667,6 +668,9 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
|
||||||
String ifms=request.getHeader(HttpHeader.IF_MODIFIED_SINCE.asString());
|
String ifms=request.getHeader(HttpHeader.IF_MODIFIED_SINCE.asString());
|
||||||
if (ifms!=null)
|
if (ifms!=null)
|
||||||
{
|
{
|
||||||
|
//Get jetty's Response impl
|
||||||
|
Response r = Response.getResponse(response);
|
||||||
|
|
||||||
if (content!=null)
|
if (content!=null)
|
||||||
{
|
{
|
||||||
String mdlm=content.getLastModified();
|
String mdlm=content.getLastModified();
|
||||||
|
@ -674,9 +678,9 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
|
||||||
{
|
{
|
||||||
if (ifms.equals(mdlm))
|
if (ifms.equals(mdlm))
|
||||||
{
|
{
|
||||||
response.reset();
|
r.reset(true);
|
||||||
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
|
r.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
|
||||||
response.flushBuffer();
|
r.flushBuffer();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -687,9 +691,9 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
|
||||||
{
|
{
|
||||||
if (resource.lastModified()/1000 <= ifmsl/1000)
|
if (resource.lastModified()/1000 <= ifmsl/1000)
|
||||||
{
|
{
|
||||||
response.reset();
|
r.reset(true);
|
||||||
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
|
r.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
|
||||||
response.flushBuffer();
|
r.flushBuffer();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -942,8 +946,6 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
|
||||||
pos=start;
|
pos=start;
|
||||||
}
|
}
|
||||||
|
|
||||||
System.err.println("PART "+ibr);
|
|
||||||
|
|
||||||
IO.copy(in,multi,size);
|
IO.copy(in,multi,size);
|
||||||
pos+=size;
|
pos+=size;
|
||||||
}
|
}
|
||||||
|
@ -1023,6 +1025,8 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
|
||||||
response.setHeader(HttpHeader.CACHE_CONTROL.asString(),_cacheControl);
|
response.setHeader(HttpHeader.CACHE_CONTROL.asString(),_cacheControl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/*
|
/*
|
||||||
* @see javax.servlet.Servlet#destroy()
|
* @see javax.servlet.Servlet#destroy()
|
||||||
|
|
|
@ -47,6 +47,7 @@ import javax.servlet.descriptor.JspPropertyGroupDescriptor;
|
||||||
import javax.servlet.descriptor.TaglibDescriptor;
|
import javax.servlet.descriptor.TaglibDescriptor;
|
||||||
|
|
||||||
import org.eclipse.jetty.security.ConstraintAware;
|
import org.eclipse.jetty.security.ConstraintAware;
|
||||||
|
import org.eclipse.jetty.security.ConstraintMapping;
|
||||||
import org.eclipse.jetty.security.ConstraintSecurityHandler;
|
import org.eclipse.jetty.security.ConstraintSecurityHandler;
|
||||||
import org.eclipse.jetty.security.SecurityHandler;
|
import org.eclipse.jetty.security.SecurityHandler;
|
||||||
import org.eclipse.jetty.server.Dispatcher;
|
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.server.session.SessionHandler;
|
||||||
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||||
import org.eclipse.jetty.util.annotation.ManagedObject;
|
import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||||
|
import org.eclipse.jetty.util.security.Constraint;
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
|
@ -393,10 +395,21 @@ public class ServletContextHandler extends ContextHandler
|
||||||
* @param registration ServletRegistration.Dynamic instance that setServletSecurity was called on
|
* @param registration ServletRegistration.Dynamic instance that setServletSecurity was called on
|
||||||
* @param servletSecurityElement new security info
|
* @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
|
* @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)
|
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();
|
return Collections.emptySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -509,6 +509,8 @@ public class ServletHolder extends Holder<Servlet> implements UserIdentity.Scope
|
||||||
initJspServlet();
|
initJspServlet();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initMultiPart();
|
||||||
|
|
||||||
_servlet.init(_config);
|
_servlet.init(_config);
|
||||||
}
|
}
|
||||||
catch (UnavailableException e)
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
package org.eclipse.jetty.servlet;
|
package org.eclipse.jetty.servlet;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.EnumSet;
|
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.ContextHandlerCollection;
|
||||||
import org.eclipse.jetty.server.handler.ResourceHandler;
|
import org.eclipse.jetty.server.handler.ResourceHandler;
|
||||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||||
|
import org.eclipse.jetty.util.TypeUtil;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
@ -111,6 +113,22 @@ public class DispatcherTest
|
||||||
assertEquals(expected, responses);
|
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
|
@Test
|
||||||
public void testForwardWithParam() throws Exception
|
public void testForwardWithParam() throws Exception
|
||||||
{
|
{
|
||||||
|
@ -299,6 +317,7 @@ public class DispatcherTest
|
||||||
dispatcher = getServletContext().getRequestDispatcher("/IncludeServlet/includepath?do=assertforwardinclude");
|
dispatcher = getServletContext().getRequestDispatcher("/IncludeServlet/includepath?do=assertforwardinclude");
|
||||||
else if(request.getParameter("do").equals("assertincludeforward"))
|
else if(request.getParameter("do").equals("assertincludeforward"))
|
||||||
dispatcher = getServletContext().getRequestDispatcher("/AssertIncludeForwardServlet/assertpath?do=end");
|
dispatcher = getServletContext().getRequestDispatcher("/AssertIncludeForwardServlet/assertpath?do=end");
|
||||||
|
|
||||||
else if(request.getParameter("do").equals("assertforward"))
|
else if(request.getParameter("do").equals("assertforward"))
|
||||||
dispatcher = getServletContext().getRequestDispatcher("/AssertForwardServlet?do=end&do=the");
|
dispatcher = getServletContext().getRequestDispatcher("/AssertForwardServlet?do=end&do=the");
|
||||||
else if(request.getParameter("do").equals("ctx.echo"))
|
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
|
* Forward filter works with roger, echo and reverse echo servlets to test various
|
||||||
* forwarding bits using filters.
|
* forwarding bits using filters.
|
||||||
|
@ -528,6 +559,45 @@ public class DispatcherTest
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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
|
public static class AssertIncludeServlet extends HttpServlet implements Servlet
|
||||||
{
|
{
|
||||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||||
|
|
|
@ -89,7 +89,6 @@ public class ResponseHeadersTest
|
||||||
}
|
}
|
||||||
int port = connector.getLocalPort();
|
int port = connector.getLocalPort();
|
||||||
serverUri = new URI(String.format("http://%s:%d/",host,port));
|
serverUri = new URI(String.format("http://%s:%d/",host,port));
|
||||||
System.out.printf("Server URI: %s%n",serverUri);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterClass
|
@AfterClass
|
||||||
|
@ -130,10 +129,8 @@ public class ResponseHeadersTest
|
||||||
|
|
||||||
// Read response
|
// Read response
|
||||||
String respHeader = readResponseHeader(in);
|
String respHeader = readResponseHeader(in);
|
||||||
System.out.println("RESPONSE: " + respHeader);
|
|
||||||
|
|
||||||
// Now test for properly formatted HTTP Response Headers.
|
// Now test for properly formatted HTTP Response Headers.
|
||||||
|
|
||||||
Assert.assertThat("Response Code",respHeader,startsWith("HTTP/1.1 101 Switching Protocols"));
|
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 Upgrade",respHeader,containsString("Upgrade: WebSocket\r\n"));
|
||||||
Assert.assertThat("Response Header Connection",respHeader,containsString("Connection: Upgrade\r\n"));
|
Assert.assertThat("Response Header Connection",respHeader,containsString("Connection: Upgrade\r\n"));
|
||||||
|
|
|
@ -106,6 +106,7 @@ public class GzipFilter extends UserAgentFilter
|
||||||
public final static String GZIP="gzip";
|
public final static String GZIP="gzip";
|
||||||
public final static String DEFLATE="deflate";
|
public final static String DEFLATE="deflate";
|
||||||
|
|
||||||
|
|
||||||
protected Set<String> _mimeTypes;
|
protected Set<String> _mimeTypes;
|
||||||
protected int _bufferSize=8192;
|
protected int _bufferSize=8192;
|
||||||
protected int _minGzipSize=256;
|
protected int _minGzipSize=256;
|
||||||
|
@ -116,6 +117,11 @@ public class GzipFilter extends UserAgentFilter
|
||||||
protected Set<String> _excludedPaths;
|
protected Set<String> _excludedPaths;
|
||||||
protected Set<Pattern> _excludedPathPatterns;
|
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,16 +268,90 @@ public class GzipFilter extends UserAgentFilter
|
||||||
{
|
{
|
||||||
// TODO, this could be a little more robust.
|
// TODO, this could be a little more robust.
|
||||||
// prefer gzip over deflate
|
// prefer gzip over deflate
|
||||||
|
String compression = null;
|
||||||
if (encodingHeader!=null)
|
if (encodingHeader!=null)
|
||||||
{
|
{
|
||||||
if (encodingHeader.toLowerCase().contains(GZIP))
|
|
||||||
return GZIP;
|
String[] encodings = getEncodings(encodingHeader);
|
||||||
else if (encodingHeader.toLowerCase().contains(DEFLATE))
|
if (encodings != null)
|
||||||
return DEFLATE;
|
{
|
||||||
|
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 compression;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private String[] getEncodings (String encodingHeader)
|
||||||
|
{
|
||||||
|
if (encodingHeader == null)
|
||||||
return 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)
|
protected CompressedResponseWrapper createWrappedResponse(HttpServletRequest request, HttpServletResponse response, final String compressionType)
|
||||||
{
|
{
|
||||||
CompressedResponseWrapper wrappedResponse = null;
|
CompressedResponseWrapper wrappedResponse = null;
|
||||||
|
|
|
@ -77,7 +77,7 @@ import org.eclipse.jetty.util.StringUtil;
|
||||||
public class MultiPartFilter implements Filter
|
public class MultiPartFilter implements Filter
|
||||||
{
|
{
|
||||||
public final static String CONTENT_TYPE_SUFFIX=".org.eclipse.jetty.servlet.contentType";
|
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 File tempdir;
|
||||||
private boolean _deleteFiles;
|
private boolean _deleteFiles;
|
||||||
private ServletContext _context;
|
private ServletContext _context;
|
||||||
|
@ -141,7 +141,8 @@ public class MultiPartFilter implements Filter
|
||||||
|
|
||||||
MultipartConfigElement config = new MultipartConfigElement(tempdir.getCanonicalPath(), _maxFileSize, _maxRequestSize, _fileOutputBuffer);
|
MultipartConfigElement config = new MultipartConfigElement(tempdir.getCanonicalPath(), _maxFileSize, _maxRequestSize, _fileOutputBuffer);
|
||||||
MultiPartInputStream mpis = new MultiPartInputStream(in, content_type, config, tempdir);
|
MultiPartInputStream mpis = new MultiPartInputStream(in, content_type, config, tempdir);
|
||||||
|
mpis.setDeleteOnExit(_deleteFiles);
|
||||||
|
request.setAttribute(MULTIPART, mpis);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Collection<Part> parts = mpis.getParts();
|
Collection<Part> parts = mpis.getParts();
|
||||||
|
@ -161,18 +162,6 @@ public class MultiPartFilter implements Filter
|
||||||
if (mp.getContentType() != null)
|
if (mp.getContentType() != null)
|
||||||
params.add(mp.getName()+CONTENT_TYPE_SUFFIX, mp.getContentType());
|
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
|
else
|
||||||
{
|
{
|
||||||
|
@ -194,23 +183,26 @@ public class MultiPartFilter implements Filter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------ */
|
||||||
private void deleteFiles(ServletRequest request)
|
private void deleteFiles(ServletRequest request)
|
||||||
{
|
{
|
||||||
ArrayList<File> files = (ArrayList<File>)request.getAttribute(FILES);
|
if (!_deleteFiles)
|
||||||
if (files!=null)
|
return;
|
||||||
{
|
|
||||||
for (File file : files)
|
MultiPartInputStream mpis = (MultiPartInputStream)request.getAttribute(MULTIPART);
|
||||||
|
if (mpis != null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
file.delete();
|
mpis.deleteParts();
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
_context.log("failed to delete " + file, e);
|
_context.log("Error deleting multipart tmp files", e);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
request.removeAttribute(MULTIPART);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------------- */
|
||||||
|
|
|
@ -65,6 +65,10 @@ public class GzipFilterDefaultNoRecompressTest
|
||||||
{ "jetty_logo.tiff", "image/tiff", GzipFilter.GZIP },
|
{ "jetty_logo.tiff", "image/tiff", GzipFilter.GZIP },
|
||||||
{ "jetty_logo.xcf", "image/xcf", GzipFilter.GZIP },
|
{ "jetty_logo.xcf", "image/xcf", GzipFilter.GZIP },
|
||||||
{ "jetty_logo.jp2", "image/jpeg2000", 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
|
// Same tests again for deflate
|
||||||
// Some already compressed files
|
// Some already compressed files
|
||||||
|
|
|
@ -122,6 +122,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
|
@Test
|
||||||
public void testIsGzipCompressedLarge() throws Exception
|
public void testIsGzipCompressedLarge() throws Exception
|
||||||
{
|
{
|
||||||
|
@ -145,6 +191,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
|
@Test
|
||||||
public void testIsNotGzipCompressed() throws Exception
|
public void testIsNotGzipCompressed() throws Exception
|
||||||
{
|
{
|
||||||
|
|
|
@ -113,7 +113,7 @@ public class MultipartFilterTest
|
||||||
|
|
||||||
|
|
||||||
response = HttpTester.parseResponse(tester.getResponses(request.generate()));
|
response = HttpTester.parseResponse(tester.getResponses(request.generate()));
|
||||||
assertEquals(HttpServletResponse.SC_OK,response.getStatus());
|
assertEquals(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,response.getStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -98,7 +98,11 @@ public class GzipTester
|
||||||
// Assert the response headers
|
// Assert the response headers
|
||||||
// Assert.assertThat("Response.status",response.getStatus(),is(HttpServletResponse.SC_OK));
|
// 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-Length]",response.get("Content-Length"),notNullValue());
|
||||||
|
int qindex = compressionType.indexOf(";");
|
||||||
|
if (qindex < 0)
|
||||||
Assert.assertThat("Response.header[Content-Encoding]",response.get("Content-Encoding"),containsString(compressionType));
|
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.
|
// Assert that the decompressed contents are what we expect.
|
||||||
File serverFile = testdir.getFile(serverFilename);
|
File serverFile = testdir.getFile(serverFilename);
|
||||||
|
@ -111,11 +115,11 @@ public class GzipTester
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
bais = new ByteArrayInputStream(response.getContentBytes());
|
bais = new ByteArrayInputStream(response.getContentBytes());
|
||||||
if (compressionType.equals(GzipFilter.GZIP))
|
if (compressionType.startsWith(GzipFilter.GZIP))
|
||||||
{
|
{
|
||||||
in = new GZIPInputStream(bais);
|
in = new GZIPInputStream(bais);
|
||||||
}
|
}
|
||||||
else if (compressionType.equals(GzipFilter.DEFLATE))
|
else if (compressionType.startsWith(GzipFilter.DEFLATE))
|
||||||
{
|
{
|
||||||
in = new InflaterInputStream(bais, new Inflater(true));
|
in = new InflaterInputStream(bais, new Inflater(true));
|
||||||
}
|
}
|
||||||
|
@ -248,6 +252,7 @@ public class GzipTester
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asserts that the request results in a properly structured GzipFilter response, where the content is
|
* Asserts that the request results in a properly structured GzipFilter response, where the content is
|
||||||
* not compressed, and the content-length is returned appropriately.
|
* not compressed, and the content-length is returned appropriately.
|
||||||
|
|
|
@ -41,6 +41,7 @@ public class CommandLineBuilder
|
||||||
*/
|
*/
|
||||||
public void addArg(String arg)
|
public void addArg(String arg)
|
||||||
{
|
{
|
||||||
|
if (arg != null)
|
||||||
args.add(quote(arg));
|
args.add(quote(arg));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,6 +85,7 @@ public class CommandLineBuilder
|
||||||
*/
|
*/
|
||||||
public void addRawArg(String arg)
|
public void addRawArg(String arg)
|
||||||
{
|
{
|
||||||
|
if (arg != null)
|
||||||
args.add(arg);
|
args.add(arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,7 +108,7 @@ public class CommandLineBuilder
|
||||||
return arg;
|
return arg;
|
||||||
}
|
}
|
||||||
StringBuilder buf = new StringBuilder();
|
StringBuilder buf = new StringBuilder();
|
||||||
// buf.append('"');
|
// buf.append('"');
|
||||||
boolean escaped = false;
|
boolean escaped = false;
|
||||||
for (char c : arg.toCharArray())
|
for (char c : arg.toCharArray())
|
||||||
{
|
{
|
||||||
|
@ -117,7 +119,7 @@ public class CommandLineBuilder
|
||||||
escaped = (c == '\\');
|
escaped = (c == '\\');
|
||||||
buf.append(c);
|
buf.append(c);
|
||||||
}
|
}
|
||||||
// buf.append('"');
|
// buf.append('"');
|
||||||
return buf.toString();
|
return buf.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,7 +135,7 @@ public class CommandLineBuilder
|
||||||
{
|
{
|
||||||
buf.append(' ');
|
buf.append(' ');
|
||||||
}
|
}
|
||||||
buf.append(arg);
|
buf.append(quote(arg));
|
||||||
delim = true;
|
delim = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@ import java.io.FilenameFilter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.LineNumberReader;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
@ -167,7 +168,15 @@ public class Main
|
||||||
{
|
{
|
||||||
int port = Integer.parseInt(Config.getProperty("STOP.PORT","-1"));
|
int port = Integer.parseInt(Config.getProperty("STOP.PORT","-1"));
|
||||||
String key = Config.getProperty("STOP.KEY",null);
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -268,7 +277,7 @@ public class Main
|
||||||
{
|
{
|
||||||
String opts[] = assign[1].split(",");
|
String opts[] = assign[1].split(",");
|
||||||
for (String opt : opts)
|
for (String opt : opts)
|
||||||
_config.addActiveOption(opt);
|
_config.addActiveOption(opt.trim());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -982,6 +991,12 @@ public class Main
|
||||||
* Stop a running jetty instance.
|
* Stop a running jetty instance.
|
||||||
*/
|
*/
|
||||||
public void stop(int port, String key)
|
public void stop(int port, String key)
|
||||||
|
{
|
||||||
|
stop (port,key,false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void stop (int port, String key, boolean wait)
|
||||||
{
|
{
|
||||||
int _port = port;
|
int _port = port;
|
||||||
String _key = key;
|
String _key = key;
|
||||||
|
@ -1005,6 +1020,14 @@ public class Main
|
||||||
OutputStream out = s.getOutputStream();
|
OutputStream out = s.getOutputStream();
|
||||||
out.write((_key + "\r\nstop\r\n").getBytes());
|
out.write((_key + "\r\nstop\r\n").getBytes());
|
||||||
out.flush();
|
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
|
finally
|
||||||
{
|
{
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
package org.eclipse.jetty.start;
|
package org.eclipse.jetty.start;
|
||||||
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.LineNumberReader;
|
import java.io.LineNumberReader;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
|
@ -113,8 +114,6 @@ public class Monitor extends Thread
|
||||||
Config.debug("command=" + cmd);
|
Config.debug("command=" + cmd);
|
||||||
if ("stop".equals(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 (_process!=null)
|
||||||
{
|
{
|
||||||
//if we have a child process, wait for it to finish before we stop
|
//if we have a child process, wait for it to finish before we stop
|
||||||
|
@ -122,12 +121,16 @@ public class Monitor extends Thread
|
||||||
{
|
{
|
||||||
_process.destroy();
|
_process.destroy();
|
||||||
_process.waitFor();
|
_process.waitFor();
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (InterruptedException e)
|
catch (InterruptedException e)
|
||||||
{
|
{
|
||||||
System.err.println("Interrupted waiting for child to terminate");
|
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);
|
System.exit(0);
|
||||||
}
|
}
|
||||||
else if ("status".equals(cmd))
|
else if ("status".equals(cmd))
|
||||||
|
|
|
@ -24,7 +24,10 @@ Command Line Options:
|
||||||
contains -X or -D arguments, but creates an extra
|
contains -X or -D arguments, but creates an extra
|
||||||
JVM instance.
|
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
|
--daemon Start in daemon mode with stderr and stdout
|
||||||
redirected to ${jetty.log}/start.log
|
redirected to ${jetty.log}/start.log
|
||||||
|
|
|
@ -18,21 +18,29 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.start;
|
package org.eclipse.jetty.start;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.is;
|
||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
|
|
||||||
public class CommandLineBuilderTest
|
public class CommandLineBuilderTest
|
||||||
{
|
{
|
||||||
|
private CommandLineBuilder cmd = new CommandLineBuilder("java");
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp()
|
||||||
|
{
|
||||||
|
cmd.addEqualsArg("-Djava.io.tmpdir","/home/java/temp dir/");
|
||||||
|
cmd.addArg("--version");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSimpleCommandline()
|
public void testSimpleCommandline()
|
||||||
{
|
{
|
||||||
CommandLineBuilder cmd = new CommandLineBuilder("java");
|
Assert.assertThat(cmd.toString(),is("java -Djava.io.tmpdir=/home/java/temp\\ dir/ --version"));
|
||||||
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
|
@Test
|
||||||
|
@ -53,6 +61,12 @@ public class CommandLineBuilderTest
|
||||||
assertQuoting("/opt/jetty 7 \"special\"/home","/opt/jetty\\ 7\\ \\\"special\\\"/home");
|
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)
|
private void assertQuoting(String raw, String expected)
|
||||||
{
|
{
|
||||||
String actual = CommandLineBuilder.quote(raw);
|
String actual = CommandLineBuilder.quote(raw);
|
||||||
|
|
|
@ -26,13 +26,17 @@ import java.util.List;
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
|
|
||||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.containsString;
|
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.hamcrest.Matchers.notNullValue;
|
||||||
import static org.junit.Assert.assertEquals;
|
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/");
|
Classpath classpath = nastyWayToCreateAClasspathObject("/jetty/home with spaces/");
|
||||||
CommandLineBuilder cmd = main.buildCommandLine(classpath,xmls);
|
CommandLineBuilder cmd = main.buildCommandLine(classpath,xmls);
|
||||||
Assert.assertThat("CommandLineBuilder shouldn't be null",cmd,notNullValue());
|
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"));
|
|
||||||
|
|
||||||
|
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
|
private Classpath nastyWayToCreateAClasspathObject(String jettyHome) throws NoSuchFieldException, IllegalAccessException
|
||||||
|
|
|
@ -32,6 +32,7 @@ import java.io.InputStreamReader;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import javax.servlet.MultipartConfigElement;
|
import javax.servlet.MultipartConfigElement;
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
|
@ -53,7 +54,7 @@ public class MultiPartInputStream
|
||||||
protected MultiMap _parts;
|
protected MultiMap _parts;
|
||||||
protected File _tmpDir;
|
protected File _tmpDir;
|
||||||
protected File _contextTmpDir;
|
protected File _contextTmpDir;
|
||||||
|
protected boolean _deleteOnExit;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -67,6 +68,7 @@ public class MultiPartInputStream
|
||||||
protected String _contentType;
|
protected String _contentType;
|
||||||
protected MultiMap _headers;
|
protected MultiMap _headers;
|
||||||
protected long _size = 0;
|
protected long _size = 0;
|
||||||
|
protected boolean _temporary = true;
|
||||||
|
|
||||||
public MultiPart (String name, String filename)
|
public MultiPart (String name, String filename)
|
||||||
throws IOException
|
throws IOException
|
||||||
|
@ -135,6 +137,8 @@ public class MultiPartInputStream
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
_file = File.createTempFile("MultiPart", "", MultiPartInputStream.this._tmpDir);
|
_file = File.createTempFile("MultiPart", "", MultiPartInputStream.this._tmpDir);
|
||||||
|
if (_deleteOnExit)
|
||||||
|
_file.deleteOnExit();
|
||||||
FileOutputStream fos = new FileOutputStream(_file);
|
FileOutputStream fos = new FileOutputStream(_file);
|
||||||
BufferedOutputStream bos = new BufferedOutputStream(fos);
|
BufferedOutputStream bos = new BufferedOutputStream(fos);
|
||||||
|
|
||||||
|
@ -197,11 +201,12 @@ public class MultiPartInputStream
|
||||||
{
|
{
|
||||||
if (_file != null)
|
if (_file != null)
|
||||||
{
|
{
|
||||||
|
//written to a file, whether temporary or not
|
||||||
return new BufferedInputStream (new FileInputStream(_file));
|
return new BufferedInputStream (new FileInputStream(_file));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//part content is in a ByteArrayOutputStream
|
//part content is in memory
|
||||||
return new ByteArrayInputStream(_bout.getBuf(),0,_bout.size());
|
return new ByteArrayInputStream(_bout.getBuf(),0,_bout.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -236,8 +241,11 @@ public class MultiPartInputStream
|
||||||
{
|
{
|
||||||
if (_file == null)
|
if (_file == null)
|
||||||
{
|
{
|
||||||
|
_temporary = false;
|
||||||
|
|
||||||
//part data is only in the ByteArrayOutputStream and never been written to disk
|
//part data is only in the ByteArrayOutputStream and never been written to disk
|
||||||
_file = new File (_tmpDir, fileName);
|
_file = new File (_tmpDir, fileName);
|
||||||
|
|
||||||
BufferedOutputStream bos = null;
|
BufferedOutputStream bos = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -255,6 +263,8 @@ public class MultiPartInputStream
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//the part data is already written to a temporary file, just rename it
|
//the part data is already written to a temporary file, just rename it
|
||||||
|
_temporary = false;
|
||||||
|
|
||||||
File f = new File(_tmpDir, fileName);
|
File f = new File(_tmpDir, fileName);
|
||||||
if (_file.renameTo(f))
|
if (_file.renameTo(f))
|
||||||
_file = 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()
|
* @see javax.servlet.http.Part#delete()
|
||||||
*/
|
*/
|
||||||
public void delete() throws IOException
|
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();
|
_file.delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -308,12 +331,65 @@ public class MultiPartInputStream
|
||||||
_contextTmpDir = contextTmpDir;
|
_contextTmpDir = contextTmpDir;
|
||||||
if (_contextTmpDir == null)
|
if (_contextTmpDir == null)
|
||||||
_contextTmpDir = new File (System.getProperty("java.io.tmpdir"));
|
_contextTmpDir = new File (System.getProperty("java.io.tmpdir"));
|
||||||
|
|
||||||
if (_config == null)
|
if (_config == null)
|
||||||
_config = new MultipartConfigElement(_contextTmpDir.getAbsolutePath());
|
_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()
|
public Collection<Part> getParts()
|
||||||
throws IOException, ServletException
|
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)
|
public Part getPart(String name)
|
||||||
throws IOException, ServletException
|
throws IOException, ServletException
|
||||||
{
|
{
|
||||||
|
@ -337,6 +421,12 @@ public class MultiPartInputStream
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse, if necessary, the multipart stream.
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
* @throws ServletException
|
||||||
|
*/
|
||||||
protected void parse ()
|
protected void parse ()
|
||||||
throws IOException, ServletException
|
throws IOException, ServletException
|
||||||
{
|
{
|
||||||
|
@ -583,6 +673,19 @@ public class MultiPartInputStream
|
||||||
part.close();
|
part.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!lastPart)
|
||||||
|
throw new IOException("Incomplete parts");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeleteOnExit(boolean deleteOnExit)
|
||||||
|
{
|
||||||
|
_deleteOnExit = deleteOnExit;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean isDeleteOnExit()
|
||||||
|
{
|
||||||
|
return _deleteOnExit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -129,9 +129,6 @@ public class MultiPartOutputStream extends FilterOutputStream
|
||||||
{
|
{
|
||||||
out.write(b,off,len);
|
out.write(b,off,len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -102,7 +102,7 @@ public class StringUtil
|
||||||
ByteBuffer slice = b.slice();
|
ByteBuffer slice = b.slice();
|
||||||
slice.position(position);
|
slice.position(position);
|
||||||
slice.limit(position+length);
|
slice.limit(position+length);
|
||||||
return BufferUtil.toString(slice);
|
return BufferUtil.toString(slice,__UTF8_CHARSET);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ package org.eclipse.jetty.util;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
@ -54,6 +55,40 @@ public class MultiPartInputStreamTest
|
||||||
protected String _contentType = "multipart/form-data, boundary=AaB03x";
|
protected String _contentType = "multipart/form-data, boundary=AaB03x";
|
||||||
protected String _multi = createMultipartRequestString(FILENAME);
|
protected String _multi = createMultipartRequestString(FILENAME);
|
||||||
protected String _dirname = System.getProperty("java.io.tmpdir")+File.separator+"myfiles-"+System.currentTimeMillis();
|
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
|
@Test
|
||||||
public void testNonMultiPartRequest()
|
public void testNonMultiPartRequest()
|
||||||
|
@ -63,7 +98,8 @@ public class MultiPartInputStreamTest
|
||||||
MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(_multi.getBytes()),
|
MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(_multi.getBytes()),
|
||||||
"Content-type: text/plain",
|
"Content-type: text/plain",
|
||||||
config,
|
config,
|
||||||
new File(_dirname));
|
_tmpDir);
|
||||||
|
mpis.setDeleteOnExit(true);
|
||||||
assertTrue(mpis.getParts().isEmpty());
|
assertTrue(mpis.getParts().isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,7 +111,8 @@ public class MultiPartInputStreamTest
|
||||||
MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(_multi.getBytes()),
|
MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(_multi.getBytes()),
|
||||||
_contentType,
|
_contentType,
|
||||||
config,
|
config,
|
||||||
new File(_dirname));
|
_tmpDir);
|
||||||
|
mpis.setDeleteOnExit(true);
|
||||||
Collection<Part> parts = mpis.getParts();
|
Collection<Part> parts = mpis.getParts();
|
||||||
assertFalse(parts.isEmpty());
|
assertFalse(parts.isEmpty());
|
||||||
}
|
}
|
||||||
|
@ -88,11 +125,12 @@ public class MultiPartInputStreamTest
|
||||||
MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(_multi.getBytes()),
|
MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(_multi.getBytes()),
|
||||||
_contentType,
|
_contentType,
|
||||||
config,
|
config,
|
||||||
new File(_dirname));
|
_tmpDir);
|
||||||
|
mpis.setDeleteOnExit(true);
|
||||||
|
Collection<Part> parts = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
mpis.getParts();
|
parts = mpis.getParts();
|
||||||
fail("Request should have exceeded maxRequestSize");
|
fail("Request should have exceeded maxRequestSize");
|
||||||
}
|
}
|
||||||
catch (IllegalStateException e)
|
catch (IllegalStateException e)
|
||||||
|
@ -109,11 +147,12 @@ public class MultiPartInputStreamTest
|
||||||
MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(_multi.getBytes()),
|
MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(_multi.getBytes()),
|
||||||
_contentType,
|
_contentType,
|
||||||
config,
|
config,
|
||||||
new File(_dirname));
|
_tmpDir);
|
||||||
|
mpis.setDeleteOnExit(true);
|
||||||
|
Collection<Part> parts = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
mpis.getParts();
|
parts = mpis.getParts();
|
||||||
fail("stuff.txt should have been larger than maxFileSize");
|
fail("stuff.txt should have been larger than maxFileSize");
|
||||||
}
|
}
|
||||||
catch (IllegalStateException e)
|
catch (IllegalStateException e)
|
||||||
|
@ -123,6 +162,48 @@ public class MultiPartInputStreamTest
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@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 ()
|
public void testMulti ()
|
||||||
throws Exception
|
throws Exception
|
||||||
{
|
{
|
||||||
|
@ -141,11 +222,11 @@ public class MultiPartInputStreamTest
|
||||||
MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(createMultipartRequestString(filename).getBytes()),
|
MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(createMultipartRequestString(filename).getBytes()),
|
||||||
_contentType,
|
_contentType,
|
||||||
config,
|
config,
|
||||||
new File(_dirname));
|
_tmpDir);
|
||||||
|
mpis.setDeleteOnExit(true);
|
||||||
Collection<Part> parts = mpis.getParts();
|
Collection<Part> parts = mpis.getParts();
|
||||||
assertThat(parts.size(), is(2));
|
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,notNullValue());
|
||||||
assertThat(field1.getName(),is("field1"));
|
assertThat(field1.getName(),is("field1"));
|
||||||
InputStream is = field1.getInputStream();
|
InputStream is = field1.getInputStream();
|
||||||
|
@ -154,17 +235,18 @@ public class MultiPartInputStreamTest
|
||||||
assertEquals("Joe Blow", new String(os.toByteArray()));
|
assertEquals("Joe Blow", new String(os.toByteArray()));
|
||||||
assertEquals(8, field1.getSize());
|
assertEquals(8, field1.getSize());
|
||||||
|
|
||||||
assertNotNull(((MultiPartInputStream.MultiPart)field1).getBytes()); //in internal buffer
|
assertNotNull(((MultiPartInputStream.MultiPart)field1).getBytes());//in internal buffer
|
||||||
field1.write("field1.txt");
|
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");
|
File f = new File (_dirname+File.separator+"field1.txt");
|
||||||
assertTrue(f.exists());
|
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");
|
File f2 = new File(_dirname+File.separator+"another_field1.txt");
|
||||||
assertTrue(f2.exists());
|
assertTrue(f2.exists());
|
||||||
assertFalse(f.exists()); //should have been renamed
|
assertFalse(f.exists()); //should have been renamed
|
||||||
field1.delete(); //file should be deleted
|
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");
|
MultiPart stuff = (MultiPart)mpis.getPart("stuff");
|
||||||
assertThat(stuff.getContentDispositionFilename(), is(filename));
|
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.getHeader("content-disposition"),is("form-data; name=\"stuff\"; filename=\"" + filename + "\""));
|
||||||
assertThat(stuff.getHeaderNames().size(),is(2));
|
assertThat(stuff.getHeaderNames().size(),is(2));
|
||||||
assertThat(stuff.getSize(),is(51L));
|
assertThat(stuff.getSize(),is(51L));
|
||||||
f = stuff.getFile();
|
File tmpfile = ((MultiPartInputStream.MultiPart)stuff).getFile();
|
||||||
assertThat(f,notNullValue()); // longer than 100 bytes, should already be a file
|
assertThat(tmpfile,notNullValue()); // longer than 100 bytes, should already be a tmp file
|
||||||
assertThat(stuff.getBytes(),nullValue()); //not in internal buffer any more
|
assertThat(((MultiPartInputStream.MultiPart)stuff).getBytes(),nullValue()); //not in an internal buffer
|
||||||
assertThat(f.exists(),is(true));
|
assertThat(tmpfile.exists(),is(true));
|
||||||
assertThat(f.getName(),is(not("stuff with space.txt")));
|
assertThat(tmpfile.getName(),is(not("stuff with space.txt")));
|
||||||
stuff.write(filename);
|
stuff.write(filename);
|
||||||
f = new File(_dirname+File.separator+filename);
|
f = new File(_dirname+File.separator+filename);
|
||||||
assertThat(f.exists(),is(true));
|
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
|
@Test
|
||||||
|
@ -197,15 +289,15 @@ public class MultiPartInputStreamTest
|
||||||
"content-disposition: form-data; name=\"stuff\"; filename=\"stuff2.txt\"\r\n"+
|
"content-disposition: form-data; name=\"stuff\"; filename=\"stuff2.txt\"\r\n"+
|
||||||
"Content-Type: text/plain\r\n"+
|
"Content-Type: text/plain\r\n"+
|
||||||
"\r\n"+
|
"\r\n"+
|
||||||
"000000000000000000000000000000000000000000000000000\r\n"+
|
"110000000000000000000000000000000000000000000000000\r\n"+
|
||||||
"--AaB03x--\r\n";
|
"--AaB03x--\r\n";
|
||||||
|
|
||||||
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
|
||||||
MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(sameNames.getBytes()),
|
MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(sameNames.getBytes()),
|
||||||
_contentType,
|
_contentType,
|
||||||
config,
|
config,
|
||||||
new File(_dirname));
|
_tmpDir);
|
||||||
|
mpis.setDeleteOnExit(true);
|
||||||
Collection<Part> parts = mpis.getParts();
|
Collection<Part> parts = mpis.getParts();
|
||||||
assertEquals(2, parts.size());
|
assertEquals(2, parts.size());
|
||||||
for (Part p:parts)
|
for (Part p:parts)
|
||||||
|
@ -219,6 +311,18 @@ public class MultiPartInputStreamTest
|
||||||
|
|
||||||
private String createMultipartRequestString(String filename)
|
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"+
|
return "--AaB03x\r\n"+
|
||||||
"content-disposition: form-data; name=\"field1\"\r\n"+
|
"content-disposition: form-data; name=\"field1\"\r\n"+
|
||||||
"\r\n"+
|
"\r\n"+
|
||||||
|
@ -226,8 +330,8 @@ public class MultiPartInputStreamTest
|
||||||
"--AaB03x\r\n"+
|
"--AaB03x\r\n"+
|
||||||
"content-disposition: form-data; name=\"stuff\"; filename=\"" + filename + "\"\r\n"+
|
"content-disposition: form-data; name=\"stuff\"; filename=\"" + filename + "\"\r\n"+
|
||||||
"Content-Type: text/plain\r\n"+
|
"Content-Type: text/plain\r\n"+
|
||||||
"\r\n"+
|
"\r\n"+name+
|
||||||
"000000000000000000000000000000000000000000000000000\r\n"+
|
filler.toString()+"\r\n" +
|
||||||
"--AaB03x--\r\n";
|
"--AaB03x--\r\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ package org.eclipse.jetty.webapp;
|
||||||
import org.eclipse.jetty.util.Loader;
|
import org.eclipse.jetty.util.Loader;
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
import org.eclipse.jetty.util.resource.Resource;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DiscoveredAnnotation
|
* DiscoveredAnnotation
|
||||||
|
@ -36,16 +37,28 @@ public abstract class DiscoveredAnnotation
|
||||||
protected WebAppContext _context;
|
protected WebAppContext _context;
|
||||||
protected String _className;
|
protected String _className;
|
||||||
protected Class<?> _clazz;
|
protected Class<?> _clazz;
|
||||||
|
protected Resource _resource; //resource it was discovered on, can be null (eg from WEB-INF/classes)
|
||||||
|
|
||||||
public abstract void apply();
|
public abstract void apply();
|
||||||
|
|
||||||
public DiscoveredAnnotation (WebAppContext context, String className)
|
public DiscoveredAnnotation (WebAppContext context, String className)
|
||||||
{
|
{
|
||||||
_context = context;
|
this(context,className, null);
|
||||||
_className = className;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public DiscoveredAnnotation(WebAppContext context, String className, Resource resource)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
_className = className;
|
||||||
|
_resource = resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Resource getResource ()
|
||||||
|
{
|
||||||
|
return _resource;
|
||||||
|
}
|
||||||
|
|
||||||
public Class<?> getTargetClass()
|
public Class<?> getTargetClass()
|
||||||
{
|
{
|
||||||
if (_clazz != null)
|
if (_clazz != null)
|
||||||
|
|
|
@ -200,7 +200,7 @@ public class MetaData
|
||||||
_metaDataComplete=true;
|
_metaDataComplete=true;
|
||||||
break;
|
break;
|
||||||
case False:
|
case False:
|
||||||
_metaDataComplete=true;
|
_metaDataComplete=false;
|
||||||
break;
|
break;
|
||||||
case NotSet:
|
case NotSet:
|
||||||
break;
|
break;
|
||||||
|
@ -270,12 +270,42 @@ public class MetaData
|
||||||
*/
|
*/
|
||||||
public void addDiscoveredAnnotations(List<DiscoveredAnnotation> annotations)
|
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)
|
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)
|
public void addDescriptorProcessor(DescriptorProcessor p)
|
||||||
|
@ -508,6 +538,15 @@ public class MetaData
|
||||||
_origins.put(name, x);
|
_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()
|
public boolean isMetaDataComplete()
|
||||||
{
|
{
|
||||||
return _metaDataComplete;
|
return _metaDataComplete;
|
||||||
|
|
|
@ -18,4 +18,4 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.webapp;
|
package org.eclipse.jetty.webapp;
|
||||||
|
|
||||||
public enum Origin {NotSet, WebXml, WebDefaults, WebOverride, WebFragment, Annotation}
|
public enum Origin {NotSet, WebXml, WebDefaults, WebOverride, WebFragment, Annotation, API}
|
|
@ -617,7 +617,7 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor
|
||||||
{
|
{
|
||||||
//no servlet mappings
|
//no servlet mappings
|
||||||
context.getMetaData().setOrigin(servlet_name+".servlet.mappings", descriptor);
|
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);
|
mapping.setDefault(context.getMetaData().getOrigin(servlet_name+".servlet.mappings") == Origin.WebDefaults);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -629,14 +629,14 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor
|
||||||
//otherwise just ignore it
|
//otherwise just ignore it
|
||||||
if (!(descriptor instanceof FragmentDescriptor))
|
if (!(descriptor instanceof FragmentDescriptor))
|
||||||
{
|
{
|
||||||
addServletMapping(servlet_name, node, context);
|
addServletMapping(servlet_name, node, context, descriptor);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case WebFragment:
|
case WebFragment:
|
||||||
{
|
{
|
||||||
//mappings previously set by another web-fragment, so merge in this web-fragment's mappings
|
//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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1161,7 +1161,7 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor
|
||||||
* @param node
|
* @param node
|
||||||
* @param context
|
* @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();
|
ServletMapping mapping = new ServletMapping();
|
||||||
mapping.setServletName(servletName);
|
mapping.setServletName(servletName);
|
||||||
|
@ -1173,6 +1173,7 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor
|
||||||
String p = iter.next().toString(false, true);
|
String p = iter.next().toString(false, true);
|
||||||
p = normalizePattern(p);
|
p = normalizePattern(p);
|
||||||
paths.add(p);
|
paths.add(p);
|
||||||
|
context.getMetaData().setOrigin(servletName+".servlet.mapping."+p, descriptor);
|
||||||
}
|
}
|
||||||
mapping.setPathSpecs((String[]) paths.toArray(new String[paths.size()]));
|
mapping.setPathSpecs((String[]) paths.toArray(new String[paths.size()]));
|
||||||
context.getServletHandler().addServletMapping(mapping);
|
context.getServletHandler().addServletMapping(mapping);
|
||||||
|
@ -1184,7 +1185,7 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor
|
||||||
* @param node
|
* @param node
|
||||||
* @param context
|
* @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();
|
FilterMapping mapping = new FilterMapping();
|
||||||
mapping.setFilterName(filterName);
|
mapping.setFilterName(filterName);
|
||||||
|
@ -1196,6 +1197,7 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor
|
||||||
String p = iter.next().toString(false, true);
|
String p = iter.next().toString(false, true);
|
||||||
p = normalizePattern(p);
|
p = normalizePattern(p);
|
||||||
paths.add(p);
|
paths.add(p);
|
||||||
|
context.getMetaData().setOrigin(filterName+".filter.mapping."+p, descriptor);
|
||||||
}
|
}
|
||||||
mapping.setPathSpecs((String[]) paths.toArray(new String[paths.size()]));
|
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.setIsXml(group.getString("is-xml", false, true));
|
||||||
jpg.setDeferredSyntaxAllowedAsLiteral(group.getString("deferred-syntax-allowed-as-literal", false, true));
|
jpg.setDeferredSyntaxAllowedAsLiteral(group.getString("deferred-syntax-allowed-as-literal", false, true));
|
||||||
jpg.setTrimDirectiveWhitespaces(group.getString("trim-directive-whitespaces", 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.setBuffer(group.getString("buffer", false, true));
|
||||||
jpg.setErrorOnUndeclaredNamespace(group.getString("error-on-undeclared-namespace", 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
|
//ServletSpec 3.0, p74 security-constraints, as minOccurs > 1, are additive
|
||||||
//across fragments
|
//across fragments
|
||||||
|
|
||||||
|
//TODO: need to remember origin of the constraints
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
XmlParser.Node auths = node.get("auth-constraint");
|
XmlParser.Node auths = node.get("auth-constraint");
|
||||||
|
@ -1400,10 +1404,18 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor
|
||||||
{
|
{
|
||||||
String url = iter2.next().toString(false, true);
|
String url = iter2.next().toString(false, true);
|
||||||
url = normalizePattern(url);
|
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> iter3 = collection.iterator("http-method");
|
||||||
|
Iterator<XmlParser.Node> iter4 = collection.iterator("http-method-omission");
|
||||||
|
|
||||||
if (iter3.hasNext())
|
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())
|
while (iter3.hasNext())
|
||||||
{
|
{
|
||||||
String method = ((XmlParser.Node) iter3.next()).toString(false, true);
|
String method = ((XmlParser.Node) iter3.next()).toString(false, true);
|
||||||
|
@ -1411,12 +1423,25 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor
|
||||||
mapping.setMethod(method);
|
mapping.setMethod(method);
|
||||||
mapping.setPathSpec(url);
|
mapping.setPathSpec(url);
|
||||||
mapping.setConstraint(sc);
|
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);
|
((ConstraintAware)context.getSecurityHandler()).addConstraintMapping(mapping);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
//No http-methods or http-method-omissions specified, the constraint applies to all
|
||||||
ConstraintMapping mapping = new ConstraintMapping();
|
ConstraintMapping mapping = new ConstraintMapping();
|
||||||
mapping.setPathSpec(url);
|
mapping.setPathSpec(url);
|
||||||
mapping.setConstraint(sc);
|
mapping.setConstraint(sc);
|
||||||
|
@ -1768,7 +1793,7 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor
|
||||||
{
|
{
|
||||||
//no filtermappings for this filter yet defined
|
//no filtermappings for this filter yet defined
|
||||||
context.getMetaData().setOrigin(filter_name+".filter.mappings", descriptor);
|
context.getMetaData().setOrigin(filter_name+".filter.mappings", descriptor);
|
||||||
addFilterMapping(filter_name, node, context);
|
addFilterMapping(filter_name, node, context, descriptor);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case WebDefaults:
|
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.
|
//filter mappings defined in a web xml file. If we're processing a fragment, we ignore filter mappings.
|
||||||
if (!(descriptor instanceof FragmentDescriptor))
|
if (!(descriptor instanceof FragmentDescriptor))
|
||||||
{
|
{
|
||||||
addFilterMapping(filter_name, node, context);
|
addFilterMapping(filter_name, node, context, descriptor);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case WebFragment:
|
case WebFragment:
|
||||||
{
|
{
|
||||||
//filter mappings first defined in a web-fragment, allow other fragments to add
|
//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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
//Get the system classpath tlds and tell jasper about them, if jasper is on the classpath
|
||||||
try
|
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;
|
assert clazz!=null;
|
||||||
Collection<Resource> tld_resources = (Collection<Resource>)_context.getAttribute(TLD_RESOURCES);
|
Collection<Resource> tld_resources = (Collection<Resource>)_context.getAttribute(TLD_RESOURCES);
|
||||||
|
|
||||||
|
|
|
@ -24,17 +24,28 @@ import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.security.PermissionCollection;
|
import java.security.PermissionCollection;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.EventListener;
|
import java.util.EventListener;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import javax.servlet.HttpMethodConstraintElement;
|
||||||
import javax.servlet.ServletContext;
|
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.HttpSessionActivationListener;
|
||||||
import javax.servlet.http.HttpSessionAttributeListener;
|
import javax.servlet.http.HttpSessionAttributeListener;
|
||||||
import javax.servlet.http.HttpSessionBindingListener;
|
import javax.servlet.http.HttpSessionBindingListener;
|
||||||
import javax.servlet.http.HttpSessionListener;
|
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.security.SecurityHandler;
|
||||||
import org.eclipse.jetty.server.Connector;
|
import org.eclipse.jetty.server.Connector;
|
||||||
import org.eclipse.jetty.server.HandlerContainer;
|
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.log.Logger;
|
||||||
import org.eclipse.jetty.util.resource.Resource;
|
import org.eclipse.jetty.util.resource.Resource;
|
||||||
import org.eclipse.jetty.util.resource.ResourceCollection;
|
import org.eclipse.jetty.util.resource.ResourceCollection;
|
||||||
|
import org.eclipse.jetty.util.security.Constraint;
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/** Web Application Context Handler.
|
/** Web Application Context Handler.
|
||||||
|
@ -1253,6 +1265,79 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
|
||||||
super.startContext();
|
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
|
public class Context extends ServletContextHandler.Context
|
||||||
{
|
{
|
||||||
|
@ -1301,6 +1386,8 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
|
||||||
return servletContext;
|
return servletContext;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
|
|
|
@ -31,4 +31,5 @@ public class UnitGenerator extends Generator
|
||||||
{
|
{
|
||||||
super(WebSocketPolicy.newServerPolicy(),new MappedByteBufferPool());
|
super(WebSocketPolicy.newServerPolicy(),new MappedByteBufferPool());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,15 +15,6 @@
|
||||||
<param-value>a context value</param-value>
|
<param-value>a context value</param-value>
|
||||||
</context-param>
|
</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 -->
|
<!-- Add or override servlet init parameter -->
|
||||||
<servlet>
|
<servlet>
|
||||||
|
|
|
@ -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: </th>");
|
||||||
|
pout.write("<td>" + notag(request.getMethod())+"</td>");
|
||||||
|
pout.write("</tr><tr>\n");
|
||||||
|
pout.write("<th align=\"right\">getContentLength: </th>");
|
||||||
|
pout.write("<td>"+Integer.toString(request.getContentLength())+"</td>");
|
||||||
|
pout.write("</tr><tr>\n");
|
||||||
|
pout.write("<th align=\"right\">getContentType: </th>");
|
||||||
|
pout.write("<td>"+notag(request.getContentType())+"</td>");
|
||||||
|
pout.write("</tr><tr>\n");
|
||||||
|
pout.write("<th align=\"right\">getRequestURI: </th>");
|
||||||
|
pout.write("<td>"+notag(request.getRequestURI())+"</td>");
|
||||||
|
pout.write("</tr><tr>\n");
|
||||||
|
pout.write("<th align=\"right\">getRequestURL: </th>");
|
||||||
|
pout.write("<td>"+notag(request.getRequestURL().toString())+"</td>");
|
||||||
|
pout.write("</tr><tr>\n");
|
||||||
|
pout.write("<th align=\"right\">getContextPath: </th>");
|
||||||
|
pout.write("<td>"+request.getContextPath()+"</td>");
|
||||||
|
pout.write("</tr><tr>\n");
|
||||||
|
pout.write("<th align=\"right\">getServletPath: </th>");
|
||||||
|
pout.write("<td>"+notag(request.getServletPath())+"</td>");
|
||||||
|
pout.write("</tr><tr>\n");
|
||||||
|
pout.write("<th align=\"right\">getPathInfo: </th>");
|
||||||
|
pout.write("<td>"+notag(request.getPathInfo())+"</td>");
|
||||||
|
pout.write("</tr><tr>\n");
|
||||||
|
pout.write("<th align=\"right\">getPathTranslated: </th>");
|
||||||
|
pout.write("<td>"+notag(request.getPathTranslated())+"</td>");
|
||||||
|
pout.write("</tr><tr>\n");
|
||||||
|
pout.write("<th align=\"right\">getQueryString: </th>");
|
||||||
|
pout.write("<td>"+notag(request.getQueryString())+"</td>");
|
||||||
|
pout.write("</tr><tr>\n");
|
||||||
|
|
||||||
|
pout.write("<th align=\"right\">getProtocol: </th>");
|
||||||
|
pout.write("<td>"+request.getProtocol()+"</td>");
|
||||||
|
pout.write("</tr><tr>\n");
|
||||||
|
pout.write("<th align=\"right\">getScheme: </th>");
|
||||||
|
pout.write("<td>"+request.getScheme()+"</td>");
|
||||||
|
pout.write("</tr><tr>\n");
|
||||||
|
pout.write("<th align=\"right\">getServerName: </th>");
|
||||||
|
pout.write("<td>"+notag(request.getServerName())+"</td>");
|
||||||
|
pout.write("</tr><tr>\n");
|
||||||
|
pout.write("<th align=\"right\">getServerPort: </th>");
|
||||||
|
pout.write("<td>"+Integer.toString(request.getServerPort())+"</td>");
|
||||||
|
pout.write("</tr><tr>\n");
|
||||||
|
pout.write("<th align=\"right\">getLocalName: </th>");
|
||||||
|
pout.write("<td>"+request.getLocalName()+"</td>");
|
||||||
|
pout.write("</tr><tr>\n");
|
||||||
|
pout.write("<th align=\"right\">getLocalAddr: </th>");
|
||||||
|
pout.write("<td>"+request.getLocalAddr()+"</td>");
|
||||||
|
pout.write("</tr><tr>\n");
|
||||||
|
pout.write("<th align=\"right\">getLocalPort: </th>");
|
||||||
|
pout.write("<td>"+Integer.toString(request.getLocalPort())+"</td>");
|
||||||
|
pout.write("</tr><tr>\n");
|
||||||
|
pout.write("<th align=\"right\">getRemoteUser: </th>");
|
||||||
|
pout.write("<td>"+request.getRemoteUser()+"</td>");
|
||||||
|
pout.write("</tr><tr>\n");
|
||||||
|
pout.write("<th align=\"right\">getUserPrincipal: </th>");
|
||||||
|
pout.write("<td>"+request.getUserPrincipal()+"</td>");
|
||||||
|
pout.write("</tr><tr>\n");
|
||||||
|
pout.write("<th align=\"right\">getRemoteAddr: </th>");
|
||||||
|
pout.write("<td>"+request.getRemoteAddr()+"</td>");
|
||||||
|
pout.write("</tr><tr>\n");
|
||||||
|
pout.write("<th align=\"right\">getRemoteHost: </th>");
|
||||||
|
pout.write("<td>"+request.getRemoteHost()+"</td>");
|
||||||
|
pout.write("</tr><tr>\n");
|
||||||
|
pout.write("<th align=\"right\">getRemotePort: </th>");
|
||||||
|
pout.write("<td>"+request.getRemotePort()+"</td>");
|
||||||
|
pout.write("</tr><tr>\n");
|
||||||
|
pout.write("<th align=\"right\">getRequestedSessionId: </th>");
|
||||||
|
pout.write("<td>"+request.getRequestedSessionId()+"</td>");
|
||||||
|
pout.write("</tr><tr>\n");
|
||||||
|
pout.write("<th align=\"right\">isSecure(): </th>");
|
||||||
|
pout.write("<td>"+request.isSecure()+"</td>");
|
||||||
|
|
||||||
|
pout.write("</tr><tr>\n");
|
||||||
|
pout.write("<th align=\"right\">isUserInRole(admin): </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,"&","&");
|
||||||
|
s=StringUtil.replace(s,"<","<");
|
||||||
|
s=StringUtil.replace(s,">",">");
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
package com.acme;
|
package com.acme;
|
||||||
|
|
||||||
|
import javax.servlet.DispatcherType;
|
||||||
import javax.servlet.ServletContextAttributeEvent;
|
import javax.servlet.ServletContextAttributeEvent;
|
||||||
import javax.servlet.ServletContextAttributeListener;
|
import javax.servlet.ServletContextAttributeListener;
|
||||||
import javax.servlet.ServletContextEvent;
|
import javax.servlet.ServletContextEvent;
|
||||||
|
@ -26,6 +27,12 @@ import javax.servlet.ServletRequestAttributeEvent;
|
||||||
import javax.servlet.ServletRequestAttributeListener;
|
import javax.servlet.ServletRequestAttributeListener;
|
||||||
import javax.servlet.ServletRequestEvent;
|
import javax.servlet.ServletRequestEvent;
|
||||||
import javax.servlet.ServletRequestListener;
|
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.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpSessionActivationListener;
|
import javax.servlet.http.HttpSessionActivationListener;
|
||||||
import javax.servlet.http.HttpSessionAttributeListener;
|
import javax.servlet.http.HttpSessionAttributeListener;
|
||||||
|
@ -33,6 +40,9 @@ import javax.servlet.http.HttpSessionBindingEvent;
|
||||||
import javax.servlet.http.HttpSessionEvent;
|
import javax.servlet.http.HttpSessionEvent;
|
||||||
import javax.servlet.http.HttpSessionListener;
|
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 class TestListener implements HttpSessionListener, HttpSessionAttributeListener, HttpSessionActivationListener, ServletContextListener, ServletContextAttributeListener, ServletRequestListener, ServletRequestAttributeListener
|
||||||
{
|
{
|
||||||
public void attributeAdded(HttpSessionBindingEvent se)
|
public void attributeAdded(HttpSessionBindingEvent se)
|
||||||
|
@ -62,16 +72,30 @@ public class TestListener implements HttpSessionListener, HttpSessionAttributeL
|
||||||
|
|
||||||
public void contextInitialized(ServletContextEvent sce)
|
public void contextInitialized(ServletContextEvent sce)
|
||||||
{
|
{
|
||||||
/* TODO for servlet 3.0
|
//configure programmatic security
|
||||||
* FilterRegistration registration=context.addFilter("TestFilter",TestFilter.class.getName());
|
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.setAsyncSupported(true);
|
||||||
registration.addMappingForUrlPatterns(
|
registration.addMappingForUrlPatterns(
|
||||||
EnumSet.of(DispatcherType.ERROR,DispatcherType.ASYNC,DispatcherType.FORWARD,DispatcherType.INCLUDE,DispatcherType.REQUEST),
|
EnumSet.of(DispatcherType.ERROR,DispatcherType.ASYNC,DispatcherType.FORWARD,DispatcherType.INCLUDE,DispatcherType.REQUEST),
|
||||||
true,
|
true,
|
||||||
new String[]{"/dump/*","/dispatch/*","*.dump"});
|
new String[]{"/*"});
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void contextDestroyed(ServletContextEvent sce)
|
public void contextDestroyed(ServletContextEvent sce)
|
||||||
|
|
|
@ -18,20 +18,6 @@
|
||||||
<listener-class>com.acme.TestListener</listener-class>
|
<listener-class>com.acme.TestListener</listener-class>
|
||||||
</listener>
|
</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>
|
||||||
<filter-name>QoSFilter</filter-name>
|
<filter-name>QoSFilter</filter-name>
|
||||||
|
@ -120,7 +106,6 @@
|
||||||
</filter-mapping>
|
</filter-mapping>
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
|
||||||
<servlet>
|
<servlet>
|
||||||
<servlet-name>Hello</servlet-name>
|
<servlet-name>Hello</servlet-name>
|
||||||
<servlet-class>com.acme.HelloWorld</servlet-class>
|
<servlet-class>com.acme.HelloWorld</servlet-class>
|
||||||
|
@ -276,6 +261,18 @@
|
||||||
<location>/error404.html</location>
|
<location>/error404.html</location>
|
||||||
</error-page>
|
</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>
|
<security-constraint>
|
||||||
<web-resource-collection>
|
<web-resource-collection>
|
||||||
<web-resource-name>Auth2</web-resource-name>
|
<web-resource-name>Auth2</web-resource-name>
|
||||||
|
|
|
@ -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/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/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="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>
|
</ul>
|
||||||
<p/>
|
<p/>
|
||||||
<p>
|
<p>
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue