Merge branch 'jetty-8' into release-8

This commit is contained in:
Jesse McConnell 2012-11-05 11:52:44 -06:00
commit e97838e9d4
143 changed files with 4650 additions and 966 deletions

View File

@ -22,7 +22,7 @@ import java.util.ArrayList;
import java.util.List;
import org.eclipse.jetty.annotations.AnnotationParser.DiscoverableAnnotationHandler;
import org.eclipse.jetty.annotations.AnnotationParser.Value;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.webapp.DiscoveredAnnotation;
import org.eclipse.jetty.webapp.WebAppContext;
@ -34,13 +34,32 @@ import org.eclipse.jetty.webapp.WebAppContext;
public abstract class AbstractDiscoverableAnnotationHandler implements DiscoverableAnnotationHandler
{
protected WebAppContext _context;
protected List<DiscoveredAnnotation> _annotations = new ArrayList<DiscoveredAnnotation>();
protected List<DiscoveredAnnotation> _annotations;
protected Resource _resource;
public AbstractDiscoverableAnnotationHandler(WebAppContext context)
{
this(context, null);
}
public AbstractDiscoverableAnnotationHandler(WebAppContext context, List<DiscoveredAnnotation> list)
{
_context = context;
if (list == null)
_annotations = new ArrayList<DiscoveredAnnotation>();
else
_annotations = list;
}
public Resource getResource()
{
return _resource;
}
public void setResource(Resource resource)
{
_resource = resource;
}
public List<DiscoveredAnnotation> getAnnotationList ()
{

View File

@ -33,6 +33,7 @@ import javax.servlet.annotation.HandlesTypes;
import org.eclipse.jetty.annotations.AnnotationParser.DiscoverableAnnotationHandler;
import org.eclipse.jetty.plus.annotation.ContainerInitializer;
import org.eclipse.jetty.util.MultiMap;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.Resource;
@ -55,12 +56,22 @@ public class AnnotationConfiguration extends AbstractConfiguration
private static final Logger LOG = Log.getLogger(AnnotationConfiguration.class);
public static final String CLASS_INHERITANCE_MAP = "org.eclipse.jetty.classInheritanceMap";
public static final String CONTAINER_INITIALIZERS = "org.eclipse.jetty.containerInitializers";
protected List<DiscoverableAnnotationHandler> _discoverableAnnotationHandlers = new ArrayList<DiscoverableAnnotationHandler>();
protected ClassInheritanceHandler _classInheritanceHandler;
protected List<ContainerInitializerAnnotationHandler> _containerInitializerAnnotationHandlers = new ArrayList<ContainerInitializerAnnotationHandler>();
public void preConfigure(final WebAppContext context) throws Exception
{
}
/**
* @see org.eclipse.jetty.webapp.AbstractConfiguration#configure(org.eclipse.jetty.webapp.WebAppContext)
*/
@Override
public void configure(WebAppContext context) throws Exception
{
@ -75,10 +86,9 @@ public class AnnotationConfiguration extends AbstractConfiguration
//If metadata isn't complete, if this is a servlet 3 webapp or isConfigDiscovered is true, we need to search for annotations
if (context.getServletContext().getEffectiveMajorVersion() >= 3 || context.isConfigurationDiscovered())
{
parser = createAnnotationParser();
parser.registerAnnotationHandler("javax.servlet.annotation.WebServlet", new WebServletAnnotationHandler(context));
parser.registerAnnotationHandler("javax.servlet.annotation.WebFilter", new WebFilterAnnotationHandler(context));
parser.registerAnnotationHandler("javax.servlet.annotation.WebListener", new WebListenerAnnotationHandler(context));
_discoverableAnnotationHandlers.add(new WebServletAnnotationHandler(context));
_discoverableAnnotationHandlers.add(new WebFilterAnnotationHandler(context));
_discoverableAnnotationHandlers.add(new WebListenerAnnotationHandler(context));
}
}
else
@ -88,11 +98,11 @@ public class AnnotationConfiguration extends AbstractConfiguration
//Regardless of metadata, if there are any ServletContainerInitializers with @HandlesTypes, then we need to scan all the
//classes so we can call their onStartup() methods correctly
List<ServletContainerInitializer> nonExcludedInitializers = getNonExcludedInitializers(context);
parser = registerServletContainerInitializerAnnotationHandlers(context, parser, nonExcludedInitializers);
createServletContainerInitializerAnnotationHandlers(context, getNonExcludedInitializers(context));
if (parser != null)
if (!_discoverableAnnotationHandlers.isEmpty() || _classInheritanceHandler != null || !_containerInitializerAnnotationHandlers.isEmpty())
{
parser = createAnnotationParser();
if (LOG.isDebugEnabled()) LOG.debug("Scanning all classses for annotations: webxmlVersion="+context.getServletContext().getEffectiveMajorVersion()+" configurationDiscovered="+context.isConfigurationDiscovered());
parseContainerPath(context, parser);
//email from Rajiv Mordani jsrs 315 7 April 2010
@ -102,11 +112,39 @@ public class AnnotationConfiguration extends AbstractConfiguration
// WEB-INF/classes + order of the elements.
parseWebInfClasses(context, parser);
parseWebInfLib (context, parser);
for (DiscoverableAnnotationHandler h:_discoverableAnnotationHandlers)
context.getMetaData().addDiscoveredAnnotations(((AbstractDiscoverableAnnotationHandler)h).getAnnotationList());
}
}
/**
* @see org.eclipse.jetty.webapp.AbstractConfiguration#postConfigure(org.eclipse.jetty.webapp.WebAppContext)
*/
@Override
public void postConfigure(WebAppContext context) throws Exception
{
MultiMap map = (MultiMap)context.getAttribute(CLASS_INHERITANCE_MAP);
if (map != null)
map.clear();
context.removeAttribute(CLASS_INHERITANCE_MAP);
List<ContainerInitializer> initializers = (List<ContainerInitializer>)context.getAttribute(CONTAINER_INITIALIZERS);
if (initializers != null)
initializers.clear();
if (_discoverableAnnotationHandlers != null)
_discoverableAnnotationHandlers.clear();
_classInheritanceHandler = null;
if (_containerInitializerAnnotationHandlers != null)
_containerInitializerAnnotationHandlers.clear();
super.postConfigure(context);
}
/**
* @return a new AnnotationParser. This method can be overridden to use a different impleemntation of
* the AnnotationParser. Note that this is considered internal API.
@ -116,6 +154,9 @@ public class AnnotationConfiguration extends AbstractConfiguration
return new AnnotationParser();
}
/**
* @see org.eclipse.jetty.webapp.AbstractConfiguration#cloneConfigure(org.eclipse.jetty.webapp.WebAppContext, org.eclipse.jetty.webapp.WebAppContext)
*/
@Override
public void cloneConfigure(WebAppContext template, WebAppContext context) throws Exception
{
@ -123,32 +164,21 @@ public class AnnotationConfiguration extends AbstractConfiguration
}
public AnnotationParser registerServletContainerInitializerAnnotationHandlers (WebAppContext context, AnnotationParser parser, List<ServletContainerInitializer> scis)
/**
* @param context
* @param scis
* @throws Exception
*/
public void createServletContainerInitializerAnnotationHandlers (WebAppContext context, List<ServletContainerInitializer> scis)
throws Exception
{
//TODO verify my interpretation of the spec. That is, that metadata-complete has nothing
//to do with finding the ServletContainerInitializers, classes designated to be of interest to them,
//or even calling them on startup.
//Get all ServletContainerInitializers, and check them for HandlesTypes annotations.
//For each class in the HandlesTypes value, if it IS an annotation, register a handler
//that will record the classes that have that annotation.
//If it is NOT an annotation, then we will interrogate the type hierarchy discovered during
//parsing later on to find the applicable classes.
{
if (scis == null || scis.isEmpty())
return parser; // nothing to do
ServletContainerInitializerListener listener = new ServletContainerInitializerListener();
listener.setWebAppContext(context);
context.addEventListener(listener);
//may need to add a listener
return; // nothing to do
ArrayList<ContainerInitializer> initializers = new ArrayList<ContainerInitializer>();
List<ContainerInitializer> initializers = new ArrayList<ContainerInitializer>();
context.setAttribute(CONTAINER_INITIALIZERS, initializers);
for (ServletContainerInitializer service : scis)
@ -164,18 +194,14 @@ public class AnnotationConfiguration extends AbstractConfiguration
if (classes != null)
{
initializer.setInterestedTypes(classes);
//We need to create a parser if we haven't already
if (parser == null)
parser = createAnnotationParser();
//If we haven't already done so, we need to register a handler that will
//process the whole class hierarchy
//process the whole class hierarchy to satisfy the ServletContainerInitializer
if (context.getAttribute(CLASS_INHERITANCE_MAP) == null)
{
ClassInheritanceHandler classHandler = new ClassInheritanceHandler();
context.setAttribute(CLASS_INHERITANCE_MAP, classHandler.getMap());
parser.registerClassHandler(classHandler);
MultiMap map = new MultiMap();
context.setAttribute(CLASS_INHERITANCE_MAP, map);
_classInheritanceHandler = new ClassInheritanceHandler(map);
}
for (Class c: classes)
@ -186,7 +212,7 @@ public class AnnotationConfiguration extends AbstractConfiguration
{
if (LOG.isDebugEnabled()) LOG.debug("Registering annotation handler for "+c.getName());
parser.registerAnnotationHandler(c.getName(), new ContainerInitializerAnnotationHandler(initializer, c));
_containerInitializerAnnotationHandlers.add(new ContainerInitializerAnnotationHandler(initializer, c));
}
}
}
@ -197,15 +223,15 @@ public class AnnotationConfiguration extends AbstractConfiguration
if (LOG.isDebugEnabled()) LOG.debug("No annotation on initializer "+service.getClass());
}
//return the parser in case we lazily created it
return parser;
//add a listener which will call the servletcontainerinitializers when appropriate
ServletContainerInitializerListener listener = new ServletContainerInitializerListener();
listener.setWebAppContext(context);
context.addEventListener(listener);
}
/**
* 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.
@ -248,7 +274,12 @@ public class AnnotationConfiguration extends AbstractConfiguration
public List<ServletContainerInitializer> getNonExcludedInitializers (WebAppContext context)
/**
* @param context
* @return
* @throws Exception
*/
public List<ServletContainerInitializer> getNonExcludedInitializers (WebAppContext context)
throws Exception
{
List<ServletContainerInitializer> nonExcludedInitializers = new ArrayList<ServletContainerInitializer>();
@ -270,14 +301,29 @@ public class AnnotationConfiguration extends AbstractConfiguration
/**
* Scan jars on container path.
*
* @param context
* @param parser
* @throws Exception
*/
public void parseContainerPath (final WebAppContext context, final AnnotationParser parser)
throws Exception
{
//if no pattern for the container path is defined, then by default scan NOTHING
LOG.debug("Scanning container jars");
//clear any previously discovered annotations
clearAnnotationList(parser.getAnnotationHandlers());
//always parse for discoverable annotations as well as class hierarchy and servletcontainerinitializer related annotations
parser.clearHandlers();
for (DiscoverableAnnotationHandler h:_discoverableAnnotationHandlers)
{
if (h instanceof AbstractDiscoverableAnnotationHandler)
((AbstractDiscoverableAnnotationHandler)h).setResource(null); //
}
parser.registerHandlers(_discoverableAnnotationHandlers);
parser.registerHandler(_classInheritanceHandler);
parser.registerHandlers(_containerInitializerAnnotationHandlers);
//Convert from Resource to URI
ArrayList<URI> containerUris = new ArrayList<URI>();
@ -304,16 +350,19 @@ public class AnnotationConfiguration extends AbstractConfiguration
return true;
return false;
}
});
});
//gather together all annotations discovered
List<DiscoveredAnnotation> annotations = new ArrayList<DiscoveredAnnotation>();
gatherAnnotations(annotations, parser.getAnnotationHandlers());
context.getMetaData().addDiscoveredAnnotations(annotations);
}
/**
* Scan jars in WEB-INF/lib
*
* @param context
* @param parser
* @throws Exception
*/
public void parseWebInfLib (final WebAppContext context, final AnnotationParser parser)
throws Exception
{
@ -332,17 +381,34 @@ public class AnnotationConfiguration extends AbstractConfiguration
for (Resource r : jars)
{
//clear any previously discovered annotations from handlers
clearAnnotationList(parser.getAnnotationHandlers());
//for each jar, we decide which set of annotations we need to parse for
parser.clearHandlers();
URI uri = r.getURI();
FragmentDescriptor f = getFragmentFromJar(r, frags);
//if a jar has no web-fragment.xml we scan it (because it is not exluded by the ordering)
//if its from a fragment jar that is metadata complete, we should skip scanning for @webservlet etc
// but yet we still need to do the scanning for the classes on behalf of the servletcontainerinitializers
//if a jar has no web-fragment.xml we scan it (because it is not excluded by the ordering)
//or if it has a fragment we scan it if it is not metadata complete
if (f == null || !isMetaDataComplete(f))
if (f == null || !isMetaDataComplete(f) || _classInheritanceHandler != null || !_containerInitializerAnnotationHandlers.isEmpty())
{
//register the classinheritance handler if there is one
parser.registerHandler(_classInheritanceHandler);
//register the handlers for the @HandlesTypes values that are themselves annotations if there are any
parser.registerHandlers(_containerInitializerAnnotationHandlers);
//only register the discoverable annotation handlers if this fragment is not metadata complete, or has no fragment descriptor
if (f == null || !isMetaDataComplete(f))
{
for (DiscoverableAnnotationHandler h:_discoverableAnnotationHandlers)
{
if (h instanceof AbstractDiscoverableAnnotationHandler)
((AbstractDiscoverableAnnotationHandler)h).setResource(r);
}
parser.registerHandlers(_discoverableAnnotationHandlers);
}
parser.parse(uri,
new ClassNameResolver()
{
@ -360,14 +426,18 @@ public class AnnotationConfiguration extends AbstractConfiguration
return false;
return true;
}
});
List<DiscoveredAnnotation> annotations = new ArrayList<DiscoveredAnnotation>();
gatherAnnotations(annotations, parser.getAnnotationHandlers());
context.getMetaData().addDiscoveredAnnotations(r, annotations);
});
}
}
}
/**
* Scan classes in WEB-INF/classes
*
* @param context
* @param parser
* @throws Exception
*/
public void parseWebInfClasses (final WebAppContext context, final AnnotationParser parser)
throws Exception
{
@ -377,7 +447,16 @@ public class AnnotationConfiguration extends AbstractConfiguration
Resource classesDir = context.getWebInf().addPath("classes/");
if (classesDir.exists())
{
clearAnnotationList(parser.getAnnotationHandlers());
parser.clearHandlers();
for (DiscoverableAnnotationHandler h:_discoverableAnnotationHandlers)
{
if (h instanceof AbstractDiscoverableAnnotationHandler)
((AbstractDiscoverableAnnotationHandler)h).setResource(null); //
}
parser.registerHandlers(_discoverableAnnotationHandlers);
parser.registerHandler(_classInheritanceHandler);
parser.registerHandlers(_containerInitializerAnnotationHandlers);
parser.parse(classesDir,
new ClassNameResolver()
{
@ -396,17 +475,20 @@ public class AnnotationConfiguration extends AbstractConfiguration
return true;
}
});
//TODO - where to set the annotations discovered from WEB-INF/classes?
List<DiscoveredAnnotation> annotations = new ArrayList<DiscoveredAnnotation>();
gatherAnnotations(annotations, parser.getAnnotationHandlers());
context.getMetaData().addDiscoveredAnnotations (annotations);
}
}
}
/**
* Get the web-fragment.xml from a jar
*
* @param jar
* @param frags
* @return
* @throws Exception
*/
public FragmentDescriptor getFragmentFromJar (Resource jar, List<FragmentDescriptor> frags)
throws Exception
{
@ -428,25 +510,4 @@ public class AnnotationConfiguration extends AbstractConfiguration
{
return (d!=null && d.getMetaDataComplete() == MetaDataComplete.True);
}
protected void clearAnnotationList (List<DiscoverableAnnotationHandler> handlers)
{
if (handlers == null)
return;
for (DiscoverableAnnotationHandler h:handlers)
{
if (h instanceof AbstractDiscoverableAnnotationHandler)
((AbstractDiscoverableAnnotationHandler)h).resetList();
}
}
protected void gatherAnnotations (List<DiscoveredAnnotation> annotations, List<DiscoverableAnnotationHandler> handlers)
{
for (DiscoverableAnnotationHandler h:handlers)
{
if (h instanceof AbstractDiscoverableAnnotationHandler)
annotations.addAll(((AbstractDiscoverableAnnotationHandler)h).getAnnotationList());
}
}
}

View File

@ -29,6 +29,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.jar.JarEntry;
@ -53,11 +54,8 @@ public class AnnotationParser
{
private static final Logger LOG = Log.getLogger(AnnotationParser.class);
protected List<String> _parsedClassNames = new ArrayList<String>();
protected Map<String, List<DiscoverableAnnotationHandler>> _annotationHandlers = new HashMap<String, List<DiscoverableAnnotationHandler>>();
protected List<ClassHandler> _classHandlers = new ArrayList<ClassHandler>();
protected List<MethodHandler> _methodHandlers = new ArrayList<MethodHandler>();
protected List<FieldHandler> _fieldHandlers = new ArrayList<FieldHandler>();
protected List<String> _parsedClassNames = new ArrayList<String>();
protected List<Handler> _handlers = new ArrayList<Handler>();
public static String normalize (String name)
{
@ -168,37 +166,122 @@ public class AnnotationParser
public interface DiscoverableAnnotationHandler
/**
* Handler
*
* Signature for all handlers that respond to parsing class files.
*/
public interface Handler
{
}
/**
* DiscoverableAnnotationHandler
*
* Processes an annotation when it is discovered on a class.
*/
public interface DiscoverableAnnotationHandler extends Handler
{
/**
* Process an annotation that was discovered on a class
* @param className
* @param version
* @param access
* @param signature
* @param superName
* @param interfaces
* @param annotation
* @param values
*/
public void handleClass (String className, int version, int access,
String signature, String superName, String[] interfaces,
String annotation, List<Value>values);
/**
* Process an annotation that was discovered on a method
* @param className
* @param methodName
* @param access
* @param desc
* @param signature
* @param exceptions
* @param annotation
* @param values
*/
public void handleMethod (String className, String methodName, int access,
String desc, String signature,String[] exceptions,
String annotation, List<Value>values);
/**
* Process an annotation that was discovered on a field
* @param className
* @param fieldName
* @param access
* @param fieldType
* @param signature
* @param value
* @param annotation
* @param values
*/
public void handleField (String className, String fieldName, int access,
String fieldType, String signature, Object value,
String annotation, List<Value>values);
/**
* Get the name of the annotation processed by this handler. Can be null
*
* @return
*/
public String getAnnotationName();
}
public interface ClassHandler
/**
* ClassHandler
*
* Responds to finding a Class
*/
public interface ClassHandler extends Handler
{
public void handle (String className, int version, int access, String signature, String superName, String[] interfaces);
}
public interface MethodHandler
/**
* MethodHandler
*
* Responds to finding a Method
*/
public interface MethodHandler extends Handler
{
public void handle (String className, String methodName, int access, String desc, String signature,String[] exceptions);
}
public interface FieldHandler
/**
* FieldHandler
*
* Responds to finding a Field
*/
public interface FieldHandler extends Handler
{
public void handle (String className, String fieldName, int access, String fieldType, String signature, Object value);
}
/**
* MyAnnotationVisitor
*
* ASM Visitor for Annotations
*/
public class MyAnnotationVisitor implements AnnotationVisitor
{
List<Value> _annotationValues;
@ -307,10 +390,13 @@ public class AnnotationParser
for (String s : interfaces)
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);
}
}
}
@ -323,12 +409,13 @@ public class AnnotationParser
super.visitEnd();
//call all AnnotationHandlers with classname, annotation name + values
List<DiscoverableAnnotationHandler> handlers = AnnotationParser.this._annotationHandlers.get(_annotationName);
if (handlers != null)
for (Handler h : AnnotationParser.this._handlers)
{
for (DiscoverableAnnotationHandler h:handlers)
if (h instanceof DiscoverableAnnotationHandler)
{
h.handleClass(_className, _version, _access, _signature, _superName, _interfaces, _annotationName, _annotationValues);
DiscoverableAnnotationHandler dah = (DiscoverableAnnotationHandler)h;
if (_annotationName.equalsIgnoreCase(dah.getAnnotationName()))
dah.handleClass(_className, _version, _access, _signature, _superName, _interfaces, _annotationName, _annotationValues);
}
}
}
@ -354,12 +441,13 @@ public class AnnotationParser
{
super.visitEnd();
//call all AnnotationHandlers with classname, method, annotation name + values
List<DiscoverableAnnotationHandler> handlers = AnnotationParser.this._annotationHandlers.get(_annotationName);
if (handlers != null)
for (Handler h : AnnotationParser.this._handlers)
{
for (DiscoverableAnnotationHandler h:handlers)
if (h instanceof DiscoverableAnnotationHandler)
{
h.handleMethod(_className, name, access, methodDesc, signature, exceptions, _annotationName, _annotationValues);
DiscoverableAnnotationHandler dah = (DiscoverableAnnotationHandler)h;
if (_annotationName.equalsIgnoreCase(dah.getAnnotationName()))
dah.handleMethod(_className, name, access, methodDesc, signature, exceptions, _annotationName, _annotationValues);
}
}
}
@ -386,12 +474,13 @@ public class AnnotationParser
public void visitEnd()
{
super.visitEnd();
List<DiscoverableAnnotationHandler> handlers = AnnotationParser.this._annotationHandlers.get(_annotationName);
if (handlers != null)
for (Handler h : AnnotationParser.this._handlers)
{
for (DiscoverableAnnotationHandler h:handlers)
if (h instanceof DiscoverableAnnotationHandler)
{
h.handleField(_className, fieldName, access, fieldType, signature, value, _annotationName, _annotationValues);
DiscoverableAnnotationHandler dah = (DiscoverableAnnotationHandler)h;
if (_annotationName.equalsIgnoreCase(dah.getAnnotationName()))
dah.handleField(_className, fieldName, access, fieldType, signature, value, _annotationName, _annotationValues);
}
}
}
@ -407,46 +496,130 @@ public class AnnotationParser
* Register a handler that will be called back when the named annotation is
* encountered on a class.
*
* @deprecated see registerHandler(Handler)
* @param annotationName
* @param handler
*/
public void registerAnnotationHandler (String annotationName, DiscoverableAnnotationHandler handler)
{
List<DiscoverableAnnotationHandler> handlers = _annotationHandlers.get(annotationName);
if (handlers == null)
{
handlers = new ArrayList<DiscoverableAnnotationHandler>();
_annotationHandlers.put(annotationName, handlers);
}
handlers.add(handler);
_handlers.add(handler);
}
/**
* @deprecated
* @param annotationName
* @return
*/
public List<DiscoverableAnnotationHandler> getAnnotationHandlers(String annotationName)
{
List<DiscoverableAnnotationHandler> handlers = _annotationHandlers.get(annotationName);
if (handlers == null)
return Collections.emptyList();
return new ArrayList<DiscoverableAnnotationHandler>();
List<DiscoverableAnnotationHandler> handlers = new ArrayList<DiscoverableAnnotationHandler>();
for (Handler h:_handlers)
{
if (h instanceof DiscoverableAnnotationHandler)
{
DiscoverableAnnotationHandler dah = (DiscoverableAnnotationHandler)h;
if (annotationName.equals(dah.getAnnotationName()))
handlers.add(dah);
}
}
return handlers;
}
/**
* @deprecated
* @return
*/
public List<DiscoverableAnnotationHandler> getAnnotationHandlers()
{
List<DiscoverableAnnotationHandler> allHandlers = new ArrayList<DiscoverableAnnotationHandler>();
for (List<DiscoverableAnnotationHandler> list:_annotationHandlers.values())
allHandlers.addAll(list);
return allHandlers;
List<DiscoverableAnnotationHandler> allAnnotationHandlers = new ArrayList<DiscoverableAnnotationHandler>();
for (Handler h:_handlers)
{
if (h instanceof DiscoverableAnnotationHandler)
allAnnotationHandlers.add((DiscoverableAnnotationHandler)h);
}
return allAnnotationHandlers;
}
/**
* @deprecated see registerHandler(Handler)
* @param handler
*/
public void registerClassHandler (ClassHandler handler)
{
_classHandlers.add(handler);
_handlers.add(handler);
}
/**
* Add a particular handler
*
* @param h
*/
public void registerHandler(Handler h)
{
if (h == null)
return;
_handlers.add(h);
}
/**
* Add a list of handlers
*
* @param handlers
*/
public void registerHandlers(List<? extends Handler> handlers)
{
if (handlers == null)
return;
_handlers.addAll(handlers);
}
/**
* Remove a particular handler
*
* @param h
* @return
*/
public boolean deregisterHandler(Handler h)
{
return _handlers.remove(h);
}
/**
* Remove all registered handlers
*/
public void clearHandlers()
{
_handlers.clear();
}
/**
* True if the class has already been processed, false otherwise
* @param className
* @return
*/
public boolean isParsed (String className)
{
return _parsedClassNames.contains(className);
}
/**
* Parse a given class
*
* @param className
* @param resolver
* @throws Exception
*/
public void parse (String className, ClassNameResolver resolver)
throws Exception
{
@ -468,6 +641,16 @@ public class AnnotationParser
}
}
/**
* Parse the given class, optionally walking its inheritance hierarchy
*
* @param clazz
* @param resolver
* @param visitSuperClasses
* @throws Exception
*/
public void parse (Class clazz, ClassNameResolver resolver, boolean visitSuperClasses)
throws Exception
{
@ -494,6 +677,15 @@ public class AnnotationParser
}
}
/**
* Parse the given classes
*
* @param classNames
* @param resolver
* @throws Exception
*/
public void parse (String[] classNames, ClassNameResolver resolver)
throws Exception
{
@ -503,6 +695,14 @@ public class AnnotationParser
parse(Arrays.asList(classNames), resolver);
}
/**
* Parse the given classes
*
* @param classNames
* @param resolver
* @throws Exception
*/
public void parse (List<String> classNames, ClassNameResolver resolver)
throws Exception
{
@ -521,6 +721,14 @@ public class AnnotationParser
}
}
/**
* Parse all classes in a directory
*
* @param dir
* @param resolver
* @throws Exception
*/
public void parse (Resource dir, ClassNameResolver resolver)
throws Exception
{
@ -556,8 +764,9 @@ public class AnnotationParser
/**
* Find annotations on classes in the supplied classloader.
* Parse classes in the supplied classloader.
* Only class files in jar files will be scanned.
*
* @param loader
* @param visitParents
* @param nullInclusive
@ -580,7 +789,7 @@ public class AnnotationParser
try
{
String name = entry.getName();
if (name.toLowerCase().endsWith(".class"))
if (name.toLowerCase(Locale.ENGLISH).endsWith(".class"))
{
String shortName = name.replace('/', '.').substring(0,name.length()-6);
if ((resolver == null)
@ -606,7 +815,8 @@ public class AnnotationParser
/**
* Find annotations in classes in the supplied url of jar files.
* Parse classes in the supplied url of jar files.
*
* @param uris
* @param resolver
* @throws Exception
@ -624,7 +834,7 @@ public class AnnotationParser
try
{
String name = entry.getName();
if (name.toLowerCase().endsWith(".class"))
if (name.toLowerCase(Locale.ENGLISH).endsWith(".class"))
{
String shortName = name.replace('/', '.').substring(0,name.length()-6);
@ -648,6 +858,12 @@ public class AnnotationParser
scanner.scan(null, uris, true);
}
/**
* Parse a particular resource
* @param uri
* @param resolver
* @throws Exception
*/
public void parse (URI uri, final ClassNameResolver resolver)
throws Exception
{
@ -657,6 +873,14 @@ public class AnnotationParser
parse(uris, resolver);
}
/**
* Use ASM on a class
*
* @param is
* @throws IOException
*/
protected void scanClass (InputStream is)
throws IOException
{

View File

@ -35,10 +35,16 @@ public class ClassInheritanceHandler implements ClassHandler
private static final Logger LOG = Log.getLogger(ClassInheritanceHandler.class);
MultiMap _inheritanceMap = new MultiMap();
MultiMap _inheritanceMap;
public ClassInheritanceHandler()
{
_inheritanceMap = new MultiMap();
}
public ClassInheritanceHandler(MultiMap map)
{
_inheritanceMap = map;
}
public void handle(String className, int version, int access, String signature, String superName, String[] interfaces)

View File

@ -69,4 +69,15 @@ public class ContainerInitializerAnnotationHandler implements DiscoverableAnnota
_initializer.addAnnotatedTypeName(className);
}
@Override
public String getAnnotationName()
{
return _annotation.getName();
}
public ContainerInitializer getContainerInitializer()
{
return _initializer;
}
}

View File

@ -21,6 +21,7 @@ package org.eclipse.jetty.annotations;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Locale;
import javax.annotation.Resource;
import javax.naming.InitialContext;
@ -262,7 +263,7 @@ public class ResourceAnnotationHandler extends AbstractIntrospectableAnnotationH
//default name is the javabean property name
String name = method.getName().substring(3);
name = name.substring(0,1).toLowerCase()+name.substring(1);
name = name.substring(0,1).toLowerCase(Locale.ENGLISH)+name.substring(1);
name = clazz.getCanonicalName()+"/"+name;
name = (resource.name()!=null && !resource.name().trim().equals("")? resource.name(): name);

View File

@ -28,6 +28,8 @@ import javax.servlet.ServletContextListener;
import org.eclipse.jetty.annotations.AnnotationConfiguration;
import org.eclipse.jetty.plus.annotation.ContainerInitializer;
import org.eclipse.jetty.util.MultiMap;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.webapp.WebAppContext;
/**
@ -37,7 +39,8 @@ import org.eclipse.jetty.webapp.WebAppContext;
*/
public class ServletContainerInitializerListener implements ServletContextListener
{
WebAppContext _context = null;
private static final Logger LOG = Log.getLogger(ServletContainerInitializerListener.class);
protected WebAppContext _context = null;
public void setWebAppContext (WebAppContext context)
@ -105,16 +108,11 @@ public class ServletContainerInitializerListener implements ServletContextListen
}
catch (Exception e)
{
//OK, how do I throw an exception such that it really stops the startup sequence?
e.printStackTrace();
LOG.warn(e);
throw new RuntimeException(e);
}
}
//Email from Jan Luehe 18 August: after all ServletContainerInitializers have been
//called, need to check to see if there are any ServletRegistrations remaining
//that are "preliminary" and fail the deployment if so. Implemented in ServletHolder.doStart().
}
}
}
@ -138,8 +136,7 @@ public class ServletContainerInitializerListener implements ServletContextListen
*/
public void contextDestroyed(ServletContextEvent sce)
{
// TODO Auto-generated method stub
}
}

View File

@ -21,6 +21,7 @@ package org.eclipse.jetty.annotations;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.ServletSecurityElement;
import javax.servlet.annotation.HttpConstraint;
import javax.servlet.annotation.HttpMethodConstraint;
import javax.servlet.annotation.ServletSecurity;
@ -30,12 +31,13 @@ import javax.servlet.annotation.ServletSecurity.TransportGuarantee;
import org.eclipse.jetty.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler;
import org.eclipse.jetty.security.ConstraintAware;
import org.eclipse.jetty.security.ConstraintMapping;
import org.eclipse.jetty.security.ConstraintSecurityHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.servlet.ServletMapping;
import org.eclipse.jetty.util.LazyList;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.security.Constraint;
import org.eclipse.jetty.webapp.Origin;
import org.eclipse.jetty.webapp.WebAppContext;
/**
@ -82,7 +84,7 @@ public class ServletSecurityAnnotationHandler extends AbstractIntrospectableAnno
if (servletSecurity == null)
return;
//If there are already constraints defined (ie from web.xml or programmatically(?)) that match any
//If there are already constraints defined (ie from web.xml) that match any
//of the url patterns defined for this servlet, then skip the security annotation.
List<ServletMapping> servletMappings = getServletMappings(clazz.getCanonicalName());
@ -97,19 +99,15 @@ public class ServletSecurityAnnotationHandler extends AbstractIntrospectableAnno
//Make a fresh list
constraintMappings = new ArrayList<ConstraintMapping>();
//Get the values that form the constraints that will apply unless there are HttpMethodConstraints to augment them
HttpConstraint defaults = servletSecurity.value();
//Make a Constraint for the <auth-constraint> and <user-data-constraint> specified by the HttpConstraint
Constraint defaultConstraint = makeConstraint (clazz,
defaults.rolesAllowed(),
defaults.value(),
defaults.transportGuarantee());
constraintMappings.addAll(makeMethodMappings(clazz,
defaultConstraint,
servletMappings,
servletSecurity.httpMethodConstraints()));
ServletSecurityElement securityElement = new ServletSecurityElement(servletSecurity);
for (ServletMapping sm : servletMappings)
{
for (String url : sm.getPathSpecs())
{
_context.getMetaData().setOrigin("constraint.url."+url, Origin.Annotation);
constraintMappings.addAll(ConstraintSecurityHandler.createConstraintsWithMappingsForPath(clazz.getName(), url, securityElement));
}
}
//set up the security constraints produced by the annotation
ConstraintAware securityHandler = (ConstraintAware)_context.getSecurityHandler();
@ -131,111 +129,10 @@ public class ServletSecurityAnnotationHandler extends AbstractIntrospectableAnno
*/
protected Constraint makeConstraint (Class servlet, String[] rolesAllowed, EmptyRoleSemantic permitOrDeny, TransportGuarantee transport)
{
Constraint constraint = new Constraint();
if (rolesAllowed == null || rolesAllowed.length==0)
{
if (permitOrDeny.equals(EmptyRoleSemantic.DENY))
{
//Equivalent to <auth-constraint> with no roles
constraint.setName(servlet.getName()+"-Deny");
constraint.setAuthenticate(true);
}
else
{
//Equivalent to no <auth-constraint>
constraint.setAuthenticate(false);
constraint.setName(servlet.getName()+"-Permit");
}
}
else
{
//Equivalent to <auth-constraint> with list of <security-role-name>s
constraint.setAuthenticate(true);
constraint.setRoles(rolesAllowed);
constraint.setName(servlet.getName()+"-RolesAllowed");
}
//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;
return ConstraintSecurityHandler.createConstraint(servlet.getName(), rolesAllowed, permitOrDeny, transport);
}
/**
* 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;
}
/**
* Get the ServletMappings for the servlet's class.
@ -284,6 +181,7 @@ public class ServletSecurityAnnotationHandler extends AbstractIntrospectableAnno
{
for (int j=0; j < pathSpecs.length; j++)
{
//TODO decide if we need to check the origin
if (pathSpecs[j].equals(constraintMappings.get(i).getPathSpec()))
{
exists = true;

View File

@ -31,6 +31,7 @@ import org.eclipse.jetty.servlet.FilterMapping;
import org.eclipse.jetty.servlet.Holder;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.webapp.DiscoveredAnnotation;
import org.eclipse.jetty.webapp.MetaData;
import org.eclipse.jetty.webapp.WebAppContext;
@ -53,6 +54,11 @@ public class WebFilterAnnotation extends DiscoveredAnnotation
{
super(context, className);
}
public WebFilterAnnotation(WebAppContext context, String className, Resource resource)
{
super(context, className, resource);
}
/**
* @see org.eclipse.jetty.annotations.ClassAnnotation#apply()

View File

@ -40,11 +40,16 @@ public class WebFilterAnnotationHandler extends AbstractDiscoverableAnnotationHa
{
super(context);
}
public WebFilterAnnotationHandler (WebAppContext context, List<DiscoveredAnnotation> list)
{
super(context, list);
}
public void handleClass(String className, int version, int access, String signature, String superName, String[] interfaces, String annotation,
List<Value> values)
{
WebFilterAnnotation wfAnnotation = new WebFilterAnnotation(_context, className);
WebFilterAnnotation wfAnnotation = new WebFilterAnnotation(_context, className, _resource);
addAnnotation(wfAnnotation);
}
@ -60,4 +65,10 @@ public class WebFilterAnnotationHandler extends AbstractDiscoverableAnnotationHa
LOG.warn ("@WebFilter not applicable for methods: "+className+"."+methodName+" "+signature);
}
@Override
public String getAnnotationName()
{
return "javax.servlet.annotation.WebFilter";
}
}

View File

@ -27,6 +27,7 @@ import javax.servlet.http.HttpSessionListener;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.webapp.DiscoveredAnnotation;
import org.eclipse.jetty.webapp.MetaData;
import org.eclipse.jetty.webapp.WebAppContext;
@ -49,6 +50,11 @@ public class WebListenerAnnotation extends DiscoveredAnnotation
{
super(context, className);
}
public WebListenerAnnotation(WebAppContext context, String className, Resource resource)
{
super(context, className, resource);
}
/**
* @see org.eclipse.jetty.annotations.ClassAnnotation#apply()

View File

@ -23,6 +23,7 @@ import java.util.List;
import org.eclipse.jetty.annotations.AnnotationParser.Value;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.webapp.DiscoveredAnnotation;
import org.eclipse.jetty.webapp.WebAppContext;
public class WebListenerAnnotationHandler extends AbstractDiscoverableAnnotationHandler
@ -34,13 +35,18 @@ public class WebListenerAnnotationHandler extends AbstractDiscoverableAnnotation
super(context);
}
public WebListenerAnnotationHandler (WebAppContext context, List<DiscoveredAnnotation> list)
{
super(context, list);
}
/**
* @see org.eclipse.jetty.annotations.AnnotationParser.DiscoverableAnnotationHandler#handleClass(java.lang.String, int, int, java.lang.String, java.lang.String, java.lang.String[], java.lang.String, java.util.List)
*/
public void handleClass(String className, int version, int access, String signature, String superName, String[] interfaces, String annotation,
List<Value> values)
{
WebListenerAnnotation wlAnnotation = new WebListenerAnnotation(_context, className);
WebListenerAnnotation wlAnnotation = new WebListenerAnnotation(_context, className, _resource);
addAnnotation(wlAnnotation);
}
@ -56,4 +62,10 @@ public class WebListenerAnnotationHandler extends AbstractDiscoverableAnnotation
LOG.warn ("@WebListener is not applicable to methods: "+className+"."+methodName+" "+signature);
}
@Override
public String getAnnotationName()
{
return "javax.servlet.annotation.WebListener";
}
}

View File

@ -30,6 +30,7 @@ import org.eclipse.jetty.servlet.ServletMapping;
import org.eclipse.jetty.util.LazyList;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.webapp.DiscoveredAnnotation;
import org.eclipse.jetty.webapp.MetaData;
import org.eclipse.jetty.webapp.WebAppContext;
@ -48,7 +49,13 @@ public class WebServletAnnotation extends DiscoveredAnnotation
{
super(context, className);
}
public WebServletAnnotation (WebAppContext context, String className, Resource resource)
{
super(context, className, resource);
}
/**
* @see org.eclipse.jetty.annotations.ClassAnnotation#apply()
*/

View File

@ -42,6 +42,11 @@ public class WebServletAnnotationHandler extends AbstractDiscoverableAnnotationH
super(context);
}
public WebServletAnnotationHandler (WebAppContext context, List<DiscoveredAnnotation> list)
{
super(context, list);
}
/**
* Handle discovering a WebServlet annotation.
@ -55,7 +60,7 @@ public class WebServletAnnotationHandler extends AbstractDiscoverableAnnotationH
if (!"javax.servlet.annotation.WebServlet".equals(annotationName))
return;
WebServletAnnotation annotation = new WebServletAnnotation (_context, className);
WebServletAnnotation annotation = new WebServletAnnotation (_context, className, _resource);
addAnnotation(annotation);
}
@ -69,5 +74,12 @@ public class WebServletAnnotationHandler extends AbstractDiscoverableAnnotationH
List<Value> values)
{
LOG.warn ("@WebServlet annotation not supported for methods");
}
@Override
public String getAnnotationName()
{
return "javax.servlet.annotation.WebServlet";
}
}

View File

@ -66,6 +66,12 @@ public class TestAnnotationInheritance
{
annotatedMethods.add(className+"."+methodName);
}
@Override
public String getAnnotationName()
{
return "org.eclipse.jetty.annotations.Sample";
}
}
@After
@ -85,7 +91,7 @@ public class TestAnnotationInheritance
SampleHandler handler = new SampleHandler();
AnnotationParser parser = new AnnotationParser();
parser.registerAnnotationHandler("org.eclipse.jetty.annotations.Sample", handler);
parser.registerHandler(handler);
parser.parse(classNames, new ClassNameResolver ()
{
public boolean isExcluded(String name)

View File

@ -41,6 +41,9 @@ public class TestAnnotationParser
{
private List<String> methods = Arrays.asList("a", "b", "c", "d", "l");
public void handleClass(String className, int version, int access, String signature, String superName, String[] interfaces, String annotation,
List<Value> values)
{
@ -81,9 +84,15 @@ public class TestAnnotationParser
assertTrue(methods.contains(methodName));
assertEquals("org.eclipse.jetty.annotations.Sample", annotation);
}
@Override
public String getAnnotationName()
{
return "org.eclipse.jetty.annotations.Sample";
}
}
parser.registerAnnotationHandler("org.eclipse.jetty.annotations.Sample", new SampleAnnotationHandler());
parser.registerHandler(new SampleAnnotationHandler());
long start = System.currentTimeMillis();
parser.parse(classNames, new ClassNameResolver ()
@ -140,9 +149,17 @@ public class TestAnnotationParser
System.err.println(anv.toString());
}
}
@Override
public String getAnnotationName()
{
return "org.eclipse.jetty.annotations.Multi";
}
}
parser.registerAnnotationHandler("org.eclipse.jetty.annotations.Multi", new MultiAnnotationHandler());
parser.registerHandler(new MultiAnnotationHandler());
parser.parse(classNames, null);
}
}

View File

@ -21,6 +21,8 @@ package org.eclipse.jetty.client;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;
import java.util.Locale;
import org.eclipse.jetty.client.helperClasses.ServerAndClientCreator;
import org.eclipse.jetty.client.helperClasses.SslServerAndClientCreator;
import org.eclipse.jetty.server.Connector;
@ -51,7 +53,7 @@ public class SslHttpExchangeTest extends HttpExchangeTest
{
// Use Junit 4.x to flag test as ignored if encountering IBM JVM
// Will show up in various junit reports as an ignored test as well.
Assume.assumeThat(System.getProperty("java.vendor").toLowerCase(),not(containsString("ibm")));
Assume.assumeThat(System.getProperty("java.vendor").toLowerCase(Locale.ENGLISH),not(containsString("ibm")));
}
/* ------------------------------------------------------------ */

View File

@ -28,6 +28,7 @@ import java.io.OutputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
@ -126,7 +127,7 @@ public class SslSecurityListenerTest
public void testSslGet() throws Exception
{
// TODO Resolve problems on IBM JVM https://bugs.eclipse.org/bugs/show_bug.cgi?id=304532
if (System.getProperty("java.vendor").toLowerCase().indexOf("ibm")>=0)
if (System.getProperty("java.vendor").toLowerCase(Locale.ENGLISH).indexOf("ibm")>=0)
{
LOG.warn("Skipped SSL testSslGet on IBM JVM");
return;

View File

@ -214,6 +214,7 @@ public class WebSocketUpgradeTest
return _websocket;
}
};
_handler.getWebSocketFactory().setMinVersion(-1);
_server.setHandler(_handler);
_server.start();

View File

@ -19,6 +19,7 @@
package org.eclipse.jetty.deploy;
import java.util.ArrayList;
import java.util.Locale;
import org.eclipse.jetty.deploy.providers.ScanningAppProvider;
import org.eclipse.jetty.server.Handler;
@ -223,7 +224,7 @@ public class WebAppDeployer extends AbstractLifeCycle
Resource app=r.addPath(r.encode(context));
if (context.toLowerCase().endsWith(".war")||context.toLowerCase().endsWith(".jar"))
if (context.toLowerCase(Locale.ENGLISH).endsWith(".war")||context.toLowerCase(Locale.ENGLISH).endsWith(".jar"))
{
context=context.substring(0,context.length()-4);
Resource unpacked=r.addPath(context);

View File

@ -20,6 +20,7 @@ package org.eclipse.jetty.deploy.providers;
import java.io.File;
import java.io.FilenameFilter;
import java.util.Locale;
import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.deploy.ConfigurationManager;
@ -45,7 +46,7 @@ public class ContextProvider extends ScanningAppProvider
{
if (!dir.exists())
return false;
String lowername = name.toLowerCase();
String lowername = name.toLowerCase(Locale.ENGLISH);
if (lowername.startsWith("."))
return false;

View File

@ -22,6 +22,7 @@ import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.Locale;
import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.deploy.util.FileID;
@ -59,7 +60,7 @@ public class WebAppProvider extends ScanningAppProvider
{
return false;
}
String lowername = name.toLowerCase();
String lowername = name.toLowerCase(Locale.ENGLISH);
File file = new File(dir,name);
// is it not a directory and not a war ?
@ -279,9 +280,9 @@ public class WebAppProvider extends ScanningAppProvider
{
context = URIUtil.SLASH;
}
else if (context.toLowerCase().startsWith("root-"))
else if (context.toLowerCase(Locale.ENGLISH).startsWith("root-"))
{
int dash=context.toLowerCase().indexOf('-');
int dash=context.toLowerCase(Locale.ENGLISH).indexOf('-');
String virtual = context.substring(dash+1);
wah.setVirtualHosts(new String[]{virtual});
context = URIUtil.SLASH;

View File

@ -19,6 +19,7 @@
package org.eclipse.jetty.deploy.util;
import java.io.File;
import java.util.Locale;
/**
* Simple, yet surprisingly common utility methods for identifying various file types commonly seen and worked with in a
@ -38,7 +39,7 @@ public class FileID
{
if (path.isFile())
{
String name = path.getName().toLowerCase();
String name = path.getName().toLowerCase(Locale.ENGLISH);
return (name.endsWith(".war") || name.endsWith(".jar"));
}
@ -62,7 +63,7 @@ public class FileID
return false;
}
String name = path.getName().toLowerCase();
String name = path.getName().toLowerCase(Locale.ENGLISH);
return (name.endsWith(".war") || name.endsWith(".jar"));
}
@ -73,7 +74,7 @@ public class FileID
return false;
}
String name = path.getName().toLowerCase();
String name = path.getName().toLowerCase(Locale.ENGLISH);
return name.endsWith(".xml");
}
}

View File

@ -976,6 +976,9 @@ public class HttpFields
QuotedStringTokenizer.quoteIfNeeded(buf, name, delim);
buf.append('=');
String start=buf.toString();
boolean hasDomain = false;
boolean hasPath = false;
if (value != null && value.length() > 0)
QuotedStringTokenizer.quoteIfNeeded(buf, value, delim);
@ -987,6 +990,7 @@ public class HttpFields
if (path != null && path.length() > 0)
{
hasPath = true;
buf.append(";Path=");
if (path.trim().startsWith("\""))
buf.append(path);
@ -995,8 +999,9 @@ public class HttpFields
}
if (domain != null && domain.length() > 0)
{
hasDomain = true;
buf.append(";Domain=");
QuotedStringTokenizer.quoteIfNeeded(buf,domain.toLowerCase(),delim);
QuotedStringTokenizer.quoteIfNeeded(buf,domain.toLowerCase(Locale.ENGLISH),delim);
}
if (maxAge >= 0)
@ -1027,14 +1032,20 @@ public class HttpFields
Field last=null;
while (field!=null)
{
if (field._value!=null && field._value.toString().startsWith(start))
String val = (field._value == null ? null : field._value.toString());
if (val!=null && val.startsWith(start))
{
_fields.remove(field);
if (last==null)
_names.put(HttpHeaders.SET_COOKIE_BUFFER,field._next);
else
last._next=field._next;
break;
//existing cookie has same name, does it also match domain and path?
if (((!hasDomain && !val.contains("Domain")) || (hasDomain && val.contains("Domain="+domain))) &&
((!hasPath && !val.contains("Path")) || (hasPath && val.contains("Path="+path))))
{
_fields.remove(field);
if (last==null)
_names.put(HttpHeaders.SET_COOKIE_BUFFER,field._next);
else
last._next=field._next;
break;
}
}
last=field;
field=field._next;

View File

@ -94,7 +94,15 @@ public class HttpURI
public HttpURI(String raw)
{
_rawString=raw;
byte[] b = raw.getBytes();
byte[] b;
try
{
b = raw.getBytes(StringUtil.__UTF8);
}
catch (UnsupportedEncodingException e)
{
throw new RuntimeException(e.getMessage());
}
parse(b,0,b.length);
}
@ -700,7 +708,7 @@ public class HttpURI
if (encoding==null || StringUtil.isUTF8(encoding))
UrlEncoded.decodeUtf8To(_raw,_query+1,_fragment-_query-1,parameters);
else
UrlEncoded.decodeTo(toUtf8String(_query+1,_fragment-_query-1),parameters,encoding);
UrlEncoded.decodeTo(StringUtil.toString(_raw,_query+1,_fragment-_query-1,encoding),parameters,encoding);
}
public void clear()

View File

@ -171,6 +171,7 @@ xhtml = application/xhtml+xml
xls = application/vnd.ms-excel
xml = application/xml
xpm = image/x-xpixmap
xsd = application/xml
xsl = application/xml
xslt = application/xslt+xml
xul = application/vnd.mozilla.xul+xml

View File

@ -25,6 +25,7 @@ import static org.junit.Assert.assertTrue;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import org.eclipse.jetty.io.Buffer;
@ -312,8 +313,8 @@ public class HttpFieldsTest
s=enum2set(fields.getFieldNames());
assertEquals(3,s.size());
assertTrue(s.contains("message-id"));
assertEquals("value",fields.getStringField("message-id").toLowerCase());
assertEquals("value",fields.getStringField("Message-ID").toLowerCase());
assertEquals("value",fields.getStringField("message-id").toLowerCase(Locale.ENGLISH));
assertEquals("value",fields.getStringField("Message-ID").toLowerCase(Locale.ENGLISH));
fields.clear();
@ -323,8 +324,8 @@ public class HttpFieldsTest
s=enum2set(fields.getFieldNames());
assertEquals(3,s.size());
assertTrue(s.contains("message-id"));
assertEquals("value",fields.getStringField("Message-ID").toLowerCase());
assertEquals("value",fields.getStringField("message-id").toLowerCase());
assertEquals("value",fields.getStringField("Message-ID").toLowerCase(Locale.ENGLISH));
assertEquals("value",fields.getStringField("message-id").toLowerCase(Locale.ENGLISH));
fields.clear();
@ -334,8 +335,8 @@ public class HttpFieldsTest
s=enum2set(fields.getFieldNames());
assertEquals(3,s.size());
assertTrue(s.contains("message-id"));
assertEquals("value",fields.getStringField("message-id").toLowerCase());
assertEquals("value",fields.getStringField("Message-ID").toLowerCase());
assertEquals("value",fields.getStringField("message-id").toLowerCase(Locale.ENGLISH));
assertEquals("value",fields.getStringField("Message-ID").toLowerCase(Locale.ENGLISH));
fields.clear();
@ -345,8 +346,8 @@ public class HttpFieldsTest
s=enum2set(fields.getFieldNames());
assertEquals(3,s.size());
assertTrue(s.contains("message-id"));
assertEquals("value",fields.getStringField("Message-ID").toLowerCase());
assertEquals("value",fields.getStringField("message-id").toLowerCase());
assertEquals("value",fields.getStringField("Message-ID").toLowerCase(Locale.ENGLISH));
assertEquals("value",fields.getStringField("message-id").toLowerCase(Locale.ENGLISH));
}
@Test
@ -364,7 +365,8 @@ public class HttpFieldsTest
assertEquals("minimal=value",fields.getStringField("Set-Cookie"));
fields.clear();
fields.addSetCookie("everything","wrong","wrong","wrong",0,"to be replaced",true,true,0);
//test cookies with same name, domain and path, only 1 allowed
fields.addSetCookie("everything","wrong","domain","path",0,"to be replaced",true,true,0);
fields.addSetCookie("everything","value","domain","path",0,"comment",true,true,0);
assertEquals("everything=value;Comment=comment;Path=path;Domain=domain;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Secure;HttpOnly",fields.getStringField("Set-Cookie"));
Enumeration<String> e =fields.getValues("Set-Cookie");
@ -372,6 +374,61 @@ public class HttpFieldsTest
assertEquals("everything=value;Comment=comment;Path=path;Domain=domain;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Secure;HttpOnly",e.nextElement());
assertFalse(e.hasMoreElements());
assertEquals("Thu, 01 Jan 1970 00:00:00 GMT",fields.getStringField("Expires"));
assertFalse(e.hasMoreElements());
//test cookies with same name, different domain
fields.clear();
fields.addSetCookie("everything","other","domain1","path",0,"blah",true,true,0);
fields.addSetCookie("everything","value","domain2","path",0,"comment",true,true,0);
e =fields.getValues("Set-Cookie");
assertTrue(e.hasMoreElements());
assertEquals("everything=other;Comment=blah;Path=path;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Secure;HttpOnly",e.nextElement());
assertTrue(e.hasMoreElements());
assertEquals("everything=value;Comment=comment;Path=path;Domain=domain2;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Secure;HttpOnly",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;Comment=blah;Path=path;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Secure;HttpOnly",e.nextElement());
assertTrue(e.hasMoreElements());
assertEquals("everything=value;Comment=comment;Path=path;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Secure;HttpOnly",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;Comment=blah;Path=path1;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Secure;HttpOnly",e.nextElement());
assertTrue(e.hasMoreElements());
assertEquals("everything=value;Comment=comment;Path=path2;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Secure;HttpOnly",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;Comment=blah;Path=path1;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Secure;HttpOnly",e.nextElement());
assertTrue(e.hasMoreElements());
assertEquals("everything=value;Comment=comment;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Secure;HttpOnly",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;Comment=comment;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Secure;HttpOnly",e.nextElement());
assertFalse(e.hasMoreElements());
fields.clear();
fields.addSetCookie("ev erything","va lue","do main","pa th",1,"co mment",true,true,2);
@ -402,7 +459,7 @@ public class HttpFieldsTest
{
Set<String> s=new HashSet<String>();
while(e.hasMoreElements())
s.add(e.nextElement().toLowerCase());
s.add(e.nextElement().toLowerCase(Locale.ENGLISH));
return s;
}

View File

@ -21,6 +21,7 @@ package org.eclipse.jetty.io;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.log.Log;
@ -644,6 +645,23 @@ public abstract class AbstractBuffer implements Buffer
}
}
/* ------------------------------------------------------------ */
public String toString(Charset charset)
{
try
{
byte[] bytes=array();
if (bytes!=null)
return new String(bytes,getIndex(),length(),charset);
return new String(asArray(), 0, length(),charset);
}
catch(Exception e)
{
LOG.warn(e);
return new String(asArray(), 0, length());
}
}
/* ------------------------------------------------------------ */
public String toDebugString()
{

View File

@ -21,6 +21,7 @@ package org.eclipse.jetty.io;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
/**
@ -365,7 +366,10 @@ public interface Buffer extends Cloneable
/* ------------------------------------------------------------ */
String toString(String charset);
/*
/* ------------------------------------------------------------ */
String toString(Charset charset);
/*
* Buffers implementing this interface should be compared with case insensitive equals
*
*/

View File

@ -354,6 +354,6 @@ public class BufferUtil
{
if (buffer instanceof CachedBuffer)
return buffer.toString();
return buffer.toString(StringUtil.__ISO_8859_1);
return buffer.toString(StringUtil.__ISO_8859_1_CHARSET);
}
}

View File

@ -23,6 +23,7 @@ import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.util.Locale;
import org.eclipse.jetty.io.AsyncEndPoint;
import org.eclipse.jetty.io.Buffer;
@ -42,7 +43,7 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo
{
public static final Logger LOG=Log.getLogger("org.eclipse.jetty.io.nio");
private final boolean WORK_AROUND_JVM_BUG_6346658 = System.getProperty("os.name").toLowerCase().contains("win");
private final boolean WORK_AROUND_JVM_BUG_6346658 = System.getProperty("os.name").toLowerCase(Locale.ENGLISH).contains("win");
private final SelectorManager.SelectSet _selectSet;
private final SelectorManager _manager;
private SelectionKey _key;

View File

@ -31,21 +31,30 @@ import javax.security.auth.message.config.ServerAuthConfig;
import javax.security.auth.message.config.ServerAuthContext;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.eclipse.jetty.security.Authenticator;
import org.eclipse.jetty.security.IdentityService;
import org.eclipse.jetty.security.ServerAuthException;
import org.eclipse.jetty.security.UserAuthentication;
import org.eclipse.jetty.security.authentication.DeferredAuthentication;
import org.eclipse.jetty.security.authentication.LoginAuthenticator;
import org.eclipse.jetty.security.authentication.SessionAuthentication;
import org.eclipse.jetty.security.jaspi.modules.BaseAuthModule;
import org.eclipse.jetty.server.Authentication;
import org.eclipse.jetty.server.UserIdentity;
import org.eclipse.jetty.server.Authentication.User;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
/**
* @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 static final Logger LOG = Log.getLogger(JaspiAuthenticator.class.getName());
private final ServerAuthConfig _authConfig;
private final Map _authProperties;
@ -58,7 +67,7 @@ public class JaspiAuthenticator implements Authenticator
private final IdentityService _identityService;
private final DeferredAuthentication _deferred;
public JaspiAuthenticator(ServerAuthConfig authConfig, Map authProperties, ServletCallbackHandler callbackHandler, Subject serviceSubject,
boolean allowLazyAuthentication, IdentityService identityService)
@ -72,11 +81,11 @@ public class JaspiAuthenticator implements Authenticator
this._serviceSubject = serviceSubject;
this._allowLazyAuthentication = allowLazyAuthentication;
this._identityService = identityService;
this._deferred = new DeferredAuthentication(this);
}
public void setConfiguration(AuthConfiguration configuration)
{
super.setConfiguration(configuration);
}
public String getAuthMethod()
@ -93,7 +102,7 @@ public class JaspiAuthenticator implements Authenticator
//if its not mandatory to authenticate, and the authenticator returned UNAUTHENTICATED, we treat it as authentication deferred
if (_allowLazyAuthentication && !info.isAuthMandatory() && a == Authentication.UNAUTHENTICATED)
a =_deferred;
a = new DeferredAuthentication(this);
return a;
}
@ -106,6 +115,28 @@ public class JaspiAuthenticator implements Authenticator
}
/**
* @see org.eclipse.jetty.security.authentication.LoginAuthenticator#login(java.lang.String, java.lang.Object, javax.servlet.ServletRequest)
*/
@Override
public UserIdentity login(String username, Object password, ServletRequest request)
{
UserIdentity user = _loginService.login(username, password);
if (user != null)
{
renewSession((HttpServletRequest)request, null);
HttpSession session = ((HttpServletRequest)request).getSession(true);
if (session != null)
{
SessionAuthentication sessionAuth = new SessionAuthentication(getAuthMethod(), user, password);
session.setAttribute(SessionAuthentication.__J_AUTHENTICATED, sessionAuth);
}
}
return user;
}
public Authentication validateRequest(JaspiMessageInfo messageInfo) throws ServerAuthException
{
try
@ -150,6 +181,12 @@ public class JaspiAuthenticator implements Authenticator
String[] groups = groupPrincipalCallback == null ? null : groupPrincipalCallback.getGroups();
userIdentity = _identityService.newUserIdentity(clientSubject, principal, groups);
}
HttpSession session = ((HttpServletRequest)messageInfo.getRequestMessage()).getSession(false);
Authentication cached = (session == null?null:(SessionAuthentication)session.getAttribute(SessionAuthentication.__J_AUTHENTICATED));
if (cached != null)
return cached;
return new UserAuthentication(getAuthMethod(), userIdentity);
}
if (authStatus == AuthStatus.SEND_SUCCESS)

View File

@ -43,7 +43,10 @@ import org.eclipse.jetty.util.security.Password;
import org.eclipse.jetty.security.CrossContextPsuedoSession;
import org.eclipse.jetty.security.authentication.DeferredAuthentication;
import org.eclipse.jetty.security.authentication.LoginCallbackImpl;
import org.eclipse.jetty.security.authentication.SessionAuthentication;
import org.eclipse.jetty.security.jaspi.callback.CredentialValidationCallback;
import org.eclipse.jetty.server.Authentication;
import org.eclipse.jetty.server.UserIdentity;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.log.Log;
@ -214,21 +217,22 @@ public class FormAuthModule extends BaseAuthModule
// Check if the session is already authenticated.
FormCredential form_cred = (FormCredential) session.getAttribute(__J_AUTHENTICATED);
if (form_cred != null)
SessionAuthentication sessionAuth = (SessionAuthentication)session.getAttribute(SessionAuthentication.__J_AUTHENTICATED);
if (sessionAuth != null)
{
//TODO: ideally we would like the form auth module to be able to invoke the
//loginservice.validate() method to check the previously authed user, but it is not visible
//to FormAuthModule
if (form_cred._subject == null)
if (sessionAuth.getUserIdentity().getSubject() == null)
return AuthStatus.SEND_FAILURE;
Set<Object> credentials = form_cred._subject.getPrivateCredentials();
Set<Object> credentials = sessionAuth.getUserIdentity().getSubject().getPrivateCredentials();
if (credentials == null || credentials.isEmpty())
return AuthStatus.SEND_FAILURE; //if no private credentials, assume it cannot be authenticated
clientSubject.getPrivateCredentials().addAll(credentials);
clientSubject.getPrivateCredentials().add(sessionAuth.getUserIdentity());
//boolean success = tryLogin(messageInfo, clientSubject, response, session, form_cred._jUserName, new Password(new String(form_cred._jPassword)));
return AuthStatus.SUCCESS;
}
else if (ssoSource != null)
@ -300,8 +304,14 @@ public class FormAuthModule extends BaseAuthModule
if (!loginCallbacks.isEmpty())
{
LoginCallbackImpl loginCallback = loginCallbacks.iterator().next();
FormCredential form_cred = new FormCredential(username, pwdChars, loginCallback.getUserPrincipal(), loginCallback.getSubject());
session.setAttribute(__J_AUTHENTICATED, form_cred);
Set<UserIdentity> userIdentities = clientSubject.getPrivateCredentials(UserIdentity.class);
if (!userIdentities.isEmpty())
{
UserIdentity userIdentity = userIdentities.iterator().next();
SessionAuthentication sessionAuth = new SessionAuthentication(Constraint.__FORM_AUTH, userIdentity, password);
session.setAttribute(SessionAuthentication.__J_AUTHENTICATED, sessionAuth);
}
}
// Sign-on to SSO mechanism
@ -320,61 +330,4 @@ public class FormAuthModule extends BaseAuthModule
return pathInContext != null && (pathInContext.equals(_formErrorPath) || pathInContext.equals(_formLoginPath));
}
/* ------------------------------------------------------------ */
/**
* FORM Authentication credential holder.
*/
private static class FormCredential implements Serializable, HttpSessionBindingListener
{
String _jUserName;
char[] _jPassword;
transient Principal _userPrincipal;
transient Subject _subject;
private FormCredential(String _jUserName, char[] _jPassword, Principal _userPrincipal, Subject subject)
{
this._jUserName = _jUserName;
this._jPassword = _jPassword;
this._userPrincipal = _userPrincipal;
this._subject = subject;
}
public void valueBound(HttpSessionBindingEvent event)
{
}
public void valueUnbound(HttpSessionBindingEvent event)
{
if (LOG.isDebugEnabled()) LOG.debug("Logout " + _jUserName);
// TODO jaspi call cleanSubject()
// if (_realm instanceof SSORealm)
// ((SSORealm) _realm).clearSingleSignOn(_jUserName);
//
// if (_realm != null && _userPrincipal != null)
// _realm.logout(_userPrincipal);
}
public int hashCode()
{
return _jUserName.hashCode() + _jPassword.hashCode();
}
public boolean equals(Object o)
{
if (!(o instanceof FormCredential)) return false;
FormCredential fc = (FormCredential) o;
return _jUserName.equals(fc._jUserName) && Arrays.equals(_jPassword, fc._jPassword);
}
public String toString()
{
return "Cred[" + _jUserName + "]";
}
}
}

View File

@ -24,6 +24,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
@ -251,7 +252,7 @@ public class MBeanContainer extends AbstractLifeCycle implements Container.Liste
//no override mbean object name, so make a generic one
if (oname == null)
{
String type = obj.getClass().getName().toLowerCase();
String type = obj.getClass().getName().toLowerCase(Locale.ENGLISH);
int dot = type.lastIndexOf('.');
if (dot >= 0)
type = type.substring(dot + 1);

View File

@ -564,7 +564,7 @@ public class ObjectMBean implements DynamicMBean
}
String uName = name.substring(0, 1).toUpperCase() + name.substring(1);
String uName = name.substring(0, 1).toUpperCase(Locale.ENGLISH) + name.substring(1);
Class oClass = onMBean ? this.getClass() : _managed.getClass();
if (LOG.isDebugEnabled())

View File

@ -99,74 +99,41 @@ public class ContextFactory implements ObjectFactory
return ctx;
}
// Next, see if we are in a webapp context, if we are, use
// the classloader of the webapp to find the right jndi comp context
ClassLoader loader = null;
if (ContextHandler.getCurrentContext() != null)
loader = 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();
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
ctx = (Context)__contextMap.get(loader);
//The map does not contain an entry for this classloader
if (ctx == null)
{
//Check if a parent classloader has created the context
ctx = getParentClassLoaderContext(loader);
//Didn't find a context to match, make one
Reference ref = (Reference)obj;
StringRefAddr parserAddr = (StringRefAddr)ref.get("parser");
String parserClassName = (parserAddr==null?null:(String)parserAddr.getContent());
NameParser parser = (NameParser)(parserClassName==null?null:loader.loadClass(parserClassName).newInstance());
//Didn't find a context to match any of the ancestors
//of the classloader, so make a context
if (ctx == null)
{
Reference ref = (Reference)obj;
StringRefAddr parserAddr = (StringRefAddr)ref.get("parser");
String parserClassName = (parserAddr==null?null:(String)parserAddr.getContent());
NameParser parser = (NameParser)(parserClassName==null?null:loader.loadClass(parserClassName).newInstance());
ctx = new NamingContext (env,
name.get(0),
(NamingContext)nameCtx,
parser);
if(__log.isDebugEnabled())__log.debug("No entry for classloader: "+loader);
__contextMap.put (loader, ctx);
}
ctx = new NamingContext (env,
name.get(0),
(NamingContext)nameCtx,
parser);
if(__log.isDebugEnabled())__log.debug("Made context "+name.get(0)+" for classloader: "+loader);
__contextMap.put (loader, ctx);
}
return ctx;
}
/**
* Keep trying ancestors of the given classloader to find one to which
* the context is bound.
* @param loader
* @return the context from the parent class loader
*/
public Context getParentClassLoaderContext (ClassLoader loader)
{
Context ctx = null;
ClassLoader cl = loader;
for (cl = cl.getParent(); (cl != null) && (ctx == null); cl = cl.getParent())
{
ctx = (Context)__contextMap.get(cl);
}
return ctx;
}
/**

View File

@ -37,6 +37,7 @@ import javax.naming.Reference;
import javax.naming.StringRefAddr;
import javax.naming.spi.ObjectFactory;
import org.eclipse.jetty.jndi.ContextFactory;
import org.eclipse.jetty.jndi.NamingContext;
import org.eclipse.jetty.jndi.NamingUtil;
import org.eclipse.jetty.jndi.local.localContextRoot;
@ -47,7 +48,8 @@ import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
/**
*
*/
@ -74,14 +76,16 @@ public class TestJNDI
@Test
public void testIt() throws Exception
{
//set up some classloaders
Thread currentThread = Thread.currentThread();
ClassLoader currentLoader = currentThread.getContextClassLoader();
ClassLoader childLoader1 = new URLClassLoader(new URL[0], currentLoader);
ClassLoader childLoader2 = new URLClassLoader(new URL[0], currentLoader);
try
{
//set up some classloaders
Thread currentThread = Thread.currentThread();
ClassLoader currentLoader = currentThread.getContextClassLoader();
ClassLoader childLoader1 = new URLClassLoader(new URL[0], currentLoader);
ClassLoader childLoader2 = new URLClassLoader(new URL[0], currentLoader);
//Uncomment to aid with debug
/*
javaRootURLContext.getRoot().addListener(new NamingContext.Listener()
{
@ -119,7 +123,19 @@ public class TestJNDI
InitialContext initCtxA = new InitialContext();
initCtxA.bind ("blah", "123");
assertEquals ("123", initCtxA.lookup("blah"));
initCtxA.destroySubcontext("blah");
try
{
initCtxA.lookup("blah");
fail("context blah was not destroyed");
}
catch (NameNotFoundException e)
{
//expected
}
InitialContext initCtx = new InitialContext();
Context sub0 = (Context)initCtx.lookup("java:");
@ -219,6 +235,7 @@ public class TestJNDI
try
{
initCtx.lookup("java:comp/env/rubbish");
fail("env should not exist for this classloader");
}
catch (NameNotFoundException e)
{
@ -287,18 +304,77 @@ public class TestJNDI
{
//expected failure to modify immutable context
}
System.err.println("java:"+javaRootURLContext.getRoot().dump());
System.err.println("local:"+localContextRoot.getRoot().dump());
//test what happens when you close an initial context that was used
initCtx.close();
}
finally
{
//make some effort to clean up
InitialContext ic = new InitialContext();
Context java = (Context)ic.lookup("java:");
java.destroySubcontext("zero");
java.destroySubcontext("fee");
currentThread.setContextClassLoader(childLoader1);
Context comp = (Context)ic.lookup("java:comp");
comp.destroySubcontext("env");
comp.unbind("crud");
comp.unbind("crud2");
}
}
@Test
public void testParent()
throws Exception
{
//set up some classloaders
Thread currentThread = Thread.currentThread();
ClassLoader parentLoader = currentThread.getContextClassLoader();
ClassLoader childLoader1 = new URLClassLoader(new URL[0], parentLoader);
try
{
//Test creating a comp for the parent loader does not leak to child
InitialContext initCtx = new InitialContext();
Context comp = (Context)initCtx.lookup("java:comp");
assertNotNull(comp);
Context env = (Context)comp.createSubcontext("env");
assertNotNull(env);
env.bind("foo", "aaabbbcccddd");
assertEquals("aaabbbcccddd", (String)initCtx.lookup("java:comp/env/foo"));
//Change to child loader
currentThread.setContextClassLoader(childLoader1);
comp = (Context)initCtx.lookup("java:comp");
Context childEnv = (Context)comp.createSubcontext("env");
assertNotSame(env, childEnv);
childEnv.bind("foo", "eeefffggghhh");
assertEquals("eeefffggghhh", (String)initCtx.lookup("java:comp/env/foo"));
//Change back to parent
currentThread.setContextClassLoader(parentLoader);
assertEquals("aaabbbcccddd", (String)initCtx.lookup("java:comp/env/foo"));
}
finally
{
//make some effort to clean up
InitialContext ic = new InitialContext();
currentThread.setContextClassLoader(parentLoader);
Context comp = (Context)ic.lookup("java:comp");
comp.destroySubcontext("env");
currentThread.setContextClassLoader(childLoader1);
comp = (Context)ic.lookup("java:comp");
comp.destroySubcontext("env");
}
}
}

View File

@ -216,7 +216,7 @@ public class MongoSessionIdManager extends AbstractSessionIdManager
BasicDBObject invalidQuery = new BasicDBObject();
invalidQuery.put(MongoSessionManager.__ACCESSED, new BasicDBObject("$lt",System.currentTimeMillis() - _purgeInvalidAge));
invalidQuery.put(MongoSessionManager.__VALID, __valid_false);
invalidQuery.put(MongoSessionManager.__VALID, false);
DBCursor oldSessions = _sessions.find(invalidQuery, new BasicDBObject(MongoSessionManager.__ID, 1));
@ -234,9 +234,9 @@ public class MongoSessionIdManager extends AbstractSessionIdManager
BasicDBObject validQuery = new BasicDBObject();
validQuery.put(MongoSessionManager.__ACCESSED,new BasicDBObject("$lt",System.currentTimeMillis() - _purgeValidAge));
validQuery.put(MongoSessionManager.__VALID, __valid_false);
validQuery.put(MongoSessionManager.__VALID, true);
oldSessions = _sessions.find(invalidQuery,new BasicDBObject(MongoSessionManager.__ID,1));
oldSessions = _sessions.find(validQuery,new BasicDBObject(MongoSessionManager.__ID,1));
for (DBObject session : oldSessions)
{
@ -410,7 +410,8 @@ public class MongoSessionIdManager extends AbstractSessionIdManager
purge();
}
};
_purgeTimer.schedule(_purgeTask,_purgeDelay);
_purgeTimer.schedule(_purgeTask,0,_purgeDelay);
}
}
}

View File

@ -21,6 +21,8 @@ package org.eclipse.jetty.osgi.annotations;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jetty.annotations.AbstractDiscoverableAnnotationHandler;
import org.eclipse.jetty.annotations.AnnotationParser.DiscoverableAnnotationHandler;
import org.eclipse.jetty.annotations.ClassNameResolver;
import org.eclipse.jetty.osgi.boot.OSGiWebappConstants;
import org.eclipse.jetty.osgi.boot.utils.internal.PackageAdminServiceTracker;
@ -151,20 +153,25 @@ public class AnnotationConfiguration extends org.eclipse.jetty.annotations.Annot
protected void parseBundle(WebAppContext context, AnnotationParser parser,
Bundle webbundle, Bundle bundle) throws Exception
{
Resource bundleRes = parser.getResource(bundle);
parser.clearHandlers();
for (DiscoverableAnnotationHandler h:_discoverableAnnotationHandlers)
{
if (h instanceof AbstractDiscoverableAnnotationHandler)
{
if (webbundle == bundle)
((AbstractDiscoverableAnnotationHandler)h).setResource(null);
else
((AbstractDiscoverableAnnotationHandler)h).setResource(bundleRes);
}
}
parser.registerHandlers(_discoverableAnnotationHandlers);
parser.registerHandler(_classInheritanceHandler);
parser.registerHandlers(_containerInitializerAnnotationHandlers);
parser.parse(bundle,createClassNameResolver(context));
List<DiscoveredAnnotation> annotations = new ArrayList<DiscoveredAnnotation>();
gatherAnnotations(annotations, parser.getAnnotationHandlers());
if (webbundle == bundle)
{
//just like the super with its question about annotations in WEB-INF/classes:
//"TODO - where to set the annotations discovered from WEB-INF/classes?"
context.getMetaData().addDiscoveredAnnotations(annotations);
}
else
{
context.getMetaData().addDiscoveredAnnotations(bundleRes, annotations);
}
}
/**

View File

@ -26,6 +26,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
@ -109,7 +110,7 @@ public class LibExtClassLoaderHelper
for (File f : jettyResources.listFiles())
{
jettyResFiles.put(f.getName(), f);
if (f.getName().toLowerCase().startsWith("readme"))
if (f.getName().toLowerCase(Locale.ENGLISH).startsWith("readme"))
{
continue;
}

View File

@ -32,6 +32,7 @@ import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
@ -389,7 +390,7 @@ public class OverlayedAppProvider extends AbstractLifeCycle implements AppProvid
List<URL> libs = new ArrayList<URL>();
for (String jar :instance_lib.list())
{
if (!jar.toLowerCase().endsWith(".jar"))
if (!jar.toLowerCase(Locale.ENGLISH).endsWith(".jar"))
continue;
libs.add(instance_lib.addPath(jar).getURL());
}
@ -610,7 +611,7 @@ public class OverlayedAppProvider extends AbstractLifeCycle implements AppProvid
{
for (String jar :lib.list())
{
if (!jar.toLowerCase().endsWith(".jar"))
if (!jar.toLowerCase(Locale.ENGLISH).endsWith(".jar"))
continue;
libs.add(lib.addPath(jar).getURL());
}
@ -832,12 +833,12 @@ public class OverlayedAppProvider extends AbstractLifeCycle implements AppProvid
File origin = new File(new URI(_scanDir.toURI()+ruri));
String name=origin.getName();
Monitor monitor = Monitor.valueOf(origin.getParentFile().getName().toUpperCase());
Monitor monitor = Monitor.valueOf(origin.getParentFile().getName().toUpperCase(Locale.ENGLISH));
String ext=".war";
// check directory vs archive
if (origin.isDirectory() || !origin.exists() && !ruri.toLowerCase().endsWith(ext))
if (origin.isDirectory() || !origin.exists() && !ruri.toLowerCase(Locale.ENGLISH).endsWith(ext))
{
// directories have priority over archives
directory=origin;
@ -846,7 +847,7 @@ public class OverlayedAppProvider extends AbstractLifeCycle implements AppProvid
else
{
// check extension name
if (!ruri.toLowerCase().endsWith(ext))
if (!ruri.toLowerCase(Locale.ENGLISH).endsWith(ext))
continue;
name=name.substring(0,name.length()-4);

View File

@ -21,6 +21,7 @@ package org.eclipse.jetty.plus.annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.Locale;
import javax.naming.InitialContext;
import javax.naming.NamingException;
@ -141,7 +142,7 @@ public class Injection
_resourceClass = resourceType;
//first look for a javabeans style setter matching the targetName
String setter = "set"+target.substring(0,1).toUpperCase()+target.substring(1);
String setter = "set"+target.substring(0,1).toUpperCase(Locale.ENGLISH)+target.substring(1);
try
{
LOG.debug("Looking for method for setter: "+setter+" with arg "+_resourceClass);

View File

@ -22,6 +22,7 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
@ -653,12 +654,12 @@ public class LdapLoginModule extends AbstractLoginModule
public static String convertCredentialJettyToLdap(String encryptedPassword)
{
if ("MD5:".startsWith(encryptedPassword.toUpperCase()))
if ("MD5:".startsWith(encryptedPassword.toUpperCase(Locale.ENGLISH)))
{
return "{MD5}" + encryptedPassword.substring("MD5:".length(), encryptedPassword.length());
}
if ("CRYPT:".startsWith(encryptedPassword.toUpperCase()))
if ("CRYPT:".startsWith(encryptedPassword.toUpperCase(Locale.ENGLISH)))
{
return "{CRYPT}" + encryptedPassword.substring("CRYPT:".length(), encryptedPassword.length());
}
@ -673,12 +674,12 @@ public class LdapLoginModule extends AbstractLoginModule
return encryptedPassword;
}
if ("{MD5}".startsWith(encryptedPassword.toUpperCase()))
if ("{MD5}".startsWith(encryptedPassword.toUpperCase(Locale.ENGLISH)))
{
return "MD5:" + encryptedPassword.substring("{MD5}".length(), encryptedPassword.length());
}
if ("{CRYPT}".startsWith(encryptedPassword.toUpperCase()))
if ("{CRYPT}".startsWith(encryptedPassword.toUpperCase(Locale.ENGLISH)))
{
return "CRYPT:" + encryptedPassword.substring("{CRYPT}".length(), encryptedPassword.length());
}

View File

@ -26,6 +26,7 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import javax.naming.InitialContext;
import javax.naming.NameNotFoundException;
@ -414,7 +415,7 @@ public class DataSourceLoginService extends MappedLoginService
DatabaseMetaData metaData = connection.getMetaData();
//check if tables exist
String tableName = (metaData.storesLowerCaseIdentifiers()? _userTableName.toLowerCase(): (metaData.storesUpperCaseIdentifiers()?_userTableName.toUpperCase(): _userTableName));
String tableName = (metaData.storesLowerCaseIdentifiers()? _userTableName.toLowerCase(Locale.ENGLISH): (metaData.storesUpperCaseIdentifiers()?_userTableName.toUpperCase(Locale.ENGLISH): _userTableName));
ResultSet result = metaData.getTables(null, null, tableName, null);
if (!result.next())
{
@ -432,7 +433,7 @@ public class DataSourceLoginService extends MappedLoginService
result.close();
tableName = (metaData.storesLowerCaseIdentifiers()? _roleTableName.toLowerCase(): (metaData.storesUpperCaseIdentifiers()?_roleTableName.toUpperCase(): _roleTableName));
tableName = (metaData.storesLowerCaseIdentifiers()? _roleTableName.toLowerCase(Locale.ENGLISH): (metaData.storesUpperCaseIdentifiers()?_roleTableName.toUpperCase(Locale.ENGLISH): _roleTableName));
result = metaData.getTables(null, null, tableName, null);
if (!result.next())
{
@ -449,7 +450,7 @@ public class DataSourceLoginService extends MappedLoginService
result.close();
tableName = (metaData.storesLowerCaseIdentifiers()? _userRoleTableName.toLowerCase(): (metaData.storesUpperCaseIdentifiers()?_userRoleTableName.toUpperCase(): _userRoleTableName));
tableName = (metaData.storesLowerCaseIdentifiers()? _userRoleTableName.toLowerCase(Locale.ENGLISH): (metaData.storesUpperCaseIdentifiers()?_userRoleTableName.toUpperCase(Locale.ENGLISH): _userRoleTableName));
result = metaData.getTables(null, null, tableName, null);
if (!result.next())
{

View File

@ -24,6 +24,7 @@ import java.io.OutputStream;
import java.net.MalformedURLException;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Locale;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@ -229,7 +230,7 @@ public class ProxyRule extends PatternRule
@Override
protected void onResponseHeader(Buffer name, Buffer value) throws IOException
{
String s = name.toString().toLowerCase();
String s = name.toString().toLowerCase(Locale.ENGLISH);
if (!_DontProxyHeaders.contains(s) || (HttpHeaders.CONNECTION_BUFFER.equals(name) && HttpHeaderValues.CLOSE_BUFFER.equals(value)))
{
if (debug != 0)
@ -348,7 +349,7 @@ public class ProxyRule extends PatternRule
String connectionHdr = request.getHeader("Connection");
if (connectionHdr != null)
{
connectionHdr = connectionHdr.toLowerCase();
connectionHdr = connectionHdr.toLowerCase(Locale.ENGLISH);
if (connectionHdr.indexOf("keep-alive") < 0 && connectionHdr.indexOf("close") < 0)
{
connectionHdr = null;
@ -370,7 +371,7 @@ public class ProxyRule extends PatternRule
{
// TODO could be better than this!
String hdr = (String)enm.nextElement();
String lhdr = hdr.toLowerCase();
String lhdr = hdr.toLowerCase(Locale.ENGLISH);
if (_DontProxyHeaders.contains(lhdr))
continue;

View File

@ -77,6 +77,20 @@ public class RewriteHandlerTest extends AbstractRuleTestCase
@Test
public void test() throws Exception
{
_response.setStatus(200);
_request.setHandled(false);
_handler.setOriginalPathAttribute("/before");
_handler.setRewriteRequestURI(true);
_handler.setRewritePathInfo(true);
_request.setRequestURI("/xxx/bar");
_request.setPathInfo("/xxx/bar");
_handler.handle("/xxx/bar",_request,_request, _response);
assertEquals(201,_response.getStatus());
assertEquals("/bar/zzz",_request.getAttribute("target"));
assertEquals("/bar/zzz",_request.getAttribute("URI"));
assertEquals("/bar/zzz",_request.getAttribute("info"));
assertEquals(null,_request.getAttribute("before"));
_response.setStatus(200);
_request.setHandled(false);
_handler.setOriginalPathAttribute("/before");

View File

@ -19,16 +19,24 @@
package org.eclipse.jetty.security;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import javax.servlet.HttpConstraintElement;
import javax.servlet.HttpMethodConstraintElement;
import javax.servlet.ServletSecurityElement;
import javax.servlet.annotation.ServletSecurity.EmptyRoleSemantic;
import javax.servlet.annotation.ServletSecurity.TransportGuarantee;
import org.eclipse.jetty.http.PathMap;
import org.eclipse.jetty.server.AbstractHttpConnection;
import org.eclipse.jetty.server.Connector;
@ -42,17 +50,225 @@ import org.eclipse.jetty.util.security.Constraint;
/* ------------------------------------------------------------ */
/**
* Handler to enforce SecurityConstraints. This implementation is servlet spec
* 2.4 compliant and precomputes the constraint combinations for runtime
* 3.0 compliant and precomputes the constraint combinations for runtime
* efficiency.
*
*/
public class ConstraintSecurityHandler extends SecurityHandler implements ConstraintAware
{
private static final String OMISSION_SUFFIX = ".omission";
private final List<ConstraintMapping> _constraintMappings= new CopyOnWriteArrayList<ConstraintMapping>();
private final Set<String> _roles = new CopyOnWriteArraySet<String>();
private final PathMap _constraintMap = new PathMap();
private boolean _strict = true;
/* ------------------------------------------------------------ */
/**
* @return
*/
public static Constraint createConstraint()
{
return new Constraint();
}
/* ------------------------------------------------------------ */
/**
* @param constraint
* @return
*/
public static Constraint createConstraint(Constraint constraint)
{
try
{
return (Constraint)constraint.clone();
}
catch (CloneNotSupportedException e)
{
throw new IllegalStateException (e);
}
}
/* ------------------------------------------------------------ */
/**
* Create a security constraint
*
* @param name
* @param authenticate
* @param roles
* @param dataConstraint
* @return
*/
public static Constraint createConstraint (String name, boolean authenticate, String[] roles, int dataConstraint)
{
Constraint constraint = createConstraint();
if (name != null)
constraint.setName(name);
constraint.setAuthenticate(authenticate);
constraint.setRoles(roles);
constraint.setDataConstraint(dataConstraint);
return constraint;
}
/* ------------------------------------------------------------ */
/**
* @param name
* @param element
* @return
*/
public static Constraint createConstraint (String name, HttpConstraintElement element)
{
return createConstraint(name, element.getRolesAllowed(), element.getEmptyRoleSemantic(), element.getTransportGuarantee());
}
/* ------------------------------------------------------------ */
/**
* @param name
* @param rolesAllowed
* @param permitOrDeny
* @param transport
* @return
*/
public static Constraint createConstraint (String name, String[] rolesAllowed, EmptyRoleSemantic permitOrDeny, TransportGuarantee transport)
{
Constraint constraint = createConstraint();
if (rolesAllowed == null || rolesAllowed.length==0)
{
if (permitOrDeny.equals(EmptyRoleSemantic.DENY))
{
//Equivalent to <auth-constraint> with no roles
constraint.setName(name+"-Deny");
constraint.setAuthenticate(true);
}
else
{
//Equivalent to no <auth-constraint>
constraint.setName(name+"-Permit");
constraint.setAuthenticate(false);
}
}
else
{
//Equivalent to <auth-constraint> with list of <security-role-name>s
constraint.setAuthenticate(true);
constraint.setRoles(rolesAllowed);
constraint.setName(name+"-RolesAllowed");
}
//Equivalent to //<user-data-constraint><transport-guarantee>CONFIDENTIAL</transport-guarantee></user-data-constraint>
constraint.setDataConstraint((transport.equals(TransportGuarantee.CONFIDENTIAL)?Constraint.DC_CONFIDENTIAL:Constraint.DC_NONE));
return constraint;
}
/* ------------------------------------------------------------ */
/**
* @param pathSpec
* @param constraintMappings
* @return
*/
public static List<ConstraintMapping> getConstraintMappingsForPath(String pathSpec, List<ConstraintMapping> constraintMappings)
{
if (pathSpec == null || "".equals(pathSpec.trim()) || constraintMappings == null || constraintMappings.size() == 0)
return Collections.emptyList();
List<ConstraintMapping> mappings = new ArrayList<ConstraintMapping>();
for (ConstraintMapping mapping:constraintMappings)
{
if (pathSpec.equals(mapping.getPathSpec()))
{
mappings.add(mapping);
}
}
return mappings;
}
/* ------------------------------------------------------------ */
/** Take out of the constraint mappings those that match the
* given path.
*
* @param pathSpec
* @param constraintMappings a new list minus the matching constraints
* @return
*/
public static List<ConstraintMapping> removeConstraintMappingsForPath(String pathSpec, List<ConstraintMapping> constraintMappings)
{
if (pathSpec == null || "".equals(pathSpec.trim()) || constraintMappings == null || constraintMappings.size() == 0)
return Collections.emptyList();
List<ConstraintMapping> mappings = new ArrayList<ConstraintMapping>();
for (ConstraintMapping mapping:constraintMappings)
{
//Remove the matching mappings by only copying in non-matching mappings
if (!pathSpec.equals(mapping.getPathSpec()))
{
mappings.add(mapping);
}
}
return mappings;
}
/* ------------------------------------------------------------ */
/** Generate Constraints and ContraintMappings for the given url pattern and ServletSecurityElement
*
* @param name
* @param pathSpec
* @param securityElement
* @return
*/
public static List<ConstraintMapping> createConstraintsWithMappingsForPath (String name, String pathSpec, ServletSecurityElement securityElement)
{
List<ConstraintMapping> mappings = new ArrayList<ConstraintMapping>();
//Create a constraint that will describe the default case (ie if not overridden by specific HttpMethodConstraints)
Constraint constraint = ConstraintSecurityHandler.createConstraint(name, securityElement);
//Create a mapping for the pathSpec for the default case
ConstraintMapping defaultMapping = new ConstraintMapping();
defaultMapping.setPathSpec(pathSpec);
defaultMapping.setConstraint(constraint);
mappings.add(defaultMapping);
//See Spec 13.4.1.2 p127
List<String> methodOmissions = new ArrayList<String>();
//make constraint mappings for this url for each of the HttpMethodConstraintElements
Collection<HttpMethodConstraintElement> methodConstraints = securityElement.getHttpMethodConstraints();
if (methodConstraints != null)
{
for (HttpMethodConstraintElement methodConstraint:methodConstraints)
{
//Make a Constraint that captures the <auth-constraint> and <user-data-constraint> elements supplied for the HttpMethodConstraintElement
Constraint mconstraint = ConstraintSecurityHandler.createConstraint(name, methodConstraint);
ConstraintMapping mapping = new ConstraintMapping();
mapping.setConstraint(mconstraint);
mapping.setPathSpec(pathSpec);
if (methodConstraint.getMethodName() != null)
{
mapping.setMethod(methodConstraint.getMethodName());
//See spec 13.4.1.2 p127 - add an omission for every method name to the default constraint
methodOmissions.add(methodConstraint.getMethodName());
}
mappings.add(mapping);
}
}
//See spec 13.4.1.2 p127 - add an omission for every method name to the default constraint
if (methodOmissions.size() > 0)
defaultMapping.setMethodOmissions(methodOmissions.toArray(new String[methodOmissions.size()]));
return mappings;
}
/* ------------------------------------------------------------ */
/** Get the strict mode.
* @return true if the security handler is running in strict mode.
@ -136,8 +352,6 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
*/
public void setConstraintMappings(List<ConstraintMapping> constraintMappings, Set<String> roles)
{
if (isStarted())
throw new IllegalStateException("Started");
_constraintMappings.clear();
_constraintMappings.addAll(constraintMappings);
@ -156,6 +370,14 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
}
}
setRoles(roles);
if (isStarted())
{
for (ConstraintMapping mapping : _constraintMappings)
{
processConstraintMapping(mapping);
}
}
}
/* ------------------------------------------------------------ */
@ -168,9 +390,6 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
*/
public void setRoles(Set<String> roles)
{
if (isStarted())
throw new IllegalStateException("Started");
_roles.clear();
_roles.addAll(roles);
}
@ -232,7 +451,9 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
}
super.doStart();
}
/* ------------------------------------------------------------ */
@Override
protected void doStop() throws Exception
{
@ -241,7 +462,15 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
_roles.clear();
super.doStop();
}
/* ------------------------------------------------------------ */
/**
* Create and combine the constraint with the existing processed
* constraints.
*
* @param mapping
*/
protected void processConstraintMapping(ConstraintMapping mapping)
{
Map<String, RoleInfo> mappings = (Map<String, RoleInfo>)_constraintMap.get(mapping.getPathSpec());
@ -253,8 +482,15 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
RoleInfo allMethodsRoleInfo = mappings.get(null);
if (allMethodsRoleInfo != null && allMethodsRoleInfo.isForbidden())
return;
if (mapping.getMethodOmissions() != null && mapping.getMethodOmissions().length > 0)
{
processConstraintMappingWithMethodOmissions(mapping, mappings);
return;
}
String httpMethod = mapping.getMethod();
String httpMethod = mapping.getMethod();
RoleInfo roleInfo = mappings.get(httpMethod);
if (roleInfo == null)
{
@ -268,10 +504,10 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
if (roleInfo.isForbidden())
return;
Constraint constraint = mapping.getConstraint();
boolean forbidden = constraint.isForbidden();
roleInfo.setForbidden(forbidden);
if (forbidden)
//add in info from the constraint
configureRoleInfo(roleInfo, mapping);
if (roleInfo.isForbidden())
{
if (httpMethod == null)
{
@ -281,36 +517,7 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
}
else
{
UserDataConstraint userDataConstraint = UserDataConstraint.get(constraint.getDataConstraint());
roleInfo.setUserDataConstraint(userDataConstraint);
boolean checked = constraint.getAuthenticate();
roleInfo.setChecked(checked);
if (roleInfo.isChecked())
{
if (constraint.isAnyRole())
{
if (_strict)
{
// * means "all defined roles"
for (String role : _roles)
roleInfo.addRole(role);
}
else
// * means any role
roleInfo.setAnyRole(true);
}
else
{
String[] newRoles = constraint.getRoles();
for (String role : newRoles)
{
if (_strict &&!_roles.contains(role))
throw new IllegalArgumentException("Attempt to use undeclared role: " + role + ", known roles: " + _roles);
roleInfo.addRole(role);
}
}
}
//combine with any entry that covers all methods
if (httpMethod == null)
{
for (Map.Entry<String, RoleInfo> entry : mappings.entrySet())
@ -325,6 +532,105 @@ 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)
*/
protected Object prepareConstraintInfo(String pathInContext, Request request)
{
Map<String, RoleInfo> mappings = (Map<String, RoleInfo>)_constraintMap.match(pathInContext);
@ -334,13 +640,46 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
String httpMethod = request.getMethod();
RoleInfo roleInfo = mappings.get(httpMethod);
if (roleInfo == null)
roleInfo = mappings.get(null);
{
//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(null);
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 null;
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.security.SecurityHandler#checkUserDataPermissions(java.lang.String, org.eclipse.jetty.server.Request, org.eclipse.jetty.server.Response, java.lang.Object)
*/
protected boolean checkUserDataPermissions(String pathInContext, Request request, Response response, Object constraintInfo) throws IOException
{
if (constraintInfo == null)
@ -404,7 +743,11 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
}
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.security.SecurityHandler#isAuthMandatory(org.eclipse.jetty.server.Request, org.eclipse.jetty.server.Response, java.lang.Object)
*/
protected boolean isAuthMandatory(Request baseRequest, Response base_response, Object constraintInfo)
{
if (constraintInfo == null)
@ -413,7 +756,12 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
}
return ((RoleInfo)constraintInfo).isChecked();
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.security.SecurityHandler#checkWebResourcePermissions(java.lang.String, org.eclipse.jetty.server.Request, org.eclipse.jetty.server.Response, java.lang.Object, org.eclipse.jetty.server.UserIdentity)
*/
@Override
protected boolean checkWebResourcePermissions(String pathInContext, Request request, Response response, Object constraintInfo, UserIdentity userIdentity)
throws IOException
@ -454,4 +802,5 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
getBeans(),
TypeUtil.asList(getHandlers()));
}
}

View File

@ -335,18 +335,22 @@ public abstract class SecurityHandler extends HandlerWrapper implements Authenti
if (_identityService==null)
{
if (_loginService!=null)
_identityService=_loginService.getIdentityService();
System.err.println("Null identity service, trying login service: "+_identityService);
if (_identityService==null)
_identityService=findIdentityService();
System.err.println("Finding identity service: "+_identityService);
if (_identityService==null && _realmName!=null)
_identityService=new DefaultIdentityService();
}
if (_loginService!=null)
{
System.err.println("LoginService="+_loginService + " identityService="+_identityService);
if (_loginService.getIdentityService()==null)
_loginService.setIdentityService(_identityService);
else if (_loginService.getIdentityService()!=_identityService)
@ -525,8 +529,6 @@ public abstract class SecurityHandler extends HandlerWrapper implements Authenti
else if (authentication instanceof Authentication.Deferred)
{
DeferredAuthentication deferred= (DeferredAuthentication)authentication;
deferred.setIdentityService(_identityService);
deferred.setLoginService(_loginService);
baseRequest.setAuthentication(authentication);
try
@ -536,7 +538,6 @@ public abstract class SecurityHandler extends HandlerWrapper implements Authenti
finally
{
previousIdentity = deferred.getPreviousAssociation();
deferred.setIdentityService(null);
}
if (authenticator!=null)

View File

@ -54,6 +54,8 @@ public class BasicAuthenticator extends LoginAuthenticator
return Constraint.__BASIC_AUTH;
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.security.Authenticator#validateRequest(javax.servlet.ServletRequest, javax.servlet.ServletResponse, boolean)
@ -67,8 +69,8 @@ public class BasicAuthenticator extends LoginAuthenticator
try
{
if (!mandatory)
return _deferred;
return new DeferredAuthentication(this);
if (credentials != null)
{
int space=credentials.indexOf(' ');
@ -85,10 +87,9 @@ public class BasicAuthenticator extends LoginAuthenticator
String username = credentials.substring(0,i);
String password = credentials.substring(i+1);
UserIdentity user = _loginService.login(username,password);
UserIdentity user = login (username, password, request);
if (user!=null)
{
renewSession(request,response);
return new UserAuthentication(getAuthMethod(),user);
}
}
@ -96,7 +97,7 @@ public class BasicAuthenticator extends LoginAuthenticator
}
}
if (_deferred.isDeferred(response))
if (DeferredAuthentication.isDeferred(response))
return Authentication.UNAUTHENTICATED;
response.setHeader(HttpHeaders.WWW_AUTHENTICATE, "basic realm=\"" + _loginService.getName() + '"');

View File

@ -81,6 +81,8 @@ public class ClientCertAuthenticator extends LoginAuthenticator
return Constraint.__CERT_AUTH;
}
/**
* @return Authentication for request
* @throws ServerAuthException
@ -88,8 +90,8 @@ public class ClientCertAuthenticator extends LoginAuthenticator
public Authentication validateRequest(ServletRequest req, ServletResponse res, boolean mandatory) throws ServerAuthException
{
if (!mandatory)
return _deferred;
return new DeferredAuthentication(this);
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)res;
X509Certificate[] certs = (X509Certificate[]) request.getAttribute("javax.servlet.request.X509Certificate");
@ -121,16 +123,15 @@ public class ClientCertAuthenticator extends LoginAuthenticator
final char[] credential = B64Code.encode(cert.getSignature());
UserIdentity user = _loginService.login(username,credential);
UserIdentity user = login(username, credential, req);
if (user!=null)
{
renewSession(request,response);
return new UserAuthentication(getAuthMethod(),user);
}
}
}
if (!_deferred.isDeferred(response))
if (!DeferredAuthentication.isDeferred(response))
{
response.sendError(HttpServletResponse.SC_FORBIDDEN);
return Authentication.SEND_FAILURE;

View File

@ -29,6 +29,7 @@ import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.security.Authenticator;
@ -45,22 +46,9 @@ import org.eclipse.jetty.util.log.Logger;
public class DeferredAuthentication implements Authentication.Deferred
{
private static final Logger LOG = Log.getLogger(DeferredAuthentication.class);
protected final Authenticator _authenticator;
private LoginService _loginService;
private IdentityService _identityService;
protected final LoginAuthenticator _authenticator;
private Object _previousAssociation;
/* ------------------------------------------------------------ */
public DeferredAuthentication(Authenticator authenticator)
{
if (authenticator == null)
throw new NullPointerException("No Authenticator");
this._authenticator = authenticator;
}
/* ------------------------------------------------------------ */
public DeferredAuthentication(LoginAuthenticator authenticator)
{
@ -68,36 +56,6 @@ public class DeferredAuthentication implements Authentication.Deferred
throw new NullPointerException("No Authenticator");
this._authenticator = authenticator;
}
/* ------------------------------------------------------------ */
/** Get the identityService.
* @return the identityService
*/
public IdentityService getIdentityService()
{
return _identityService;
}
/* ------------------------------------------------------------ */
/** Set the identityService.
* @param identityService the identityService to set
*/
public void setIdentityService(IdentityService identityService)
{
_identityService = identityService;
}
/* ------------------------------------------------------------ */
public LoginService getLoginService()
{
return _loginService;
}
/* ------------------------------------------------------------ */
public void setLoginService(LoginService loginService)
{
_loginService = loginService;
}
/* ------------------------------------------------------------ */
/**
@ -111,8 +69,12 @@ public class DeferredAuthentication implements Authentication.Deferred
if (authentication!=null && (authentication instanceof Authentication.User) && !(authentication instanceof Authentication.ResponseSent))
{
if (_identityService!=null)
_previousAssociation=_identityService.associate(((Authentication.User)authentication).getUserIdentity());
LoginService login_service= _authenticator.getLoginService();
IdentityService identity_service=login_service.getIdentityService();
if (identity_service!=null)
_previousAssociation=identity_service.associate(((Authentication.User)authentication).getUserIdentity());
return authentication;
}
}
@ -120,6 +82,7 @@ public class DeferredAuthentication implements Authentication.Deferred
{
LOG.debug(e);
}
return this;
}
@ -131,9 +94,12 @@ public class DeferredAuthentication implements Authentication.Deferred
{
try
{
LoginService login_service= _authenticator.getLoginService();
IdentityService identity_service=login_service.getIdentityService();
Authentication authentication = _authenticator.validateRequest(request,response,true);
if (authentication instanceof Authentication.User && _identityService!=null)
_previousAssociation=_identityService.associate(((Authentication.User)authentication).getUserIdentity());
if (authentication instanceof Authentication.User && identity_service!=null)
_previousAssociation=identity_service.associate(((Authentication.User)authentication).getUserIdentity());
return authentication;
}
catch (ServerAuthException e)
@ -147,18 +113,16 @@ public class DeferredAuthentication implements Authentication.Deferred
/**
* @see org.eclipse.jetty.server.Authentication.Deferred#login(java.lang.String, java.lang.String)
*/
public Authentication login(String username, String password)
public Authentication login(String username, Object password, ServletRequest request)
{
if (_loginService!=null)
UserIdentity identity = _authenticator.login(username, password, request);
if (identity != null)
{
UserIdentity user = _loginService.login(username,password);
if (user!=null)
{
UserAuthentication authentication = new UserAuthentication("API",user);
if (_identityService!=null)
_previousAssociation=_identityService.associate(user);
return authentication;
}
IdentityService identity_service = _authenticator.getLoginService().getIdentityService();
UserAuthentication authentication = new UserAuthentication("API",identity);
if (identity_service != null)
_previousAssociation=identity_service.associate(identity);
return authentication;
}
return null;
}

View File

@ -116,13 +116,15 @@ public class DigestAuthenticator extends LoginAuthenticator
{
return true;
}
/* ------------------------------------------------------------ */
public Authentication validateRequest(ServletRequest req, ServletResponse res, boolean mandatory) throws ServerAuthException
{
if (!mandatory)
return _deferred;
return new DeferredAuthentication(this);
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)res;
String credentials = request.getHeader(HttpHeaders.AUTHORIZATION);
@ -185,10 +187,10 @@ public class DigestAuthenticator extends LoginAuthenticator
if (n > 0)
{
UserIdentity user = _loginService.login(digest.username,digest);
//UserIdentity user = _loginService.login(digest.username,digest);
UserIdentity user = login(digest.username, digest, req);
if (user!=null)
{
renewSession(request,response);
return new UserAuthentication(getAuthMethod(),user);
}
}
@ -197,7 +199,7 @@ public class DigestAuthenticator extends LoginAuthenticator
}
if (!_deferred.isDeferred(response))
if (!DeferredAuthentication.isDeferred(response))
{
String domain = request.getContextPath();
if (domain == null)

View File

@ -21,6 +21,7 @@ package org.eclipse.jetty.security.authentication;
import java.io.IOException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Locale;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
@ -179,6 +180,22 @@ public class FormAuthenticator extends LoginAuthenticator
_formErrorPath = _formErrorPath.substring(0, _formErrorPath.indexOf('?'));
}
}
/* ------------------------------------------------------------ */
@Override
public UserIdentity login(String username, Object password, ServletRequest request)
{
UserIdentity user = super.login(username,password,request);
if (user!=null)
{
HttpSession session = ((HttpServletRequest)request).getSession(true);
Authentication cached=new SessionAuthentication(getAuthMethod(),user,password);
session.setAttribute(SessionAuthentication.__J_AUTHENTICATED, cached);
}
return user;
}
/* ------------------------------------------------------------ */
public Authentication validateRequest(ServletRequest req, ServletResponse res, boolean mandatory) throws ServerAuthException
@ -191,11 +208,11 @@ public class FormAuthenticator extends LoginAuthenticator
mandatory|=isJSecurityCheck(uri);
if (!mandatory)
return _deferred;
return new DeferredAuthentication(this);
if (isLoginOrErrorPage(URIUtil.addPaths(request.getServletPath(),request.getPathInfo())) &&!DeferredAuthentication.isDeferred(response))
return _deferred;
return new DeferredAuthentication(this);
HttpSession session = request.getSession(true);
try
@ -206,11 +223,10 @@ public class FormAuthenticator extends LoginAuthenticator
final String username = request.getParameter(__J_USERNAME);
final String password = request.getParameter(__J_PASSWORD);
UserIdentity user = _loginService.login(username,password);
UserIdentity user = login(username, password, request);
session = request.getSession(true);
if (user!=null)
{
session=renewSession(request,response);
{
// Redirect to original request
String nuri;
synchronized(session)
@ -223,9 +239,6 @@ public class FormAuthenticator extends LoginAuthenticator
if (nuri.length() == 0)
nuri = URIUtil.SLASH;
}
Authentication cached=new SessionAuthentication(getAuthMethod(),user,password);
session.setAttribute(SessionAuthentication.__J_AUTHENTICATED, cached);
}
response.setContentLength(0);
response.sendRedirect(response.encodeRedirectURL(nuri));
@ -300,9 +313,12 @@ public class FormAuthenticator extends LoginAuthenticator
}
// if we can't send challenge
if (_deferred.isDeferred(response))
return Authentication.UNAUTHENTICATED;
if (DeferredAuthentication.isDeferred(response))
{
LOG.debug("auth deferred {}",session.getId());
return Authentication.UNAUTHENTICATED;
}
// remember the current URI
synchronized (session)
{
@ -387,7 +403,7 @@ public class FormAuthenticator extends LoginAuthenticator
@Override
public long getDateHeader(String name)
{
if (name.toLowerCase().startsWith("if-"))
if (name.toLowerCase(Locale.ENGLISH).startsWith("if-"))
return -1;
return super.getDateHeader(name);
}
@ -395,7 +411,7 @@ public class FormAuthenticator extends LoginAuthenticator
@Override
public String getHeader(String name)
{
if (name.toLowerCase().startsWith("if-"))
if (name.toLowerCase(Locale.ENGLISH).startsWith("if-"))
return null;
return super.getHeader(name);
}
@ -409,7 +425,7 @@ public class FormAuthenticator extends LoginAuthenticator
@Override
public Enumeration getHeaders(String name)
{
if (name.toLowerCase().startsWith("if-"))
if (name.toLowerCase(Locale.ENGLISH).startsWith("if-"))
return Collections.enumeration(Collections.EMPTY_LIST);
return super.getHeaders(name);
}

View File

@ -18,6 +18,7 @@
package org.eclipse.jetty.security.authentication;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
@ -25,11 +26,12 @@ import javax.servlet.http.HttpSession;
import org.eclipse.jetty.security.Authenticator;
import org.eclipse.jetty.security.IdentityService;
import org.eclipse.jetty.security.LoginService;
import org.eclipse.jetty.server.Authentication;
import org.eclipse.jetty.server.UserIdentity;
import org.eclipse.jetty.server.session.AbstractSessionManager;
public abstract class LoginAuthenticator implements Authenticator
{
protected final DeferredAuthentication _deferred=new DeferredAuthentication(this);
protected LoginService _loginService;
protected IdentityService _identityService;
private boolean _renewSession;
@ -38,6 +40,20 @@ public abstract class LoginAuthenticator implements Authenticator
{
}
/* ------------------------------------------------------------ */
public UserIdentity login(String username, Object password, ServletRequest request)
{
UserIdentity user = _loginService.login(username,password);
if (user!=null)
{
renewSession((HttpServletRequest)request, null);
return user;
}
return null;
}
public void setConfiguration(AuthConfiguration configuration)
{
_loginService=configuration.getLoginService();

View File

@ -98,8 +98,8 @@ public class SessionAuthentication implements Authentication.User, Serializable,
{
if (_session!=null && _session.getAttribute(__J_AUTHENTICATED)!=null)
_session.removeAttribute(__J_AUTHENTICATED);
else
doLogout();
doLogout();
}
private void doLogout()

View File

@ -60,6 +60,8 @@ public class SpnegoAuthenticator extends LoginAuthenticator
return _authMethod;
}
public Authentication validateRequest(ServletRequest request, ServletResponse response, boolean mandatory) throws ServerAuthException
{
HttpServletRequest req = (HttpServletRequest)request;
@ -69,7 +71,7 @@ public class SpnegoAuthenticator extends LoginAuthenticator
if (!mandatory)
{
return _deferred;
return new DeferredAuthentication(this);
}
// check to see if we have authorization headers required to continue
@ -77,7 +79,7 @@ public class SpnegoAuthenticator extends LoginAuthenticator
{
try
{
if (_deferred.isDeferred(res))
if (DeferredAuthentication.isDeferred(res))
{
return Authentication.UNAUTHENTICATED;
}
@ -96,7 +98,7 @@ public class SpnegoAuthenticator extends LoginAuthenticator
{
String spnegoToken = header.substring(10);
UserIdentity user = _loginService.login(null,spnegoToken);
UserIdentity user = login(null,spnegoToken, request);
if ( user != null )
{

View File

@ -22,9 +22,11 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -76,6 +78,8 @@ public class ConstraintTest
_loginService.putUser("user",new Password("password"));
_loginService.putUser("user2",new Password("password"), new String[] {"user"});
_loginService.putUser("admin",new Password("password"), new String[] {"user","administrator"});
_loginService.putUser("user3", new Password("password"), new String[] {"foo"});
_context.setContextPath("/ctx");
_server.setHandler(_context);
@ -189,17 +193,59 @@ public class ConstraintTest
@Test
public void testBasic() throws Exception
{
List<ConstraintMapping> list = new ArrayList<ConstraintMapping>(_security.getConstraintMappings());
Constraint constraint6 = new Constraint();
constraint6.setAuthenticate(true);
constraint6.setName("omit POST and GET");
constraint6.setRoles(new String[]{"user"});
ConstraintMapping mapping6 = new ConstraintMapping();
mapping6.setPathSpec("/omit/*");
mapping6.setConstraint(constraint6);
mapping6.setMethodOmissions(new String[]{"GET", "HEAD"}); //requests for every method except GET and HEAD must be in role "user"
list.add(mapping6);
Constraint constraint7 = new Constraint();
constraint7.setAuthenticate(true);
constraint7.setName("non-omitted GET");
constraint7.setRoles(new String[]{"administrator"});
ConstraintMapping mapping7 = new ConstraintMapping();
mapping7.setPathSpec("/omit/*");
mapping7.setConstraint(constraint7);
mapping7.setMethod("GET"); //requests for GET must be in role "admin"
list.add(mapping7);
Constraint constraint8 = new Constraint();
constraint8.setAuthenticate(true);
constraint8.setName("non specific");
constraint8.setRoles(new String[]{"foo"});
ConstraintMapping mapping8 = new ConstraintMapping();
mapping8.setPathSpec("/omit/*");
mapping8.setConstraint(constraint8);//requests for all methods must be in role "foo"
list.add(mapping8);
Set<String> knownRoles=new HashSet<String>();
knownRoles.add("user");
knownRoles.add("administrator");
knownRoles.add("foo");
_security.setConstraintMappings(list, knownRoles);
_security.setAuthenticator(new BasicAuthenticator());
_security.setStrict(false);
_server.start();
String response;
/*
response = _connector.getResponses("GET /ctx/noauth/info HTTP/1.0\r\n\r\n");
assertTrue(response.startsWith("HTTP/1.1 200 OK"));
*/
response = _connector.getResponses("GET /ctx/forbid/info HTTP/1.0\r\n\r\n");
assertTrue(response.startsWith("HTTP/1.1 403 Forbidden"));
/*
response = _connector.getResponses("GET /ctx/auth/info HTTP/1.0\r\n\r\n");
assertTrue(response.startsWith("HTTP/1.1 401 Unauthorized"));
assertTrue(response.indexOf("WWW-Authenticate: basic realm=\"TestRealm\"") > 0);
@ -214,8 +260,8 @@ public class ConstraintTest
"Authorization: Basic " + B64Code.encode("user:password") + "\r\n" +
"\r\n");
assertTrue(response.startsWith("HTTP/1.1 200 OK"));
*/
/*
// test admin
response = _connector.getResponses("GET /ctx/admin/info HTTP/1.0\r\n\r\n");
assertTrue(response.startsWith("HTTP/1.1 401 Unauthorized"));
@ -241,7 +287,33 @@ public class ConstraintTest
response = _connector.getResponses("GET /ctx/admin/relax/info HTTP/1.0\r\n\r\n");
assertTrue(response.startsWith("HTTP/1.1 200 OK"));
//check GET is in role administrator
response = _connector.getResponses("GET /ctx/omit/x HTTP/1.0\r\n" +
"Authorization: Basic " + B64Code.encode("admin:password") + "\r\n" +
"\r\n");
assertTrue(response.startsWith("HTTP/1.1 200 OK"));
//check POST is in role user
response = _connector.getResponses("POST /ctx/omit/x HTTP/1.0\r\n" +
"Authorization: Basic " + B64Code.encode("user2:password") + "\r\n" +
"\r\n");
assertTrue(response.startsWith("HTTP/1.1 200 OK"));
//check POST can be in role foo too
response = _connector.getResponses("POST /ctx/omit/x HTTP/1.0\r\n" +
"Authorization: Basic " + B64Code.encode("user3:password") + "\r\n" +
"\r\n");
assertTrue(response.startsWith("HTTP/1.1 200 OK"));
//check HEAD cannot be in role user
response = _connector.getResponses("HEAD /ctx/omit/x HTTP/1.0\r\n" +
"Authorization: Basic " + B64Code.encode("user2:password") + "\r\n" +
"\r\n");
assertTrue(response.startsWith("HTTP/1.1 200 OK"));*/
}
@Test
public void testFormDispatch() throws Exception
@ -847,7 +919,7 @@ public class ConstraintTest
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response ) throws IOException, ServletException
{
baseRequest.setHandled(true);
if (request.getAuthType()==null || "user".equals(request.getRemoteUser()) || request.isUserInRole("user"))
if (request.getAuthType()==null || "user".equals(request.getRemoteUser()) || request.isUserInRole("user") || request.isUserInRole("foo"))
{
response.setStatus(200);
response.setContentType("text/plain; charset=UTF-8");

View File

@ -0,0 +1,311 @@
//
// ========================================================================
// 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.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.junit.After;
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.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");
assertTrue(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");
assertTrue(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");
assertTrue(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");
assertTrue(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");
assertTrue(response.startsWith("HTTP/1.1 403 !Confidential"));
//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");
assertTrue(response.startsWith("HTTP/1.1 200 OK"));
}
private class RequestHandler extends AbstractHandler
{
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"));
}
}
}

View File

@ -85,7 +85,7 @@ public interface Authentication
* @param password
* @return The new Authentication state
*/
Authentication login(String username,String password);
Authentication login(String username,Object password,ServletRequest request);
}

View File

@ -17,6 +17,8 @@
//
package org.eclipse.jetty.server;
import java.util.Locale;
import javax.servlet.http.Cookie;
import org.eclipse.jetty.util.LazyList;
@ -286,7 +288,7 @@ public class CookieCutter
{
if (name.startsWith("$"))
{
String lowercaseName = name.toLowerCase();
String lowercaseName = name.toLowerCase(Locale.ENGLISH);
if ("$path".equals(lowercaseName))
{
if (cookie!=null)

View File

@ -50,6 +50,8 @@ import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletRequestAttributeEvent;
import javax.servlet.ServletRequestAttributeListener;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
@ -80,6 +82,7 @@ import org.eclipse.jetty.server.handler.ContextHandler.Context;
import org.eclipse.jetty.util.Attributes;
import org.eclipse.jetty.util.AttributesMap;
import org.eclipse.jetty.util.LazyList;
import org.eclipse.jetty.util.MultiException;
import org.eclipse.jetty.util.MultiMap;
import org.eclipse.jetty.util.MultiPartInputStream;
import org.eclipse.jetty.util.StringUtil;
@ -123,12 +126,50 @@ import org.eclipse.jetty.util.log.Logger;
public class Request implements HttpServletRequest
{
public static final String __MULTIPART_CONFIG_ELEMENT = "org.eclipse.multipartConfig";
public static final String __MULTIPART_INPUT_STREAM = "org.eclipse.multiPartInputStream";
public static final String __MULTIPART_CONTEXT = "org.eclipse.multiPartContext";
private static final Logger LOG = Log.getLogger(Request.class);
private static final String __ASYNC_FWD = "org.eclipse.asyncfwd";
private static final Collection __defaultLocale = Collections.singleton(Locale.getDefault());
private static final int __NONE = 0, _STREAM = 1, __READER = 2;
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()
}
}
/* ------------------------------------------------------------ */
public static Request getRequest(HttpServletRequest request)
{
@ -1271,6 +1312,7 @@ public class Request implements HttpServletRequest
UserIdentity user = ((Authentication.User)_authentication).getUserIdentity();
return user.getUserPrincipal();
}
return null;
}
@ -1762,6 +1804,7 @@ public class Request implements HttpServletRequest
public void setQueryString(String queryString)
{
_queryString = queryString;
_queryEncoding = null; //assume utf-8
}
/* ------------------------------------------------------------ */
@ -1938,7 +1981,7 @@ public class Request implements HttpServletRequest
{
if (_authentication instanceof Authentication.Deferred)
{
setAuthentication(((Authentication.Deferred)_authentication).authenticate(this,response));
setAuthentication(((Authentication.Deferred)_authentication).authenticate(this,response));
return !(_authentication instanceof Authentication.ResponseSent);
}
response.sendError(HttpStatus.UNAUTHORIZED_401);
@ -1953,9 +1996,16 @@ public class Request implements HttpServletRequest
if (_multiPartInputStream == null)
{
MultipartConfigElement config = (MultipartConfigElement)getAttribute(__MULTIPART_CONFIG_ELEMENT);
if (config == null)
throw new IllegalStateException("No multipart config for servlet");
_multiPartInputStream = new MultiPartInputStream(getInputStream(),
getContentType(),(MultipartConfigElement)getAttribute(__MULTIPART_CONFIG_ELEMENT),
getContentType(),config,
(_context != null?(File)_context.getAttribute("javax.servlet.context.tempdir"):null));
setAttribute(__MULTIPART_INPUT_STREAM, _multiPartInputStream);
setAttribute(__MULTIPART_CONTEXT, _context);
Collection<Part> parts = _multiPartInputStream.getParts(); //causes parsing
for (Part p:parts)
{
@ -1984,9 +2034,17 @@ public class Request implements HttpServletRequest
if (_multiPartInputStream == null)
{
MultipartConfigElement config = (MultipartConfigElement)getAttribute(__MULTIPART_CONFIG_ELEMENT);
if (config == null)
throw new IllegalStateException("No multipart config for servlet");
_multiPartInputStream = new MultiPartInputStream(getInputStream(),
getContentType(),(MultipartConfigElement)getAttribute(__MULTIPART_CONFIG_ELEMENT),
getContentType(), config,
(_context != null?(File)_context.getAttribute("javax.servlet.context.tempdir"):null));
setAttribute(__MULTIPART_INPUT_STREAM, _multiPartInputStream);
setAttribute(__MULTIPART_CONTEXT, _context);
Collection<Part> parts = _multiPartInputStream.getParts(); //causes parsing
for (Part p:parts)
{
@ -2012,7 +2070,7 @@ public class Request implements HttpServletRequest
{
if (_authentication instanceof Authentication.Deferred)
{
_authentication=((Authentication.Deferred)_authentication).login(username,password);
_authentication=((Authentication.Deferred)_authentication).login(username,password,this);
if (_authentication == null)
throw new ServletException();
}
@ -2042,7 +2100,7 @@ public class Request implements HttpServletRequest
{
// extract parameters from dispatch query
MultiMap<String> parameters = new MultiMap<String>();
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;
@ -2077,10 +2135,11 @@ public class Request implements HttpServletRequest
{
StringBuilder overridden_query_string = new StringBuilder();
MultiMap<String> overridden_old_query = new MultiMap<String>();
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<String>();
UrlEncoded.decodeTo(query,overridden_new_query,getCharacterEncoding());
UrlEncoded.decodeTo(query,overridden_new_query,StringUtil.__UTF8); //have to assume utf8 as we cannot know otherwise
Iterator<Entry<String, Object>> iter = overridden_old_query.entrySet().iterator();
while (iter.hasNext())

View File

@ -388,7 +388,7 @@ public class Server extends HandlerWrapper implements Attributes
baseRequest.setRequestURI(null);
baseRequest.setPathInfo(baseRequest.getRequestURI());
if (uri.getQuery()!=null)
baseRequest.mergeQueryString(uri.getQuery());
baseRequest.mergeQueryString(uri.getQuery()); //we have to assume dispatch path and query are UTF8
}
final String target=baseRequest.getPathInfo();

View File

@ -50,7 +50,7 @@ import org.eclipse.jetty.util.log.Logger;
<pre>
public static void attemptShutdown(int port, String shutdownCookie) {
try {
URL url = new URL("http://localhost:" + port + "/shutdown?cookie=" + shutdownCookie);
URL url = new URL("http://localhost:" + port + "/shutdown?token=" + shutdownCookie);
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
connection.setRequestMethod("POST");
connection.getResponseCode();

View File

@ -65,8 +65,7 @@ public abstract class AbstractSession implements AbstractSessionManager.SessionI
private boolean _newSession;
private int _requests;
// TODO remove this.
protected final Map<String,Object> _jdbcAttributes=_attributes;
/* ------------------------------------------------------------- */
protected AbstractSession(AbstractSessionManager abstractSessionManager, HttpServletRequest request)
@ -255,6 +254,18 @@ public abstract class AbstractSession implements AbstractSessionManager.SessionI
return (String[])_attributes.keySet().toArray(a);
}
}
/* ------------------------------------------------------------ */
protected Map<String,Object> getAttributeMap ()
{
return _attributes;
}
/* ------------------------------------------------------------ */
protected void addAttributes(Map<String,Object> map)
{
_attributes.putAll(map);
}
/* ------------------------------------------------------------ */
protected boolean access(long time)

View File

@ -483,32 +483,40 @@ public class HashSessionManager extends AbstractSessionManager
protected synchronized HashedSession restoreSession(String idInCuster)
{
File file = new File(_storeDir,idInCuster);
FileInputStream in = null;
Exception error = null;
try
{
if (file.exists())
{
FileInputStream in = new FileInputStream(file);
in = new FileInputStream(file);
HashedSession session = restoreSession(in, null);
in.close();
addSession(session, false);
session.didActivate();
file.delete();
return session;
}
}
catch (Exception e)
{
if (isDeleteUnrestorableSessions())
error = e;
}
finally
{
if (in != null)
try {in.close();} catch (Exception x) {__log.ignore(x);}
if (error != null)
{
if (file.exists())
if (isDeleteUnrestorableSessions() && file.exists())
{
file.delete();
__log.warn("Deleting file for unrestorable session "+idInCuster, e);
__log.warn("Deleting file for unrestorable session "+idInCuster, error);
}
else
__log.warn("Problem restoring session "+idInCuster, error);
}
else
__log.warn("Problem restoring session "+idInCuster, e);
file.delete(); //delete successfully restored file
}
return null;

View File

@ -30,8 +30,11 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
@ -81,7 +84,7 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
protected String _createSessionIdTable;
protected String _createSessionTable;
protected String _selectExpiredSessions;
protected String _selectBoundedExpiredSessions;
protected String _deleteOldExpiredSessions;
protected String _insertId;
@ -96,6 +99,8 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
protected DatabaseAdaptor _dbAdaptor;
private String _selectExpiredSessions;
/**
* DatabaseAdaptor
@ -114,16 +119,16 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
String _dbName;
boolean _isLower;
boolean _isUpper;
public DatabaseAdaptor (DatabaseMetaData dbMeta)
throws SQLException
{
_dbName = dbMeta.getDatabaseProductName().toLowerCase();
_dbName = dbMeta.getDatabaseProductName().toLowerCase(Locale.ENGLISH);
LOG.debug ("Using database {}",_dbName);
_isLower = dbMeta.storesLowerCaseIdentifiers();
_isUpper = dbMeta.storesUpperCaseIdentifiers();
_isUpper = dbMeta.storesUpperCaseIdentifiers();
}
/**
@ -136,9 +141,9 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
public String convertIdentifier (String identifier)
{
if (_isLower)
return identifier.toLowerCase();
return identifier.toLowerCase(Locale.ENGLISH);
if (_isUpper)
return identifier.toUpperCase();
return identifier.toUpperCase(Locale.ENGLISH);
return identifier;
}
@ -455,6 +460,7 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
inUse = _sessionIds.contains(clusterId);
}
if (inUse)
return true; //optimisation - if this session is one we've been managing, we can check locally
@ -514,7 +520,8 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
try
{
initializeDatabase();
prepareTables();
prepareTables();
cleanExpiredSessions();
super.doStart();
if (LOG.isDebugEnabled())
LOG.debug("Scavenging interval = "+getScavengeInterval()+" sec");
@ -542,6 +549,7 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
_timer.cancel();
_timer=null;
}
_sessionIds.clear();
super.doStop();
}
@ -559,31 +567,9 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
else
return DriverManager.getConnection(_connectionUrl);
}
private void initializeDatabase ()
throws Exception
{
if (_datasource != null)
return; //already set up
if (_jndiName!=null)
{
InitialContext ic = new InitialContext();
_datasource = (DataSource)ic.lookup(_jndiName);
}
else if ( _driver != null && _connectionUrl != null )
{
DriverManager.registerDriver(_driver);
}
else if (_driverClassName != null && _connectionUrl != null)
{
Class.forName(_driverClassName);
}
else
throw new IllegalStateException("No database configured for sessions");
}
/**
@ -594,7 +580,8 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
throws SQLException
{
_createSessionIdTable = "create table "+_sessionIdTable+" (id varchar(120), primary key(id))";
_selectExpiredSessions = "select * from "+_sessionTable+" where expiryTime >= ? and expiryTime <= ?";
_selectBoundedExpiredSessions = "select * from "+_sessionTable+" where expiryTime >= ? and expiryTime <= ?";
_selectExpiredSessions = "select * from "+_sessionTable+" where expiryTime >0 and expiryTime <= ?";
_deleteOldExpiredSessions = "delete from "+_sessionTable+" where expiryTime >0 and expiryTime <= ?";
_insertId = "insert into "+_sessionIdTable+" (id) values (?)";
@ -794,7 +781,7 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
connection = getConnection();
connection.setAutoCommit(true);
//"select sessionId from JettySessions where expiryTime > (lastScavengeTime - scanInterval) and expiryTime < lastScavengeTime";
PreparedStatement statement = connection.prepareStatement(_selectExpiredSessions);
PreparedStatement statement = connection.prepareStatement(_selectBoundedExpiredSessions);
long lowerBound = (_lastScavengeTime - _scavengeIntervalMs);
long upperBound = _lastScavengeTime;
if (LOG.isDebugEnabled())
@ -833,7 +820,8 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
if (LOG.isDebugEnabled()) LOG.debug("Deleting old expired sessions expired before "+upperBound);
statement = connection.prepareStatement(_deleteOldExpiredSessions);
statement.setLong(1, upperBound);
statement.executeUpdate();
int rows = statement.executeUpdate();
if (LOG.isDebugEnabled()) LOG.debug("Deleted "+rows+" rows");
}
}
}
@ -861,4 +849,121 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
}
}
}
/**
* Get rid of sessions and sessionids from sessions that have already expired
* @throws Exception
*/
private void cleanExpiredSessions ()
throws Exception
{
Connection connection = null;
List<String> expiredSessionIds = new ArrayList<String>();
try
{
connection = getConnection();
connection.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
connection.setAutoCommit(false);
PreparedStatement statement = connection.prepareStatement(_selectExpiredSessions);
long now = System.currentTimeMillis();
if (LOG.isDebugEnabled()) LOG.debug ("Searching for sessions expired before {}", now);
statement.setLong(1, now);
ResultSet result = statement.executeQuery();
while (result.next())
{
String sessionId = result.getString("sessionId");
expiredSessionIds.add(sessionId);
if (LOG.isDebugEnabled()) LOG.debug ("Found expired sessionId={}", sessionId);
}
Statement sessionsTableStatement = null;
Statement sessionIdsTableStatement = null;
if (!expiredSessionIds.isEmpty())
{
sessionsTableStatement = connection.createStatement();
sessionsTableStatement.executeUpdate(createCleanExpiredSessionsSql("delete from "+_sessionTable+" where sessionId in ", expiredSessionIds));
sessionIdsTableStatement = connection.createStatement();
sessionIdsTableStatement.executeUpdate(createCleanExpiredSessionsSql("delete from "+_sessionIdTable+" where id in ", expiredSessionIds));
}
connection.commit();
synchronized (_sessionIds)
{
_sessionIds.removeAll(expiredSessionIds); //in case they were in our local cache of session ids
}
}
catch (Exception e)
{
if (connection != null)
connection.rollback();
throw e;
}
finally
{
try
{
if (connection != null)
connection.close();
}
catch (SQLException e)
{
LOG.warn(e);
}
}
}
/**
*
* @param sql
* @param connection
* @param expiredSessionIds
* @throws Exception
*/
private String createCleanExpiredSessionsSql (String sql,Collection<String> expiredSessionIds)
throws Exception
{
StringBuffer buff = new StringBuffer();
buff.append(sql);
buff.append("(");
Iterator<String> itor = expiredSessionIds.iterator();
while (itor.hasNext())
{
buff.append("'"+(itor.next())+"'");
if (itor.hasNext())
buff.append(",");
}
buff.append(")");
if (LOG.isDebugEnabled()) LOG.debug("Cleaning expired sessions with: {}", buff);
return buff.toString();
}
private void initializeDatabase ()
throws Exception
{
if (_datasource != null)
return; //already set up
if (_jndiName!=null)
{
InitialContext ic = new InitialContext();
_datasource = (DataSource)ic.lookup(_jndiName);
}
else if ( _driver != null && _connectionUrl != null )
{
DriverManager.registerDriver(_driver);
}
else if (_driverClassName != null && _connectionUrl != null)
{
Class.forName(_driverClassName);
}
else
throw new IllegalStateException("No database configured for sessions");
}
}

View File

@ -267,7 +267,8 @@ public class JDBCSessionManager extends AbstractSessionManager
private static final long serialVersionUID = 5208464051134226143L;
private final SessionData _data;
private boolean _dirty=false;
/**
* Session from a request.
*
@ -276,7 +277,7 @@ public class JDBCSessionManager extends AbstractSessionManager
protected Session (HttpServletRequest request)
{
super(JDBCSessionManager.this,request);
_data = new SessionData(getClusterId(),_jdbcAttributes);
_data = new SessionData(getClusterId(),getAttributeMap());
if (_dftMaxIdleSecs>0)
_data.setMaxIdleMs(_dftMaxIdleSecs*1000L);
_data.setCanonicalContext(canonicalize(_context.getContextPath()));
@ -286,18 +287,18 @@ public class JDBCSessionManager extends AbstractSessionManager
}
/**
* Session restored in database.
* @param data
*/
protected Session (long accessed, SessionData data)
{
super(JDBCSessionManager.this,data.getCreated(), accessed, data.getId());
_data=data;
if (_dftMaxIdleSecs>0)
_data.setMaxIdleMs(_dftMaxIdleSecs*1000L);
_jdbcAttributes.putAll(_data.getAttributeMap());
_data.setAttributeMap(_jdbcAttributes);
}
* Session restored in database.
* @param data
*/
protected Session (long accessed, SessionData data)
{
super(JDBCSessionManager.this,data.getCreated(), accessed, data.getId());
_data=data;
if (_dftMaxIdleSecs>0)
_data.setMaxIdleMs(_dftMaxIdleSecs*1000L);
addAttributes(_data.getAttributeMap());
_data.setAttributeMap(getAttributeMap());
}
@Override
public void setAttribute (String name, Object value)
@ -541,7 +542,7 @@ public class JDBCSessionManager extends AbstractSessionManager
//if the session has no expiry, or it is not already expired
if (data._expiryTime <= 0 || data._expiryTime > now)
{
LOG.debug("getSession("+idInCluster+"): lastNode="+data.getLastNode()+" thisNode="+getSessionIdManager().getWorkerName());
if (LOG.isDebugEnabled()) LOG.debug("getSession("+idInCluster+"): lastNode="+data.getLastNode()+" thisNode="+getSessionIdManager().getWorkerName());
data.setLastNode(getSessionIdManager().getWorkerName());
//session last used on a different node, or we don't have it in memory
session = new Session(now,data);
@ -552,18 +553,21 @@ public class JDBCSessionManager extends AbstractSessionManager
updateSessionNode(data);
}
else
if (LOG.isDebugEnabled()) LOG.debug("getSession("+idInCluster+"): Session has expired");
{
LOG.debug("getSession ({}): Session has expired", idInCluster);
}
}
else
if (LOG.isDebugEnabled()) LOG.debug("getSession("+idInCluster+"): Session not stale "+session._data);
LOG.debug("getSession({}): Session not stale {}", idInCluster,session._data);
//session in db shares same id, but is not for this context
}
else
{
//No session in db with matching id and context path.
session=null;
if (LOG.isDebugEnabled()) LOG.debug("getSession("+idInCluster+"): No session in database matching id="+idInCluster);
LOG.debug("getSession({}): No session in database matching id={}",idInCluster,idInCluster);
}
return session;
@ -607,6 +611,7 @@ public class JDBCSessionManager extends AbstractSessionManager
_jdbcSessionIdMgr = (JDBCSessionIdManager)_sessionIdManager;
_sessions = new ConcurrentHashMap<String, AbstractSession>();
super.doStart();
}

View File

@ -19,6 +19,7 @@
package org.eclipse.jetty.server;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@ -26,12 +27,16 @@ import static org.junit.Assert.fail;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Iterator;
import java.util.Set;
import junit.framework.Assert;
import org.eclipse.jetty.http.EncodedHttpURI;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.io.ByteArrayBuffer;
import org.eclipse.jetty.util.MultiMap;
import org.eclipse.jetty.util.TypeUtil;
import org.junit.Test;
public class HttpURITest
@ -205,6 +210,106 @@ 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
EncodedHttpURI uri = new EncodedHttpURI("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();
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();
request.setUri(httpuri);
request.setAttribute("org.eclipse.jetty.server.Request.queryEncoding", "ISO-8859-1");
assertNotNull (request.getQueryString()); //will be incorrect encoding but not null
request.setAttribute("org.eclipse.jetty.server.Request.queryEncoding", "cp1251");
assertEquals(expectedCP1251String, request.getQueryString());
}
@Test
public void testUnicodeErrors() throws UnsupportedEncodingException

View File

@ -37,7 +37,9 @@ import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.MultipartConfigElement;
import javax.servlet.ServletException;
import javax.servlet.ServletRequestEvent;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@ -49,6 +51,7 @@ import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.MultiPartInputStream;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.log.Log;
import org.junit.After;
@ -131,7 +134,7 @@ public class RequestTest
}
@Test
public void testMultiPart() throws Exception
public void testMultiPartNoConfig() throws Exception
{
_handler._checker = new RequestTester()
{
@ -140,14 +143,16 @@ public class RequestTest
try
{
Part foo = request.getPart("stuff");
assertNotNull(foo);
String value = request.getParameter("stuff");
byte[] expected = "000000000000000000000000000000000000000000000000000".getBytes("ISO-8859-1");
return value.equals(new String(expected, "ISO-8859-1"));
return false;
}
catch (IllegalStateException e)
{
//expected exception because no multipart config is set up
assertTrue(e.getMessage().startsWith("No multipart config"));
return true;
}
catch (Exception e)
{
e.printStackTrace();
return false;
}
}
@ -174,6 +179,66 @@ public class RequestTest
String responses=_connector.getResponses(request);
assertTrue(responses.startsWith("HTTP/1.1 200"));
}
@Test
public void testMultiPart() throws Exception
{
final File tmpDir = new File (System.getProperty("java.io.tmpdir"));
final File testTmpDir = new File (tmpDir, "reqtest");
testTmpDir.deleteOnExit();
assertTrue(testTmpDir.mkdirs());
assertTrue(testTmpDir.list().length == 0);
ContextHandler contextHandler = new ContextHandler();
contextHandler.setContextPath("/foo");
contextHandler.setResourceBase(".");
contextHandler.setHandler(new MultiPartRequestHandler(testTmpDir));
contextHandler.addEventListener(new Request.MultiPartCleanerListener()
{
@Override
public void requestDestroyed(ServletRequestEvent sre)
{
MultiPartInputStream m = (MultiPartInputStream)sre.getServletRequest().getAttribute(Request.__MULTIPART_INPUT_STREAM);
ContextHandler.Context c = (ContextHandler.Context)sre.getServletRequest().getAttribute(Request.__MULTIPART_CONTEXT);
assertNotNull (m);
assertNotNull (c);
assertTrue(c == sre.getServletContext());
assertTrue(!m.getParsedParts().isEmpty());
assertTrue(testTmpDir.list().length == 2);
super.requestDestroyed(sre);
String[] files = testTmpDir.list();
assertTrue(files.length == 0);
}
});
_server.stop();
_server.setHandler(contextHandler);
_server.start();
String multipart = "--AaB03x\r\n"+
"content-disposition: form-data; name=\"field1\"\r\n"+
"\r\n"+
"Joe Blow\r\n"+
"--AaB03x\r\n"+
"content-disposition: form-data; name=\"stuff\"; filename=\"foo.upload\"\r\n"+
"Content-Type: text/plain;charset=ISO-8859-1\r\n"+
"\r\n"+
"000000000000000000000000000000000000000000000000000\r\n"+
"--AaB03x--\r\n";
String request="GET /foo/x.html HTTP/1.1\r\n"+
"Host: whatever\r\n"+
"Content-Type: multipart/form-data; boundary=\"AaB03x\"\r\n"+
"Content-Length: "+multipart.getBytes().length+"\r\n"+
"\r\n"+
multipart;
String responses=_connector.getResponses(request);
System.err.println(responses);
assertTrue(responses.startsWith("HTTP/1.1 200"));
}
@Test
public void testBadUtf8ParamExtraction() throws Exception
@ -912,4 +977,43 @@ public class RequestTest
}
}
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);
}
}
}
}

View File

@ -22,6 +22,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.util.Locale;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.eclipse.jetty.server.session.SessionHandler;
@ -54,7 +55,7 @@ public class SelectChannelTimeoutTest extends ConnectorTimeoutTest
_handler.setSuspendFor(100);
_handler.setResumeAfter(25);
assertTrue(process(null).toUpperCase().contains("RESUMED"));
assertTrue(process(null).toUpperCase(Locale.ENGLISH).contains("RESUMED"));
}
@Test
@ -68,7 +69,7 @@ public class SelectChannelTimeoutTest extends ConnectorTimeoutTest
_server.start();
_handler.setSuspendFor(50);
assertTrue(process(null).toUpperCase().contains("TIMEOUT"));
assertTrue(process(null).toUpperCase(Locale.ENGLISH).contains("TIMEOUT"));
}
@Test
@ -83,7 +84,7 @@ public class SelectChannelTimeoutTest extends ConnectorTimeoutTest
_handler.setSuspendFor(100);
_handler.setCompleteAfter(25);
assertTrue(process(null).toUpperCase().contains("COMPLETED"));
assertTrue(process(null).toUpperCase(Locale.ENGLISH).contains("COMPLETED"));
}
private synchronized String process(String content) throws UnsupportedEncodingException, IOException, InterruptedException

View File

@ -24,6 +24,7 @@ import java.io.IOException;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -104,7 +105,7 @@ public abstract class AbstractConnectHandlerTest
assertTrue(header.lookingAt());
String headerName = header.group(1);
String headerValue = header.group(2);
headers.put(headerName.toLowerCase(), headerValue.toLowerCase());
headers.put(headerName.toLowerCase(Locale.ENGLISH), headerValue.toLowerCase(Locale.ENGLISH));
}
StringBuilder body;

View File

@ -30,6 +30,7 @@ import java.net.Socket;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -152,7 +153,7 @@ public class IPAccessHandlerTest
assertTrue(header.lookingAt());
String headerName = header.group(1);
String headerValue = header.group(2);
headers.put(headerName.toLowerCase(), headerValue.toLowerCase());
headers.put(headerName.toLowerCase(Locale.ENGLISH), headerValue.toLowerCase(Locale.ENGLISH));
}
StringBuilder body = new StringBuilder();

View File

@ -50,6 +50,7 @@ public class ErrorPageErrorHandler extends ErrorHandler
private static final Logger LOG = Log.getLogger(ErrorPageErrorHandler.class);
public final static String ERROR_PAGE="org.eclipse.jetty.server.error_page";
public final static String GLOBAL_ERROR_PAGE = "org.eclipse.jetty.server.error_page.global";
protected ServletContext _servletContext;
private final Map<String,String> _errorPages= new HashMap<String,String>(); // code or exception to URL
@ -120,6 +121,12 @@ public class ErrorPageErrorHandler extends ErrorHandler
}
}
}
//try new servlet 3.0 global error page
if (error_page == null)
{
error_page = _errorPages.get(GLOBAL_ERROR_PAGE);
}
if (error_page!=null)
{

View File

@ -21,6 +21,7 @@ package org.eclipse.jetty.servlet;
import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import javax.servlet.ServletContext;
@ -91,7 +92,7 @@ public class Invoker extends HttpServlet
{
String param=(String)e.nextElement();
String value=getInitParameter(param);
String lvalue=value.toLowerCase();
String lvalue=value.toLowerCase(Locale.ENGLISH);
if ("nonContextServlets".equals(param))
{
_nonContextServlets=value.length()>0 && lvalue.startsWith("t");

View File

@ -48,6 +48,7 @@ import javax.servlet.descriptor.JspPropertyGroupDescriptor;
import javax.servlet.descriptor.TaglibDescriptor;
import org.eclipse.jetty.security.ConstraintAware;
import org.eclipse.jetty.security.ConstraintMapping;
import org.eclipse.jetty.security.ConstraintSecurityHandler;
import org.eclipse.jetty.security.SecurityHandler;
import org.eclipse.jetty.server.Dispatcher;
@ -59,6 +60,7 @@ import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.server.handler.HandlerWrapper;
import org.eclipse.jetty.server.session.SessionHandler;
import org.eclipse.jetty.util.LazyList;
import org.eclipse.jetty.util.security.Constraint;
/* ------------------------------------------------------------ */
@ -377,10 +379,21 @@ public class ServletContextHandler extends ContextHandler
* @param registration ServletRegistration.Dynamic instance that setServletSecurity was called on
* @param servletSecurityElement new security info
* @return the set of exact URL mappings currently associated with the registration that are also present in the web.xml
* security constratins and thus will be unaffected by this call.
* security constraints and thus will be unaffected by this call.
*/
public Set<String> setServletSecurity(ServletRegistration.Dynamic registration, ServletSecurityElement servletSecurityElement)
{
//Default implementation is to just accept them all. If using a webapp, then this behaviour is overridden in WebAppContext.setServletSecurity
Collection<String> pathSpecs = registration.getMappings();
if (pathSpecs != null)
{
for (String pathSpec:pathSpecs)
{
List<ConstraintMapping> mappings = ConstraintSecurityHandler.createConstraintsWithMappingsForPath(registration.getName(), pathSpec, servletSecurityElement);
for (ConstraintMapping m:mappings)
((ConstraintAware)getSecurityHandler()).addConstraintMapping(m);
}
}
return Collections.emptySet();
}

View File

@ -514,6 +514,8 @@ public class ServletHolder extends Holder<Servlet> implements UserIdentity.Scope
initJspServlet();
}
initMultiPart();
_servlet.init(_config);
}
catch (UnavailableException e)
@ -570,6 +572,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());
}
}
/* ------------------------------------------------------------ */
/**

View File

@ -56,6 +56,7 @@ import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.server.handler.ResourceHandler;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.util.TypeUtil;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@ -114,6 +115,23 @@ public class DispatcherTest
assertEquals(expected, responses);
}
@Test public void testForwardNonUTF8() throws Exception
{
_contextHandler.addServlet(ForwardNonUTF8Servlet.class, "/ForwardServlet/*");
_contextHandler.addServlet(AssertNonUTF8ForwardServlet.class, "/AssertForwardServlet/*");
String expected=
"HTTP/1.1 200 OK\r\n"+
"Content-Type: text/html\r\n"+
"Content-Length: 0\r\n"+
"\r\n";
String responses = _connector.getResponses("GET /context/ForwardServlet?do=assertforward&foreign=%d2%e5%ec%ef%e5%f0%e0%f2%f3%f0%e0&test=1 HTTP/1.1\n" + "Host: localhost\n\n");
assertEquals(expected, responses);
}
@Test
public void testForwardWithParam() throws Exception
{
@ -306,6 +324,7 @@ public class DispatcherTest
dispatcher = getServletContext().getRequestDispatcher("/IncludeServlet/includepath?do=assertforwardinclude");
else if(request.getParameter("do").equals("assertincludeforward"))
dispatcher = getServletContext().getRequestDispatcher("/AssertIncludeForwardServlet/assertpath?do=end");
else if(request.getParameter("do").equals("assertforward"))
dispatcher = getServletContext().getRequestDispatcher("/AssertForwardServlet?do=end&do=the");
else if(request.getParameter("do").equals("ctx.echo"))
@ -316,6 +335,18 @@ public class DispatcherTest
}
}
public static class ForwardNonUTF8Servlet extends HttpServlet implements Servlet
{
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
RequestDispatcher dispatcher = null;
request.setAttribute("org.eclipse.jetty.server.Request.queryEncoding", "cp1251");
dispatcher = getServletContext().getRequestDispatcher("/AssertForwardServlet?do=end&else=%D0%B2%D1%8B%D0%B1%D1%80%D0%B0%D0%BD%D0%BE%3D%D0%A2%D0%B5%D0%BC%D0%BF%D0%B5%D1%80%D0%B0%D1%82%D1%83%D1%80%D0%B0");
dispatcher.forward(request, response);
}
}
/*
* Forward filter works with roger, echo and reverse echo servlets to test various
* forwarding bits using filters.
@ -534,6 +565,45 @@ public class DispatcherTest
response.setStatus(HttpServletResponse.SC_OK);
}
}
public static class AssertNonUTF8ForwardServlet extends HttpServlet implements Servlet
{
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
byte[] cp1251_bytes = TypeUtil.fromHexString("d2e5ecefe5f0e0f2f3f0e0");
String expectedCP1251String = new String(cp1251_bytes, "cp1251");
assertEquals( "/context/ForwardServlet", request.getAttribute(Dispatcher.FORWARD_REQUEST_URI));
assertEquals( "/context", request.getAttribute(Dispatcher.FORWARD_CONTEXT_PATH) );
assertEquals( "/ForwardServlet", request.getAttribute(Dispatcher.FORWARD_SERVLET_PATH));
assertEquals( null, request.getAttribute(Dispatcher.FORWARD_PATH_INFO));
assertEquals( "do=assertforward&foreign=%d2%e5%ec%ef%e5%f0%e0%f2%f3%f0%e0&test=1", request.getAttribute(Dispatcher.FORWARD_QUERY_STRING) );
List<String> expectedAttributeNames = Arrays.asList(Dispatcher.FORWARD_REQUEST_URI, Dispatcher.FORWARD_CONTEXT_PATH,
Dispatcher.FORWARD_SERVLET_PATH, Dispatcher.FORWARD_QUERY_STRING);
List<String> requestAttributeNames = Collections.list(request.getAttributeNames());
assertTrue(requestAttributeNames.containsAll(expectedAttributeNames));
assertEquals(null, request.getPathInfo());
assertEquals(null, request.getPathTranslated());
assertTrue(request.getQueryString().startsWith("do=end&else=%D0%B2%D1%8B%D0%B1%D1%80%D0%B0%D0%BD%D0%BE%3D%D0%A2%D0%B5%D0%BC%D0%BF%D0%B5%D1%80%D0%B0%D1%82%D1%83%D1%80%D0%B0&test=1&foreign="));
String[] vals = request.getParameterValues("foreign");
assertTrue(vals!=null);
assertEquals(1, vals.length);
assertEquals(expectedCP1251String, vals[0]);
assertEquals("/context/AssertForwardServlet", request.getRequestURI());
assertEquals("/context", request.getContextPath());
assertEquals("/AssertForwardServlet", request.getServletPath());
response.setContentType("text/html");
response.setStatus(HttpServletResponse.SC_OK);
}
}
public static class AssertIncludeServlet extends HttpServlet implements Servlet
{

View File

@ -24,6 +24,7 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import javax.servlet.ServletException;
@ -139,7 +140,7 @@ public class CGI extends HttpServlet
if (!_env.envMap.containsKey("SystemRoot"))
{
String os = System.getProperty("os.name");
if (os != null && os.toLowerCase().indexOf("windows") != -1)
if (os != null && os.toLowerCase(Locale.ENGLISH).indexOf("windows") != -1)
{
_env.set("SystemRoot","C:\\WINDOWS");
}
@ -256,7 +257,7 @@ public class CGI extends HttpServlet
{
String name = (String)enm.nextElement();
String value = req.getHeader(name);
env.set("HTTP_" + name.toUpperCase().replace('-','_'),value);
env.set("HTTP_" + name.toUpperCase(Locale.ENGLISH).replace('-','_'),value);
}
// these extra ones were from printenv on www.dev.nomura.co.uk

View File

@ -20,6 +20,7 @@ package org.eclipse.jetty.servlets;
import java.io.IOException;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.regex.Pattern;
@ -278,7 +279,7 @@ public class GzipFilter extends UserAgentFilter
{
for (int i=0; i< encodings.length; i++)
{
if (encodings[i].toLowerCase().contains(GZIP))
if (encodings[i].toLowerCase(Locale.ENGLISH).contains(GZIP))
{
if (isEncodingAcceptable(encodings[i]))
{
@ -287,7 +288,7 @@ public class GzipFilter extends UserAgentFilter
}
}
if (encodings[i].toLowerCase().contains(DEFLATE))
if (encodings[i].toLowerCase(Locale.ENGLISH).contains(DEFLATE))
{
if (isEncodingAcceptable(encodings[i]))
{

View File

@ -34,6 +34,7 @@ import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import javax.servlet.Filter;
@ -84,7 +85,7 @@ import org.eclipse.jetty.util.TypeUtil;
public class MultiPartFilter implements Filter
{
public final static String CONTENT_TYPE_SUFFIX=".org.eclipse.jetty.servlet.contentType";
private final static String FILES ="org.eclipse.jetty.servlet.MultiPartFilter.files";
private final static String MULTIPART = "org.eclipse.jetty.servlet.MultiPartFile.multiPartInputStream";
private File tempdir;
private boolean _deleteFiles;
private ServletContext _context;
@ -149,7 +150,8 @@ public class MultiPartFilter implements Filter
MultipartConfigElement config = new MultipartConfigElement(tempdir.getCanonicalPath(), _maxFileSize, _maxRequestSize, _fileOutputBuffer);
MultiPartInputStream mpis = new MultiPartInputStream(in, content_type, config, tempdir);
mpis.setDeleteOnExit(_deleteFiles);
request.setAttribute(MULTIPART, mpis);
try
{
@ -170,18 +172,6 @@ public class MultiPartFilter implements Filter
if (mp.getContentType() != null)
params.add(mp.getName()+CONTENT_TYPE_SUFFIX, mp.getContentType());
}
if (_deleteFiles)
{
mp.getFile().deleteOnExit();
ArrayList files = (ArrayList)request.getAttribute(FILES);
if (files==null)
{
files=new ArrayList();
request.setAttribute(FILES,files);
}
files.add(mp.getFile());
}
}
else
{
@ -202,26 +192,27 @@ public class MultiPartFilter implements Filter
deleteFiles(request);
}
}
/* ------------------------------------------------------------ */
private void deleteFiles(ServletRequest request)
{
ArrayList files = (ArrayList)request.getAttribute(FILES);
if (files!=null)
if (!_deleteFiles)
return;
MultiPartInputStream mpis = (MultiPartInputStream)request.getAttribute(MULTIPART);
if (mpis != null)
{
Iterator iter = files.iterator();
while (iter.hasNext())
try
{
File file=(File)iter.next();
try
{
file.delete();
}
catch(Exception e)
{
_context.log("failed to delete "+file,e);
}
mpis.deleteParts();
}
catch (Exception e)
{
_context.log("Error deleting multipart tmp files", e);
}
}
request.removeAttribute(MULTIPART);
}
/* ------------------------------------------------------------ */

View File

@ -30,6 +30,7 @@ import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.StringTokenizer;
@ -489,7 +490,7 @@ public class ProxyServlet implements Servlet
protected void onResponseHeader(Buffer name, Buffer value) throws IOException
{
String nameString = name.toString();
String s = nameString.toLowerCase();
String s = nameString.toLowerCase(Locale.ENGLISH);
if (!_DontProxyHeaders.contains(s) || (HttpHeaders.CONNECTION_BUFFER.equals(name) && HttpHeaderValues.CLOSE_BUFFER.equals(value)))
{
if (debug != 0)
@ -560,7 +561,7 @@ public class ProxyServlet implements Servlet
String connectionHdr = request.getHeader("Connection");
if (connectionHdr != null)
{
connectionHdr = connectionHdr.toLowerCase();
connectionHdr = connectionHdr.toLowerCase(Locale.ENGLISH);
if (connectionHdr.indexOf("keep-alive") < 0 && connectionHdr.indexOf("close") < 0)
connectionHdr = null;
}
@ -578,7 +579,7 @@ public class ProxyServlet implements Servlet
{
// TODO could be better than this!
String hdr = (String)enm.nextElement();
String lhdr = hdr.toLowerCase();
String lhdr = hdr.toLowerCase(Locale.ENGLISH);
if (_DontProxyHeaders.contains(lhdr))
continue;

View File

@ -121,7 +121,7 @@ public class MultipartFilterTest
response.parse(tester.getResponses(request.generate()));
assertTrue(response.getMethod()==null);
assertEquals(HttpServletResponse.SC_OK,response.getStatus());
assertEquals(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,response.getStatus());
}

View File

@ -30,6 +30,7 @@ import java.util.EnumSet;
import javax.servlet.DispatcherType;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import javax.servlet.http.HttpServletResponse;
@ -63,7 +64,7 @@ public class PutFilterTest
FilterHolder holder = tester.addFilter(PutFilter.class,"/*",EnumSet.of(DispatcherType.REQUEST));
holder.setInitParameter("delAllowed","true");
// Bloody Windows does not allow file renaming
if (!System.getProperty("os.name").toLowerCase().contains("windows"))
if (!System.getProperty("os.name").toLowerCase(Locale.ENGLISH).contains("windows"))
holder.setInitParameter("putAtomic","true");
tester.start();
}

View File

@ -23,6 +23,7 @@ import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
@ -93,7 +94,7 @@ public class Headers implements Iterable<Headers.Header>
*/
public Header get(String name)
{
return headers.get(name.trim().toLowerCase());
return headers.get(name.trim().toLowerCase(Locale.ENGLISH));
}
/**
@ -106,7 +107,7 @@ public class Headers implements Iterable<Headers.Header>
{
name = name.trim();
Header header = new Header(name, value.trim());
headers.put(name.toLowerCase(), header);
headers.put(name.toLowerCase(Locale.ENGLISH), header);
}
/**
@ -117,7 +118,7 @@ public class Headers implements Iterable<Headers.Header>
public void put(Header header)
{
if (header != null)
headers.put(header.name().toLowerCase(), header);
headers.put(header.name().toLowerCase(Locale.ENGLISH), header);
}
/**
@ -130,16 +131,16 @@ public class Headers implements Iterable<Headers.Header>
public void add(String name, String value)
{
name = name.trim();
Header header = headers.get(name.toLowerCase());
Header header = headers.get(name.toLowerCase(Locale.ENGLISH));
if (header == null)
{
header = new Header(name, value.trim());
headers.put(name.toLowerCase(), header);
headers.put(name.toLowerCase(Locale.ENGLISH), header);
}
else
{
header = new Header(header.name(), header.value() + "," + value.trim());
headers.put(name.toLowerCase(), header);
headers.put(name.toLowerCase(Locale.ENGLISH), header);
}
}
@ -152,7 +153,7 @@ public class Headers implements Iterable<Headers.Header>
public Header remove(String name)
{
name = name.trim();
return headers.remove(name.toLowerCase());
return headers.remove(name.toLowerCase(Locale.ENGLISH));
}
/**
@ -229,7 +230,7 @@ public class Headers implements Iterable<Headers.Header>
@Override
public int hashCode()
{
int result = name.toLowerCase().hashCode();
int result = name.toLowerCase(Locale.ENGLISH).hashCode();
result = 31 * result + Arrays.hashCode(values);
return result;
}

View File

@ -21,6 +21,7 @@ package org.eclipse.jetty.spdy.generator;
import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.Locale;
import org.eclipse.jetty.spdy.CompressionDictionary;
import org.eclipse.jetty.spdy.CompressionFactory;
@ -45,7 +46,7 @@ public class HeadersBlockGenerator
writeCount(version, buffer, headers.size());
for (Headers.Header header : headers)
{
String name = header.name().toLowerCase();
String name = header.name().toLowerCase(Locale.ENGLISH);
byte[] nameBytes = name.getBytes(iso1);
writeNameLength(version, buffer, nameBytes.length);
buffer.write(nameBytes, 0, nameBytes.length);

View File

@ -23,6 +23,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
@ -205,7 +206,7 @@ public class ReferrerPushStrategy implements PushStrategy
if (header == null)
return true;
String contentType = header.value().toLowerCase();
String contentType = header.value().toLowerCase(Locale.ENGLISH);
for (String pushContentType : pushContentTypes)
if (contentType.startsWith(pushContentType))
return true;

View File

@ -24,6 +24,7 @@ import java.io.IOException;
import java.io.InterruptedIOException;
import java.nio.ByteBuffer;
import java.util.LinkedList;
import java.util.Locale;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
@ -664,7 +665,7 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
for (int i = 0; i < fields.size(); ++i)
{
HttpFields.Field field = fields.getField(i);
String name = field.getName().toLowerCase();
String name = field.getName().toLowerCase(Locale.ENGLISH);
String value = field.getValue();
headers.put(name, value);
logger.debug("HTTP < {}: {}", name, value);

View File

@ -21,6 +21,7 @@ package org.eclipse.jetty.spdy.proxy;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -95,7 +96,7 @@ public class ProxyHTTPSPDYAsyncConnection extends AsyncHttpConnection
@Override
protected void parsedHeader(Buffer name, Buffer value) throws IOException
{
String headerName = name.toString("UTF-8").toLowerCase();
String headerName = name.toString("UTF-8").toLowerCase(Locale.ENGLISH);
String headerValue = value.toString("UTF-8");
switch (headerName)
{

View File

@ -38,6 +38,7 @@ import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
@ -269,7 +270,7 @@ public class Config
}
else
{
String name = entry.getName().toLowerCase();
String name = entry.getName().toLowerCase(Locale.ENGLISH);
if (name.endsWith(".jar") || name.endsWith(".zip"))
{
String jar = entry.getCanonicalPath();
@ -796,7 +797,7 @@ public class Config
}
// Add XML configuration
if (subject.toLowerCase().endsWith(".xml"))
if (subject.toLowerCase(Locale.ENGLISH).endsWith(".xml"))
{
// Config file
File f = new File(fixPath(file));
@ -807,7 +808,7 @@ public class Config
}
// Set the main class to execute (overrides any previously set)
if (subject.toLowerCase().endsWith(".class"))
if (subject.toLowerCase(Locale.ENGLISH).endsWith(".class"))
{
// Class
String cn = expand(subject.substring(0,subject.length() - 6));
@ -820,7 +821,7 @@ public class Config
}
// Add raw classpath entry
if (subject.toLowerCase().endsWith(".path"))
if (subject.toLowerCase(Locale.ENGLISH).endsWith(".path"))
{
// classpath (jetty.class.path?) to add to runtime classpath
String cn = expand(subject.substring(0,subject.length() - 5));

View File

@ -30,6 +30,7 @@ import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.lang.reflect.InvocationTargetException;
@ -37,6 +38,7 @@ import java.lang.reflect.Method;
import java.net.ConnectException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
@ -44,6 +46,7 @@ import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import java.util.Set;
@ -152,7 +155,7 @@ public class Main
{
public boolean accept(File dir, String name)
{
return name.toLowerCase().endsWith(".ini");
return name.toLowerCase(Locale.ENGLISH).endsWith(".ini");
}
});
Arrays.sort(inis);
@ -184,6 +187,15 @@ public class Main
stop(port,key);
return null;
}
if ("--stop-wait".equals(arg))
{
int port = Integer.parseInt(Config.getProperty("STOP.PORT","-1"));
String key = Config.getProperty("STOP.KEY",null);
int timeout = Integer.parseInt(Config.getProperty("STOP.WAIT", "0"));
stop(port,key, true, timeout);
return null;
}
if ("--version".equals(arg) || "-v".equals(arg) || "--info".equals(arg))
{
@ -288,7 +300,7 @@ public class Main
{
String opts[] = assign[1].split(",");
for (String opt : opts)
_config.addActiveOption(opt);
_config.addActiveOption(opt.trim());
}
else
{
@ -374,7 +386,7 @@ public class Main
return false;
}
String name = path.getName().toLowerCase();
String name = path.getName().toLowerCase(Locale.ENGLISH);
return (name.startsWith("jetty") && name.endsWith(".xml"));
}
});
@ -648,7 +660,7 @@ public class Main
private String resolveXmlConfig(String xmlFilename) throws FileNotFoundException
{
if (!xmlFilename.toLowerCase().endsWith(".xml"))
if (!xmlFilename.toLowerCase(Locale.ENGLISH).endsWith(".xml"))
{
// Nothing to resolve.
return xmlFilename;
@ -862,7 +874,7 @@ public class Main
if (element.isFile())
{
String name = element.getName().toLowerCase();
String name = element.getName().toLowerCase(Locale.ENGLISH);
if (name.endsWith(".jar"))
{
return JarVersion.getVersion(element);
@ -1002,6 +1014,12 @@ public class Main
* Stop a running jetty instance.
*/
public void stop(int port, String key)
{
stop (port,key,false, 0);
}
public void stop (int port, String key, boolean wait, int timeout)
{
int _port = port;
String _key = key;
@ -1020,17 +1038,33 @@ public class Main
}
Socket s = new Socket(InetAddress.getByName("127.0.0.1"),_port);
if (wait && timeout > 0)
s.setSoTimeout(timeout*1000);
try
{
OutputStream out = s.getOutputStream();
out.write((_key + "\r\nstop\r\n").getBytes());
out.flush();
if (wait)
{
System.err.println("Waiting"+(timeout > 0 ? (" "+timeout+"sec") : "")+" for jetty to stop");
LineNumberReader lin = new LineNumberReader(new InputStreamReader(s.getInputStream()));
String response=lin.readLine();
if ("Stopped".equals(response))
System.err.println("Stopped");
}
}
finally
{
s.close();
}
}
catch (SocketTimeoutException e)
{
System.err.println("Timed out waiting for stop confirmation");
System.exit(ERR_UNKNOWN);
}
catch (ConnectException e)
{
usageExit(e,ERR_NOT_STOPPED);

View File

@ -17,6 +17,7 @@
//
package org.eclipse.jetty.start;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.net.InetAddress;
@ -113,21 +114,23 @@ public class Monitor extends Thread
Config.debug("command=" + cmd);
if ("stop".equals(cmd))
{
try {socket.close();}catch(Exception e){e.printStackTrace();}
try {_socket.close();}catch(Exception e){e.printStackTrace();}
if (_process!=null)
{
//if we have a child process, wait for it to finish before we stop
try
{
_process.destroy();
_process.waitFor();
_process.destroy();
_process.waitFor();
}
catch (InterruptedException e)
{
System.err.println("Interrupted waiting for child to terminate");
}
}
socket.getOutputStream().write("Stopped\r\n".getBytes());
try {socket.close();}catch(Exception e){e.printStackTrace();}
try {_socket.close();}catch(Exception e){e.printStackTrace();}
System.exit(0);
}
else if ("status".equals(cmd))

View File

@ -24,7 +24,10 @@ Command Line Options:
contains -X or -D arguments, but creates an extra
JVM instance.
--stop Stop the running Jetty instance.
--stop Send a stop signal to the running Jetty instance.
--stop-wait Send a stop signal to the running Jetty instance, waiting for
confirmation that it is stopping.
--daemon Start in daemon mode with stderr and stdout
redirected to ${jetty.log}/start.log
@ -92,6 +95,11 @@ Properties:
STOP.KEY=[alphanumeric]
The passphrase defined to stop the server.
Requried along with STOP.PORT if you want to use the --stop option above.
STOP.WAIT=[number]
The time (in seconds) to wait for confirmation that the running Jetty server
has stopped. If not specified, the stopper will wait indefinitely. Use in
conjunction with the --stop-wait option.
DEBUG=true
Enable debug on the start mechanism and sets the

View File

@ -34,8 +34,10 @@ import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.StringTokenizer;
@ -59,7 +61,7 @@ public class MultiPartInputStream
protected MultiMap<String> _parts;
protected File _tmpDir;
protected File _contextTmpDir;
protected boolean _deleteOnExit;
@ -73,6 +75,7 @@ public class MultiPartInputStream
protected String _contentType;
protected MultiMap<String> _headers;
protected long _size = 0;
protected boolean _temporary = true;
public MultiPart (String name, String filename)
throws IOException
@ -141,6 +144,8 @@ public class MultiPartInputStream
throws IOException
{
_file = File.createTempFile("MultiPart", "", MultiPartInputStream.this._tmpDir);
if (_deleteOnExit)
_file.deleteOnExit();
FileOutputStream fos = new FileOutputStream(_file);
BufferedOutputStream bos = new BufferedOutputStream(fos);
@ -177,7 +182,7 @@ public class MultiPartInputStream
{
if (name == null)
return null;
return (String)_headers.getValue(name.toLowerCase(), 0);
return (String)_headers.getValue(name.toLowerCase(Locale.ENGLISH), 0);
}
/**
@ -203,11 +208,12 @@ public class MultiPartInputStream
{
if (_file != null)
{
//written to a file, whether temporary or not
return new BufferedInputStream (new FileInputStream(_file));
}
else
{
//part content is in a ByteArrayOutputStream
//part content is in memory
return new ByteArrayInputStream(_bout.getBuf(),0,_bout.size());
}
}
@ -232,7 +238,7 @@ public class MultiPartInputStream
*/
public long getSize()
{
return _size;
return _size;
}
/**
@ -242,8 +248,11 @@ public class MultiPartInputStream
{
if (_file == null)
{
_temporary = false;
//part data is only in the ByteArrayOutputStream and never been written to disk
_file = new File (_tmpDir, fileName);
BufferedOutputStream bos = null;
try
{
@ -261,6 +270,8 @@ public class MultiPartInputStream
else
{
//the part data is already written to a temporary file, just rename it
_temporary = false;
File f = new File(_tmpDir, fileName);
if (_file.renameTo(f))
_file = f;
@ -268,14 +279,27 @@ public class MultiPartInputStream
}
/**
* Remove the file, whether or not Part.write() was called on it
* (ie no longer temporary)
* @see javax.servlet.http.Part#delete()
*/
public void delete() throws IOException
{
if (_file != null)
if (_file != null && _file.exists())
_file.delete();
}
/**
* Only remove tmp files.
*
* @throws IOException
*/
public void cleanUp() throws IOException
{
if (_temporary && _file != null && _file.exists())
_file.delete();
}
/**
* Get the file, if any, the data has been written to.
@ -314,12 +338,65 @@ public class MultiPartInputStream
_contextTmpDir = contextTmpDir;
if (_contextTmpDir == null)
_contextTmpDir = new File (System.getProperty("java.io.tmpdir"));
if (_config == null)
_config = new MultipartConfigElement(_contextTmpDir.getAbsolutePath());
}
/**
* Get the already parsed parts.
*
* @return
*/
public Collection<Part> getParsedParts()
{
if (_parts == null)
return Collections.emptyList();
Collection<Object> values = _parts.values();
List<Part> parts = new ArrayList<Part>();
for (Object o: values)
{
List<Part> asList = LazyList.getList(o, false);
parts.addAll(asList);
}
return parts;
}
/**
* Delete any tmp storage for parts, and clear out the parts list.
*
* @throws MultiException
*/
public void deleteParts ()
throws MultiException
{
Collection<Part> parts = getParsedParts();
MultiException err = new MultiException();
for (Part p:parts)
{
try
{
((MultiPartInputStream.MultiPart)p).cleanUp();
}
catch(Exception e)
{
err.add(e);
}
}
_parts.clear();
err.ifExceptionThrowMulti();
}
/**
* Parse, if necessary, the multipart data and return the list of Parts.
*
* @return
* @throws IOException
* @throws ServletException
*/
public Collection<Part> getParts()
throws IOException, ServletException
{
@ -335,6 +412,14 @@ public class MultiPartInputStream
}
/**
* Get the named Part.
*
* @param name
* @return
* @throws IOException
* @throws ServletException
*/
public Part getPart(String name)
throws IOException, ServletException
{
@ -343,6 +428,12 @@ public class MultiPartInputStream
}
/**
* Parse, if necessary, the multipart stream.
*
* @throws IOException
* @throws ServletException
*/
protected void parse ()
throws IOException, ServletException
{
@ -415,7 +506,7 @@ public class MultiPartInputStream
int c=line.indexOf(':',0);
if(c>0)
{
String key=line.substring(0,c).trim().toLowerCase();
String key=line.substring(0,c).trim().toLowerCase(Locale.ENGLISH);
String value=line.substring(c+1,line.length()).trim();
headers.put(key, value);
if (key.equalsIgnoreCase("content-disposition"))
@ -441,7 +532,7 @@ public class MultiPartInputStream
while(tok.hasMoreTokens())
{
String t=tok.nextToken().trim();
String tl=t.toLowerCase();
String tl=t.toLowerCase(Locale.ENGLISH);
if(t.startsWith("form-data"))
form_data=true;
else if(tl.startsWith("name="))
@ -589,9 +680,22 @@ public class MultiPartInputStream
part.close();
}
}
if (!lastPart)
throw new IOException("Incomplete parts");
}
public void setDeleteOnExit(boolean deleteOnExit)
{
_deleteOnExit = deleteOnExit;
}
public boolean isDeleteOnExit()
{
return _deleteOnExit;
}
/* ------------------------------------------------------------ */
private String value(String nameEqualsValue, boolean splitAfterSpace)
{

View File

@ -27,6 +27,7 @@ import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.TimeZone;
import java.util.Timer;
import java.util.TimerTask;
@ -221,7 +222,7 @@ public class RolloverFileOutputStream extends FilterOutputStream
// Is this a rollover file?
String filename=file.getName();
int i=filename.toLowerCase().indexOf(YYYY_MM_DD);
int i=filename.toLowerCase(Locale.ENGLISH).indexOf(YYYY_MM_DD);
if (i>=0)
{
file=new File(dir,
@ -258,7 +259,7 @@ public class RolloverFileOutputStream extends FilterOutputStream
File file= new File(_filename);
File dir = new File(file.getParent());
String fn=file.getName();
int s=fn.toLowerCase().indexOf(YYYY_MM_DD);
int s=fn.toLowerCase(Locale.ENGLISH).indexOf(YYYY_MM_DD);
if (s<0)
return;
String prefix=fn.substring(0,s);

View File

@ -22,6 +22,7 @@ import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
@ -89,9 +90,9 @@ public class JSONObjectConvertor implements JSON.Convertor
{
String name=m.getName();
if (name.startsWith("is"))
name=name.substring(2,3).toLowerCase()+name.substring(3);
name=name.substring(2,3).toLowerCase(Locale.ENGLISH)+name.substring(3);
else if (name.startsWith("get"))
name=name.substring(3,4).toLowerCase()+name.substring(4);
name=name.substring(3,4).toLowerCase(Locale.ENGLISH)+name.substring(4);
else
continue;

View File

@ -26,6 +26,7 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
@ -122,9 +123,9 @@ public class JSONPojoConvertor implements JSON.Convertor
if(m.getReturnType()!=null)
{
if (name.startsWith("is") && name.length()>2)
name=name.substring(2,3).toLowerCase()+name.substring(3);
name=name.substring(2,3).toLowerCase(Locale.ENGLISH)+name.substring(3);
else if (name.startsWith("get") && name.length()>3)
name=name.substring(3,4).toLowerCase()+name.substring(4);
name=name.substring(3,4).toLowerCase(Locale.ENGLISH)+name.substring(4);
else
break;
if(includeField(name, m))
@ -134,7 +135,7 @@ public class JSONPojoConvertor implements JSON.Convertor
case 1:
if (name.startsWith("set") && name.length()>3)
{
name=name.substring(3,4).toLowerCase()+name.substring(4);
name=name.substring(3,4).toLowerCase(Locale.ENGLISH)+name.substring(4);
if(includeField(name, m))
addSetter(name, m);
}

View File

@ -31,12 +31,11 @@ import org.eclipse.jetty.util.DateCache;
* the eclipse jetty root level logger level to that specified level. (Default level is INFO)
* <p>
* If the system property "org.eclipse.jetty.util.log.SOURCE" is set, then the source method/file of a log is logged.
* For named debuggers, the system property name+".SOURCE" is checked. If it is not not set, then
* "org.eclipse.jetty.util.log.SOURCE" is used as the default.
* For named debuggers, the system property name+".SOURCE" is checked, eg "org.eclipse.jetty.util.log.stderr.SOURCE".
* If it is not not set, then "org.eclipse.jetty.util.log.SOURCE" is used as the default.
* <p>
* If the system property "org.eclipse.jetty.util.log.LONG" is set, then the full, unabbreviated name of the logger is
* used for logging. For named debuggers, the system property name+".LONG" is checked. If it is not not set, then
* "org.eclipse.jetty.util.log.LONG" is used as the default.
* If the system property "org.eclipse.jetty.util.log.stderr.LONG" is set, then the full, unabbreviated name of the logger is
* used for logging.
*/
public class StdErrLog extends AbstractLogger
{

View File

@ -27,6 +27,7 @@ import static org.junit.Assert.assertThat;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
@ -50,8 +51,42 @@ public class MultiPartInputStreamTest extends TestCase
protected String _contentType = "multipart/form-data, boundary=AaB03x";
protected String _multi = createMultipartRequestString(FILENAME);
protected String _dirname = System.getProperty("java.io.tmpdir")+File.separator+"myfiles-"+System.currentTimeMillis();
protected File _tmpDir = new File(_dirname);
public MultiPartInputStreamTest ()
{
_tmpDir.deleteOnExit();
}
public void testBadMultiPartRequest()
throws Exception
{
String boundary = "X0Y0";
String str = "--" + boundary + "\r\n"+
"Content-Disposition: form-data; name=\"fileup\"; filename=\"test.upload\"\r\n"+
"Content-Type: application/octet-stream\r\n\r\n"+
"How now brown cow."+
"\r\n--" + boundary + "-\r\n\r\n";
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(str.getBytes()),
"multipart/form-data, boundary="+boundary,
config,
_tmpDir);
mpis.setDeleteOnExit(true);
try
{
mpis.getParts();
fail ("Multipart incomplete");
}
catch (IOException e)
{
assertTrue(e.getMessage().startsWith("Incomplete"));
}
}
public void testNonMultiPartRequest()
throws Exception
{
@ -59,7 +94,8 @@ public class MultiPartInputStreamTest extends TestCase
MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(_multi.getBytes()),
"Content-type: text/plain",
config,
new File(_dirname));
_tmpDir);
mpis.setDeleteOnExit(true);
assertTrue(mpis.getParts().isEmpty());
}
@ -70,7 +106,8 @@ public class MultiPartInputStreamTest extends TestCase
MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(_multi.getBytes()),
_contentType,
config,
new File(_dirname));
_tmpDir);
mpis.setDeleteOnExit(true);
Collection<Part> parts = mpis.getParts();
assertFalse(parts.isEmpty());
}
@ -82,11 +119,12 @@ public class MultiPartInputStreamTest extends TestCase
MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(_multi.getBytes()),
_contentType,
config,
new File(_dirname));
_tmpDir);
mpis.setDeleteOnExit(true);
Collection<Part> parts = null;
try
{
mpis.getParts();
parts = mpis.getParts();
fail("Request should have exceeded maxRequestSize");
}
catch (IllegalStateException e)
@ -102,11 +140,12 @@ public class MultiPartInputStreamTest extends TestCase
MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(_multi.getBytes()),
_contentType,
config,
new File(_dirname));
_tmpDir);
mpis.setDeleteOnExit(true);
Collection<Part> parts = null;
try
{
mpis.getParts();
parts = mpis.getParts();
fail("stuff.txt should have been larger than maxFileSize");
}
catch (IllegalStateException e)
@ -115,6 +154,48 @@ public class MultiPartInputStreamTest extends TestCase
}
}
public void testPartFileNotDeleted () throws Exception
{
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(createMultipartRequestString("tptfd").getBytes()),
_contentType,
config,
_tmpDir);
mpis.setDeleteOnExit(true);
Collection<Part> parts = mpis.getParts();
MultiPart part = (MultiPart)mpis.getPart("stuff");
File stuff = ((MultiPartInputStream.MultiPart)part).getFile();
assertThat(stuff,notNullValue()); // longer than 100 bytes, should already be a tmp file
part.write("tptfd.txt");
File tptfd = new File (_dirname+File.separator+"tptfd.txt");
assertThat(tptfd.exists(), is(true));
assertThat(stuff.exists(), is(false)); //got renamed
part.cleanUp();
assertThat(tptfd.exists(), is(true)); //explicitly written file did not get removed after cleanup
tptfd.deleteOnExit(); //clean up test
}
public void testPartTmpFileDeletion () throws Exception
{
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(createMultipartRequestString("tptfd").getBytes()),
_contentType,
config,
_tmpDir);
mpis.setDeleteOnExit(true);
Collection<Part> parts = mpis.getParts();
MultiPart part = (MultiPart)mpis.getPart("stuff");
File stuff = ((MultiPartInputStream.MultiPart)part).getFile();
assertThat(stuff,notNullValue()); // longer than 100 bytes, should already be a tmp file
assertThat (stuff.exists(), is(true));
part.cleanUp();
assertThat(stuff.exists(), is(false)); //tmp file was removed after cleanup
}
public void testMulti ()
throws Exception
@ -133,11 +214,11 @@ public class MultiPartInputStreamTest extends TestCase
MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(createMultipartRequestString(filename).getBytes()),
_contentType,
config,
new File(_dirname));
_tmpDir);
mpis.setDeleteOnExit(true);
Collection<Part> parts = mpis.getParts();
assertThat(parts.size(), is(2));
Part field1 = mpis.getPart("field1");
Part field1 = mpis.getPart("field1"); //field 1 too small to go into tmp file, should be in internal buffer
assertThat(field1,notNullValue());
assertThat(field1.getName(),is("field1"));
InputStream is = field1.getInputStream();
@ -146,17 +227,18 @@ public class MultiPartInputStreamTest extends TestCase
assertEquals("Joe Blow", new String(os.toByteArray()));
assertEquals(8, field1.getSize());
assertNotNull(((MultiPartInputStream.MultiPart)field1).getBytes()); //in internal buffer
assertNotNull(((MultiPartInputStream.MultiPart)field1).getBytes());//in internal buffer
field1.write("field1.txt");
assertNull(((MultiPartInputStream.MultiPart)field1).getBytes()); //no longer in internal buffer
assertNull(((MultiPartInputStream.MultiPart)field1).getBytes());//no longer in internal buffer
File f = new File (_dirname+File.separator+"field1.txt");
assertTrue(f.exists());
field1.write("another_field1.txt");
field1.write("another_field1.txt"); //write after having already written
File f2 = new File(_dirname+File.separator+"another_field1.txt");
assertTrue(f2.exists());
assertFalse(f.exists()); //should have been renamed
field1.delete(); //file should be deleted
assertFalse(f2.exists());
assertFalse(f.exists()); //original file was renamed
assertFalse(f2.exists()); //2nd written file was explicitly deleted
MultiPart stuff = (MultiPart)mpis.getPart("stuff");
assertThat(stuff.getContentDispositionFilename(), is(filename));
@ -166,14 +248,24 @@ public class MultiPartInputStreamTest extends TestCase
assertThat(stuff.getHeader("content-disposition"),is("form-data; name=\"stuff\"; filename=\"" + filename + "\""));
assertThat(stuff.getHeaderNames().size(),is(2));
assertThat(stuff.getSize(),is(51L));
f = ((MultiPartInputStream.MultiPart)stuff).getFile();
assertThat(f,notNullValue()); // longer than 100 bytes, should already be a file
assertThat(((MultiPartInputStream.MultiPart)stuff).getBytes(),nullValue()); //not in internal buffer any more
assertThat(f.exists(),is(true));
assertThat(f.getName(),is(not("stuff with space.txt")));
File tmpfile = ((MultiPartInputStream.MultiPart)stuff).getFile();
assertThat(tmpfile,notNullValue()); // longer than 100 bytes, should already be a tmp file
assertThat(((MultiPartInputStream.MultiPart)stuff).getBytes(),nullValue()); //not in an internal buffer
assertThat(tmpfile.exists(),is(true));
assertThat(tmpfile.getName(),is(not("stuff with space.txt")));
stuff.write(filename);
f = new File(_dirname+File.separator+filename);
assertThat(f.exists(),is(true));
assertThat(tmpfile.exists(), is(false));
try
{
stuff.getInputStream();
}
catch (Exception e)
{
fail("Part.getInputStream() after file rename operation");
}
f.deleteOnExit(); //clean up after test
}
public void testMultiSameNames ()
@ -188,15 +280,15 @@ public class MultiPartInputStreamTest extends TestCase
"content-disposition: form-data; name=\"stuff\"; filename=\"stuff2.txt\"\r\n"+
"Content-Type: text/plain\r\n"+
"\r\n"+
"000000000000000000000000000000000000000000000000000\r\n"+
"110000000000000000000000000000000000000000000000000\r\n"+
"--AaB03x--\r\n";
MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(sameNames.getBytes()),
_contentType,
config,
new File(_dirname));
_tmpDir);
mpis.setDeleteOnExit(true);
Collection<Part> parts = mpis.getParts();
assertEquals(2, parts.size());
for (Part p:parts)
@ -210,6 +302,18 @@ public class MultiPartInputStreamTest extends TestCase
private String createMultipartRequestString(String filename)
{
int length = filename.length();
String name = filename;
if (length > 10)
name = filename.substring(0,10);
StringBuffer filler = new StringBuffer();
int i = name.length();
while (i < 51)
{
filler.append("0");
i++;
}
return "--AaB03x\r\n"+
"content-disposition: form-data; name=\"field1\"\r\n"+
"\r\n"+
@ -217,8 +321,8 @@ public class MultiPartInputStreamTest extends TestCase
"--AaB03x\r\n"+
"content-disposition: form-data; name=\"stuff\"; filename=\"" + filename + "\"\r\n"+
"Content-Type: text/plain\r\n"+
"\r\n"+
"000000000000000000000000000000000000000000000000000\r\n"+
"\r\n"+name+
filler.toString()+"\r\n" +
"--AaB03x--\r\n";
}
}

View File

@ -21,6 +21,7 @@ package org.eclipse.jetty.webapp;
import org.eclipse.jetty.util.Loader;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.Resource;
/**
* DiscoveredAnnotation
@ -36,16 +37,28 @@ public abstract class DiscoveredAnnotation
protected WebAppContext _context;
protected String _className;
protected Class<?> _clazz;
protected Resource _resource; //resource it was discovered on, can be null (eg from WEB-INF/classes)
public abstract void apply();
public DiscoveredAnnotation (WebAppContext context, String className)
{
_context = context;
_className = className;
this(context,className, null);
}
public DiscoveredAnnotation(WebAppContext context, String className, Resource resource)
{
_context = context;
_className = className;
_resource = resource;
}
public Resource getResource ()
{
return _resource;
}
public Class<?> getTargetClass()
{
if (_clazz != null)

View File

@ -136,7 +136,7 @@ public class FragmentDescriptor extends WebDescriptor
node = (XmlParser.Node) o;
if (node.getTag().equalsIgnoreCase("others"))
{
if (_otherType != OtherType.After)
if (_otherType != OtherType.None)
throw new IllegalStateException("Duplicate <other> clause detected in "+_xml.getURI());
_otherType = OtherType.After;

Some files were not shown because too many files have changed in this diff Show More