Merge branch jetty-10.0.x into jetty-10.0.x-4538-MessageReaderWriter
This commit is contained in:
commit
fef25e7825
34
VERSION.txt
34
VERSION.txt
|
@ -355,6 +355,40 @@ jetty-10.0.0-alpha0 - 11 July 2019
|
|||
+ 3849 ClosedChannelException from jetty-test-webapp javax websocket chat
|
||||
example
|
||||
|
||||
jetty-9.4.27.v20200227 - 27 February 2020
|
||||
+ 3247 Generate jetty-maven-plugin website
|
||||
+ 4247 Cookie security attributes are going to mandated by Google Chrome
|
||||
+ 4360 Upgrade to Apache Jasper 8.5.49
|
||||
+ 4475 WebSocket JSR356 implementation not honoring javadoc of MessageHandler
|
||||
on Whole<Reader>
|
||||
+ 4495 Review ReservedThreadExecutor's concurrency model
|
||||
+ 4504 X-Forwarded-Server header overwrites X-Forwarded-Host
|
||||
+ 4520 Jetty jdbc session manager causing exceptions for violating primary key
|
||||
in inserting session in the table
|
||||
+ 4529 ErrorHandler showing servlet info, can not be disabled unless
|
||||
overriding most of its functionality
|
||||
+ 4533 Reinstate hard close in dispatcher
|
||||
+ 4537 High CPU on Jetty Websocket thread
|
||||
+ 4541 Jetty server always allocates maximum response header size
|
||||
+ 4550 XmlConfiguration constructor selection based on number of arguments
|
||||
+ 4567 Jetty logging supporting Throwable as last argument
|
||||
+ 4573 Order dependency of X-Forwarded-Host and X-Forwarded-Port
|
||||
+ 4575 Stopping ReservedThreadExecutor may hang
|
||||
+ 4577 request getPathInfo returns null
|
||||
+ 4594 ServletContextListeners added to destroyServletContextListeners in
|
||||
ContextHandler::startContext() are not removed by
|
||||
ContextHandler::removeEventListener()
|
||||
+ 4606 DateCache.formatNow(long now) does not honor the passed in long
|
||||
+ 4612 ReservedThreadExecutor hangs when the last reserved thread idles out
|
||||
|
||||
jetty-9.4.26.v20200117 - 17 January 2020
|
||||
+ 2620 Exception from user endpoint onClose results in unclosed
|
||||
WebSocketSession
|
||||
+ 4383 Errors deleting multipart tmp files java.lang.NullPointerException
|
||||
under heavy load
|
||||
+ 4444 TLS Connection Timeout Intermittently
|
||||
+ 4461 IllegalStateException in HttpOutput with Jersey
|
||||
|
||||
jetty-9.4.25.v20191220 - 20 December 2019
|
||||
+ 995 UrlEncoded.encodeString should skip more characters
|
||||
+ 2195 Add parameter expansion to start.jar --exec parameters
|
||||
|
|
|
@ -39,6 +39,7 @@ import java.util.concurrent.CountDownLatch;
|
|||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.servlet.ServletContainerInitializer;
|
||||
import javax.servlet.annotation.HandlesTypes;
|
||||
|
||||
|
@ -58,7 +59,6 @@ import org.eclipse.jetty.webapp.AbstractConfiguration;
|
|||
import org.eclipse.jetty.webapp.FragmentConfiguration;
|
||||
import org.eclipse.jetty.webapp.FragmentDescriptor;
|
||||
import org.eclipse.jetty.webapp.JettyWebXmlConfiguration;
|
||||
import org.eclipse.jetty.webapp.MetaDataComplete;
|
||||
import org.eclipse.jetty.webapp.MetaInfConfiguration;
|
||||
import org.eclipse.jetty.webapp.WebAppContext;
|
||||
import org.eclipse.jetty.webapp.WebDescriptor;
|
||||
|
@ -335,11 +335,13 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
@Override
|
||||
public void configure(WebAppContext context) throws Exception
|
||||
{
|
||||
//handle introspectable annotations (postconstruct,predestroy, multipart etc etc)
|
||||
context.getObjectFactory().addDecorator(new AnnotationDecorator(context));
|
||||
|
||||
if (!context.getMetaData().isMetaDataComplete())
|
||||
{
|
||||
//If metadata isn't complete, if this is a servlet 3 webapp or isConfigDiscovered is true, we need to search for annotations
|
||||
//If web.xml not metadata-complete, if this is a servlet 3 webapp or above
|
||||
//or configDiscovered is true, we need to search for annotations
|
||||
if (context.getServletContext().getEffectiveMajorVersion() >= 3 || context.isConfigurationDiscovered())
|
||||
{
|
||||
_discoverableAnnotationHandlers.add(new WebServletAnnotationHandler(context));
|
||||
|
@ -408,7 +410,8 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
}
|
||||
|
||||
/**
|
||||
* Perform scanning of classes for annotations
|
||||
* Perform scanning of classes for discoverable
|
||||
* annotations such as WebServlet/WebFilter/WebListener
|
||||
*
|
||||
* @param context the context for the scan
|
||||
* @throws Exception if unable to scan
|
||||
|
@ -431,6 +434,7 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
isUseMultiThreading(context),
|
||||
getMaxScanWait(context));
|
||||
|
||||
//scan selected jars on the container classpath first
|
||||
parseContainerPath(context, parser);
|
||||
//email from Rajiv Mordani jsrs 315 7 April 2010
|
||||
// If there is a <others/> then the ordering should be
|
||||
|
@ -438,6 +442,7 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
// In case there is no others then it is
|
||||
// WEB-INF/classes + order of the elements.
|
||||
parseWebInfClasses(context, parser);
|
||||
//scan non-excluded, non medatadata-complete jars in web-inf lib
|
||||
parseWebInfLib(context, parser);
|
||||
|
||||
long start = System.nanoTime();
|
||||
|
@ -691,14 +696,14 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
}
|
||||
|
||||
//If no ordering, nothing is excluded
|
||||
if (context.getMetaData().getOrdering() == null)
|
||||
if (!context.getMetaData().isOrdered())
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("!Excluded {} no ordering", sci);
|
||||
return false;
|
||||
}
|
||||
|
||||
List<Resource> orderedJars = context.getMetaData().getOrderedWebInfJars();
|
||||
List<Resource> orderedJars = context.getMetaData().getWebInfResources(true);
|
||||
|
||||
//there is an ordering, but there are no jars resulting from the ordering, everything excluded
|
||||
if (orderedJars.isEmpty())
|
||||
|
@ -710,17 +715,17 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
|
||||
//Check if it is excluded by an ordering
|
||||
URI loadingJarURI = sciResource.getURI();
|
||||
boolean found = false;
|
||||
Iterator<Resource> itor = orderedJars.iterator();
|
||||
while (!found && itor.hasNext())
|
||||
boolean included = false;
|
||||
for (Resource r : orderedJars)
|
||||
{
|
||||
Resource r = itor.next();
|
||||
found = r.getURI().equals(loadingJarURI);
|
||||
included = r.getURI().equals(loadingJarURI);
|
||||
if (included)
|
||||
break;
|
||||
}
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{}Excluded {} found={}", found ? "!" : "", sci, found);
|
||||
return !found;
|
||||
LOG.debug("{}Excluded {} found={}", included ? "!" : "", sci, included);
|
||||
return !included;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -785,7 +790,7 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
*/
|
||||
public boolean isFromWebInfClasses(WebAppContext context, Resource sci)
|
||||
{
|
||||
for (Resource dir : context.getMetaData().getWebInfClassesDirs())
|
||||
for (Resource dir : context.getMetaData().getWebInfClassesResources())
|
||||
{
|
||||
if (dir.equals(sci))
|
||||
{
|
||||
|
@ -831,7 +836,7 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
catch (Error e)
|
||||
{
|
||||
// Probably a SCI discovered on the system classpath that is hidden by the context classloader
|
||||
LOG.info("Error: " + e.getMessage() + " for " + context);
|
||||
LOG.info("Error: {} for {}", e.getMessage(), context);
|
||||
LOG.debug(e);
|
||||
continue;
|
||||
}
|
||||
|
@ -867,7 +872,7 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
if (initializerOrdering != null && !initializerOrdering.isDefaultOrder())
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Ordering ServletContainerInitializers with " + initializerOrdering);
|
||||
LOG.debug("Ordering ServletContainerInitializers with {}", initializerOrdering);
|
||||
|
||||
//There is an ordering that is not just "*".
|
||||
//Arrange ServletContainerInitializers according to the ordering of classnames given, irrespective of coming from container or webapp classpaths
|
||||
|
@ -894,7 +899,7 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
}
|
||||
else
|
||||
{
|
||||
for (Resource dir : context.getMetaData().getWebInfClassesDirs())
|
||||
for (Resource dir : context.getMetaData().getWebInfClassesResources())
|
||||
{
|
||||
if (dir.equals(entry.getValue()))//from WEB-INF/classes so can't be ordered/excluded
|
||||
{
|
||||
|
@ -923,7 +928,7 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
LOG.debug("Ordering ServletContainerInitializers with ordering {}", context.getMetaData().getOrdering());
|
||||
|
||||
//add SCIs according to the ordering of its containing jar
|
||||
for (Resource webInfJar : context.getMetaData().getOrderedWebInfJars())
|
||||
for (Resource webInfJar : context.getMetaData().getWebInfResources(true))
|
||||
{
|
||||
for (Map.Entry<ServletContainerInitializer, Resource> entry : sciResourceMap.entrySet())
|
||||
{
|
||||
|
@ -977,7 +982,7 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
return null;
|
||||
|
||||
String tmp = (String)context.getAttribute(SERVLET_CONTAINER_INITIALIZER_ORDER);
|
||||
if (tmp == null || "".equals(tmp.trim()))
|
||||
if (StringUtil.isBlank(tmp))
|
||||
return null;
|
||||
|
||||
return new ServletContainerInitializerOrdering(tmp);
|
||||
|
@ -1002,9 +1007,10 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
if (LOG.isDebugEnabled())
|
||||
_containerPathStats = new CounterStatistic();
|
||||
|
||||
//scan the container classpath jars that were selected by
|
||||
//filtering in MetaInfConfiguration
|
||||
for (Resource r : context.getMetaData().getContainerResources())
|
||||
{
|
||||
//queue it up for scanning if using multithreaded mode
|
||||
if (_parserTasks != null)
|
||||
{
|
||||
ParserTask task = new ParserTask(parser, handlers, r);
|
||||
|
@ -1019,7 +1025,10 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
}
|
||||
|
||||
/**
|
||||
* Scan jars in WEB-INF/lib
|
||||
* Scan jars in WEB-INF/lib.
|
||||
*
|
||||
* Only jars selected by MetaInfConfiguration, and that are not excluded
|
||||
* by an ordering will be considered.
|
||||
*
|
||||
* @param context the context for the scan
|
||||
* @param parser the annotation parser to use
|
||||
|
@ -1027,20 +1036,13 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
*/
|
||||
public void parseWebInfLib(final WebAppContext context, final AnnotationParser parser) throws Exception
|
||||
{
|
||||
List<FragmentDescriptor> frags = context.getMetaData().getFragments();
|
||||
|
||||
//email from Rajiv Mordani jsrs 315 7 April 2010
|
||||
//jars that do not have a web-fragment.xml are still considered fragments
|
||||
//they have to participate in the ordering
|
||||
ArrayList<URI> webInfUris = new ArrayList<URI>();
|
||||
|
||||
List<Resource> jars = null;
|
||||
|
||||
if (context.getMetaData().getOrdering() != null)
|
||||
jars = context.getMetaData().getOrderedWebInfJars();
|
||||
else
|
||||
//No ordering just use the jars in any order
|
||||
jars = context.getMetaData().getWebInfJars();
|
||||
//if there is an ordering, the ordered jars should be used.
|
||||
//If there is no ordering, jars will be unordered.
|
||||
List<Resource> jars = context.getMetaData().getWebInfResources(context.getMetaData().isOrdered());
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
|
@ -1053,13 +1055,13 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
//for each jar, we decide which set of annotations we need to parse for
|
||||
final Set<Handler> handlers = new HashSet<Handler>();
|
||||
|
||||
FragmentDescriptor f = getFragmentFromJar(r, frags);
|
||||
FragmentDescriptor f = context.getMetaData().getFragmentDescriptorForJar(r);
|
||||
|
||||
//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) || _classInheritanceHandler != null || !_containerInitializerAnnotationHandlers.isEmpty())
|
||||
if (f == null || !WebDescriptor.isMetaDataComplete(f) || _classInheritanceHandler != null || !_containerInitializerAnnotationHandlers.isEmpty())
|
||||
{
|
||||
//register the classinheritance handler if there is one
|
||||
if (_classInheritanceHandler != null)
|
||||
|
@ -1069,7 +1071,7 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
handlers.addAll(_containerInitializerAnnotationHandlers);
|
||||
|
||||
//only register the discoverable annotation handlers if this fragment is not metadata complete, or has no fragment descriptor
|
||||
if (f == null || !isMetaDataComplete(f))
|
||||
if (f == null || !WebDescriptor.isMetaDataComplete(f))
|
||||
handlers.addAll(_discoverableAnnotationHandlers);
|
||||
|
||||
if (_parserTasks != null)
|
||||
|
@ -1087,7 +1089,7 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
}
|
||||
|
||||
/**
|
||||
* Scan classes in WEB-INF/classes
|
||||
* Scan classes in WEB-INF/classes.
|
||||
*
|
||||
* @param context the context for the scan
|
||||
* @param parser the annotation parser to use
|
||||
|
@ -1105,7 +1107,7 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
if (LOG.isDebugEnabled())
|
||||
_webInfClassesStats = new CounterStatistic();
|
||||
|
||||
for (Resource dir : context.getMetaData().getWebInfClassesDirs())
|
||||
for (Resource dir : context.getMetaData().getWebInfClassesResources())
|
||||
{
|
||||
if (_parserTasks != null)
|
||||
{
|
||||
|
@ -1120,39 +1122,8 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the web-fragment.xml from a jar
|
||||
*
|
||||
* @param jar the jar to look in for a fragment
|
||||
* @param frags the fragments previously found
|
||||
* @return true if the fragment if found, or null of not found
|
||||
* @throws Exception if unable to determine the the fragment contains
|
||||
*/
|
||||
public FragmentDescriptor getFragmentFromJar(Resource jar, List<FragmentDescriptor> frags)
|
||||
throws Exception
|
||||
{
|
||||
//check if the jar has a web-fragment.xml
|
||||
FragmentDescriptor d = null;
|
||||
for (FragmentDescriptor frag : frags)
|
||||
{
|
||||
Resource fragResource = frag.getResource(); //eg jar:file:///a/b/c/foo.jar!/META-INF/web-fragment.xml
|
||||
if (Resource.isContainedIn(fragResource, jar))
|
||||
{
|
||||
d = frag;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
public boolean isMetaDataComplete(WebDescriptor d)
|
||||
{
|
||||
return (d != null && d.getMetaDataComplete() == MetaDataComplete.True);
|
||||
}
|
||||
|
||||
public static class ClassInheritanceMap extends ConcurrentHashMap<String, Set<String>>
|
||||
{
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
|
|
|
@ -18,6 +18,9 @@
|
|||
|
||||
package org.eclipse.jetty.annotations;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import org.eclipse.jetty.util.DecoratedObjectFactory;
|
||||
import org.eclipse.jetty.util.Decorator;
|
||||
import org.eclipse.jetty.webapp.WebAppContext;
|
||||
|
||||
|
@ -26,23 +29,26 @@ import org.eclipse.jetty.webapp.WebAppContext;
|
|||
*/
|
||||
public class AnnotationDecorator implements Decorator
|
||||
{
|
||||
protected AnnotationIntrospector _introspector = new AnnotationIntrospector();
|
||||
protected AnnotationIntrospector _introspector;
|
||||
protected WebAppContext _context;
|
||||
|
||||
public AnnotationDecorator(WebAppContext context)
|
||||
{
|
||||
registerHandlers(context);
|
||||
_context = Objects.requireNonNull(context);
|
||||
_introspector = new AnnotationIntrospector(_context);
|
||||
registerHandlers();
|
||||
}
|
||||
|
||||
public void registerHandlers(WebAppContext context)
|
||||
private void registerHandlers()
|
||||
{
|
||||
_introspector.registerHandler(new ResourceAnnotationHandler(context));
|
||||
_introspector.registerHandler(new ResourcesAnnotationHandler(context));
|
||||
_introspector.registerHandler(new RunAsAnnotationHandler(context));
|
||||
_introspector.registerHandler(new PostConstructAnnotationHandler(context));
|
||||
_introspector.registerHandler(new PreDestroyAnnotationHandler(context));
|
||||
_introspector.registerHandler(new DeclareRolesAnnotationHandler(context));
|
||||
_introspector.registerHandler(new MultiPartConfigAnnotationHandler(context));
|
||||
_introspector.registerHandler(new ServletSecurityAnnotationHandler(context));
|
||||
_introspector.registerHandler(new ResourceAnnotationHandler(_context));
|
||||
_introspector.registerHandler(new ResourcesAnnotationHandler(_context));
|
||||
_introspector.registerHandler(new RunAsAnnotationHandler(_context));
|
||||
_introspector.registerHandler(new PostConstructAnnotationHandler(_context));
|
||||
_introspector.registerHandler(new PreDestroyAnnotationHandler(_context));
|
||||
_introspector.registerHandler(new DeclareRolesAnnotationHandler(_context));
|
||||
_introspector.registerHandler(new MultiPartConfigAnnotationHandler(_context));
|
||||
_introspector.registerHandler(new ServletSecurityAnnotationHandler(_context));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -50,22 +56,28 @@ public class AnnotationDecorator implements Decorator
|
|||
* <ul>
|
||||
* <li> Resource </li>
|
||||
* <li> Resources </li>
|
||||
* <li> RunAs </li>
|
||||
* <li> PostConstruct </li>
|
||||
* <li> PreDestroy </li>
|
||||
* <li> ServletSecurity? </li>
|
||||
* <li> DeclareRoles </li>
|
||||
* <li> MultiPart </li>
|
||||
* <li> ServletSecurity</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param o the object ot introspect
|
||||
* @param o the object to introspect
|
||||
* @param metaInfo information about the object to introspect
|
||||
*/
|
||||
protected void introspect(Object o)
|
||||
protected void introspect(Object o, Object metaInfo)
|
||||
{
|
||||
_introspector.introspect(o.getClass());
|
||||
if (o == null)
|
||||
return;
|
||||
_introspector.introspect(o, metaInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object decorate(Object o)
|
||||
{
|
||||
introspect(o);
|
||||
introspect(o, DecoratedObjectFactory.getAssociatedInfo());
|
||||
return o;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,11 +18,21 @@
|
|||
|
||||
package org.eclipse.jetty.annotations;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jetty.servlet.BaseHolder;
|
||||
import org.eclipse.jetty.servlet.Source.Origin;
|
||||
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.WebAppContext;
|
||||
import org.eclipse.jetty.webapp.WebDescriptor;
|
||||
|
||||
/**
|
||||
* AnnotationIntrospector
|
||||
* Introspects a class to find various types of
|
||||
|
@ -30,8 +40,10 @@ import java.util.Set;
|
|||
*/
|
||||
public class AnnotationIntrospector
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(AnnotationIntrospector.class);
|
||||
private final Set<Class<?>> _introspectedClasses = new HashSet<>();
|
||||
private final List<IntrospectableAnnotationHandler> _handlers = new ArrayList<IntrospectableAnnotationHandler>();
|
||||
private final WebAppContext _context;
|
||||
|
||||
/**
|
||||
* IntrospectableAnnotationHandler
|
||||
|
@ -51,12 +63,14 @@ public class AnnotationIntrospector
|
|||
*/
|
||||
public abstract static class AbstractIntrospectableAnnotationHandler implements IntrospectableAnnotationHandler
|
||||
{
|
||||
private boolean _introspectAncestors;
|
||||
protected boolean _introspectAncestors;
|
||||
protected WebAppContext _context;
|
||||
|
||||
public abstract void doHandle(Class<?> clazz);
|
||||
|
||||
public AbstractIntrospectableAnnotationHandler(boolean introspectAncestors)
|
||||
public AbstractIntrospectableAnnotationHandler(boolean introspectAncestors, WebAppContext context)
|
||||
{
|
||||
_context = Objects.requireNonNull(context);
|
||||
_introspectAncestors = introspectAncestors;
|
||||
}
|
||||
|
||||
|
@ -75,6 +89,16 @@ public class AnnotationIntrospector
|
|||
c = c.getSuperclass();
|
||||
}
|
||||
}
|
||||
|
||||
public WebAppContext getContext()
|
||||
{
|
||||
return _context;
|
||||
}
|
||||
}
|
||||
|
||||
public AnnotationIntrospector(WebAppContext context)
|
||||
{
|
||||
_context = Objects.requireNonNull(context);
|
||||
}
|
||||
|
||||
public void registerHandler(IntrospectableAnnotationHandler handler)
|
||||
|
@ -82,13 +106,96 @@ public class AnnotationIntrospector
|
|||
_handlers.add(handler);
|
||||
}
|
||||
|
||||
public void introspect(Class<?> clazz)
|
||||
/**
|
||||
* Test if an object should be introspected for some specific types of annotations
|
||||
* like PostConstruct/PreDestroy/MultiPart etc etc.
|
||||
*
|
||||
* According to servlet 4.0, these types of annotations should only be evaluated iff any
|
||||
* of the following are true:
|
||||
* <ol>
|
||||
* <li>the object was created by the javax.servlet.ServletContext.createServlet/Filter/Listener method</li>
|
||||
* <li>the object comes either from a discovered annotation (WebServlet/Filter/Listener) or a declaration
|
||||
* in a descriptor AND web.xml is NOT metadata-complete AND any web-fragment.xml associated with the location of
|
||||
* the class is NOT metadata-complete</li>
|
||||
* </ol>
|
||||
*
|
||||
* We also support evaluations of these types of annotations for objects that were created directly
|
||||
* by the jetty api.
|
||||
*
|
||||
* @param o the object to check for its ability to be introspected for annotations
|
||||
* @param metaInfo meta information about the object to be introspected
|
||||
* @return true if it can be introspected according to servlet 4.0 rules
|
||||
*/
|
||||
public boolean isIntrospectable(Object o, Object metaInfo)
|
||||
{
|
||||
if (_handlers == null)
|
||||
return;
|
||||
if (clazz == null)
|
||||
if (o == null)
|
||||
return false; //nothing to introspect
|
||||
|
||||
if (metaInfo == null)
|
||||
return true; //no information about the object to introspect, assume introspectable
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
BaseHolder holder = null;
|
||||
|
||||
try
|
||||
{
|
||||
holder = (BaseHolder)metaInfo;
|
||||
}
|
||||
catch (ClassCastException e)
|
||||
{
|
||||
LOG.warn(e);
|
||||
return true; //not the type of information we were expecting, assume introspectable
|
||||
}
|
||||
|
||||
Origin origin = (holder.getSource() == null ? null : holder.getSource().getOrigin());
|
||||
if (origin == null)
|
||||
return true; //assume introspectable
|
||||
|
||||
switch (origin)
|
||||
{
|
||||
case EMBEDDED:
|
||||
case JAVAX_API:
|
||||
{
|
||||
return true; //objects created from the jetty or servlet api are always introspectable
|
||||
}
|
||||
case ANNOTATION:
|
||||
{
|
||||
return true; //we will have discovered annotations only if metadata-complete==false
|
||||
}
|
||||
default:
|
||||
{
|
||||
//must be from a descriptor. Only introspect if the descriptor with which it was associated
|
||||
//is not metadata-complete
|
||||
if (_context.getMetaData().isMetaDataComplete())
|
||||
return false;
|
||||
|
||||
String descriptorLocation = holder.getSource().getResource();
|
||||
if (descriptorLocation == null)
|
||||
return true; //no descriptor, can't be metadata-complete
|
||||
try
|
||||
{
|
||||
return !WebDescriptor.isMetaDataComplete(_context.getMetaData().getFragmentDescriptor(Resource.newResource(descriptorLocation)));
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
LOG.warn("Unable to get Resource for descriptor {}", descriptorLocation, e);
|
||||
return false; //something wrong with the descriptor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param o
|
||||
* @param metaInfo
|
||||
*/
|
||||
public void introspect(Object o, Object metaInfo)
|
||||
{
|
||||
if (!isIntrospectable(o, metaInfo))
|
||||
return;
|
||||
|
||||
Class<?> clazz = o.getClass();
|
||||
|
||||
synchronized (_introspectedClasses)
|
||||
{
|
||||
//Synchronize on the set of already introspected classes.
|
||||
|
|
|
@ -35,12 +35,9 @@ public class DeclareRolesAnnotationHandler extends AbstractIntrospectableAnnotat
|
|||
{
|
||||
private static final Logger LOG = Log.getLogger(DeclareRolesAnnotationHandler.class);
|
||||
|
||||
protected WebAppContext _context;
|
||||
|
||||
public DeclareRolesAnnotationHandler(WebAppContext context)
|
||||
{
|
||||
super(false);
|
||||
_context = context;
|
||||
super(false, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -33,13 +33,10 @@ import org.eclipse.jetty.webapp.WebAppContext;
|
|||
*/
|
||||
public class MultiPartConfigAnnotationHandler extends AbstractIntrospectableAnnotationHandler
|
||||
{
|
||||
protected WebAppContext _context;
|
||||
|
||||
public MultiPartConfigAnnotationHandler(WebAppContext context)
|
||||
{
|
||||
//TODO verify that MultipartConfig is not inheritable
|
||||
super(false);
|
||||
_context = context;
|
||||
super(false, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -31,12 +31,9 @@ import org.eclipse.jetty.webapp.WebAppContext;
|
|||
|
||||
public class PostConstructAnnotationHandler extends AbstractIntrospectableAnnotationHandler
|
||||
{
|
||||
protected WebAppContext _context;
|
||||
|
||||
public PostConstructAnnotationHandler(WebAppContext wac)
|
||||
{
|
||||
super(true);
|
||||
_context = wac;
|
||||
super(true, wac);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -31,12 +31,9 @@ import org.eclipse.jetty.webapp.WebAppContext;
|
|||
|
||||
public class PreDestroyAnnotationHandler extends AbstractIntrospectableAnnotationHandler
|
||||
{
|
||||
WebAppContext _context;
|
||||
|
||||
public PreDestroyAnnotationHandler(WebAppContext wac)
|
||||
{
|
||||
super(true);
|
||||
_context = wac;
|
||||
super(true, wac);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -48,12 +48,9 @@ public class ResourceAnnotationHandler extends AbstractIntrospectableAnnotationH
|
|||
Float.class
|
||||
});
|
||||
|
||||
protected WebAppContext _context;
|
||||
|
||||
public ResourceAnnotationHandler(WebAppContext wac)
|
||||
{
|
||||
super(true);
|
||||
_context = wac;
|
||||
super(true, wac);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -31,12 +31,9 @@ public class ResourcesAnnotationHandler extends AbstractIntrospectableAnnotation
|
|||
{
|
||||
private static final Logger LOG = Log.getLogger(ResourcesAnnotationHandler.class);
|
||||
|
||||
protected WebAppContext _wac;
|
||||
|
||||
public ResourcesAnnotationHandler(WebAppContext wac)
|
||||
{
|
||||
super(true);
|
||||
_wac = wac;
|
||||
super(true, wac);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -64,8 +61,8 @@ public class ResourcesAnnotationHandler extends AbstractIntrospectableAnnotation
|
|||
{
|
||||
//TODO don't ignore the shareable, auth etc etc
|
||||
|
||||
if (!org.eclipse.jetty.plus.jndi.NamingEntryUtil.bindToENC(_wac, name, mappedName))
|
||||
if (!org.eclipse.jetty.plus.jndi.NamingEntryUtil.bindToENC(_wac.getServer(), name, mappedName))
|
||||
if (!org.eclipse.jetty.plus.jndi.NamingEntryUtil.bindToENC(_context, name, mappedName))
|
||||
if (!org.eclipse.jetty.plus.jndi.NamingEntryUtil.bindToENC(_context.getServer(), name, mappedName))
|
||||
LOG.warn("Skipping Resources(Resource) annotation on " + clazz.getName() + " for name " + name + ": No resource bound at " + (mappedName == null ? name : mappedName));
|
||||
}
|
||||
catch (NamingException e)
|
||||
|
|
|
@ -33,14 +33,11 @@ public class RunAsAnnotationHandler extends AbstractIntrospectableAnnotationHand
|
|||
{
|
||||
private static final Logger LOG = Log.getLogger(RunAsAnnotationHandler.class);
|
||||
|
||||
protected WebAppContext _context;
|
||||
|
||||
public RunAsAnnotationHandler(WebAppContext wac)
|
||||
{
|
||||
//Introspect only the given class for a RunAs annotation, as it is a class level annotation,
|
||||
//and according to Common Annotation Spec p2-6 a class-level annotation is not inheritable.
|
||||
super(false);
|
||||
_context = wac;
|
||||
super(false, wac);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -57,12 +57,9 @@ public class ServletSecurityAnnotationHandler extends AbstractIntrospectableAnno
|
|||
{
|
||||
private static final Logger LOG = Log.getLogger(ServletSecurityAnnotationHandler.class);
|
||||
|
||||
private WebAppContext _context;
|
||||
|
||||
public ServletSecurityAnnotationHandler(WebAppContext wac)
|
||||
{
|
||||
super(false);
|
||||
_context = wac;
|
||||
super(false, wac);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under
|
||||
// the terms of the Eclipse Public License 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// This Source Code may also be made available under the following
|
||||
// Secondary Licenses when the conditions for such availability set
|
||||
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||
// the Apache License v2.0 which is available at
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.annotations;
|
||||
|
||||
import javax.annotation.PreDestroy;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
|
||||
public class ServletE extends HttpServlet
|
||||
{
|
||||
@PreDestroy
|
||||
public void preDestroy()
|
||||
{
|
||||
}
|
||||
}
|
|
@ -33,6 +33,7 @@ import org.eclipse.jetty.util.resource.Resource;
|
|||
import org.eclipse.jetty.webapp.FragmentDescriptor;
|
||||
import org.eclipse.jetty.webapp.RelativeOrdering;
|
||||
import org.eclipse.jetty.webapp.WebAppContext;
|
||||
import org.eclipse.jetty.webapp.WebDescriptor;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
|
@ -125,7 +126,7 @@ public class TestAnnotationConfiguration
|
|||
context25.setClassLoader(Thread.currentThread().getContextClassLoader());
|
||||
context25.setAttribute(AnnotationConfiguration.MULTI_THREADED, Boolean.FALSE);
|
||||
context25.setAttribute(AnnotationConfiguration.MAX_SCAN_WAIT, 0);
|
||||
context25.getMetaData().setWebXml(Resource.newResource(web25));
|
||||
context25.getMetaData().setWebDescriptor(new WebDescriptor(Resource.newResource(web25)));
|
||||
context25.getServletContext().setEffectiveMajorVersion(2);
|
||||
context25.getServletContext().setEffectiveMinorVersion(5);
|
||||
config25.configure(context25);
|
||||
|
@ -138,7 +139,7 @@ public class TestAnnotationConfiguration
|
|||
context25b.setAttribute(AnnotationConfiguration.MULTI_THREADED, Boolean.FALSE);
|
||||
context25b.setAttribute(AnnotationConfiguration.MAX_SCAN_WAIT, 0);
|
||||
context25b.setConfigurationDiscovered(true);
|
||||
context25b.getMetaData().setWebXml(Resource.newResource(web25));
|
||||
context25b.getMetaData().setWebDescriptor(new WebDescriptor(Resource.newResource(web25)));
|
||||
context25b.getServletContext().setEffectiveMajorVersion(2);
|
||||
context25b.getServletContext().setEffectiveMinorVersion(5);
|
||||
config25b.configure(context25b);
|
||||
|
@ -150,7 +151,7 @@ public class TestAnnotationConfiguration
|
|||
context31.setClassLoader(Thread.currentThread().getContextClassLoader());
|
||||
context31.setAttribute(AnnotationConfiguration.MULTI_THREADED, Boolean.FALSE);
|
||||
context31.setAttribute(AnnotationConfiguration.MAX_SCAN_WAIT, 0);
|
||||
context31.getMetaData().setWebXml(Resource.newResource(web31true));
|
||||
context31.getMetaData().setWebDescriptor(new WebDescriptor(Resource.newResource(web31true)));
|
||||
context31.getServletContext().setEffectiveMajorVersion(3);
|
||||
context31.getServletContext().setEffectiveMinorVersion(1);
|
||||
config31.configure(context31);
|
||||
|
@ -162,7 +163,7 @@ public class TestAnnotationConfiguration
|
|||
context31b.setClassLoader(Thread.currentThread().getContextClassLoader());
|
||||
context31b.setAttribute(AnnotationConfiguration.MULTI_THREADED, Boolean.FALSE);
|
||||
context31b.setAttribute(AnnotationConfiguration.MAX_SCAN_WAIT, 0);
|
||||
context31b.getMetaData().setWebXml(Resource.newResource(web31false));
|
||||
context31b.getMetaData().setWebDescriptor(new WebDescriptor(Resource.newResource(web31false)));
|
||||
context31b.getServletContext().setEffectiveMajorVersion(3);
|
||||
context31b.getServletContext().setEffectiveMinorVersion(1);
|
||||
config31b.configure(context31b);
|
||||
|
@ -183,9 +184,9 @@ public class TestAnnotationConfiguration
|
|||
|
||||
//test 3.1 webapp loads both server and app scis
|
||||
context.setClassLoader(webAppLoader);
|
||||
context.getMetaData().addWebInfJar(Resource.newResource(testSciJar.toURI().toURL()));
|
||||
context.getMetaData().setWebXml(Resource.newResource(web31true));
|
||||
context.getMetaData().setWebInfClassesDirs(classes);
|
||||
context.getMetaData().addWebInfResource(Resource.newResource(testSciJar.toURI().toURL()));
|
||||
context.getMetaData().setWebDescriptor(new WebDescriptor(Resource.newResource(web31true)));
|
||||
context.getMetaData().setWebInfClassesResources(classes);
|
||||
context.getServletContext().setEffectiveMajorVersion(3);
|
||||
context.getServletContext().setEffectiveMinorVersion(1);
|
||||
scis = config.getNonExcludedInitializers(context);
|
||||
|
@ -215,9 +216,9 @@ public class TestAnnotationConfiguration
|
|||
// test a 3.1 webapp with metadata-complete=false loads both server
|
||||
// and webapp scis
|
||||
context.setClassLoader(webAppLoader);
|
||||
context.getMetaData().setWebXml(Resource.newResource(web31false));
|
||||
context.getMetaData().setWebInfClassesDirs(classes);
|
||||
context.getMetaData().addWebInfJar(Resource.newResource(testSciJar.toURI().toURL()));
|
||||
context.getMetaData().setWebDescriptor(new WebDescriptor(Resource.newResource(web31false)));
|
||||
context.getMetaData().setWebInfClassesResources(classes);
|
||||
context.getMetaData().addWebInfResource(Resource.newResource(testSciJar.toURI().toURL()));
|
||||
context.getServletContext().setEffectiveMajorVersion(3);
|
||||
context.getServletContext().setEffectiveMinorVersion(1);
|
||||
scis = config.getNonExcludedInitializers(context);
|
||||
|
@ -260,12 +261,12 @@ public class TestAnnotationConfiguration
|
|||
WebAppContext context = new WebAppContext();
|
||||
List<ServletContainerInitializer> scis;
|
||||
context.setClassLoader(orderedLoader);
|
||||
context.getMetaData().setWebXml(Resource.newResource(web31true));
|
||||
context.getMetaData().setWebDescriptor(new WebDescriptor(Resource.newResource(web31true)));
|
||||
RelativeOrdering ordering = new RelativeOrdering(context.getMetaData());
|
||||
context.getMetaData().setOrdering(ordering);
|
||||
context.getMetaData().addWebInfJar(Resource.newResource(orderedFragmentJar.toURI().toURL()));
|
||||
context.getMetaData().addWebInfJar(Resource.newResource(testSciJar.toURI().toURL()));
|
||||
context.getMetaData().setWebInfClassesDirs(classes);
|
||||
context.getMetaData().addWebInfResource(Resource.newResource(orderedFragmentJar.toURI().toURL()));
|
||||
context.getMetaData().addWebInfResource(Resource.newResource(testSciJar.toURI().toURL()));
|
||||
context.getMetaData().setWebInfClassesResources(classes);
|
||||
context.getMetaData().orderFragments();
|
||||
context.getServletContext().setEffectiveMajorVersion(3);
|
||||
context.getServletContext().setEffectiveMinorVersion(1);
|
||||
|
@ -295,9 +296,9 @@ public class TestAnnotationConfiguration
|
|||
WebAppContext context = new WebAppContext();
|
||||
List<ServletContainerInitializer> scis;
|
||||
context.setClassLoader(webAppLoader);
|
||||
context.getMetaData().setWebXml(Resource.newResource(web25));
|
||||
context.getMetaData().setWebInfClassesDirs(classes);
|
||||
context.getMetaData().addWebInfJar(Resource.newResource(testSciJar.toURI().toURL()));
|
||||
context.getMetaData().setWebDescriptor(new WebDescriptor(Resource.newResource(web25)));
|
||||
context.getMetaData().setWebInfClassesResources(classes);
|
||||
context.getMetaData().addWebInfResource(Resource.newResource(testSciJar.toURI().toURL()));
|
||||
context.getServletContext().setEffectiveMajorVersion(2);
|
||||
context.getServletContext().setEffectiveMinorVersion(5);
|
||||
scis = config.getNonExcludedInitializers(context);
|
||||
|
@ -332,9 +333,9 @@ public class TestAnnotationConfiguration
|
|||
List<ServletContainerInitializer> scis;
|
||||
context.setConfigurationDiscovered(true);
|
||||
context.setClassLoader(webAppLoader);
|
||||
context.getMetaData().setWebXml(Resource.newResource(web25));
|
||||
context.getMetaData().setWebInfClassesDirs(classes);
|
||||
context.getMetaData().addWebInfJar(Resource.newResource(testSciJar.toURI().toURL()));
|
||||
context.getMetaData().setWebDescriptor(new WebDescriptor(Resource.newResource(web25)));
|
||||
context.getMetaData().setWebInfClassesResources(classes);
|
||||
context.getMetaData().addWebInfResource(Resource.newResource(testSciJar.toURI().toURL()));
|
||||
context.getServletContext().setEffectiveMajorVersion(2);
|
||||
context.getServletContext().setEffectiveMinorVersion(5);
|
||||
scis = config.getNonExcludedInitializers(context);
|
||||
|
@ -349,24 +350,4 @@ public class TestAnnotationConfiguration
|
|||
Thread.currentThread().setContextClassLoader(old);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetFragmentFromJar() throws Exception
|
||||
{
|
||||
String dir = MavenTestingUtils.getTargetTestingDir("getFragmentFromJar").getAbsolutePath();
|
||||
File file = new File(dir);
|
||||
file = new File(file.getCanonicalPath());
|
||||
URL url = file.toURI().toURL();
|
||||
|
||||
Resource jar1 = Resource.newResource(url + "file.jar");
|
||||
|
||||
AnnotationConfiguration config = new AnnotationConfiguration();
|
||||
WebAppContext wac = new WebAppContext();
|
||||
|
||||
List<FragmentDescriptor> frags = new ArrayList<FragmentDescriptor>();
|
||||
frags.add(new FragmentDescriptor(Resource.newResource("jar:" + url + "file.jar!/fooa.props")));
|
||||
frags.add(new FragmentDescriptor(Resource.newResource("jar:" + url + "file2.jar!/foob.props")));
|
||||
|
||||
assertNotNull(config.getFragmentFromJar(jar1, frags));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under
|
||||
// the terms of the Eclipse Public License 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// This Source Code may also be made available under the following
|
||||
// Secondary Licenses when the conditions for such availability set
|
||||
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||
// the Apache License v2.0 which is available at
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.annotations;
|
||||
|
||||
import org.eclipse.jetty.plus.annotation.LifeCycleCallbackCollection;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.eclipse.jetty.servlet.Source;
|
||||
import org.eclipse.jetty.util.DecoratedObjectFactory;
|
||||
import org.eclipse.jetty.util.resource.EmptyResource;
|
||||
import org.eclipse.jetty.webapp.MetaData;
|
||||
import org.eclipse.jetty.webapp.WebAppContext;
|
||||
import org.eclipse.jetty.webapp.WebDescriptor;
|
||||
import org.eclipse.jetty.xml.XmlParser;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class TestAnnotationDecorator
|
||||
{
|
||||
public class TestWebDescriptor extends WebDescriptor
|
||||
{
|
||||
public TestWebDescriptor(MetaData.Complete metadata)
|
||||
{
|
||||
super(EmptyResource.INSTANCE);
|
||||
_metaDataComplete = metadata;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parse(XmlParser parser) throws Exception
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processVersion()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processOrdering()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processDistributable()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMajorVersion()
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinorVersion()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAnnotationDecorator() throws Exception
|
||||
{
|
||||
assertThrows(NullPointerException.class, () ->
|
||||
{
|
||||
new AnnotationDecorator(null);
|
||||
});
|
||||
|
||||
WebAppContext context = new WebAppContext();
|
||||
AnnotationDecorator decorator = new AnnotationDecorator(context);
|
||||
ServletE servlet = new ServletE();
|
||||
//test without BaseHolder metadata
|
||||
decorator.decorate(servlet);
|
||||
LifeCycleCallbackCollection callbacks = (LifeCycleCallbackCollection)context.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION);
|
||||
assertNotNull(callbacks);
|
||||
assertFalse(callbacks.getPreDestroyCallbacks().isEmpty());
|
||||
|
||||
//reset
|
||||
context.removeAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION);
|
||||
|
||||
//test with BaseHolder metadata, should not introspect with metdata-complete==true
|
||||
context.getMetaData().setWebDescriptor(new TestWebDescriptor(MetaData.Complete.True));
|
||||
assertTrue(context.getMetaData().isMetaDataComplete());
|
||||
ServletHolder holder = new ServletHolder(new Source(Source.Origin.DESCRIPTOR, ""));
|
||||
holder.setHeldClass(ServletE.class);
|
||||
context.getServletHandler().addServlet(holder);
|
||||
DecoratedObjectFactory.associateInfo(holder);
|
||||
decorator = new AnnotationDecorator(context);
|
||||
decorator.decorate(servlet);
|
||||
DecoratedObjectFactory.disassociateInfo();
|
||||
callbacks = (LifeCycleCallbackCollection)context.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION);
|
||||
assertNull(callbacks);
|
||||
|
||||
//reset
|
||||
context.removeAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION);
|
||||
|
||||
//test with BaseHolder metadata, should introspect with metadata-complete==false
|
||||
context.getMetaData().setWebDescriptor(new TestWebDescriptor(MetaData.Complete.False));
|
||||
DecoratedObjectFactory.associateInfo(holder);
|
||||
decorator = new AnnotationDecorator(context);
|
||||
decorator.decorate(servlet);
|
||||
DecoratedObjectFactory.disassociateInfo();
|
||||
callbacks = (LifeCycleCallbackCollection)context.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION);
|
||||
assertNotNull(callbacks);
|
||||
assertFalse(callbacks.getPreDestroyCallbacks().isEmpty());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under
|
||||
// the terms of the Eclipse Public License 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// This Source Code may also be made available under the following
|
||||
// Secondary Licenses when the conditions for such availability set
|
||||
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||
// the Apache License v2.0 which is available at
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.annotations;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.eclipse.jetty.servlet.Source;
|
||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||
import org.eclipse.jetty.util.log.StacklessLogging;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.webapp.FragmentDescriptor;
|
||||
import org.eclipse.jetty.webapp.WebAppContext;
|
||||
import org.eclipse.jetty.webapp.WebDescriptor;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class TestAnnotationIntrospector
|
||||
{
|
||||
@Test
|
||||
public void testIsIntrospectable() throws Exception
|
||||
{
|
||||
try (StacklessLogging ignore = new StacklessLogging(AnnotationIntrospector.class))
|
||||
{
|
||||
WebAppContext wac = new WebAppContext();
|
||||
AnnotationIntrospector introspector = new AnnotationIntrospector(wac);
|
||||
//can't introspect nothing
|
||||
assertFalse(introspector.isIntrospectable(null, null));
|
||||
|
||||
//can introspect if no metadata to say otherwise
|
||||
assertTrue(introspector.isIntrospectable(new Object(), null));
|
||||
|
||||
//can introspect if metdata isn't a BaseHolder
|
||||
assertTrue(introspector.isIntrospectable(new Object(), new Object()));
|
||||
|
||||
//an EMBEDDED sourced servlet can be introspected
|
||||
ServletHolder holder = new ServletHolder();
|
||||
holder.setHeldClass(ServletE.class);
|
||||
assertTrue(introspector.isIntrospectable(new ServletE(), holder));
|
||||
|
||||
//a JAVAX API sourced servlet can be introspected
|
||||
holder = new ServletHolder(Source.JAVAX_API);
|
||||
holder.setHeldClass(ServletE.class);
|
||||
assertTrue(introspector.isIntrospectable(new ServletE(), holder));
|
||||
|
||||
//an ANNOTATION sourced servlet can be introspected
|
||||
holder = new ServletHolder(new Source(Source.Origin.ANNOTATION, ServletE.class.getName()));
|
||||
holder.setHeldClass(ServletE.class);
|
||||
assertTrue(introspector.isIntrospectable(new ServletE(), holder));
|
||||
|
||||
//a DESCRIPTOR sourced servlet can be introspected if web.xml metdata-complete==false
|
||||
File file = MavenTestingUtils.getTestResourceFile("web31false.xml");
|
||||
Resource resource = Resource.newResource(file);
|
||||
wac.getMetaData().setWebDescriptor(new WebDescriptor(resource));
|
||||
holder = new ServletHolder(new Source(Source.Origin.DESCRIPTOR, resource.toString()));
|
||||
assertTrue(introspector.isIntrospectable(new ServletE(), holder));
|
||||
|
||||
//a DESCRIPTOR sourced servlet can be introspected if web-fragment.xml medata-complete==false && web.xml metadata-complete==false
|
||||
file = MavenTestingUtils.getTestResourceFile("web-fragment4false.xml");
|
||||
resource = Resource.newResource(file);
|
||||
wac.getMetaData().addFragmentDescriptor(Resource.newResource(file.getParentFile()), new FragmentDescriptor(resource));
|
||||
holder = new ServletHolder(new Source(Source.Origin.DESCRIPTOR, resource.toString()));
|
||||
assertTrue(introspector.isIntrospectable(new ServletE(), holder));
|
||||
|
||||
//a DESCRIPTOR sourced servlet cannot be introspected if web-fragment.xml medata-complete==true (&& web.xml metadata-complete==false)
|
||||
file = MavenTestingUtils.getTestResourceFile("web-fragment4true.xml");
|
||||
resource = Resource.newResource(file);
|
||||
wac.getMetaData().addFragmentDescriptor(Resource.newResource(file.getParentFile()), new FragmentDescriptor(resource));
|
||||
holder = new ServletHolder(new Source(Source.Origin.DESCRIPTOR, resource.toString()));
|
||||
assertFalse(introspector.isIntrospectable(new ServletE(), holder));
|
||||
|
||||
//a DESCRIPTOR sourced servlet cannot be introspected if web.xml medata-complete==true
|
||||
file = MavenTestingUtils.getTestResourceFile("web31true.xml");
|
||||
resource = Resource.newResource(file);
|
||||
wac.getMetaData().setWebDescriptor(new WebDescriptor(resource));
|
||||
holder = new ServletHolder(new Source(Source.Origin.DESCRIPTOR, resource.toString()));
|
||||
assertFalse(introspector.isIntrospectable(new ServletE(), holder));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -89,7 +89,7 @@ public class TestSecurityAnnotationConversions
|
|||
|
||||
//Assume we found 1 servlet with a @HttpConstraint with value=EmptyRoleSemantic.DENY security annotation
|
||||
ServletSecurityAnnotationHandler annotationHandler = new ServletSecurityAnnotationHandler(wac);
|
||||
AnnotationIntrospector introspector = new AnnotationIntrospector();
|
||||
AnnotationIntrospector introspector = new AnnotationIntrospector(wac);
|
||||
introspector.registerHandler(annotationHandler);
|
||||
|
||||
//set up the expected outcomes:
|
||||
|
@ -108,7 +108,7 @@ public class TestSecurityAnnotationConversions
|
|||
expectedMappings[1].setConstraint(expectedConstraint);
|
||||
expectedMappings[1].setPathSpec("*.foo");
|
||||
|
||||
introspector.introspect(DenyServlet.class);
|
||||
introspector.introspect(new DenyServlet(), null);
|
||||
|
||||
compareResults(expectedMappings, ((ConstraintAware)wac.getSecurityHandler()).getConstraintMappings());
|
||||
}
|
||||
|
@ -122,15 +122,15 @@ public class TestSecurityAnnotationConversions
|
|||
});
|
||||
|
||||
ServletSecurityAnnotationHandler annotationHandler = new ServletSecurityAnnotationHandler(wac);
|
||||
AnnotationIntrospector introspector = new AnnotationIntrospector();
|
||||
AnnotationIntrospector introspector = new AnnotationIntrospector(wac);
|
||||
introspector.registerHandler(annotationHandler);
|
||||
|
||||
//set up the expected outcomes - no constraints at all as per Servlet Spec 3.1 pg 129
|
||||
//1 ConstraintMapping per ServletMapping pathSpec
|
||||
|
||||
ConstraintMapping[] expectedMappings = new ConstraintMapping[]{};
|
||||
|
||||
introspector.introspect(PermitServlet.class);
|
||||
PermitServlet permit = new PermitServlet();
|
||||
introspector.introspect(permit, null);
|
||||
|
||||
compareResults(expectedMappings, ((ConstraintAware)wac.getSecurityHandler()).getConstraintMappings());
|
||||
}
|
||||
|
@ -146,7 +146,7 @@ public class TestSecurityAnnotationConversions
|
|||
});
|
||||
|
||||
ServletSecurityAnnotationHandler annotationHandler = new ServletSecurityAnnotationHandler(wac);
|
||||
AnnotationIntrospector introspector = new AnnotationIntrospector();
|
||||
AnnotationIntrospector introspector = new AnnotationIntrospector(wac);
|
||||
introspector.registerHandler(annotationHandler);
|
||||
|
||||
//set up the expected outcomes:compareResults
|
||||
|
@ -164,8 +164,7 @@ public class TestSecurityAnnotationConversions
|
|||
expectedMappings[1] = new ConstraintMapping();
|
||||
expectedMappings[1].setConstraint(expectedConstraint);
|
||||
expectedMappings[1].setPathSpec("*.foo");
|
||||
|
||||
introspector.introspect(RolesServlet.class);
|
||||
introspector.introspect(new RolesServlet(), null);
|
||||
compareResults(expectedMappings, ((ConstraintAware)wac.getSecurityHandler()).getConstraintMappings());
|
||||
}
|
||||
|
||||
|
@ -211,10 +210,10 @@ public class TestSecurityAnnotationConversions
|
|||
expectedMappings[3].setPathSpec("*.foo");
|
||||
expectedMappings[3].setMethod("GET");
|
||||
|
||||
AnnotationIntrospector introspector = new AnnotationIntrospector();
|
||||
AnnotationIntrospector introspector = new AnnotationIntrospector(wac);
|
||||
ServletSecurityAnnotationHandler annotationHandler = new ServletSecurityAnnotationHandler(wac);
|
||||
introspector.registerHandler(annotationHandler);
|
||||
introspector.introspect(Method1Servlet.class);
|
||||
introspector.introspect(new Method1Servlet(), null);
|
||||
compareResults(expectedMappings, ((ConstraintAware)wac.getSecurityHandler()).getConstraintMappings());
|
||||
}
|
||||
|
||||
|
@ -227,7 +226,7 @@ public class TestSecurityAnnotationConversions
|
|||
"/foo/*", "*.foo"
|
||||
});
|
||||
|
||||
AnnotationIntrospector introspector = new AnnotationIntrospector();
|
||||
AnnotationIntrospector introspector = new AnnotationIntrospector(wac);
|
||||
ServletSecurityAnnotationHandler annotationHandler = new ServletSecurityAnnotationHandler(wac);
|
||||
introspector.registerHandler(annotationHandler);
|
||||
|
||||
|
@ -263,7 +262,7 @@ public class TestSecurityAnnotationConversions
|
|||
expectedMappings[3].setPathSpec("*.foo");
|
||||
expectedMappings[3].setMethod("GET");
|
||||
|
||||
introspector.introspect(Method2Servlet.class);
|
||||
introspector.introspect(new Method2Servlet(), null);
|
||||
compareResults(expectedMappings, ((ConstraintAware)wac.getSecurityHandler()).getConstraintMappings());
|
||||
}
|
||||
|
||||
|
|
|
@ -73,11 +73,14 @@ public class TestResourceAnnotations
|
|||
new org.eclipse.jetty.plus.jndi.EnvEntry(server, "resA", objA, false);
|
||||
new org.eclipse.jetty.plus.jndi.EnvEntry(server, "resB", objB, false);
|
||||
|
||||
AnnotationIntrospector parser = new AnnotationIntrospector();
|
||||
AnnotationIntrospector parser = new AnnotationIntrospector(wac);
|
||||
ResourceAnnotationHandler handler = new ResourceAnnotationHandler(wac);
|
||||
parser.registerHandler(handler);
|
||||
parser.introspect(ResourceA.class);
|
||||
parser.introspect(ResourceB.class);
|
||||
|
||||
ResourceA resourceA = new ResourceA();
|
||||
ResourceB resourceB = new ResourceB();
|
||||
parser.introspect(resourceA, null);
|
||||
parser.introspect(resourceB, null);
|
||||
|
||||
//processing classA should give us these jndi name bindings:
|
||||
// java:comp/env/myf
|
||||
|
@ -155,11 +158,13 @@ public class TestResourceAnnotations
|
|||
new org.eclipse.jetty.plus.jndi.EnvEntry(server, "resA", objA, false);
|
||||
new org.eclipse.jetty.plus.jndi.EnvEntry(server, "resB", objB, false);
|
||||
|
||||
AnnotationIntrospector introspector = new AnnotationIntrospector();
|
||||
AnnotationIntrospector introspector = new AnnotationIntrospector(wac);
|
||||
ResourcesAnnotationHandler handler = new ResourcesAnnotationHandler(wac);
|
||||
introspector.registerHandler(handler);
|
||||
introspector.introspect(ResourceA.class);
|
||||
introspector.introspect(ResourceB.class);
|
||||
ResourceA resourceA = new ResourceA();
|
||||
ResourceB resourceB = new ResourceB();
|
||||
introspector.introspect(resourceA, null);
|
||||
introspector.introspect(resourceB, null);
|
||||
|
||||
assertEquals(objA, env.lookup("peach"));
|
||||
assertEquals(objB, env.lookup("pear"));
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<web-fragment
|
||||
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-fragment_4_0.xsd"
|
||||
version="4.0">
|
||||
<name>ardvaark</name>
|
||||
</web-fragment>
|
||||
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<web-fragment
|
||||
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-fragment_4_0.xsd"
|
||||
metadata-complete="true"
|
||||
version="4.0">
|
||||
|
||||
<name>badger</name>
|
||||
|
||||
</web-fragment>
|
||||
|
||||
|
|
@ -228,6 +228,12 @@ public class HttpSenderOverHTTP extends HttpSender
|
|||
headerBuffer = byteBufferPool.acquire(httpClient.getRequestBufferSize(), useDirectByteBuffers);
|
||||
break;
|
||||
}
|
||||
case HEADER_OVERFLOW:
|
||||
{
|
||||
httpClient.getByteBufferPool().release(headerBuffer);
|
||||
headerBuffer = null;
|
||||
throw new IllegalArgumentException("Request header too large");
|
||||
}
|
||||
case NEED_CHUNK:
|
||||
{
|
||||
chunkBuffer = byteBufferPool.acquire(HttpGenerator.CHUNK_SIZE, useDirectByteBuffers);
|
||||
|
|
|
@ -45,6 +45,7 @@ import org.junit.jupiter.api.Test;
|
|||
import static org.eclipse.jetty.client.ProxyProtocolClientConnectionFactory.V1;
|
||||
import static org.eclipse.jetty.client.ProxyProtocolClientConnectionFactory.V2;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
|
@ -165,6 +166,7 @@ public class HttpClientProxyProtocolTest
|
|||
@Test
|
||||
public void testClientProxyProtocolV2WithVectors() throws Exception
|
||||
{
|
||||
int typeTLS = 0x20;
|
||||
String tlsVersion = "TLSv1.3";
|
||||
byte[] tlsVersionBytes = tlsVersion.getBytes(StandardCharsets.US_ASCII);
|
||||
startServer(new EmptyServerHandler()
|
||||
|
@ -176,7 +178,10 @@ public class HttpClientProxyProtocolTest
|
|||
assertTrue(endPoint instanceof ProxyConnectionFactory.ProxyEndPoint);
|
||||
ProxyConnectionFactory.ProxyEndPoint proxyEndPoint = (ProxyConnectionFactory.ProxyEndPoint)endPoint;
|
||||
if (target.equals("/tls_version"))
|
||||
{
|
||||
assertNotNull(proxyEndPoint.getTLV(typeTLS));
|
||||
assertEquals(tlsVersion, proxyEndPoint.getAttribute(ProxyConnectionFactory.TLS_VERSION));
|
||||
}
|
||||
response.setContentType(MimeTypes.Type.TEXT_PLAIN.asString());
|
||||
response.getOutputStream().print(request.getRemotePort());
|
||||
}
|
||||
|
@ -186,7 +191,6 @@ public class HttpClientProxyProtocolTest
|
|||
int serverPort = connector.getLocalPort();
|
||||
|
||||
int clientPort = ThreadLocalRandom.current().nextInt(1024, 65536);
|
||||
int typeTLS = 0x20;
|
||||
byte[] dataTLS = new byte[1 + 4 + (1 + 2 + tlsVersionBytes.length)];
|
||||
dataTLS[0] = 0x01; // CLIENT_SSL
|
||||
dataTLS[5] = 0x21; // SUBTYPE_SSL_VERSION
|
||||
|
|
|
@ -74,4 +74,5 @@ include::jndi/chapter.adoc[]
|
|||
include::alpn/chapter.adoc[]
|
||||
include::fastcgi/chapter.adoc[]
|
||||
include::extras/chapter.adoc[]
|
||||
include::runner/chapter.adoc[]
|
||||
include::tuning/chapter.adoc[]
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under
|
||||
// the terms of the Eclipse Public License 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// This Source Code may also be made available under the following
|
||||
// Secondary Licenses when the conditions for such availability set
|
||||
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||
// the Apache License v2.0 which is available at
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
[[runner]]
|
||||
== Jetty Runner
|
||||
|
||||
This chapter explains how to use the `jetty-runner` to run your webapps without needing an installation of Jetty.
|
||||
|
||||
include::jetty-runner.adoc[]
|
|
@ -0,0 +1,349 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under
|
||||
// the terms of the Eclipse Public License 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// This Source Code may also be made available under the following
|
||||
// Secondary Licenses when the conditions for such availability set
|
||||
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||
// the Apache License v2.0 which is available at
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
[[jetty-runner]]
|
||||
=== Use Jetty Without an Installed Distribution
|
||||
|
||||
The idea of the `jetty-runner` is extremely simple – run a webapp directly from the command line using a single jar file and as much default configuration as possible.
|
||||
Of course, if your webapp is not as straightforward, the `jetty-runner` has command line options which allow you to customize the execution environment.
|
||||
|
||||
[[jetty-runner-preparation]]
|
||||
==== Preparation
|
||||
|
||||
You will need the `jetty-runner` jar:
|
||||
|
||||
1. Download the `jetty-runner` jar available at https://repo1.maven.org/maven2/org/eclipse/jetty/jetty-runner/[Maven Central].
|
||||
|
||||
==== Deploying a Simple Context
|
||||
|
||||
Let's assume we have a very simple webapp that does not need any resources from its environment, nor any configuration apart from the defaults.
|
||||
Starting it is as simple as performing the following:
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
....
|
||||
> java -jar jetty-runner.jar simple.war
|
||||
....
|
||||
|
||||
This will start Jetty on port 8080, and deploy the webapp to `/`.
|
||||
|
||||
Your webapp does not have to be packed into a war, you can deploy a webapp that is a directory instead in the same way:
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
....
|
||||
> java -jar jetty-runner.jar simple
|
||||
....
|
||||
|
||||
In fact, the webapp does not have to be a war or even a directory, it can simply be a Jetty link:#using-context-provider[context xml] file that describes your webapp:
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
....
|
||||
> java -jar jetty-runner.jar simple-context.xml
|
||||
....
|
||||
|
||||
____
|
||||
[NOTE]
|
||||
When using a context xml file, the application being deployed is not even required to be a fully-fledged webapp.
|
||||
It can simply be a Jetty link:#what-is-a-context[context].
|
||||
____
|
||||
|
||||
By default, `jetty-runner` implements all Configuration Classes so that users can set up and deploy new instances with as little configuration as possible.
|
||||
If you wish to only implement certain Configuration Classes, they will need to be defined in the context xml for the webapp/context.
|
||||
The default Configuration Classes are:
|
||||
|
||||
`org.eclipse.jetty.webapp.WebInfConfiguration`
|
||||
`org.eclipse.jetty.webapp.WebXmlConfiguration`
|
||||
`org.eclipse.jetty.webapp.MetaInfConfiguration`
|
||||
`org.eclipse.jetty.webapp.FragmentConfiguration`
|
||||
`org.eclipse.jetty.webapp.JettyWebXmlConfiguration`
|
||||
`org.eclipse.jetty.plus.webapp.EnvConfiguration`
|
||||
`org.eclipse.jetty.plus.webapp.PlusConfiguration`
|
||||
`org.eclipse.jetty.annotations.AnnotationConfiguration`
|
||||
|
||||
You can learn more about implementing specific Configuration Classes link:https://www.eclipse.org/jetty/documentation/current/configuring-webapps.html#webapp-configurations[here.]
|
||||
|
||||
==== Deploying Multiple Contexts
|
||||
|
||||
If you have more than one webapp that must be deployed, simply provide them all on the command line.
|
||||
You can control the context paths for them using the `--path` parameter.
|
||||
Here's an example of deploying 2 wars (although either or both of them could be unpacked directories instead):
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
....
|
||||
> java -jar jetty-runner.jar --path /one my1.war --path /two my2.war
|
||||
....
|
||||
|
||||
If you have context xml files that describe your webapps, you can fully configure your webapps in them and hence you won't need to use the command line switches.
|
||||
Just provide the list of context files like so:
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
....
|
||||
> java -jar jetty-runner.jar my-first-context.xml my-second-context.xml my-third-context.xml
|
||||
....
|
||||
|
||||
____
|
||||
[NOTE]
|
||||
Switched used on the command line override configuration file settings.
|
||||
So, for example, you could set the context path for the webapp inside the context xml file, and use the `--path` switch to override it on the command line.
|
||||
____
|
||||
|
||||
|
||||
===== Changing the Default Port
|
||||
|
||||
By default the `jetty-runner` will listen on port 8080.
|
||||
You can easily change this on the command line using the `--port` command.
|
||||
Here's an example that runs our simple.war on port 9090:
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
....
|
||||
> java -jar jetty-runner.jar --port 9090 simple.war
|
||||
....
|
||||
|
||||
===== Using jetty.xml Files
|
||||
|
||||
Instead of, or in addition to, using command line switches, you can use one or more `jetty.xml` files to configure the environment for your webapps.
|
||||
Here's an example where we apply two different `jetty.xml` files:
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
....
|
||||
> java -jar jetty-runner.jar --config jetty.xml --config jetty-https.xml simple.war
|
||||
....
|
||||
|
||||
[[runner-configuration-reference]]
|
||||
==== Full Configuration Reference
|
||||
|
||||
You can see the fill set of configuration options using the `--help` switch:
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
....
|
||||
> java -jar jetty-runner.jar --help
|
||||
....
|
||||
|
||||
Here's what the output will look like:
|
||||
|
||||
[source, plain, subs="{sub-order}"]
|
||||
----
|
||||
|
||||
Usage: java [-Djetty.home=dir] -jar jetty-runner.jar [--help|--version] [ server opts] [[ context opts] context ...]
|
||||
Server opts:
|
||||
--version - display version and exit
|
||||
--log file - request log filename (with optional 'yyyy_mm_dd' wildcard
|
||||
--out file - info/warn/debug log filename (with optional 'yyyy_mm_dd' wildcard
|
||||
--host name|ip - interface to listen on (default is all interfaces)
|
||||
--port n - port to listen on (default 8080)
|
||||
--stop-port n - port to listen for stop command (or -DSTOP.PORT=n)
|
||||
--stop-key n - security string for stop command (required if --stop-port is present) (or -DSTOP.KEY=n)
|
||||
[--jar file]*n - each tuple specifies an extra jar to be added to the classloader
|
||||
[--lib dir]*n - each tuple specifies an extra directory of jars to be added to the classloader
|
||||
[--classes dir]*n - each tuple specifies an extra directory of classes to be added to the classloader
|
||||
--stats [unsecure|realm.properties] - enable stats gathering servlet context
|
||||
[--config file]*n - each tuple specifies the name of a jetty xml config file to apply (in the order defined)
|
||||
Context opts:
|
||||
[[--path /path] context]*n - WAR file, web app dir or context xml file, optionally with a context path
|
||||
----
|
||||
|
||||
===== Printing the Version
|
||||
Print out the version of Jetty and then exit immediately.
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
....
|
||||
> java -jar jetty-runner.jar --version
|
||||
....
|
||||
|
||||
===== Configuring a Request Log
|
||||
Cause Jetty to write a request log with the given name.
|
||||
If the file is prefixed with `yyyy_mm_dd` then the file will be automatically rolled over.
|
||||
Note that for finer grained configuration of the link:{JDURL}/org/eclipse/jetty/server/NCSARequestLog.html[request log], you will need to use a Jetty xml file instead.
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
....
|
||||
> java -jar jetty-runner.jar --log yyyy_mm_dd-requests.log my.war
|
||||
....
|
||||
|
||||
===== Configuring the Output Log
|
||||
Redirect the output of jetty logging to the named file.
|
||||
If the file is prefixed with `yyyy_mm_dd` then the file will be automatically rolled over.
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
....
|
||||
> java -jar jetty-runner.jar --out yyyy_mm_dd-output.log my.war
|
||||
....
|
||||
|
||||
===== Configuring the Interface for HTTP
|
||||
Like Jetty standalone, the default is for the connectors to listen on all interfaces on a machine.
|
||||
You can control that by specifying the name or ip address of the particular interface you wish to use with the `--host` argument:
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
....
|
||||
> java -jar jetty-runner.jar --host 192.168.22.19 my.war
|
||||
....
|
||||
|
||||
===== Configuring the Port for HTTP
|
||||
The default port number is 8080.
|
||||
To link:#how-to-configure-connectors[configure a https connector], use a Jetty xml config file instead.
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
....
|
||||
> java -jar jetty-runner.jar --port 9090 my.war
|
||||
....
|
||||
|
||||
===== Configuring Stop
|
||||
You can configure a port number for Jetty to listen on for a stop command, so you are able to stop it from a different terminal.
|
||||
This requires the use of a "secret" key, to prevent malicious or accidental termination.
|
||||
Use the `--stop-port` and `--stop-key` (or `-DSTOP.PORT=` and `-DSTOP.KEY=`, respectively) parameters as arguments to the `jetty-runner`:
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
....
|
||||
> java -jar jetty-runner.jar --stop-port 8181 --stop-key abc123
|
||||
....
|
||||
|
||||
Then, to stop Jetty from a different terminal, you need to supply the same port and key information.
|
||||
For this you'll either need a local installation of Jetty, the link:#jetty-maven-plugin[jetty-maven-plugin], the link:#jetty-ant[jetty-ant plugin], or a custom class.
|
||||
Here's how to use a Jetty installation to perform a stop:
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
....
|
||||
> java -jar start.jar -DSTOP.PORT=8181 -DSTOP.KEY=abc123 --stop
|
||||
....
|
||||
|
||||
===== Configuring the Container Classpath
|
||||
With a local installation of Jetty, you add jars and classes to the container's classpath by putting them in the `{$jetty.base}/lib` directory.
|
||||
With the `jetty-runner`, you can use the `--lib`, `--jar` and `--classes` arguments instead to achieve the same thing.
|
||||
|
||||
`--lib` adds the location of a directory which contains jars to add to the container classpath.
|
||||
You can add 1 or more.
|
||||
Here's an example of configuring 2 directories:
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
....
|
||||
> java -jar jetty-runner.jar --lib /usr/local/external/lib --lib $HOME/external-other/lib my.war
|
||||
....
|
||||
|
||||
`--jar` adds a single jar file to the container classpath.
|
||||
You can add 1 or more.
|
||||
Here's an example of configuring 3 extra jars:
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
....
|
||||
> java -jar jetty-runner.jar --jar /opt/stuff/jars/jar1.jar --jar $HOME/jars/jar2.jar --jar /usr/local/proj/jars/jar3.jar my.war
|
||||
....
|
||||
|
||||
`--classes` add the location of a directory containing classes to add to the container classpath.
|
||||
You can add 1 or more.
|
||||
Here's an example of configuring a single extra classes dir:
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
....
|
||||
> java -jar jetty-runner.jar --classes /opt/stuff/classes my.war
|
||||
....
|
||||
|
||||
____
|
||||
[NOTE]
|
||||
When using the `--jar` and/or `--lib` arguments, by default these will *not* be inspected for `META-INF` information such as `META-INF/resources`, `META-INF/web-fragment.xml`, or `META-INF/taglib.tld`.
|
||||
If you require these jar files inspected you will need to define the link:https://www.eclipse.org/jetty/documentation/current/configuring-webapps.html#webapp-context-attributes[jar pattern in your context xml file].
|
||||
Jetty-Runner automatically provides and appends a suitable pattern for jtsl taglibs (this pattern is different than the one in the standard Jetty distribution).
|
||||
____
|
||||
|
||||
|
||||
===== Gathering Statistics
|
||||
If statistics gathering is enabled, then they are viewable by surfing to the context `/stats`.
|
||||
You may optionally protect access to that context with a password.
|
||||
Here's an example of enabling statistics, with no password protection:
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
....
|
||||
> java -jar jetty-runner.jar --stats unsecure my.war
|
||||
....
|
||||
|
||||
If we wished to protect access to the `/stats` context, we would provide the location of a Jetty realm configuration file containing authentication and authorization information.
|
||||
For example, we could use the following example realm file from the Jetty distribution:
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
....
|
||||
jetty: MD5:164c88b302622e17050af52c89945d44,user
|
||||
admin: CRYPT:adpexzg3FUZAk,server-administrator,content-administrator,admin
|
||||
other: OBF:1xmk1w261u9r1w1c1xmq,user
|
||||
plain: plain,user
|
||||
user: password,user
|
||||
# This entry is for digest auth. The credential is a MD5 hash of username:realmname:password
|
||||
digest: MD5:6e120743ad67abfbc385bc2bb754e297,user
|
||||
....
|
||||
|
||||
Assuming we've copied it into the local directory, we would apply it like so
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
....
|
||||
> java -jar jetty-runner.jar --stats realm.properties my.war
|
||||
....
|
||||
|
||||
After navigating to http://localhost:8080/ a few times, we can point to the stats servlet on http://localhost:8080/stats to see the output:
|
||||
|
||||
....
|
||||
Statistics:
|
||||
Statistics gathering started 1490627ms ago
|
||||
|
||||
Requests:
|
||||
Total requests: 9
|
||||
Active requests: 1
|
||||
Max active requests: 1
|
||||
Total requests time: 63
|
||||
Mean request time: 7.875
|
||||
Max request time: 26
|
||||
Request time standard deviation: 8.349764752888037
|
||||
|
||||
|
||||
Dispatches:
|
||||
Total dispatched: 9
|
||||
Active dispatched: 1
|
||||
Max active dispatched: 1
|
||||
Total dispatched time: 63
|
||||
Mean dispatched time: 7.875
|
||||
Max dispatched time: 26
|
||||
Dispatched time standard deviation: 8.349764752888037
|
||||
Total requests suspended: 0
|
||||
Total requests expired: 0
|
||||
Total requests resumed: 0
|
||||
|
||||
|
||||
Responses:
|
||||
1xx responses: 0
|
||||
2xx responses: 7
|
||||
3xx responses: 1
|
||||
4xx responses: 0
|
||||
5xx responses: 0
|
||||
Bytes sent total: 1453
|
||||
|
||||
|
||||
Connections:
|
||||
org.eclipse.jetty.server.ServerConnector@203822411
|
||||
Protocols:http/1.1
|
||||
Statistics gathering started 1490606ms ago
|
||||
Total connections: 7
|
||||
Current connections open: 1
|
||||
Max concurrent connections open: 2
|
||||
Total connections duration: 72883
|
||||
Mean connection duration: 12147.166666666666
|
||||
Max connection duration: 65591
|
||||
Connection duration standard deviation: 23912.40292977684
|
||||
Total messages in: 7
|
||||
Total messages out: 7
|
||||
|
||||
|
||||
Memory:
|
||||
Heap memory usage: 49194840 bytes
|
||||
Non-heap memory usage: 12611696 bytes
|
||||
....
|
|
@ -26,3 +26,4 @@ include::secure-passwords.adoc[]
|
|||
include::setting-port80-access-for-non-root-user.adoc[]
|
||||
include::jaas-support.adoc[]
|
||||
include::spnego-support.adoc[]
|
||||
include::openid-support.adoc[]
|
||||
|
|
|
@ -0,0 +1,139 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under
|
||||
// the terms of the Eclipse Public License 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// This Source Code may also be made available under the following
|
||||
// Secondary Licenses when the conditions for such availability set
|
||||
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||
// the Apache License v2.0 which is available at
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
[[openid-support]]
|
||||
=== OpenID Support
|
||||
|
||||
==== External Setup
|
||||
|
||||
===== Registering an App with OpenID Provider
|
||||
You must register the app with an OpenID Provider such as link:https://developers.google.com/identity/protocols/OpenIDConnect#authenticatingtheuser[Google] or link:https://images-na.ssl-images-amazon.com/images/G/01/lwa/dev/docs/website-developer-guide._TTH_.pdf[Amazon.]
|
||||
This will give you a Client ID and Client Secret.
|
||||
Once set up you must also register all the possible URI's for your webapp with the path `/j_security_check` so that the OpenId Provider will allow redirection back to the webapp.
|
||||
|
||||
These may look like
|
||||
|
||||
* `http://localhost:8080/openid-webapp/j_security_check`
|
||||
|
||||
* `https://example.com/j_security_check`
|
||||
|
||||
==== Distribution Configuration
|
||||
|
||||
===== OpenID Provider Configuration
|
||||
To enable OpenID support, you first need to activate the `openid` module in your implementation.
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
----
|
||||
java -jar {JETTY_HOME}/start.jar --add-to-start=openid
|
||||
----
|
||||
|
||||
To configure OpenID Authentication with Jetty you will need to specify the OpenID Provider's issuer identifier (case sensitive URL using the `https` scheme) and the OAuth 2.0 Client ID and Client Secret.
|
||||
If the OpenID Provider does not allow metadata discovery you will also need to specify the token endpoint and authorization endpoint of the OpenID Provider.
|
||||
These can be set as properties in the `start.ini` or `start.d/openid.ini` files.
|
||||
|
||||
===== WebApp Specific Configuration in web.xml
|
||||
|
||||
The `web.xml` file needs some specific configuration to use OpenID.
|
||||
There must be a `login-config` element with an `auth-method` value of `OPENID`, and a `realm-name` value of the exact URL string used to set the OpenID Provider.
|
||||
|
||||
To set the error page, an init param is set at `"org.eclipse.jetty.security.openid.error_page"`, its value should be a path relative to the webapp where authentication errors should be redirected.
|
||||
|
||||
Example:
|
||||
|
||||
[source, xml, subs="{sub-order}"]
|
||||
----
|
||||
<login-config>
|
||||
<auth-method>OPENID</auth-method>
|
||||
<realm-name>https://accounts.google.com</realm-name>
|
||||
</login-config>
|
||||
<context-param>
|
||||
<param-name>org.eclipse.jetty.security.openid.error_page</param-name>
|
||||
<param-value>/error</param-value>
|
||||
</context-param>
|
||||
----
|
||||
|
||||
==== Embedded Configuration
|
||||
|
||||
===== Define the `OpenIdConfiguration` for a specific OpenID Provider.
|
||||
|
||||
If the OpenID Provider allows metadata discovery then you can use.
|
||||
|
||||
[source, java, subs="{sub-order}"]
|
||||
----
|
||||
OpenIdConfiguration openIdConfig = new OpenIdConfiguration(ISSUER, CLIENT_ID, CLIENT_SECRET);
|
||||
----
|
||||
|
||||
Otherwise you can manually enter the necessary information:
|
||||
|
||||
[source, java, subs="{sub-order}"]
|
||||
----
|
||||
OpenIdConfiguration openIdConfig = new OpenIdConfiguration(ISSUER, TOKEN_ENDPOINT, AUTH_ENDPOINT, CLIENT_ID, CLIENT_SECRET);
|
||||
----
|
||||
|
||||
===== Configuring an `OpenIdLoginService`
|
||||
[source, java, subs="{sub-order}"]
|
||||
----
|
||||
LoginService loginService = new OpenIdLoginService(openIdConfig);
|
||||
securityHandler.setLoginService(loginService);
|
||||
----
|
||||
|
||||
===== Configuring an `OpenIdAuthenticator` with `OpenIdConfiguration` and Error Page Redirect
|
||||
[source, java, subs="{sub-order}"]
|
||||
----
|
||||
Authenticator authenticator = new OpenIdAuthenticator(openIdConfig, "/error");
|
||||
securityHandler.setAuthenticator(authenticator);
|
||||
servletContextHandler.setSecurityHandler(securityHandler);
|
||||
----
|
||||
|
||||
===== Usage
|
||||
|
||||
====== Claims and Access Token
|
||||
Claims about the user can be found using attributes on the session attribute `"org.eclipse.jetty.security.openid.claims"`, and the full response containing the OAuth 2.0 Access Token can be found with the session attribute `"org.eclipse.jetty.security.openid.response"`.
|
||||
|
||||
Example:
|
||||
[source, java, subs="{sub-order}"]
|
||||
----
|
||||
Map<String, Object> claims = (Map)request.getSession().getAttribute("org.eclipse.jetty.security.openid.claims");
|
||||
String userId = claims.get("sub");
|
||||
|
||||
Map<String, Object> response = (Map)request.getSession().getAttribute("org.eclipse.jetty.security.openid.response");
|
||||
String accessToken = response.get("access_token");
|
||||
----
|
||||
|
||||
==== Scopes
|
||||
The OpenID scope is always used but additional scopes can be requested which can give you additional resources or privileges.
|
||||
For the Google OpenID Provider it can be useful to request the scopes `profile` and `email` which will give you additional user claims.
|
||||
|
||||
Additional scopes can be requested through the `start.ini` or `start.d/openid.ini` files, or with `OpenIdConfiguration.addScopes(...);` in embedded code.
|
||||
|
||||
==== Roles
|
||||
|
||||
If security roles are required they can be configured through a wrapped `LoginService` which is deferred to for role information by the `OpenIdLoginService`.
|
||||
|
||||
This can be configured in XML through `etc/openid-baseloginservice.xml` in the Distribution, or in embedded code using the constructor for the `OpenIdLoginService`.
|
||||
|
||||
[source, java, subs="{sub-order}"]
|
||||
----
|
||||
LoginService wrappedLoginService = ...; // Optional LoginService for Roles
|
||||
LoginService loginService = new OpenIdLoginService(openIdConfig, wrappedLoginService);
|
||||
----
|
||||
|
||||
When using authorization roles, the setting `authenticateNewUsers` becomes significant.
|
||||
If set to `true` users not found by the wrapped `LoginService` will still be authenticated but will have no roles.
|
||||
If set to `false` those users will be not be allowed to authenticate and are redirected to the error page.
|
||||
This setting is configured through the property `jetty.openid.authenticateNewUsers` in the `start.ini` or `start.d/openid.ini` file, or with `OpenIdLoginService.setAuthenticateNewUsers(...);` in embedded code.
|
|
@ -11,7 +11,7 @@
|
|||
<name>Jetty :: Hazelcast Session Manager</name>
|
||||
|
||||
<properties>
|
||||
<hazelcast.version>3.9.4</hazelcast.version>
|
||||
<hazelcast.version>3.12.6</hazelcast.version>
|
||||
<bundle-symbolic-name>${project.groupId}.hazelcast</bundle-symbolic-name>
|
||||
</properties>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
<!-- ===================================================================== -->
|
||||
<!-- Configure a factory for HazelcastSessionDataStore using -->
|
||||
<!-- an embedded Hazelcast Instance -->
|
||||
<!-- an remote Hazelcast Instance -->
|
||||
<!-- ===================================================================== -->
|
||||
<Call name="addBean">
|
||||
<Arg>
|
||||
|
@ -16,6 +16,7 @@
|
|||
<Set name="gracePeriodSec"><Property name="jetty.session.gracePeriod.seconds" default="3600" /></Set>
|
||||
<Set name="savePeriodSec"><Property name="jetty.session.savePeriod.seconds" default="0" /></Set>
|
||||
<Set name="onlyClient"><Property name="jetty.session.hazelcast.onlyClient" default="true" /></Set>
|
||||
<Set name="addresses"><Property name="jetty.session.hazelcast.addresses" default="" /></Set>
|
||||
</New>
|
||||
</Arg>
|
||||
</Call>
|
||||
|
|
|
@ -13,7 +13,7 @@ session-store
|
|||
sessions
|
||||
|
||||
[files]
|
||||
maven://com.hazelcast/hazelcast/3.9.4|lib/hazelcast/hazelcast-3.9.4.jar
|
||||
maven://com.hazelcast/hazelcast/3.12.6|lib/hazelcast/hazelcast-3.12.6.jar
|
||||
|
||||
[xml]
|
||||
etc/sessions/hazelcast/default.xml
|
||||
|
@ -31,7 +31,6 @@ http://www.apache.org/licenses/LICENSE-2.0.html
|
|||
[ini-template]
|
||||
jetty.session.hazelcast.mapName=jetty-distributed-session-map
|
||||
jetty.session.hazelcast.hazelcastInstanceName=JETTY_DISTRIBUTED_SESSION_INSTANCE
|
||||
#jetty.session.hazelcast.configurationLocation=
|
||||
jetty.session.hazelcast.scavengeZombies=false
|
||||
jetty.session.gracePeriod.seconds=3600
|
||||
jetty.session.savePeriod.seconds=0
|
||||
|
|
|
@ -13,8 +13,8 @@ session-store
|
|||
sessions
|
||||
|
||||
[files]
|
||||
maven://com.hazelcast/hazelcast/3.9.4|lib/hazelcast/hazelcast-3.9.4.jar
|
||||
maven://com.hazelcast/hazelcast-client/3.9.4|lib/hazelcast/hazelcast-client-3.9.4.jar
|
||||
maven://com.hazelcast/hazelcast/3.12.6|lib/hazelcast/hazelcast-3.12.6.jar
|
||||
maven://com.hazelcast/hazelcast-client/3.12.6|lib/hazelcast/hazelcast-client-3.12.6.jar
|
||||
|
||||
[xml]
|
||||
etc/sessions/hazelcast/remote.xml
|
||||
|
@ -34,6 +34,6 @@ jetty.session.hazelcast.mapName=jetty-distributed-session-map
|
|||
jetty.session.hazelcast.hazelcastInstanceName=JETTY_DISTRIBUTED_SESSION_INSTANCE
|
||||
jetty.session.hazelcast.onlyClient=true
|
||||
jetty.session.hazelcast.scavengeZombies=false
|
||||
#jetty.session.hazelcast.configurationLocation=
|
||||
jetty.session.gracePeriod.seconds=3600
|
||||
jetty.session.savePeriod.seconds=0
|
||||
#jetty.session.hazelcast.addresses=
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
package org.eclipse.jetty.hazelcast.session;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import com.hazelcast.client.HazelcastClient;
|
||||
import com.hazelcast.client.config.ClientConfig;
|
||||
|
@ -29,6 +30,7 @@ import com.hazelcast.config.SerializerConfig;
|
|||
import com.hazelcast.config.XmlConfigBuilder;
|
||||
import com.hazelcast.core.Hazelcast;
|
||||
import com.hazelcast.core.HazelcastInstance;
|
||||
|
||||
import org.eclipse.jetty.server.session.AbstractSessionDataStoreFactory;
|
||||
import org.eclipse.jetty.server.session.SessionData;
|
||||
import org.eclipse.jetty.server.session.SessionDataStore;
|
||||
|
@ -57,6 +59,8 @@ public class HazelcastSessionDataStoreFactory
|
|||
|
||||
private boolean scavengeZombies = false;
|
||||
|
||||
private String addresses;
|
||||
|
||||
public boolean isScavengeZombies()
|
||||
{
|
||||
return scavengeZombies;
|
||||
|
@ -82,6 +86,12 @@ public class HazelcastSessionDataStoreFactory
|
|||
if (configurationLocation == null)
|
||||
{
|
||||
ClientConfig config = new ClientConfig();
|
||||
|
||||
if (addresses != null && !addresses.isEmpty())
|
||||
{
|
||||
config.getNetworkConfig().setAddresses(Arrays.asList(addresses.split(",")));
|
||||
}
|
||||
|
||||
SerializerConfig sc = new SerializerConfig()
|
||||
.setImplementation(new SessionDataSerializer())
|
||||
.setTypeClass(SessionData.class);
|
||||
|
@ -202,4 +212,14 @@ public class HazelcastSessionDataStoreFactory
|
|||
{
|
||||
this.hazelcastInstanceName = hazelcastInstanceName;
|
||||
}
|
||||
|
||||
public String getAddresses()
|
||||
{
|
||||
return addresses;
|
||||
}
|
||||
|
||||
public void setAddresses(String addresses)
|
||||
{
|
||||
this.addresses = addresses;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,9 @@
|
|||
<Set name="umaskOctal"><Property name="jetty.setuid.umask" default="002"/></Set>
|
||||
<Set name="username"><Property name="jetty.setuid.userName" default="jetty"/></Set>
|
||||
<Set name="groupname"><Property name="jetty.setuid.groupName" default="jetty"/></Set>
|
||||
<Set name="clearSupplementalGroups">
|
||||
<Property name="jetty.setuid.clearSupplementalGroups" default="false" />
|
||||
</Set>
|
||||
<!-- uncomment to change the limits on number of open file descriptors for root -->
|
||||
<!--
|
||||
<Call name="setRLimitNoFiles">
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Enables the unix setUID configuration so that the server
|
||||
|
@ -9,7 +9,7 @@ changing to a restricted user (eg jetty).
|
|||
server
|
||||
|
||||
[lib]
|
||||
lib/setuid/jetty-setuid-java-1.0.3.jar
|
||||
lib/setuid/jetty-setuid-java-1.0.4.jar
|
||||
|
||||
[xml]
|
||||
etc/jetty-setuid.xml
|
||||
|
@ -20,3 +20,4 @@ etc/jetty-setuid.xml
|
|||
# jetty.setuid.userName=jetty
|
||||
# jetty.setuid.groupName=jetty
|
||||
# jetty.setuid.umask=002
|
||||
# jetty.setuid.clearSupplementalGroups=false
|
||||
|
|
|
@ -74,6 +74,7 @@ public class HttpGenerator
|
|||
NEED_CHUNK, // Need a small chunk buffer of CHUNK_SIZE
|
||||
NEED_INFO, // Need the request/response metadata info
|
||||
NEED_HEADER, // Need a buffer to build HTTP headers into
|
||||
HEADER_OVERFLOW, // The header buffer overflowed
|
||||
NEED_CHUNK_TRAILER, // Need a large chunk buffer for last chunk and trailers
|
||||
FLUSH, // The buffers previously generated should be flushed
|
||||
CONTINUE, // Continue generating the message
|
||||
|
@ -250,7 +251,8 @@ public class HttpGenerator
|
|||
}
|
||||
catch (BufferOverflowException e)
|
||||
{
|
||||
throw new BadMessageException(INTERNAL_SERVER_ERROR_500, "Request header too large", e);
|
||||
LOG.ignore(e);
|
||||
return Result.HEADER_OVERFLOW;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@ -427,7 +429,8 @@ public class HttpGenerator
|
|||
}
|
||||
catch (BufferOverflowException e)
|
||||
{
|
||||
throw new BadMessageException(INTERNAL_SERVER_ERROR_500, "Response header too large", e);
|
||||
LOG.ignore(e);
|
||||
return Result.HEADER_OVERFLOW;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.junit.jupiter.api.Test;
|
|||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class HttpGeneratorClientTest
|
||||
|
@ -122,6 +123,42 @@ public class HttpGeneratorClientTest
|
|||
assertThat(out, Matchers.not(Matchers.containsString("Null:")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHeaderOverflow() throws Exception
|
||||
{
|
||||
HttpGenerator gen = new HttpGenerator();
|
||||
|
||||
Info info = new Info("GET", "/index.html");
|
||||
info.getFields().add("Host", "localhost");
|
||||
info.getFields().add("Field", "SomeWhatLongValue");
|
||||
info.setHttpVersion(HttpVersion.HTTP_1_0);
|
||||
|
||||
HttpGenerator.Result result = gen.generateRequest(info, null, null, null, true);
|
||||
assertEquals(HttpGenerator.Result.NEED_HEADER, result);
|
||||
|
||||
ByteBuffer header = BufferUtil.allocate(16);
|
||||
result = gen.generateRequest(info, header, null, null, true);
|
||||
assertEquals(HttpGenerator.Result.HEADER_OVERFLOW, result);
|
||||
|
||||
header = BufferUtil.allocate(2048);
|
||||
result = gen.generateRequest(info, header, null, null, true);
|
||||
assertEquals(HttpGenerator.Result.FLUSH, result);
|
||||
assertEquals(HttpGenerator.State.COMPLETING, gen.getState());
|
||||
assertFalse(gen.isChunking());
|
||||
String out = BufferUtil.toString(header);
|
||||
BufferUtil.clear(header);
|
||||
|
||||
result = gen.generateResponse(null, false, null, null, null, false);
|
||||
assertEquals(HttpGenerator.Result.SHUTDOWN_OUT, result);
|
||||
assertEquals(HttpGenerator.State.END, gen.getState());
|
||||
assertFalse(gen.isChunking());
|
||||
|
||||
assertEquals(0, gen.getContentPrepared());
|
||||
assertThat(out, Matchers.containsString("GET /index.html HTTP/1.0"));
|
||||
assertThat(out, Matchers.not(Matchers.containsString("Content-Length")));
|
||||
assertThat(out, Matchers.containsString("Field: SomeWhatLongValue"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPOSTRequestNoContent() throws Exception
|
||||
{
|
||||
|
|
|
@ -156,6 +156,12 @@ public class HttpGeneratorServerHTTPTest
|
|||
header = BufferUtil.allocate(2048);
|
||||
continue;
|
||||
|
||||
case HEADER_OVERFLOW:
|
||||
if (header.capacity() >= 8192)
|
||||
throw new BadMessageException(500, "Header too large");
|
||||
header = BufferUtil.allocate(8192);
|
||||
continue;
|
||||
|
||||
case NEED_CHUNK:
|
||||
chunk = BufferUtil.allocate(HttpGenerator.CHUNK_SIZE);
|
||||
continue;
|
||||
|
|
|
@ -110,6 +110,38 @@ public class HttpGeneratorServerTest
|
|||
assertThat(response, containsString("\r\n0123456789"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHeaderOverflow() throws Exception
|
||||
{
|
||||
HttpGenerator gen = new HttpGenerator();
|
||||
|
||||
MetaData.Response info = new MetaData.Response(HttpVersion.HTTP_1_1, 302, null, new HttpFields(), 0);
|
||||
info.getFields().add("Location", "http://somewhere/else");
|
||||
|
||||
HttpGenerator.Result result = gen.generateResponse(info, false, null, null, null, true);
|
||||
assertEquals(HttpGenerator.Result.NEED_HEADER, result);
|
||||
|
||||
ByteBuffer header = BufferUtil.allocate(16);
|
||||
result = gen.generateResponse(info, false, header, null, null, true);
|
||||
assertEquals(HttpGenerator.Result.HEADER_OVERFLOW, result);
|
||||
|
||||
header = BufferUtil.allocate(8096);
|
||||
result = gen.generateResponse(info, false, header, null, null, true);
|
||||
assertEquals(HttpGenerator.Result.FLUSH, result);
|
||||
assertEquals(HttpGenerator.State.COMPLETING, gen.getState());
|
||||
String response = BufferUtil.toString(header);
|
||||
BufferUtil.clear(header);
|
||||
|
||||
result = gen.generateResponse(null, false, null, null, null, false);
|
||||
assertEquals(HttpGenerator.Result.DONE, result);
|
||||
assertEquals(HttpGenerator.State.END, gen.getState());
|
||||
|
||||
assertEquals(0, gen.getContentPrepared());
|
||||
|
||||
assertThat(response, containsString("HTTP/1.1 302 Found"));
|
||||
assertThat(response, containsString("Location: http://somewhere/else"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test204() throws Exception
|
||||
{
|
||||
|
|
|
@ -25,9 +25,11 @@ import java.nio.charset.StandardCharsets;
|
|||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
@ -39,6 +41,7 @@ import org.eclipse.jetty.http.HttpStatus;
|
|||
import org.eclipse.jetty.http.HttpVersion;
|
||||
import org.eclipse.jetty.http.MetaData;
|
||||
import org.eclipse.jetty.http2.ErrorCode;
|
||||
import org.eclipse.jetty.http2.HTTP2Session;
|
||||
import org.eclipse.jetty.http2.api.Session;
|
||||
import org.eclipse.jetty.http2.api.Stream;
|
||||
import org.eclipse.jetty.http2.api.server.ServerSessionListener;
|
||||
|
@ -59,6 +62,7 @@ import org.eclipse.jetty.util.FuturePromise;
|
|||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.Jetty;
|
||||
import org.eclipse.jetty.util.Promise;
|
||||
import org.eclipse.jetty.util.component.Graceful;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
@ -66,6 +70,7 @@ import static org.hamcrest.Matchers.instanceOf;
|
|||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
|
@ -466,7 +471,7 @@ public class HTTP2Test extends AbstractTest
|
|||
// The third stream must not be created.
|
||||
MetaData.Request request3 = newRequest("GET", new HttpFields());
|
||||
CountDownLatch maxStreamsLatch = new CountDownLatch(1);
|
||||
session.newStream(new HeadersFrame(request3, null, false), new Promise.Adapter<Stream>()
|
||||
session.newStream(new HeadersFrame(request3, null, false), new Promise.Adapter<>()
|
||||
{
|
||||
@Override
|
||||
public void failed(Throwable x)
|
||||
|
@ -494,7 +499,7 @@ public class HTTP2Test extends AbstractTest
|
|||
// Create a fourth stream.
|
||||
MetaData.Request request4 = newRequest("GET", new HttpFields());
|
||||
CountDownLatch exchangeLatch4 = new CountDownLatch(2);
|
||||
session.newStream(new HeadersFrame(request4, null, true), new Promise.Adapter<Stream>()
|
||||
session.newStream(new HeadersFrame(request4, null, true), new Promise.Adapter<>()
|
||||
{
|
||||
@Override
|
||||
public void succeeded(Stream result)
|
||||
|
@ -893,6 +898,125 @@ public class HTTP2Test extends AbstractTest
|
|||
assertTrue(resetLatch.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGracefulServerGoAway() throws Exception
|
||||
{
|
||||
AtomicReference<Session> serverSessionRef = new AtomicReference<>();
|
||||
CountDownLatch serverSessionLatch = new CountDownLatch(1);
|
||||
CountDownLatch dataLatch = new CountDownLatch(2);
|
||||
start(new ServerSessionListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onAccept(Session session)
|
||||
{
|
||||
serverSessionRef.set(session);
|
||||
serverSessionLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
|
||||
{
|
||||
return new Stream.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onData(Stream stream, DataFrame frame, Callback callback)
|
||||
{
|
||||
callback.succeeded();
|
||||
dataLatch.countDown();
|
||||
if (frame.isEndStream())
|
||||
{
|
||||
MetaData.Response response = new MetaData.Response(HttpVersion.HTTP_2, HttpStatus.OK_200, new HttpFields());
|
||||
stream.headers(new HeadersFrame(stream.getId(), response, null, true), Callback.NOOP);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
// Avoid aggressive idle timeout to allow the test verifications.
|
||||
connector.setShutdownIdleTimeout(connector.getIdleTimeout());
|
||||
|
||||
CountDownLatch clientCloseLatch = new CountDownLatch(1);
|
||||
Session clientSession = newClient(new Session.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onClose(Session session, GoAwayFrame frame)
|
||||
{
|
||||
clientCloseLatch.countDown();
|
||||
}
|
||||
});
|
||||
assertTrue(serverSessionLatch.await(5, TimeUnit.SECONDS));
|
||||
Session serverSession = serverSessionRef.get();
|
||||
|
||||
// Start 2 requests without completing them yet.
|
||||
CountDownLatch responseLatch = new CountDownLatch(2);
|
||||
MetaData.Request metaData1 = newRequest("GET", new HttpFields());
|
||||
HeadersFrame request1 = new HeadersFrame(metaData1, null, false);
|
||||
FuturePromise<Stream> promise1 = new FuturePromise<>();
|
||||
Stream.Listener.Adapter listener = new Stream.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onHeaders(Stream stream, HeadersFrame frame)
|
||||
{
|
||||
if (frame.isEndStream())
|
||||
{
|
||||
MetaData.Response response = (MetaData.Response)frame.getMetaData();
|
||||
assertEquals(HttpStatus.OK_200, response.getStatus());
|
||||
responseLatch.countDown();
|
||||
}
|
||||
}
|
||||
};
|
||||
clientSession.newStream(request1, promise1, listener);
|
||||
Stream stream1 = promise1.get(5, TimeUnit.SECONDS);
|
||||
stream1.data(new DataFrame(stream1.getId(), ByteBuffer.allocate(1), false), Callback.NOOP);
|
||||
|
||||
MetaData.Request metaData2 = newRequest("GET", new HttpFields());
|
||||
HeadersFrame request2 = new HeadersFrame(metaData2, null, false);
|
||||
FuturePromise<Stream> promise2 = new FuturePromise<>();
|
||||
clientSession.newStream(request2, promise2, listener);
|
||||
Stream stream2 = promise2.get(5, TimeUnit.SECONDS);
|
||||
stream2.data(new DataFrame(stream2.getId(), ByteBuffer.allocate(1), false), Callback.NOOP);
|
||||
|
||||
assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
|
||||
|
||||
// Both requests are now on the server, shutdown gracefully the server session.
|
||||
int port = connector.getLocalPort();
|
||||
CompletableFuture<Void> shutdown = Graceful.shutdown(server);
|
||||
|
||||
// GOAWAY should not arrive to the client yet.
|
||||
assertFalse(clientCloseLatch.await(1, TimeUnit.SECONDS));
|
||||
|
||||
// New requests should be immediately rejected.
|
||||
HostPortHttpField authority3 = new HostPortHttpField("localhost" + ":" + port);
|
||||
MetaData.Request metaData3 = new MetaData.Request("GET", HttpScheme.HTTP, authority3, servletPath, HttpVersion.HTTP_2, new HttpFields());
|
||||
HeadersFrame request3 = new HeadersFrame(metaData3, null, false);
|
||||
FuturePromise<Stream> promise3 = new FuturePromise<>();
|
||||
CountDownLatch resetLatch = new CountDownLatch(1);
|
||||
clientSession.newStream(request3, promise3, new Stream.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onReset(Stream stream, ResetFrame frame)
|
||||
{
|
||||
resetLatch.countDown();
|
||||
}
|
||||
});
|
||||
Stream stream3 = promise3.get(5, TimeUnit.SECONDS);
|
||||
stream3.data(new DataFrame(stream3.getId(), ByteBuffer.allocate(1), true), Callback.NOOP);
|
||||
assertTrue(resetLatch.await(5, TimeUnit.SECONDS));
|
||||
|
||||
// Finish the previous requests and expect the responses.
|
||||
stream1.data(new DataFrame(stream1.getId(), BufferUtil.EMPTY_BUFFER, true), Callback.NOOP);
|
||||
stream2.data(new DataFrame(stream2.getId(), BufferUtil.EMPTY_BUFFER, true), Callback.NOOP);
|
||||
assertTrue(responseLatch.await(5, TimeUnit.SECONDS));
|
||||
assertNull(shutdown.get(5, TimeUnit.SECONDS));
|
||||
|
||||
// Now GOAWAY should arrive to the client.
|
||||
assertTrue(clientCloseLatch.await(5, TimeUnit.SECONDS));
|
||||
// Wait to process the GOAWAY frames and close the EndPoints.
|
||||
Thread.sleep(1000);
|
||||
assertFalse(((HTTP2Session)clientSession).getEndPoint().isOpen());
|
||||
assertFalse(((HTTP2Session)serverSession).getEndPoint().isOpen());
|
||||
}
|
||||
|
||||
private static void sleep(long time)
|
||||
{
|
||||
try
|
||||
|
|
|
@ -26,6 +26,7 @@ import java.util.ArrayList;
|
|||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
@ -77,6 +78,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
|||
private static final Logger LOG = Log.getLogger(HTTP2Session.class);
|
||||
|
||||
private final ConcurrentMap<Integer, IStream> streams = new ConcurrentHashMap<>();
|
||||
private final AtomicBiInteger streamCount = new AtomicBiInteger(); // Hi = closed, Lo = stream count
|
||||
private final AtomicInteger localStreamIds = new AtomicInteger();
|
||||
private final AtomicInteger lastRemoteStreamId = new AtomicInteger();
|
||||
private final AtomicInteger localStreamCount = new AtomicInteger();
|
||||
|
@ -100,6 +102,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
|||
private boolean connectProtocolEnabled;
|
||||
private long idleTime;
|
||||
private GoAwayFrame closeFrame;
|
||||
private Callback.Completable shutdownCallback;
|
||||
|
||||
public HTTP2Session(Scheduler scheduler, EndPoint endPoint, Generator generator, Session.Listener listener, FlowControlStrategy flowControl, int initialStreamId)
|
||||
{
|
||||
|
@ -436,14 +439,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
|||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Received {}", frame);
|
||||
|
||||
while (true)
|
||||
{
|
||||
CloseState current = closed.get();
|
||||
switch (current)
|
||||
{
|
||||
case NOT_CLOSED:
|
||||
{
|
||||
if (closed.compareAndSet(current, CloseState.REMOTELY_CLOSED))
|
||||
if (closed.compareAndSet(CloseState.NOT_CLOSED, CloseState.REMOTELY_CLOSED))
|
||||
{
|
||||
// We received a GO_AWAY, so try to write
|
||||
// what's in the queue and then disconnect.
|
||||
|
@ -451,16 +447,9 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
|||
notifyClose(this, frame, new DisconnectCallback());
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Ignored {}, already closed", frame);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -537,7 +526,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
|||
|
||||
protected void onConnectionFailure(int error, String reason, Callback callback)
|
||||
{
|
||||
notifyFailure(this, new IOException(String.format("%d/%s", error, reason)), new CloseCallback(error, reason, callback));
|
||||
notifyFailure(this, new IOException(String.format("%d/%s", error, reason)), new FailureCallback(error, reason, callback));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -672,30 +661,42 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
|||
@Override
|
||||
public boolean close(int error, String reason, Callback callback)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
CloseState current = closed.get();
|
||||
switch (current)
|
||||
{
|
||||
case NOT_CLOSED:
|
||||
{
|
||||
if (closed.compareAndSet(current, CloseState.LOCALLY_CLOSED))
|
||||
if (closed.compareAndSet(CloseState.NOT_CLOSED, CloseState.LOCALLY_CLOSED))
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Closing {}/{}", error, reason);
|
||||
closeFrame = newGoAwayFrame(CloseState.LOCALLY_CLOSED, error, reason);
|
||||
control(null, callback, closeFrame);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Ignoring close {}/{}, already closed", error, reason);
|
||||
callback.succeeded();
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> shutdown()
|
||||
{
|
||||
if (closed.compareAndSet(CloseState.NOT_CLOSED, CloseState.LOCALLY_CLOSED))
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Shutting down {}", this);
|
||||
closeFrame = newGoAwayFrame(CloseState.LOCALLY_CLOSED, ErrorCode.NO_ERROR.code, "shutdown");
|
||||
shutdownCallback = new Callback.Completable();
|
||||
// Only send the close frame when we can flip Hi and Lo = 0, see onStreamClosed().
|
||||
if (streamCount.compareAndSet(0, 1, 0, 0))
|
||||
control(null, shutdownCallback, closeFrame);
|
||||
return shutdownCallback;
|
||||
}
|
||||
}
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Ignoring shutdown, already closed");
|
||||
Callback.Completable result = shutdownCallback;
|
||||
// Result may be null if the shutdown is in progress,
|
||||
// don't wait and return a completed CompletableFuture.
|
||||
return result != null ? result : CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
private GoAwayFrame newGoAwayFrame(CloseState closeState, int error, String reason)
|
||||
|
@ -1041,10 +1042,28 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
|||
|
||||
protected void onStreamOpened(IStream stream)
|
||||
{
|
||||
streamCount.addAndGetLo(1);
|
||||
}
|
||||
|
||||
protected void onStreamClosed(IStream stream)
|
||||
{
|
||||
Callback callback = null;
|
||||
while (true)
|
||||
{
|
||||
long encoded = streamCount.get();
|
||||
int closed = AtomicBiInteger.getHi(encoded);
|
||||
int streams = AtomicBiInteger.getLo(encoded) - 1;
|
||||
if (streams == 0 && closed == 0)
|
||||
{
|
||||
callback = shutdownCallback;
|
||||
closed = 1;
|
||||
}
|
||||
if (streamCount.compareAndSet(encoded, closed, streams))
|
||||
break;
|
||||
}
|
||||
// Only send the close frame if we can flip Hi and Lo = 0, see shutdown().
|
||||
if (callback != null)
|
||||
control(null, callback, closeFrame);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1321,8 +1340,9 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
|||
{
|
||||
case HEADERS:
|
||||
{
|
||||
onStreamOpened(stream);
|
||||
HeadersFrame headersFrame = (HeadersFrame)frame;
|
||||
if (headersFrame.getMetaData().isRequest())
|
||||
onStreamOpened(stream);
|
||||
if (stream.updateClose(headersFrame.isEndStream(), CloseState.Event.AFTER_SEND))
|
||||
removeStream(stream);
|
||||
break;
|
||||
|
@ -1598,12 +1618,12 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
|||
}
|
||||
}
|
||||
|
||||
private class CloseCallback extends Callback.Nested
|
||||
private class FailureCallback extends Callback.Nested
|
||||
{
|
||||
private final int error;
|
||||
private final String reason;
|
||||
|
||||
private CloseCallback(int error, String reason, Callback callback)
|
||||
private FailureCallback(int error, String reason, Callback callback)
|
||||
{
|
||||
super(callback);
|
||||
this.error = error;
|
||||
|
|
|
@ -322,7 +322,7 @@ public class HTTP2Stream extends IdleTimeout implements IStream, Callback, Dumpa
|
|||
{
|
||||
// It's a bad client, it does not deserve to be
|
||||
// treated gently by just resetting the stream.
|
||||
session.close(ErrorCode.FLOW_CONTROL_ERROR.code, "stream_window_exceeded", Callback.NOOP);
|
||||
((HTTP2Session)session).onConnectionFailure(ErrorCode.FLOW_CONTROL_ERROR.code, "stream_window_exceeded");
|
||||
callback.failed(new IOException("stream_window_exceeded"));
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
package org.eclipse.jetty.http2;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.eclipse.jetty.http2.api.Session;
|
||||
import org.eclipse.jetty.http2.api.Stream;
|
||||
|
@ -151,4 +152,14 @@ public interface ISession extends Session
|
|||
* @param callback the callback to notify when the frame has been processed
|
||||
*/
|
||||
public void onData(DataFrame frame, Callback callback);
|
||||
|
||||
/**
|
||||
* <p>Gracefully closes the session, returning a {@code CompletableFuture} that
|
||||
* is completed when all the streams currently being processed are completed.</p>
|
||||
* <p>Implementation is idempotent, i.e. calling this method a second time
|
||||
* or concurrently results in a no-operation.</p>
|
||||
*
|
||||
* @return a {@code CompletableFuture} that is completed when all the streams are completed
|
||||
*/
|
||||
public CompletableFuture<Void> shutdown();
|
||||
}
|
||||
|
|
|
@ -24,11 +24,14 @@ import java.util.HashSet;
|
|||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.eclipse.jetty.http2.BufferingFlowControlStrategy;
|
||||
import org.eclipse.jetty.http2.FlowControlStrategy;
|
||||
import org.eclipse.jetty.http2.HTTP2Connection;
|
||||
import org.eclipse.jetty.http2.ISession;
|
||||
import org.eclipse.jetty.http2.api.Session;
|
||||
import org.eclipse.jetty.http2.api.server.ServerSessionListener;
|
||||
import org.eclipse.jetty.http2.frames.Frame;
|
||||
|
@ -46,6 +49,7 @@ import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
|||
import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||
import org.eclipse.jetty.util.annotation.Name;
|
||||
import org.eclipse.jetty.util.component.Dumpable;
|
||||
import org.eclipse.jetty.util.component.Graceful;
|
||||
import org.eclipse.jetty.util.component.LifeCycle;
|
||||
|
||||
@ManagedObject
|
||||
|
@ -296,22 +300,25 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne
|
|||
}
|
||||
|
||||
@ManagedObject("The container of HTTP/2 sessions")
|
||||
public static class HTTP2SessionContainer implements Connection.Listener, Dumpable
|
||||
public static class HTTP2SessionContainer implements Connection.Listener, Graceful, Dumpable
|
||||
{
|
||||
private final Set<Session> sessions = ConcurrentHashMap.newKeySet();
|
||||
private final Set<ISession> sessions = ConcurrentHashMap.newKeySet();
|
||||
private final AtomicReference<CompletableFuture<Void>> shutdown = new AtomicReference<>();
|
||||
|
||||
@Override
|
||||
public void onOpened(Connection connection)
|
||||
{
|
||||
Session session = ((HTTP2Connection)connection).getSession();
|
||||
ISession session = ((HTTP2Connection)connection).getSession();
|
||||
sessions.add(session);
|
||||
LifeCycle.start(session);
|
||||
if (isShutdown())
|
||||
shutdown(session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClosed(Connection connection)
|
||||
{
|
||||
Session session = ((HTTP2Connection)connection).getSession();
|
||||
ISession session = ((HTTP2Connection)connection).getSession();
|
||||
if (sessions.remove(session))
|
||||
LifeCycle.stop(session);
|
||||
}
|
||||
|
@ -327,6 +334,39 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne
|
|||
return sessions.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> shutdown()
|
||||
{
|
||||
CompletableFuture<Void> result = new CompletableFuture<>();
|
||||
if (shutdown.compareAndSet(null, result))
|
||||
{
|
||||
CompletableFuture.allOf(sessions.stream().map(this::shutdown).toArray(CompletableFuture[]::new))
|
||||
.whenComplete((v, x) ->
|
||||
{
|
||||
if (x == null)
|
||||
result.complete(v);
|
||||
else
|
||||
result.completeExceptionally(x);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return shutdown.get();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShutdown()
|
||||
{
|
||||
return shutdown.get() != null;
|
||||
}
|
||||
|
||||
private CompletableFuture<Void> shutdown(ISession session)
|
||||
{
|
||||
return session.shutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String dump()
|
||||
{
|
||||
|
|
|
@ -103,6 +103,12 @@ public class HTTP2ServerSession extends HTTP2Session implements ServerParser.Lis
|
|||
onConnectionFailure(ErrorCode.STREAM_CLOSED_ERROR.code, "unexpected_headers_frame");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isClosed())
|
||||
{
|
||||
reset(new ResetFrame(streamId, ErrorCode.REFUSED_STREAM_ERROR.code), Callback.NOOP);
|
||||
}
|
||||
else
|
||||
{
|
||||
stream = createRemoteStream(streamId, (MetaData.Request)metaData);
|
||||
if (stream != null)
|
||||
|
@ -124,6 +130,7 @@ public class HTTP2ServerSession extends HTTP2Session implements ServerParser.Lis
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
onConnectionFailure(ErrorCode.PROTOCOL_ERROR.code, "duplicate_stream");
|
||||
|
|
|
@ -95,6 +95,16 @@ import org.eclipse.jetty.util.thread.Invocable;
|
|||
*/
|
||||
public interface EndPoint extends Closeable
|
||||
{
|
||||
/**
|
||||
* Marks an <code>EndPoint</code> that wraps another <code>EndPoint</code>.
|
||||
*/
|
||||
public interface Wrapper
|
||||
{
|
||||
/**
|
||||
* @return The wrapped <code>EndPoint</code>
|
||||
*/
|
||||
EndPoint unwrap();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The local Inet address to which this <code>EndPoint</code> is bound, or <code>null</code>
|
||||
|
|
|
@ -457,7 +457,7 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr
|
|||
return getEndPoint().flush(output);
|
||||
}
|
||||
|
||||
public class DecryptedEndPoint extends AbstractEndPoint
|
||||
public class DecryptedEndPoint extends AbstractEndPoint implements EndPoint.Wrapper
|
||||
{
|
||||
private final Callback _incompleteWriteCallback = new IncompleteWriteCallback();
|
||||
private Throwable _failure;
|
||||
|
@ -469,6 +469,12 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr
|
|||
super.setIdleTimeout(-1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EndPoint unwrap()
|
||||
{
|
||||
return getEndPoint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getIdleTimeout()
|
||||
{
|
||||
|
@ -682,7 +688,7 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr
|
|||
try
|
||||
{
|
||||
_underflown = false;
|
||||
unwrapResult = unwrap(_sslEngine, _encryptedInput, appIn);
|
||||
unwrapResult = SslConnection.this.unwrap(_sslEngine, _encryptedInput, appIn);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@ -1554,5 +1560,6 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr
|
|||
return String.format("SSL@%h.DEP.writeCallback", SslConnection.this);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -126,9 +126,9 @@ public class AnnotationConfiguration extends org.eclipse.jetty.annotations.Annot
|
|||
continue;
|
||||
|
||||
Resource bundleRes = oparser.indexBundle(bundle);
|
||||
if (!context.getMetaData().getWebInfJars().contains(bundleRes))
|
||||
if (!context.getMetaData().getWebInfResources(false).contains(bundleRes))
|
||||
{
|
||||
context.getMetaData().addWebInfJar(bundleRes);
|
||||
context.getMetaData().addWebInfResource(bundleRes);
|
||||
}
|
||||
|
||||
if (bundle.getHeaders().get(Constants.FRAGMENT_HOST) != null)
|
||||
|
|
|
@ -70,7 +70,7 @@
|
|||
<Call class="org.eclipse.jetty.webapp.Configurations" name="setServerDefault">
|
||||
<Arg><Ref refid="Server"/></Arg>
|
||||
<Call name="add">
|
||||
<Arg>
|
||||
<Arg name="configClass">
|
||||
<Array type="String">
|
||||
<Item>org.eclipse.jetty.webapp.FragmentConfiguration</Item>
|
||||
<Item>org.eclipse.jetty.webapp.JettyWebXmlConfiguration</Item>
|
||||
|
|
|
@ -48,6 +48,16 @@
|
|||
<groupId>jakarta.transaction</groupId>
|
||||
<artifactId>jakarta.transaction-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.derby</groupId>
|
||||
<artifactId>derby</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.toolchain</groupId>
|
||||
<artifactId>jetty-test-helper</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-jndi</artifactId>
|
||||
|
|
|
@ -748,8 +748,9 @@ public class PlusDescriptorProcessor extends IterativeDescriptorProcessor
|
|||
injections.add(injection);
|
||||
|
||||
//Record which was the first descriptor to declare an injection for this name
|
||||
if (context.getMetaData().getOriginDescriptor(node.getTag() + "." + jndiName + ".injection") == null)
|
||||
context.getMetaData().setOrigin(node.getTag() + "." + jndiName + ".injection", descriptor);
|
||||
String name = node.getTag() + "." + jndiName + ".injection";
|
||||
if (context.getMetaData().getOriginDescriptor(name) == null)
|
||||
context.getMetaData().setOrigin(name, descriptor);
|
||||
}
|
||||
catch (ClassNotFoundException e)
|
||||
{
|
||||
|
|
|
@ -74,20 +74,20 @@ public class PlusDescriptorProcessorTest
|
|||
|
||||
URL webXml = Thread.currentThread().getContextClassLoader().getResource("web.xml");
|
||||
webDescriptor = new WebDescriptor(org.eclipse.jetty.util.resource.Resource.newResource(webXml));
|
||||
webDescriptor.parse();
|
||||
webDescriptor.parse(WebDescriptor.getParser(false));
|
||||
|
||||
URL frag1Xml = Thread.currentThread().getContextClassLoader().getResource("web-fragment-1.xml");
|
||||
fragDescriptor1 = new FragmentDescriptor(org.eclipse.jetty.util.resource.Resource.newResource(frag1Xml));
|
||||
fragDescriptor1.parse();
|
||||
fragDescriptor1.parse(WebDescriptor.getParser(false));
|
||||
URL frag2Xml = Thread.currentThread().getContextClassLoader().getResource("web-fragment-2.xml");
|
||||
fragDescriptor2 = new FragmentDescriptor(org.eclipse.jetty.util.resource.Resource.newResource(frag2Xml));
|
||||
fragDescriptor2.parse();
|
||||
fragDescriptor2.parse(WebDescriptor.getParser(false));
|
||||
URL frag3Xml = Thread.currentThread().getContextClassLoader().getResource("web-fragment-3.xml");
|
||||
fragDescriptor3 = new FragmentDescriptor(org.eclipse.jetty.util.resource.Resource.newResource(frag3Xml));
|
||||
fragDescriptor3.parse();
|
||||
fragDescriptor3.parse(WebDescriptor.getParser(false));
|
||||
URL frag4Xml = Thread.currentThread().getContextClassLoader().getResource("web-fragment-4.xml");
|
||||
fragDescriptor4 = new FragmentDescriptor(org.eclipse.jetty.util.resource.Resource.newResource(frag4Xml));
|
||||
fragDescriptor4.parse();
|
||||
fragDescriptor4.parse(WebDescriptor.getParser(false));
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
|
|
|
@ -36,6 +36,7 @@ import org.eclipse.jetty.webapp.AbstractConfiguration;
|
|||
import org.eclipse.jetty.webapp.Configuration;
|
||||
import org.eclipse.jetty.webapp.StandardDescriptorProcessor;
|
||||
import org.eclipse.jetty.webapp.WebAppContext;
|
||||
import org.eclipse.jetty.webapp.WebDescriptor;
|
||||
import org.eclipse.jetty.webapp.WebInfConfiguration;
|
||||
import org.eclipse.jetty.webapp.WebXmlConfiguration;
|
||||
|
||||
|
@ -211,9 +212,9 @@ public class QuickStartConfiguration extends AbstractConfiguration
|
|||
context.setConfigurations(context.getConfigurations().stream()
|
||||
.filter(c -> !__replacedConfigurations.contains(c.replaces()) && !__replacedConfigurations.contains(c.getClass()))
|
||||
.collect(Collectors.toList()).toArray(new Configuration[]{}));
|
||||
context.getMetaData().setWebXml((Resource)context.getAttribute(QUICKSTART_WEB_XML));
|
||||
context.getServletContext().setEffectiveMajorVersion(context.getMetaData().getWebXml().getMajorVersion());
|
||||
context.getServletContext().setEffectiveMinorVersion(context.getMetaData().getWebXml().getMinorVersion());
|
||||
context.getMetaData().setWebDescriptor(new WebDescriptor((Resource)context.getAttribute(QUICKSTART_WEB_XML)));
|
||||
context.getServletContext().setEffectiveMajorVersion(context.getMetaData().getWebDescriptor().getMajorVersion());
|
||||
context.getServletContext().setEffectiveMinorVersion(context.getMetaData().getWebDescriptor().getMinorVersion());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-project</artifactId>
|
||||
<version>10.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jetty-runner</artifactId>
|
||||
<name>Jetty :: Runner</name>
|
||||
|
||||
<properties>
|
||||
<assembly-directory>target/distribution</assembly-directory>
|
||||
<bundle-symbolic-name>${project.groupId}.runner</bundle-symbolic-name>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>unpack-dependencies</id>
|
||||
<phase>prepare-package</phase>
|
||||
<goals>
|
||||
<goal>unpack-dependencies</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<includes>**</includes>
|
||||
<excludes>**/MANIFEST.MF,META-INF/*.RSA,META-INF/*.DSA,META-INF/*.SF,module-info.class</excludes>
|
||||
<outputDirectory>${project.build.directory}/classes</outputDirectory>
|
||||
<overWriteReleases>false</overWriteReleases>
|
||||
<overWriteSnapshots>true</overWriteSnapshots>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-invoker-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>integration-test</id>
|
||||
<phase>integration-test</phase>
|
||||
<goals>
|
||||
<goal>install</goal>
|
||||
<goal>integration-test</goal>
|
||||
<goal>verify</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<scriptVariables>
|
||||
<maven.dependency.plugin.version>${maven.dependency.plugin.version}</maven.dependency.plugin.version>
|
||||
</scriptVariables>
|
||||
<goals>
|
||||
<goal>clean</goal>
|
||||
</goals>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.neo4j.build.plugins</groupId>
|
||||
<artifactId>clirr-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<!-- Clirr fails with "org.apache.tools.ant.Task not found" on this project -->
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>maven-bundle-plugin</artifactId>
|
||||
<configuration>
|
||||
<!-- jetty-runner is not an OSGi component -->
|
||||
<skip>true</skip>
|
||||
<!-- there is no way to skip MANIFEST creation by the plugin so just configure dummy location -->
|
||||
<manifestLocation>${project.build.directory}/NON_USED_MANIFEST</manifestLocation>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifestFile>src/main/resources/MANIFEST.MF</manifestFile>
|
||||
<manifest>
|
||||
<mainClass>org.eclipse.jetty.runner.Runner</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-plus</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-annotations</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-jaas</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.websocket</groupId>
|
||||
<artifactId>websocket-jetty-server</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-jndi</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>apache-jsp</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>apache-jstl</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
|
@ -0,0 +1,36 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<settings>
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>it-repo</id>
|
||||
<activation>
|
||||
<activeByDefault>true</activeByDefault>
|
||||
</activation>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>local.central</id>
|
||||
<url>@localRepositoryUrl@</url>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
</repositories>
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>local.central</id>
|
||||
<url>@localRepositoryUrl@</url>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
</profile>
|
||||
</profiles>
|
||||
</settings>
|
|
@ -0,0 +1 @@
|
|||
invoker.goals = generate-resources
|
|
@ -0,0 +1,59 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>org.eclipse.jetty.its</groupId>
|
||||
<artifactId>jetty-runner-it-test</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<packaging>war</packaging>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-runner</artifactId>
|
||||
<version>@project.version@</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<version>@maven.dependency.plugin.version@</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-jetty-runner</id>
|
||||
<phase>generate-resources</phase>
|
||||
<goals>
|
||||
<goal>copy</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-runner</artifactId>
|
||||
<version>@project.version@</version>
|
||||
<type>jar</type>
|
||||
<overWrite>false</overWrite>
|
||||
<outputDirectory>${project.build.directory}/</outputDirectory>
|
||||
<destFileName>jetty-runner.jar</destFileName>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
<overWriteReleases>false</overWriteReleases>
|
||||
<overWriteSnapshots>true</overWriteSnapshots>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
||||
</build>
|
||||
</project>
|
|
@ -0,0 +1,10 @@
|
|||
import java.util.jar.*
|
||||
|
||||
File artifact = new File( basedir, "target/jetty-runner.jar" )
|
||||
assert artifact.exists()
|
||||
|
||||
JarFile jar = new JarFile( artifact );
|
||||
|
||||
Attributes manifest = jar.getManifest().getMainAttributes();
|
||||
|
||||
assert manifest.getValue( new Attributes.Name( "Main-Class" ) ).equals( "org.eclipse.jetty.runner.Runner" )
|
|
@ -0,0 +1,595 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under
|
||||
// the terms of the Eclipse Public License 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// This Source Code may also be made available under the following
|
||||
// Secondary Licenses when the conditions for such availability set
|
||||
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||
// the Apache License v2.0 which is available at
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.runner;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.eclipse.jetty.io.ConnectionStatistics;
|
||||
import org.eclipse.jetty.security.ConstraintMapping;
|
||||
import org.eclipse.jetty.security.ConstraintSecurityHandler;
|
||||
import org.eclipse.jetty.security.HashLoginService;
|
||||
import org.eclipse.jetty.security.authentication.BasicAuthenticator;
|
||||
import org.eclipse.jetty.server.AbstractConnector;
|
||||
import org.eclipse.jetty.server.Connector;
|
||||
import org.eclipse.jetty.server.CustomRequestLog;
|
||||
import org.eclipse.jetty.server.Handler;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.server.ShutdownMonitor;
|
||||
import org.eclipse.jetty.server.handler.ContextHandler;
|
||||
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
|
||||
import org.eclipse.jetty.server.handler.DefaultHandler;
|
||||
import org.eclipse.jetty.server.handler.HandlerCollection;
|
||||
import org.eclipse.jetty.server.handler.StatisticsHandler;
|
||||
import org.eclipse.jetty.server.session.SessionHandler;
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.eclipse.jetty.servlet.StatisticsServlet;
|
||||
import org.eclipse.jetty.util.RolloverFileOutputStream;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.util.security.Constraint;
|
||||
import org.eclipse.jetty.webapp.MetaInfConfiguration;
|
||||
import org.eclipse.jetty.webapp.WebAppContext;
|
||||
import org.eclipse.jetty.xml.XmlConfiguration;
|
||||
|
||||
/**
|
||||
* Runner
|
||||
* <p>
|
||||
* Combine jetty classes into a single executable jar and run webapps based on the args to it.
|
||||
*
|
||||
* @deprecated No replacement provided or available. Migrate to jetty-home (and use {@code ${jetty.base}} directory).
|
||||
*/
|
||||
@Deprecated
|
||||
public class Runner
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(Runner.class);
|
||||
|
||||
public static final String[] PLUS_CONFIGURATION_CLASSES =
|
||||
{
|
||||
org.eclipse.jetty.webapp.WebInfConfiguration.class.getCanonicalName(),
|
||||
org.eclipse.jetty.webapp.WebXmlConfiguration.class.getCanonicalName(),
|
||||
org.eclipse.jetty.webapp.MetaInfConfiguration.class.getCanonicalName(),
|
||||
org.eclipse.jetty.webapp.FragmentConfiguration.class.getCanonicalName(),
|
||||
org.eclipse.jetty.plus.webapp.EnvConfiguration.class.getCanonicalName(),
|
||||
org.eclipse.jetty.plus.webapp.PlusConfiguration.class.getCanonicalName(),
|
||||
org.eclipse.jetty.annotations.AnnotationConfiguration.class.getCanonicalName(),
|
||||
org.eclipse.jetty.webapp.JettyWebXmlConfiguration.class.getCanonicalName()
|
||||
};
|
||||
public static final String CONTAINER_INCLUDE_JAR_PATTERN = ".*/jetty-runner-[^/]*\\.jar$";
|
||||
public static final String DEFAULT_CONTEXT_PATH = "/";
|
||||
public static final int DEFAULT_PORT = 8080;
|
||||
|
||||
protected Server _server;
|
||||
protected URLClassLoader _classLoader;
|
||||
protected Classpath _classpath = new Classpath();
|
||||
protected ContextHandlerCollection _contexts;
|
||||
protected String _logFile;
|
||||
protected ArrayList<String> _configFiles;
|
||||
protected boolean _enableStats = false;
|
||||
protected String _statsPropFile;
|
||||
|
||||
/**
|
||||
* Classpath
|
||||
*/
|
||||
public class Classpath
|
||||
{
|
||||
private List<URI> _classpath = new ArrayList<>();
|
||||
|
||||
public void addJars(Resource lib) throws IOException
|
||||
{
|
||||
if (lib == null || !lib.exists())
|
||||
throw new IllegalStateException("No such lib: " + lib);
|
||||
|
||||
String[] list = lib.list();
|
||||
if (list == null)
|
||||
return;
|
||||
|
||||
for (String path : list)
|
||||
{
|
||||
if (".".equals(path) || "..".equals(path))
|
||||
continue;
|
||||
|
||||
try (Resource item = lib.addPath(path))
|
||||
{
|
||||
if (item.isDirectory())
|
||||
addJars(item);
|
||||
else
|
||||
{
|
||||
String lowerCasePath = path.toLowerCase(Locale.ENGLISH);
|
||||
if (lowerCasePath.endsWith(".jar") ||
|
||||
lowerCasePath.endsWith(".zip"))
|
||||
{
|
||||
_classpath.add(item.getURI());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addPath(Resource path)
|
||||
{
|
||||
if (path == null || !path.exists())
|
||||
throw new IllegalStateException("No such path: " + path);
|
||||
_classpath.add(path.getURI());
|
||||
}
|
||||
|
||||
public URI[] asArray()
|
||||
{
|
||||
return _classpath.toArray(new URI[0]);
|
||||
}
|
||||
}
|
||||
|
||||
public Runner()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate helpful usage message and exit
|
||||
*
|
||||
* @param error the error header
|
||||
*/
|
||||
public void usage(String error)
|
||||
{
|
||||
if (error != null)
|
||||
System.err.println("ERROR: " + error);
|
||||
System.err.println("Usage: java [-Djetty.home=dir] -jar jetty-runner.jar [--help|--version] [ server opts] [[ context opts] context ...] ");
|
||||
System.err.println("Server opts:");
|
||||
System.err.println(" --version - display version and exit");
|
||||
System.err.println(" --log file - request log filename (with optional 'yyyy_mm_dd' wildcard");
|
||||
System.err.println(" --out file - info/warn/debug log filename (with optional 'yyyy_mm_dd' wildcard");
|
||||
System.err.println(" --host name|ip - interface to listen on (default is all interfaces)");
|
||||
System.err.println(" --port n - port to listen on (default 8080)");
|
||||
System.err.println(" --stop-port n - port to listen for stop command (or -DSTOP.PORT=n)");
|
||||
System.err.println(" --stop-key n - security string for stop command (required if --stop-port is present) (or -DSTOP.KEY=n)");
|
||||
System.err.println(" [--jar file]*n - each tuple specifies an extra jar to be added to the classloader");
|
||||
System.err.println(" [--lib dir]*n - each tuple specifies an extra directory of jars to be added to the classloader");
|
||||
System.err.println(" [--classes dir]*n - each tuple specifies an extra directory of classes to be added to the classloader");
|
||||
System.err.println(" --stats [unsecure|realm.properties] - enable stats gathering servlet context");
|
||||
System.err.println(" [--config file]*n - each tuple specifies the name of a jetty xml config file to apply (in the order defined)");
|
||||
System.err.println("Context opts:");
|
||||
System.err.println(" [[--path /path] context]*n - WAR file, web app dir or context xml file, optionally with a context path");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate version message and exit
|
||||
*/
|
||||
public void version()
|
||||
{
|
||||
System.err.println("org.eclipse.jetty.runner.Runner: " + Server.getVersion());
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure a jetty instance and deploy the webapps presented as args
|
||||
*
|
||||
* @param args the command line arguments
|
||||
* @throws Exception if unable to configure
|
||||
*/
|
||||
public void configure(String[] args) throws Exception
|
||||
{
|
||||
// handle classpath bits first so we can initialize the log mechanism.
|
||||
for (int i = 0; i < args.length; i++)
|
||||
{
|
||||
if ("--lib".equals(args[i]))
|
||||
{
|
||||
try (Resource lib = Resource.newResource(args[++i]))
|
||||
{
|
||||
if (!lib.exists() || !lib.isDirectory())
|
||||
usage("No such lib directory " + lib);
|
||||
_classpath.addJars(lib);
|
||||
}
|
||||
}
|
||||
else if ("--jar".equals(args[i]))
|
||||
{
|
||||
try (Resource jar = Resource.newResource(args[++i]))
|
||||
{
|
||||
if (!jar.exists() || jar.isDirectory())
|
||||
usage("No such jar " + jar);
|
||||
_classpath.addPath(jar);
|
||||
}
|
||||
}
|
||||
else if ("--classes".equals(args[i]))
|
||||
{
|
||||
try (Resource classes = Resource.newResource(args[++i]))
|
||||
{
|
||||
if (!classes.exists() || !classes.isDirectory())
|
||||
usage("No such classes directory " + classes);
|
||||
_classpath.addPath(classes);
|
||||
}
|
||||
}
|
||||
else if (args[i].startsWith("--"))
|
||||
i++;
|
||||
}
|
||||
|
||||
initClassLoader();
|
||||
|
||||
LOG.info("Runner");
|
||||
LOG.debug("Runner classpath {}", _classpath);
|
||||
|
||||
String contextPath = DEFAULT_CONTEXT_PATH;
|
||||
boolean contextPathSet = false;
|
||||
int port = DEFAULT_PORT;
|
||||
String host = null;
|
||||
int stopPort = Integer.getInteger("STOP.PORT", 0);
|
||||
String stopKey = System.getProperty("STOP.KEY", null);
|
||||
|
||||
boolean runnerServerInitialized = false;
|
||||
|
||||
for (int i = 0; i < args.length; i++)
|
||||
{
|
||||
switch (args[i])
|
||||
{
|
||||
case "--port":
|
||||
port = Integer.parseInt(args[++i]);
|
||||
break;
|
||||
case "--host":
|
||||
host = args[++i];
|
||||
break;
|
||||
case "--stop-port":
|
||||
stopPort = Integer.parseInt(args[++i]);
|
||||
break;
|
||||
case "--stop-key":
|
||||
stopKey = args[++i];
|
||||
break;
|
||||
case "--log":
|
||||
_logFile = args[++i];
|
||||
break;
|
||||
case "--out":
|
||||
String outFile = args[++i];
|
||||
PrintStream out = new PrintStream(new RolloverFileOutputStream(outFile, true, -1));
|
||||
LOG.info("Redirecting stderr/stdout to " + outFile);
|
||||
System.setErr(out);
|
||||
System.setOut(out);
|
||||
break;
|
||||
case "--path":
|
||||
contextPath = args[++i];
|
||||
contextPathSet = true;
|
||||
break;
|
||||
case "--config":
|
||||
if (_configFiles == null)
|
||||
_configFiles = new ArrayList<>();
|
||||
_configFiles.add(args[++i]);
|
||||
break;
|
||||
case "--lib":
|
||||
++i;//skip
|
||||
|
||||
break;
|
||||
case "--jar":
|
||||
++i; //skip
|
||||
|
||||
break;
|
||||
case "--classes":
|
||||
++i;//skip
|
||||
|
||||
break;
|
||||
case "--stats":
|
||||
_enableStats = true;
|
||||
_statsPropFile = args[++i];
|
||||
_statsPropFile = ("unsecure".equalsIgnoreCase(_statsPropFile) ? null : _statsPropFile);
|
||||
break;
|
||||
default:
|
||||
// process system property type argument so users can use in second args part
|
||||
if (args[i].startsWith("-D"))
|
||||
{
|
||||
String[] sysProps = args[i].substring(2).split("=", 2);
|
||||
if ("STOP.KEY".equals(sysProps[0]))
|
||||
{
|
||||
stopKey = sysProps[1];
|
||||
break;
|
||||
}
|
||||
else if ("STOP.PORT".equals(sysProps[0]))
|
||||
{
|
||||
stopPort = Integer.parseInt(sysProps[1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// process contexts
|
||||
|
||||
if (!runnerServerInitialized) // log handlers not registered, server maybe not created, etc
|
||||
{
|
||||
if (_server == null) // server not initialized yet
|
||||
{
|
||||
// build the server
|
||||
_server = new Server();
|
||||
}
|
||||
|
||||
//apply jetty config files if there are any
|
||||
if (_configFiles != null)
|
||||
{
|
||||
for (String cfg : _configFiles)
|
||||
{
|
||||
try (Resource resource = Resource.newResource(cfg))
|
||||
{
|
||||
XmlConfiguration xmlConfiguration = new XmlConfiguration(resource);
|
||||
xmlConfiguration.configure(_server);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//check that everything got configured, and if not, make the handlers
|
||||
HandlerCollection handlers = (HandlerCollection)_server.getChildHandlerByClass(HandlerCollection.class);
|
||||
if (handlers == null)
|
||||
{
|
||||
handlers = new HandlerCollection();
|
||||
_server.setHandler(handlers);
|
||||
}
|
||||
|
||||
//check if contexts already configured
|
||||
_contexts = (ContextHandlerCollection)handlers.getChildHandlerByClass(ContextHandlerCollection.class);
|
||||
if (_contexts == null)
|
||||
{
|
||||
_contexts = new ContextHandlerCollection();
|
||||
prependHandler(_contexts, handlers);
|
||||
}
|
||||
|
||||
if (_enableStats)
|
||||
{
|
||||
//if no stats handler already configured
|
||||
if (handlers.getChildHandlerByClass(StatisticsHandler.class) == null)
|
||||
{
|
||||
StatisticsHandler statsHandler = new StatisticsHandler();
|
||||
|
||||
Handler oldHandler = _server.getHandler();
|
||||
statsHandler.setHandler(oldHandler);
|
||||
_server.setHandler(statsHandler);
|
||||
|
||||
ServletContextHandler statsContext = new ServletContextHandler(_contexts, "/stats");
|
||||
statsContext.addServlet(new ServletHolder(new StatisticsServlet()), "/");
|
||||
statsContext.setSessionHandler(new SessionHandler());
|
||||
if (_statsPropFile != null)
|
||||
{
|
||||
final HashLoginService loginService = new HashLoginService("StatsRealm", _statsPropFile);
|
||||
Constraint constraint = new Constraint();
|
||||
constraint.setName("Admin Only");
|
||||
constraint.setRoles(new String[]{"admin"});
|
||||
constraint.setAuthenticate(true);
|
||||
|
||||
ConstraintMapping cm = new ConstraintMapping();
|
||||
cm.setConstraint(constraint);
|
||||
cm.setPathSpec("/*");
|
||||
|
||||
ConstraintSecurityHandler securityHandler = new ConstraintSecurityHandler();
|
||||
securityHandler.setLoginService(loginService);
|
||||
securityHandler.setConstraintMappings(Collections.singletonList(cm));
|
||||
securityHandler.setAuthenticator(new BasicAuthenticator());
|
||||
statsContext.setSecurityHandler(securityHandler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//ensure a DefaultHandler is present
|
||||
if (handlers.getChildHandlerByClass(DefaultHandler.class) == null)
|
||||
{
|
||||
handlers.addHandler(new DefaultHandler());
|
||||
}
|
||||
|
||||
//check a connector is configured to listen on
|
||||
Connector[] connectors = _server.getConnectors();
|
||||
if (connectors == null || connectors.length == 0)
|
||||
{
|
||||
ServerConnector connector = new ServerConnector(_server);
|
||||
connector.setPort(port);
|
||||
if (host != null)
|
||||
connector.setHost(host);
|
||||
_server.addConnector(connector);
|
||||
if (_enableStats)
|
||||
connector.addBean(new ConnectionStatistics());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_enableStats)
|
||||
{
|
||||
for (Connector connector : connectors)
|
||||
{
|
||||
((AbstractConnector)connector).addBean(new ConnectionStatistics());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
runnerServerInitialized = true;
|
||||
}
|
||||
|
||||
// Create a context
|
||||
try (Resource ctx = Resource.newResource(args[i]))
|
||||
{
|
||||
if (!ctx.exists())
|
||||
usage("Context '" + ctx + "' does not exist");
|
||||
|
||||
if (contextPathSet && !(contextPath.startsWith("/")))
|
||||
contextPath = "/" + contextPath;
|
||||
|
||||
// Configure the context
|
||||
if (!ctx.isDirectory() && ctx.toString().toLowerCase(Locale.ENGLISH).endsWith(".xml"))
|
||||
{
|
||||
// It is a context config file
|
||||
XmlConfiguration xmlConfiguration = new XmlConfiguration(ctx);
|
||||
xmlConfiguration.getIdMap().put("Server", _server);
|
||||
ContextHandler handler = (ContextHandler)xmlConfiguration.configure();
|
||||
if (contextPathSet)
|
||||
handler.setContextPath(contextPath);
|
||||
_contexts.addHandler(handler);
|
||||
String containerIncludeJarPattern = (String)handler.getAttribute(MetaInfConfiguration.CONTAINER_JAR_PATTERN);
|
||||
if (containerIncludeJarPattern == null)
|
||||
containerIncludeJarPattern = CONTAINER_INCLUDE_JAR_PATTERN;
|
||||
else
|
||||
{
|
||||
if (!containerIncludeJarPattern.contains(CONTAINER_INCLUDE_JAR_PATTERN))
|
||||
{
|
||||
containerIncludeJarPattern = containerIncludeJarPattern + (StringUtil.isBlank(containerIncludeJarPattern) ? "" : "|") + CONTAINER_INCLUDE_JAR_PATTERN;
|
||||
}
|
||||
}
|
||||
|
||||
handler.setAttribute(MetaInfConfiguration.CONTAINER_JAR_PATTERN, containerIncludeJarPattern);
|
||||
|
||||
//check the configurations, if not explicitly set up, then configure all of them
|
||||
if (handler instanceof WebAppContext)
|
||||
{
|
||||
WebAppContext wac = (WebAppContext)handler;
|
||||
if (wac.getConfigurationClasses() == null || wac.getConfigurationClasses().length == 0)
|
||||
wac.setConfigurationClasses(PLUS_CONFIGURATION_CLASSES);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// assume it is a WAR file
|
||||
WebAppContext webapp = new WebAppContext(_contexts, ctx.toString(), contextPath);
|
||||
webapp.setConfigurationClasses(PLUS_CONFIGURATION_CLASSES);
|
||||
webapp.setAttribute(MetaInfConfiguration.CONTAINER_JAR_PATTERN,
|
||||
CONTAINER_INCLUDE_JAR_PATTERN);
|
||||
}
|
||||
}
|
||||
//reset
|
||||
contextPathSet = false;
|
||||
contextPath = DEFAULT_CONTEXT_PATH;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (_server == null)
|
||||
usage("No Contexts defined");
|
||||
_server.setStopAtShutdown(true);
|
||||
|
||||
switch ((stopPort > 0 ? 1 : 0) + (stopKey != null ? 2 : 0))
|
||||
{
|
||||
case 1:
|
||||
usage("Must specify --stop-key when --stop-port is specified");
|
||||
break;
|
||||
|
||||
case 2:
|
||||
usage("Must specify --stop-port when --stop-key is specified");
|
||||
break;
|
||||
|
||||
case 3:
|
||||
ShutdownMonitor monitor = ShutdownMonitor.getInstance();
|
||||
monitor.setPort(stopPort);
|
||||
monitor.setKey(stopKey);
|
||||
monitor.setExitVm(true);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (_logFile != null)
|
||||
{
|
||||
CustomRequestLog requestLog = new CustomRequestLog(_logFile);
|
||||
_server.setRequestLog(requestLog);
|
||||
}
|
||||
}
|
||||
|
||||
protected void prependHandler(Handler handler, HandlerCollection handlers)
|
||||
{
|
||||
if (handler == null || handlers == null)
|
||||
return;
|
||||
|
||||
Handler[] existing = handlers.getChildHandlers();
|
||||
Handler[] children = new Handler[existing.length + 1];
|
||||
children[0] = handler;
|
||||
System.arraycopy(existing, 0, children, 1, existing.length);
|
||||
handlers.setHandlers(children);
|
||||
}
|
||||
|
||||
public void run() throws Exception
|
||||
{
|
||||
_server.start();
|
||||
_server.join();
|
||||
}
|
||||
|
||||
private URL toURL(URI uri)
|
||||
{
|
||||
try
|
||||
{
|
||||
return uri.toURL();
|
||||
}
|
||||
catch (MalformedURLException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Establish a classloader with custom paths (if any)
|
||||
*/
|
||||
protected void initClassLoader()
|
||||
{
|
||||
URL[] paths = Arrays.stream(_classpath.asArray()).map(this::toURL).toArray(URL[]::new);
|
||||
|
||||
if (_classLoader == null && paths.length > 0)
|
||||
{
|
||||
ClassLoader context = Thread.currentThread().getContextClassLoader();
|
||||
|
||||
if (context == null)
|
||||
{
|
||||
_classLoader = new URLClassLoader(paths);
|
||||
}
|
||||
else
|
||||
{
|
||||
_classLoader = new URLClassLoader(paths, context);
|
||||
}
|
||||
|
||||
Thread.currentThread().setContextClassLoader(_classLoader);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
System.err.println("WARNING: jetty-runner is deprecated.");
|
||||
System.err.println(" See Jetty Documentation for startup options");
|
||||
System.err.println(" https://www.eclipse.org/jetty/documentation/");
|
||||
|
||||
Runner runner = new Runner();
|
||||
|
||||
try
|
||||
{
|
||||
if (args.length > 0 && args[0].equalsIgnoreCase("--help"))
|
||||
{
|
||||
runner.usage(null);
|
||||
}
|
||||
else if (args.length > 0 && args[0].equalsIgnoreCase("--version"))
|
||||
{
|
||||
runner.version();
|
||||
}
|
||||
else
|
||||
{
|
||||
runner.configure(args);
|
||||
runner.run();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
runner.usage(null);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,9 +16,8 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.webapp;
|
||||
/**
|
||||
* Jetty Runner : Embedded Jetty Tool for running webapps directly
|
||||
*/
|
||||
package org.eclipse.jetty.runner;
|
||||
|
||||
public enum MetaDataComplete
|
||||
{
|
||||
NotSet, True, False
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
Comment: Jetty Runner
|
|
@ -60,6 +60,7 @@ public class AsyncContextEvent extends AsyncEvent implements Runnable
|
|||
baseRequest.setAttribute(AsyncContext.ASYNC_SERVLET_PATH, baseRequest.getAttribute(RequestDispatcher.FORWARD_SERVLET_PATH));
|
||||
baseRequest.setAttribute(AsyncContext.ASYNC_PATH_INFO, baseRequest.getAttribute(RequestDispatcher.FORWARD_PATH_INFO));
|
||||
baseRequest.setAttribute(AsyncContext.ASYNC_QUERY_STRING, baseRequest.getAttribute(RequestDispatcher.FORWARD_QUERY_STRING));
|
||||
baseRequest.setAttribute(AsyncContext.ASYNC_MAPPING, baseRequest.getAttribute(RequestDispatcher.FORWARD_MAPPING));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -68,6 +69,7 @@ public class AsyncContextEvent extends AsyncEvent implements Runnable
|
|||
baseRequest.setAttribute(AsyncContext.ASYNC_SERVLET_PATH, baseRequest.getServletPath());
|
||||
baseRequest.setAttribute(AsyncContext.ASYNC_PATH_INFO, baseRequest.getPathInfo());
|
||||
baseRequest.setAttribute(AsyncContext.ASYNC_QUERY_STRING, baseRequest.getQueryString());
|
||||
baseRequest.setAttribute(AsyncContext.ASYNC_MAPPING, baseRequest.getHttpServletMapping());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ import javax.servlet.RequestDispatcher;
|
|||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletMapping;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
|
@ -108,7 +109,7 @@ public class Dispatcher implements RequestDispatcher
|
|||
attr._servletPath = null; // set by ServletHandler
|
||||
attr._pathInfo = _pathInContext;
|
||||
attr._query = _uri.getQuery();
|
||||
|
||||
attr._mapping = null; //set by ServletHandler
|
||||
if (attr._query != null)
|
||||
baseRequest.mergeQueryParameters(baseRequest.getQueryString(), attr._query, false);
|
||||
baseRequest.setAttributes(attr);
|
||||
|
@ -147,6 +148,7 @@ public class Dispatcher implements RequestDispatcher
|
|||
final String old_context_path = baseRequest.getContextPath();
|
||||
final String old_servlet_path = baseRequest.getServletPath();
|
||||
final String old_path_info = baseRequest.getPathInfo();
|
||||
final HttpServletMapping old_mapping = baseRequest.getHttpServletMapping();
|
||||
|
||||
final MultiMap<String> old_query_params = baseRequest.getQueryParameters();
|
||||
final Attributes old_attr = baseRequest.getAttributes();
|
||||
|
@ -175,6 +177,7 @@ public class Dispatcher implements RequestDispatcher
|
|||
attr._requestURI = (String)old_attr.getAttribute(FORWARD_REQUEST_URI);
|
||||
attr._contextPath = (String)old_attr.getAttribute(FORWARD_CONTEXT_PATH);
|
||||
attr._servletPath = (String)old_attr.getAttribute(FORWARD_SERVLET_PATH);
|
||||
attr._mapping = (HttpServletMapping)old_attr.getAttribute(FORWARD_MAPPING);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -183,6 +186,7 @@ public class Dispatcher implements RequestDispatcher
|
|||
attr._requestURI = old_uri.getPath();
|
||||
attr._contextPath = old_context_path;
|
||||
attr._servletPath = old_servlet_path;
|
||||
attr._mapping = old_mapping;
|
||||
}
|
||||
|
||||
HttpURI uri = new HttpURI(old_uri.getScheme(), old_uri.getHost(), old_uri.getPort(),
|
||||
|
@ -261,6 +265,7 @@ public class Dispatcher implements RequestDispatcher
|
|||
String _servletPath;
|
||||
String _pathInfo;
|
||||
String _query;
|
||||
HttpServletMapping _mapping;
|
||||
|
||||
ForwardAttributes(Attributes attributes)
|
||||
{
|
||||
|
@ -282,6 +287,8 @@ public class Dispatcher implements RequestDispatcher
|
|||
return _contextPath;
|
||||
if (key.equals(FORWARD_QUERY_STRING))
|
||||
return _query;
|
||||
if (key.equals(FORWARD_MAPPING))
|
||||
return _mapping;
|
||||
}
|
||||
|
||||
if (key.startsWith(__INCLUDE_PREFIX))
|
||||
|
@ -312,6 +319,7 @@ public class Dispatcher implements RequestDispatcher
|
|||
set.add(FORWARD_REQUEST_URI);
|
||||
set.add(FORWARD_SERVLET_PATH);
|
||||
set.add(FORWARD_CONTEXT_PATH);
|
||||
set.add(FORWARD_MAPPING);
|
||||
if (_query != null)
|
||||
set.add(FORWARD_QUERY_STRING);
|
||||
else
|
||||
|
@ -336,7 +344,8 @@ public class Dispatcher implements RequestDispatcher
|
|||
_contextPath = (String)value;
|
||||
else if (key.equals(FORWARD_QUERY_STRING))
|
||||
_query = (String)value;
|
||||
|
||||
else if (key.equals(FORWARD_MAPPING))
|
||||
_mapping = (HttpServletMapping)value;
|
||||
else if (value == null)
|
||||
_attr.removeAttribute(key);
|
||||
else
|
||||
|
@ -376,6 +385,7 @@ public class Dispatcher implements RequestDispatcher
|
|||
String _servletPath;
|
||||
String _pathInfo;
|
||||
String _query;
|
||||
HttpServletMapping _mapping;
|
||||
|
||||
IncludeAttributes(Attributes attributes)
|
||||
{
|
||||
|
@ -397,6 +407,8 @@ public class Dispatcher implements RequestDispatcher
|
|||
return _query;
|
||||
if (key.equals(INCLUDE_REQUEST_URI))
|
||||
return _requestURI;
|
||||
if (key.equals(INCLUDE_MAPPING))
|
||||
return _mapping;
|
||||
}
|
||||
else if (key.startsWith(__INCLUDE_PREFIX))
|
||||
return null;
|
||||
|
@ -425,6 +437,7 @@ public class Dispatcher implements RequestDispatcher
|
|||
set.add(INCLUDE_REQUEST_URI);
|
||||
set.add(INCLUDE_SERVLET_PATH);
|
||||
set.add(INCLUDE_CONTEXT_PATH);
|
||||
set.add(INCLUDE_MAPPING);
|
||||
if (_query != null)
|
||||
set.add(INCLUDE_QUERY_STRING);
|
||||
else
|
||||
|
@ -449,6 +462,8 @@ public class Dispatcher implements RequestDispatcher
|
|||
_contextPath = (String)value;
|
||||
else if (key.equals(INCLUDE_QUERY_STRING))
|
||||
_query = (String)value;
|
||||
else if (key.equals(INCLUDE_MAPPING))
|
||||
_mapping = (HttpServletMapping)value;
|
||||
else if (value == null)
|
||||
_attr.removeAttribute(key);
|
||||
else
|
||||
|
|
|
@ -25,6 +25,7 @@ import java.util.concurrent.RejectedExecutionException;
|
|||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.LongAdder;
|
||||
|
||||
import org.eclipse.jetty.http.BadMessageException;
|
||||
import org.eclipse.jetty.http.HttpCompliance;
|
||||
import org.eclipse.jetty.http.HttpField;
|
||||
import org.eclipse.jetty.http.HttpGenerator;
|
||||
|
@ -33,6 +34,7 @@ import org.eclipse.jetty.http.HttpHeaderValue;
|
|||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.eclipse.jetty.http.HttpParser;
|
||||
import org.eclipse.jetty.http.HttpParser.RequestHandler;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.http.MetaData;
|
||||
import org.eclipse.jetty.http.PreEncodedHttpField;
|
||||
import org.eclipse.jetty.io.AbstractConnection;
|
||||
|
@ -781,6 +783,15 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
|
|||
|
||||
case NEED_HEADER:
|
||||
{
|
||||
_header = _bufferPool.acquire(Math.min(_config.getResponseHeaderSize(), _config.getOutputBufferSize()), useDirectByteBuffers);
|
||||
continue;
|
||||
}
|
||||
case HEADER_OVERFLOW:
|
||||
{
|
||||
int capacity = _header.capacity();
|
||||
_bufferPool.release(_header);
|
||||
if (capacity >= _config.getResponseHeaderSize())
|
||||
throw new BadMessageException(HttpStatus.INTERNAL_SERVER_ERROR_500, "Response header too large");
|
||||
_header = _bufferPool.acquire(_config.getResponseHeaderSize(), useDirectByteBuffers);
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -27,6 +27,8 @@ import java.nio.ByteBuffer;
|
|||
import java.nio.channels.ReadPendingException;
|
||||
import java.nio.channels.WritePendingException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jetty.io.AbstractConnection;
|
||||
import org.eclipse.jetty.io.Connection;
|
||||
|
@ -366,6 +368,7 @@ public class ProxyConnectionFactory extends DetectorConnectionFactory
|
|||
{
|
||||
0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A
|
||||
};
|
||||
|
||||
private final String _nextProtocol;
|
||||
private int _maxProxyHeader = 1024;
|
||||
|
||||
|
@ -545,7 +548,6 @@ public class ProxyConnectionFactory extends DetectorConnectionFactory
|
|||
|
||||
private void parseBodyAndUpgrade() throws IOException
|
||||
{
|
||||
// stop reading when bufferRemainingReserve bytes are remaining in the buffer
|
||||
int nonProxyRemaining = _buffer.remaining() - _length;
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Proxy v2 parsing body, length = {}, buffer = {}", _length, BufferUtil.toHexSummary(_buffer));
|
||||
|
@ -609,14 +611,14 @@ public class ProxyConnectionFactory extends DetectorConnectionFactory
|
|||
if (LOG.isDebugEnabled())
|
||||
LOG.debug(String.format("Proxy v2 T=%x L=%d V=%s for %s", type, length, TypeUtil.toHexString(value), this));
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case 0x20: // PP2_TYPE_SSL
|
||||
// PP2_TYPE_NOOP is only used for byte alignment, skip them.
|
||||
if (type != ProxyEndPoint.PP2_TYPE_NOOP)
|
||||
proxyEndPoint.putTLV(type, value);
|
||||
|
||||
if (type == ProxyEndPoint.PP2_TYPE_SSL)
|
||||
{
|
||||
int client = value[0] & 0xFF;
|
||||
switch (client)
|
||||
{
|
||||
case 0x01: // PP2_CLIENT_SSL
|
||||
if (client == ProxyEndPoint.PP2_TYPE_SSL_PP2_CLIENT_SSL)
|
||||
{
|
||||
int i = 5; // Index of the first sub_tlv, after verify.
|
||||
while (i < length)
|
||||
|
@ -626,36 +628,13 @@ public class ProxyConnectionFactory extends DetectorConnectionFactory
|
|||
byte[] subValue = new byte[subLength];
|
||||
System.arraycopy(value, i, subValue, 0, subLength);
|
||||
i += subLength;
|
||||
switch (subType)
|
||||
if (subType == ProxyEndPoint.PP2_SUBTYPE_SSL_VERSION)
|
||||
{
|
||||
case 0x21: // PP2_SUBTYPE_SSL_VERSION
|
||||
String tlsVersion = new String(subValue, StandardCharsets.US_ASCII);
|
||||
proxyEndPoint.setAttribute(TLS_VERSION, tlsVersion);
|
||||
break;
|
||||
case 0x22: // PP2_SUBTYPE_SSL_CN
|
||||
case 0x23: // PP2_SUBTYPE_SSL_CIPHER
|
||||
case 0x24: // PP2_SUBTYPE_SSL_SIG_ALG
|
||||
case 0x25: // PP2_SUBTYPE_SSL_KEY_ALG
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x02: // PP2_CLIENT_CERT_CONN
|
||||
case 0x04: // PP2_CLIENT_CERT_SESS
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x01: // PP2_TYPE_ALPN
|
||||
case 0x02: // PP2_TYPE_AUTHORITY
|
||||
case 0x03: // PP2_TYPE_CRC32C
|
||||
case 0x04: // PP2_TYPE_NOOP
|
||||
case 0x30: // PP2_TYPE_NETNS
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -751,71 +730,106 @@ public class ProxyConnectionFactory extends DetectorConnectionFactory
|
|||
}
|
||||
}
|
||||
|
||||
public static class ProxyEndPoint extends AttributesMap implements EndPoint
|
||||
public static class ProxyEndPoint extends AttributesMap implements EndPoint, EndPoint.Wrapper
|
||||
{
|
||||
private final EndPoint _endp;
|
||||
private static final int PP2_TYPE_NOOP = 0x04;
|
||||
private static final int PP2_TYPE_SSL = 0x20;
|
||||
private static final int PP2_TYPE_SSL_PP2_CLIENT_SSL = 0x01;
|
||||
private static final int PP2_SUBTYPE_SSL_VERSION = 0x21;
|
||||
|
||||
private final EndPoint _endPoint;
|
||||
private final InetSocketAddress _remote;
|
||||
private final InetSocketAddress _local;
|
||||
private Map<Integer, byte[]> _tlvs;
|
||||
|
||||
public ProxyEndPoint(EndPoint endp, InetSocketAddress remote, InetSocketAddress local)
|
||||
public ProxyEndPoint(EndPoint endPoint, InetSocketAddress remote, InetSocketAddress local)
|
||||
{
|
||||
_endp = endp;
|
||||
_endPoint = endPoint;
|
||||
_remote = remote;
|
||||
_local = local;
|
||||
}
|
||||
|
||||
public EndPoint unwrap()
|
||||
{
|
||||
return _endPoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets a TLV vector, see section 2.2.7 of the PROXY protocol specification.</p>
|
||||
*
|
||||
* @param type the TLV type
|
||||
* @param value the TLV value
|
||||
*/
|
||||
private void putTLV(int type, byte[] value)
|
||||
{
|
||||
if (_tlvs == null)
|
||||
_tlvs = new HashMap<>();
|
||||
_tlvs.put(type, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Gets a TLV vector, see section 2.2.7 of the PROXY protocol specification.</p>
|
||||
*
|
||||
* @param type the TLV type
|
||||
* @return the TLV value or null if not present.
|
||||
*/
|
||||
public byte[] getTLV(int type)
|
||||
{
|
||||
return _tlvs != null ? _tlvs.get(type) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close(Throwable cause)
|
||||
{
|
||||
_endp.close(cause);
|
||||
_endPoint.close(cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int fill(ByteBuffer buffer) throws IOException
|
||||
{
|
||||
return _endp.fill(buffer);
|
||||
return _endPoint.fill(buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillInterested(Callback callback) throws ReadPendingException
|
||||
{
|
||||
_endp.fillInterested(callback);
|
||||
_endPoint.fillInterested(callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean flush(ByteBuffer... buffer) throws IOException
|
||||
{
|
||||
return _endp.flush(buffer);
|
||||
return _endPoint.flush(buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connection getConnection()
|
||||
{
|
||||
return _endp.getConnection();
|
||||
return _endPoint.getConnection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setConnection(Connection connection)
|
||||
{
|
||||
_endp.setConnection(connection);
|
||||
_endPoint.setConnection(connection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getCreatedTimeStamp()
|
||||
{
|
||||
return _endp.getCreatedTimeStamp();
|
||||
return _endPoint.getCreatedTimeStamp();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getIdleTimeout()
|
||||
{
|
||||
return _endp.getIdleTimeout();
|
||||
return _endPoint.getIdleTimeout();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIdleTimeout(long idleTimeout)
|
||||
{
|
||||
_endp.setIdleTimeout(idleTimeout);
|
||||
_endPoint.setIdleTimeout(idleTimeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -833,49 +847,49 @@ public class ProxyConnectionFactory extends DetectorConnectionFactory
|
|||
@Override
|
||||
public Object getTransport()
|
||||
{
|
||||
return _endp.getTransport();
|
||||
return _endPoint.getTransport();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFillInterested()
|
||||
{
|
||||
return _endp.isFillInterested();
|
||||
return _endPoint.isFillInterested();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInputShutdown()
|
||||
{
|
||||
return _endp.isInputShutdown();
|
||||
return _endPoint.isInputShutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen()
|
||||
{
|
||||
return _endp.isOpen();
|
||||
return _endPoint.isOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOutputShutdown()
|
||||
{
|
||||
return _endp.isOutputShutdown();
|
||||
return _endPoint.isOutputShutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(Throwable cause)
|
||||
{
|
||||
_endp.onClose(cause);
|
||||
_endPoint.onClose(cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen()
|
||||
{
|
||||
_endp.onOpen();
|
||||
_endPoint.onOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdownOutput()
|
||||
{
|
||||
_endp.shutdownOutput();
|
||||
_endPoint.shutdownOutput();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -886,25 +900,25 @@ public class ProxyConnectionFactory extends DetectorConnectionFactory
|
|||
hashCode(),
|
||||
_remote,
|
||||
_local,
|
||||
_endp);
|
||||
_endPoint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tryFillInterested(Callback callback)
|
||||
{
|
||||
return _endp.tryFillInterested(callback);
|
||||
return _endPoint.tryFillInterested(callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void upgrade(Connection newConnection)
|
||||
{
|
||||
_endp.upgrade(newConnection);
|
||||
_endPoint.upgrade(newConnection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(Callback callback, ByteBuffer... buffers) throws WritePendingException
|
||||
{
|
||||
_endp.write(callback, buffers);
|
||||
_endPoint.write(callback, buffers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -190,6 +190,85 @@ public class Request implements HttpServletRequest
|
|||
return null;
|
||||
}
|
||||
|
||||
public static HttpServletMapping getServletMapping(PathSpec pathSpec, String servletPath, String servletName)
|
||||
{
|
||||
final MappingMatch match;
|
||||
final String mapping;
|
||||
if (pathSpec instanceof ServletPathSpec)
|
||||
{
|
||||
switch (((ServletPathSpec)pathSpec).getGroup())
|
||||
{
|
||||
case ROOT:
|
||||
match = MappingMatch.CONTEXT_ROOT;
|
||||
mapping = "";
|
||||
break;
|
||||
case DEFAULT:
|
||||
match = MappingMatch.DEFAULT;
|
||||
mapping = "/";
|
||||
break;
|
||||
case EXACT:
|
||||
match = MappingMatch.EXACT;
|
||||
mapping = servletPath.startsWith("/") ? servletPath.substring(1) : servletPath;
|
||||
break;
|
||||
case SUFFIX_GLOB:
|
||||
match = MappingMatch.EXTENSION;
|
||||
int dot = servletPath.lastIndexOf('.');
|
||||
mapping = servletPath.substring(0, dot);
|
||||
break;
|
||||
case PREFIX_GLOB:
|
||||
match = MappingMatch.PATH;
|
||||
mapping = servletPath;
|
||||
break;
|
||||
default:
|
||||
match = null;
|
||||
mapping = servletPath;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
match = null;
|
||||
mapping = servletPath;
|
||||
}
|
||||
|
||||
return new HttpServletMapping()
|
||||
{
|
||||
@Override
|
||||
public String getMatchValue()
|
||||
{
|
||||
return (mapping == null ? "" : mapping);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPattern()
|
||||
{
|
||||
if (pathSpec != null)
|
||||
return pathSpec.getDeclaration();
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServletName()
|
||||
{
|
||||
return (servletName == null ? "" : servletName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MappingMatch getMappingMatch()
|
||||
{
|
||||
return match;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "HttpServletMapping{matchValue=" + getMatchValue() +
|
||||
", pattern=" + getPattern() + ", servletName=" + getServletName() +
|
||||
", mappingMatch=" + getMappingMatch() + "}";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private final HttpChannel _channel;
|
||||
private final List<ServletRequestAttributeListener> _requestAttributeListeners = new ArrayList<>();
|
||||
private final HttpInput _input;
|
||||
|
@ -2354,73 +2433,6 @@ public class Request implements HttpServletRequest
|
|||
@Override
|
||||
public HttpServletMapping getHttpServletMapping()
|
||||
{
|
||||
final PathSpec pathSpec = _pathSpec;
|
||||
final MappingMatch match;
|
||||
final String mapping;
|
||||
if (pathSpec instanceof ServletPathSpec)
|
||||
{
|
||||
switch (((ServletPathSpec)pathSpec).getGroup())
|
||||
{
|
||||
case ROOT:
|
||||
match = MappingMatch.CONTEXT_ROOT;
|
||||
mapping = "";
|
||||
break;
|
||||
case DEFAULT:
|
||||
match = MappingMatch.DEFAULT;
|
||||
mapping = "/";
|
||||
break;
|
||||
case EXACT:
|
||||
match = MappingMatch.EXACT;
|
||||
mapping = _servletPath.startsWith("/") ? _servletPath.substring(1) : _servletPath;
|
||||
break;
|
||||
case SUFFIX_GLOB:
|
||||
match = MappingMatch.EXTENSION;
|
||||
int dot = _servletPath.lastIndexOf('.');
|
||||
mapping = _servletPath.substring(0, dot);
|
||||
break;
|
||||
case PREFIX_GLOB:
|
||||
match = MappingMatch.PATH;
|
||||
mapping = _servletPath;
|
||||
break;
|
||||
default:
|
||||
match = null;
|
||||
mapping = _servletPath;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
match = null;
|
||||
mapping = _servletPath;
|
||||
}
|
||||
|
||||
return new HttpServletMapping()
|
||||
{
|
||||
@Override
|
||||
public String getMatchValue()
|
||||
{
|
||||
return mapping;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPattern()
|
||||
{
|
||||
if (pathSpec != null)
|
||||
pathSpec.toString();
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServletName()
|
||||
{
|
||||
return Request.this.getServletName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MappingMatch getMappingMatch()
|
||||
{
|
||||
return match;
|
||||
}
|
||||
};
|
||||
return Request.getServletMapping(_pathSpec, _servletPath, getServletName());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,6 +52,7 @@ public class AsyncDelayHandler extends HandlerWrapper
|
|||
Object asyncQueryString = null;
|
||||
Object asyncRequestUri = null;
|
||||
Object asyncServletPath = null;
|
||||
Object asyncHttpServletMapping = null;
|
||||
|
||||
// Is this request a restarted one?
|
||||
boolean restart = false;
|
||||
|
@ -72,6 +73,8 @@ public class AsyncDelayHandler extends HandlerWrapper
|
|||
baseRequest.setAttribute(AsyncContext.ASYNC_REQUEST_URI, null);
|
||||
asyncServletPath = baseRequest.getAttribute(AsyncContext.ASYNC_SERVLET_PATH);
|
||||
baseRequest.setAttribute(AsyncContext.ASYNC_SERVLET_PATH, null);
|
||||
asyncHttpServletMapping = baseRequest.getAttribute(AsyncContext.ASYNC_MAPPING);
|
||||
baseRequest.setAttribute(AsyncContext.ASYNC_MAPPING, null);
|
||||
}
|
||||
|
||||
// Should we handle this request now?
|
||||
|
@ -101,6 +104,7 @@ public class AsyncDelayHandler extends HandlerWrapper
|
|||
baseRequest.setAttribute(AsyncContext.ASYNC_QUERY_STRING, asyncQueryString);
|
||||
baseRequest.setAttribute(AsyncContext.ASYNC_REQUEST_URI, asyncRequestUri);
|
||||
baseRequest.setAttribute(AsyncContext.ASYNC_SERVLET_PATH, asyncServletPath);
|
||||
baseRequest.setAttribute(AsyncContext.ASYNC_MAPPING, asyncHttpServletMapping);
|
||||
}
|
||||
|
||||
// signal the request is leaving the handler
|
||||
|
|
|
@ -199,7 +199,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
|
||||
private final List<EventListener> _programmaticListeners = new CopyOnWriteArrayList<>();
|
||||
private final List<ServletContextListener> _servletContextListeners = new CopyOnWriteArrayList<>();
|
||||
private final List<ServletContextListener> _destroySerletContextListeners = new ArrayList<>();
|
||||
private final List<ServletContextListener> _destroyServletContextListeners = new ArrayList<>();
|
||||
private final List<ServletContextAttributeListener> _servletContextAttributeListeners = new CopyOnWriteArrayList<>();
|
||||
private final List<ServletRequestListener> _servletRequestListeners = new CopyOnWriteArrayList<>();
|
||||
private final List<ServletRequestAttributeListener> _servletRequestAttributeListeners = new CopyOnWriteArrayList<>();
|
||||
|
@ -646,7 +646,10 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
_contextListeners.remove(listener);
|
||||
|
||||
if (listener instanceof ServletContextListener)
|
||||
{
|
||||
_servletContextListeners.remove(listener);
|
||||
_destroyServletContextListeners.remove(listener);
|
||||
}
|
||||
|
||||
if (listener instanceof ServletContextAttributeListener)
|
||||
_servletContextAttributeListeners.remove(listener);
|
||||
|
@ -838,14 +841,14 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
super.doStart();
|
||||
|
||||
// Call context listeners
|
||||
_destroySerletContextListeners.clear();
|
||||
_destroyServletContextListeners.clear();
|
||||
if (!_servletContextListeners.isEmpty())
|
||||
{
|
||||
ServletContextEvent event = new ServletContextEvent(_scontext);
|
||||
for (ServletContextListener listener : _servletContextListeners)
|
||||
{
|
||||
callContextInitialized(listener, event);
|
||||
_destroySerletContextListeners.add(listener);
|
||||
_destroyServletContextListeners.add(listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -854,9 +857,9 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
{
|
||||
// Call the context listeners
|
||||
ServletContextEvent event = new ServletContextEvent(_scontext);
|
||||
Collections.reverse(_destroySerletContextListeners);
|
||||
Collections.reverse(_destroyServletContextListeners);
|
||||
MultiException ex = new MultiException();
|
||||
for (ServletContextListener listener : _destroySerletContextListeners)
|
||||
for (ServletContextListener listener : _destroyServletContextListeners)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
|
|
@ -26,16 +26,20 @@ import javax.servlet.http.HttpServletRequest;
|
|||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.http.pathmap.PathSpec;
|
||||
import org.eclipse.jetty.io.EndPoint;
|
||||
import org.eclipse.jetty.server.HttpChannel;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.util.IncludeExclude;
|
||||
import org.eclipse.jetty.util.IncludeExcludeSet;
|
||||
import org.eclipse.jetty.util.InetAddressPattern;
|
||||
import org.eclipse.jetty.util.InetAddressSet;
|
||||
import org.eclipse.jetty.util.component.DumpableCollection;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
import static org.eclipse.jetty.server.handler.InetAccessSet.AccessTuple;
|
||||
import static org.eclipse.jetty.server.handler.InetAccessSet.PatternTuple;
|
||||
|
||||
/**
|
||||
* InetAddress Access Handler
|
||||
* <p>
|
||||
|
@ -43,18 +47,13 @@ import org.eclipse.jetty.util.log.Logger;
|
|||
* provided by and {@link IncludeExcludeSet} over a {@link InetAddressSet}. This
|
||||
* handler uses the real internet address of the connection, not one reported in
|
||||
* the forwarded for headers, as this cannot be as easily forged.
|
||||
* <p>
|
||||
* Additionally, there may be times when you want to only apply this handler to
|
||||
* a subset of your connectors. In this situation you can use
|
||||
* <b>connectorNames</b> to specify the connector names that you want this IP
|
||||
* access filter to apply to.
|
||||
* </p>
|
||||
*/
|
||||
public class InetAccessHandler extends HandlerWrapper
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(InetAccessHandler.class);
|
||||
|
||||
private final IncludeExcludeSet<String, InetAddress> _addrs = new IncludeExcludeSet<>(InetAddressSet.class);
|
||||
private final IncludeExclude<String> _names = new IncludeExclude<>();
|
||||
private final IncludeExcludeSet<PatternTuple, AccessTuple> _set = new IncludeExcludeSet<>(InetAccessSet.class);
|
||||
|
||||
/**
|
||||
* Clears all the includes, excludes, included connector names and excluded
|
||||
|
@ -62,92 +61,150 @@ public class InetAccessHandler extends HandlerWrapper
|
|||
*/
|
||||
public void clear()
|
||||
{
|
||||
_addrs.clear();
|
||||
_names.clear();
|
||||
_set.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Includes an InetAddress pattern
|
||||
* Includes an InetAccess pattern with an optional connector name, address and URI mapping.
|
||||
*
|
||||
* @param pattern InetAddress pattern to include
|
||||
* <p>The connector name is separated from the InetAddress pattern with an '@' character,
|
||||
* and the InetAddress pattern is separated from the URI pattern using the "|" (pipe)
|
||||
* character. URI patterns follow the servlet specification for simple * prefix and
|
||||
* suffix wild cards (e.g. /, /foo, /foo/bar, /foo/bar/*, *.baz).</p>
|
||||
*
|
||||
* <br>Examples:
|
||||
* <ul>
|
||||
* <li>"connector1@127.0.0.1|/foo"</li>
|
||||
* <li>"127.0.0.1|/foo"</li>
|
||||
* <li>"connector1@127.0.0.1"</li>
|
||||
* <li>"127.0.0.1"</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param pattern InetAccess pattern to include
|
||||
* @see InetAddressSet
|
||||
*/
|
||||
public void include(String pattern)
|
||||
{
|
||||
_addrs.include(pattern);
|
||||
_set.include(PatternTuple.from(pattern));
|
||||
}
|
||||
|
||||
/**
|
||||
* Includes InetAddress patterns
|
||||
* Includes InetAccess patterns
|
||||
*
|
||||
* @param patterns InetAddress patterns to include
|
||||
* @see InetAddressSet
|
||||
*/
|
||||
public void include(String... patterns)
|
||||
{
|
||||
_addrs.include(patterns);
|
||||
for (String pattern : patterns)
|
||||
include(pattern);
|
||||
}
|
||||
|
||||
/**
|
||||
* Excludes an InetAddress pattern
|
||||
* Includes an InetAccess entry.
|
||||
* @param connectorName optional name of a connector to include.
|
||||
* @param addressPattern optional InetAddress pattern to include.
|
||||
* @param pathSpec optional pathSpec to include.
|
||||
*/
|
||||
public void include(String connectorName, String addressPattern, PathSpec pathSpec)
|
||||
{
|
||||
_set.include(new PatternTuple(connectorName, InetAddressPattern.from(addressPattern), pathSpec));
|
||||
}
|
||||
|
||||
/**
|
||||
* Excludes an InetAccess entry pattern with an optional connector name, address and URI mapping.
|
||||
*
|
||||
* <p>The connector name is separated from the InetAddress pattern with an '@' character,
|
||||
* and the InetAddress pattern is separated from the URI pattern using the "|" (pipe)
|
||||
* character. URI patterns follow the servlet specification for simple * prefix and
|
||||
* suffix wild cards (e.g. /, /foo, /foo/bar, /foo/bar/*, *.baz).</p>
|
||||
*
|
||||
* <br>Examples:
|
||||
* <ul>
|
||||
* <li>"connector1@127.0.0.1|/foo"</li>
|
||||
* <li>"127.0.0.1|/foo"</li>
|
||||
* <li>"connector1@127.0.0.1"</li>
|
||||
* <li>"127.0.0.1"</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param pattern InetAddress pattern to exclude
|
||||
* @see InetAddressSet
|
||||
*/
|
||||
public void exclude(String pattern)
|
||||
{
|
||||
_addrs.exclude(pattern);
|
||||
_set.exclude(PatternTuple.from(pattern));
|
||||
}
|
||||
|
||||
/**
|
||||
* Excludes InetAddress patterns
|
||||
* Excludes InetAccess patterns
|
||||
*
|
||||
* @param patterns InetAddress patterns to exclude
|
||||
* @see InetAddressSet
|
||||
*/
|
||||
public void exclude(String... patterns)
|
||||
{
|
||||
_addrs.exclude(patterns);
|
||||
for (String pattern : patterns)
|
||||
exclude(pattern);
|
||||
}
|
||||
|
||||
/**
|
||||
* Excludes an InetAccess entry.
|
||||
* @param connectorName optional name of a connector to exclude.
|
||||
* @param addressPattern optional InetAddress pattern to exclude.
|
||||
* @param pathSpec optional pathSpec to exclude.
|
||||
*/
|
||||
public void exclude(String connectorName, String addressPattern, PathSpec pathSpec)
|
||||
{
|
||||
_set.exclude(new PatternTuple(connectorName, InetAddressPattern.from(addressPattern), pathSpec));
|
||||
}
|
||||
|
||||
/**
|
||||
* Includes a connector name.
|
||||
*
|
||||
* @param name Connector name to include in this handler.
|
||||
* @deprecated use {@link InetAccessHandler#include(String)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public void includeConnector(String name)
|
||||
{
|
||||
_names.include(name);
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Excludes a connector name.
|
||||
*
|
||||
* @param name Connector name to exclude in this handler.
|
||||
* @deprecated use {@link InetAccessHandler#include(String)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public void excludeConnector(String name)
|
||||
{
|
||||
_names.exclude(name);
|
||||
_set.exclude(new PatternTuple(name, null, null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Includes connector names.
|
||||
*
|
||||
* @param names Connector names to include in this handler.
|
||||
* @deprecated use {@link InetAccessHandler#include(String)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public void includeConnectors(String... names)
|
||||
{
|
||||
_names.include(names);
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Excludes connector names.
|
||||
*
|
||||
* @param names Connector names to exclude in this handler.
|
||||
* @deprecated use {@link InetAccessHandler#include(String)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public void excludeConnectors(String... names)
|
||||
{
|
||||
_names.exclude(names);
|
||||
for (String name : names)
|
||||
excludeConnector(name);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -187,26 +244,16 @@ public class InetAccessHandler extends HandlerWrapper
|
|||
*/
|
||||
protected boolean isAllowed(InetAddress addr, Request baseRequest, HttpServletRequest request)
|
||||
{
|
||||
String name = baseRequest.getHttpChannel().getConnector().getName();
|
||||
boolean filterAppliesToConnector = _names.test(name);
|
||||
boolean allowedByAddr = _addrs.test(addr);
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
LOG.debug("name = {}/{} addr={}/{} appliesToConnector={} allowedByAddr={}",
|
||||
name, _names, addr, _addrs, filterAppliesToConnector, allowedByAddr);
|
||||
}
|
||||
if (!filterAppliesToConnector)
|
||||
return true;
|
||||
return allowedByAddr;
|
||||
String connectorName = baseRequest.getHttpChannel().getConnector().getName();
|
||||
String path = baseRequest.getMetaData().getURI().getDecodedPath();
|
||||
return _set.test(new AccessTuple(connectorName, addr, path));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(Appendable out, String indent) throws IOException
|
||||
{
|
||||
dumpObjects(out, indent,
|
||||
new DumpableCollection("included", _addrs.getIncluded()),
|
||||
new DumpableCollection("excluded", _addrs.getExcluded()),
|
||||
new DumpableCollection("includedConnector", _names.getIncluded()),
|
||||
new DumpableCollection("excludedConnector", _names.getExcluded()));
|
||||
new DumpableCollection("included", _set.getIncluded()),
|
||||
new DumpableCollection("excluded", _set.getExcluded()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,158 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under
|
||||
// the terms of the Eclipse Public License 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// This Source Code may also be made available under the following
|
||||
// Secondary Licenses when the conditions for such availability set
|
||||
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||
// the Apache License v2.0 which is available at
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.server.handler;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.util.AbstractSet;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.eclipse.jetty.http.pathmap.PathSpec;
|
||||
import org.eclipse.jetty.http.pathmap.ServletPathSpec;
|
||||
import org.eclipse.jetty.util.InetAddressPattern;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
|
||||
public class InetAccessSet extends AbstractSet<InetAccessSet.PatternTuple> implements Set<InetAccessSet.PatternTuple>, Predicate<InetAccessSet.AccessTuple>
|
||||
{
|
||||
private ArrayList<PatternTuple> tuples = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public boolean add(PatternTuple storageTuple)
|
||||
{
|
||||
return tuples.add(storageTuple);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object o)
|
||||
{
|
||||
return tuples.remove(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<PatternTuple> iterator()
|
||||
{
|
||||
return tuples.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size()
|
||||
{
|
||||
return tuples.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(AccessTuple entry)
|
||||
{
|
||||
if (entry == null)
|
||||
return false;
|
||||
|
||||
for (PatternTuple tuple : tuples)
|
||||
{
|
||||
if (tuple.test(entry))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static class PatternTuple implements Predicate<AccessTuple>
|
||||
{
|
||||
private final String connector;
|
||||
private final InetAddressPattern address;
|
||||
private final PathSpec pathSpec;
|
||||
|
||||
public static PatternTuple from(String pattern)
|
||||
{
|
||||
|
||||
String path = null;
|
||||
int pathIndex = pattern.indexOf('|');
|
||||
if (pathIndex >= 0)
|
||||
path = pattern.substring(pathIndex + 1);
|
||||
|
||||
String connector = null;
|
||||
int connectorIndex = pattern.indexOf('@');
|
||||
if (connectorIndex >= 0)
|
||||
connector = pattern.substring(0, connectorIndex);
|
||||
|
||||
String addr = null;
|
||||
int addrStart = (connectorIndex < 0) ? 0 : connectorIndex + 1;
|
||||
int addrEnd = (pathIndex < 0) ? pattern.length() : pathIndex;
|
||||
if (addrStart != addrEnd)
|
||||
addr = pattern.substring(addrStart, addrEnd);
|
||||
|
||||
return new PatternTuple(connector, InetAddressPattern.from(addr),
|
||||
StringUtil.isEmpty(path) ? null : new ServletPathSpec(path));
|
||||
}
|
||||
|
||||
public PatternTuple(String connector, InetAddressPattern address, PathSpec pathSpec)
|
||||
{
|
||||
this.connector = connector;
|
||||
this.address = address;
|
||||
this.pathSpec = pathSpec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(AccessTuple entry)
|
||||
{
|
||||
// Match for connector.
|
||||
if ((connector != null) && !connector.equals(entry.getConnector()))
|
||||
return false;
|
||||
|
||||
// If we have a path we must must be at this path to match for an address.
|
||||
if ((pathSpec != null) && !pathSpec.matches(entry.getPath()))
|
||||
return false;
|
||||
|
||||
// Match for InetAddress.
|
||||
if ((address != null) && !address.test(entry.getAddress()))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static class AccessTuple
|
||||
{
|
||||
private final String connector;
|
||||
private final InetAddress address;
|
||||
private final String path;
|
||||
|
||||
public AccessTuple(String connector, InetAddress address, String path)
|
||||
{
|
||||
this.connector = connector;
|
||||
this.address = address;
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public String getConnector()
|
||||
{
|
||||
return connector;
|
||||
}
|
||||
|
||||
public InetAddress getAddress()
|
||||
{
|
||||
return address;
|
||||
}
|
||||
|
||||
public String getPath()
|
||||
{
|
||||
return path;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -155,8 +155,9 @@ public class HouseKeeper extends AbstractLifeCycle
|
|||
LOG.info("{} Stopped scavenging", _sessionIdManager.getWorkerName());
|
||||
}
|
||||
_task = null;
|
||||
if (_ownScheduler)
|
||||
if (_ownScheduler && _scheduler != null)
|
||||
{
|
||||
_ownScheduler = false;
|
||||
_scheduler.stop();
|
||||
_scheduler = null;
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ import java.io.IOException;
|
|||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
@ -1266,6 +1267,48 @@ public class HttpConnectionTest
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAllowedLargeResponse() throws Exception
|
||||
{
|
||||
connector.getBean(HttpConnectionFactory.class).getHttpConfiguration().setResponseHeaderSize(16 * 1024);
|
||||
connector.getBean(HttpConnectionFactory.class).getHttpConfiguration().setOutputBufferSize(8 * 1024);
|
||||
|
||||
byte[] bytes = new byte[12 * 1024];
|
||||
Arrays.fill(bytes, (byte)'X');
|
||||
final String longstr = "thisisastringthatshouldreachover12kbytes-" + new String(bytes, StandardCharsets.ISO_8859_1) + "_Z_";
|
||||
final CountDownLatch checkError = new CountDownLatch(1);
|
||||
server.stop();
|
||||
server.setHandler(new AbstractHandler()
|
||||
{
|
||||
@SuppressWarnings("unused")
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
response.setHeader(HttpHeader.CONTENT_TYPE.toString(), MimeTypes.Type.TEXT_HTML.toString());
|
||||
response.setHeader("LongStr", longstr);
|
||||
PrintWriter writer = response.getWriter();
|
||||
writer.write("<html><h1>FOO</h1></html>");
|
||||
writer.flush();
|
||||
if (writer.checkError())
|
||||
checkError.countDown();
|
||||
response.flushBuffer();
|
||||
}
|
||||
});
|
||||
server.start();
|
||||
|
||||
String response = null;
|
||||
response = connector.getResponse("GET / HTTP/1.1\r\n" +
|
||||
"Host: localhost\r\n" +
|
||||
"\r\n"
|
||||
);
|
||||
|
||||
checkContains(response, 0, "HTTP/1.1 200");
|
||||
checkContains(response, 0, "LongStr: thisisastringthatshouldreachover12kbytes");
|
||||
checkContains(response, 0, "XXX_Z_");
|
||||
assertThat(checkError.getCount(), is(1L));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsterisk() throws Exception
|
||||
{
|
||||
|
|
|
@ -25,10 +25,13 @@ import java.io.InputStreamReader;
|
|||
import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.io.EndPoint;
|
||||
import org.eclipse.jetty.server.ProxyConnectionFactory.ProxyEndPoint;
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.eclipse.jetty.util.TypeUtil;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
|
@ -118,15 +121,29 @@ public class ProxyProtocolTest
|
|||
{
|
||||
final String remoteAddr = "192.168.0.1";
|
||||
final int remotePort = 12345;
|
||||
final byte[] customE0 = new byte[] {1, 2};
|
||||
final byte[] customE1 = new byte[] {-1, -1, -1};
|
||||
|
||||
start(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
if (remoteAddr.equals(request.getRemoteAddr()) &&
|
||||
if (validateEndPoint(baseRequest) &&
|
||||
remoteAddr.equals(request.getRemoteAddr()) &&
|
||||
remotePort == request.getRemotePort())
|
||||
baseRequest.setHandled(true);
|
||||
}
|
||||
|
||||
private boolean validateEndPoint(Request request)
|
||||
{
|
||||
HttpConnection con = (HttpConnection)request.getAttribute(HttpConnection.class.getName());
|
||||
EndPoint endPoint = con.getEndPoint();
|
||||
ProxyEndPoint proxyEndPoint = (ProxyEndPoint)endPoint;
|
||||
return Arrays.equals(customE0, proxyEndPoint.getTLV(0xE0)) &&
|
||||
Arrays.equals(customE1, proxyEndPoint.getTLV(0xE1)) &&
|
||||
proxyEndPoint.getTLV(0xE2) == null;
|
||||
}
|
||||
});
|
||||
|
||||
try (Socket socket = new Socket("localhost", connector.getLocalPort()))
|
||||
|
@ -141,8 +158,8 @@ public class ProxyProtocolTest
|
|||
// 0x1 : AF_INET 0x1 : STREAM. Address length is 2*4 + 2*2 = 12 bytes.
|
||||
"11" +
|
||||
|
||||
// length of remaining header (4+4+2+2+6+3 = 21)
|
||||
"0015" +
|
||||
// length of remaining header (4+4+2+2+3+6+5+6 = 32)
|
||||
"0020" +
|
||||
|
||||
// uint32_t src_addr; uint32_t dst_addr; uint16_t src_port; uint16_t dst_port;
|
||||
"C0A80001" +
|
||||
|
@ -154,7 +171,13 @@ public class ProxyProtocolTest
|
|||
"040000" +
|
||||
|
||||
// NOOP value ABCDEF
|
||||
"040003ABCDEF";
|
||||
"040003ABCDEF" +
|
||||
|
||||
// Custom 0xEO {0x01,0x02}
|
||||
"E000020102" +
|
||||
|
||||
// Custom 0xE1 {0xFF,0xFF,0xFF}
|
||||
"E10003FFFFFF";
|
||||
|
||||
String request1 =
|
||||
"GET /1 HTTP/1.1\r\n" +
|
||||
|
@ -193,4 +216,5 @@ public class ProxyProtocolTest
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ import java.util.Arrays;
|
|||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
@ -42,6 +43,7 @@ import javax.servlet.MultipartConfigElement;
|
|||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletInputStream;
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletMapping;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.Part;
|
||||
|
@ -49,6 +51,7 @@ import javax.servlet.http.Part;
|
|||
import org.eclipse.jetty.http.BadMessageException;
|
||||
import org.eclipse.jetty.http.HttpCompliance;
|
||||
import org.eclipse.jetty.http.MimeTypes;
|
||||
import org.eclipse.jetty.http.pathmap.ServletPathSpec;
|
||||
import org.eclipse.jetty.http.tools.HttpTester;
|
||||
import org.eclipse.jetty.server.LocalConnector.LocalEndPoint;
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
|
@ -1410,6 +1413,69 @@ public class RequestTest
|
|||
assertThat(response, containsString("Hello World"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHttpServletMapping() throws Exception
|
||||
{
|
||||
String request = "GET / HTTP/1.1\n" +
|
||||
"Host: whatever\n" +
|
||||
"Connection: close\n" +
|
||||
"\n";
|
||||
|
||||
_server.stop();
|
||||
PathMappingHandler handler = new PathMappingHandler(null, null, null);
|
||||
_server.setHandler(handler);
|
||||
_server.start();
|
||||
String response = _connector.getResponse(request);
|
||||
assertTrue(response.startsWith("HTTP/1.1 200 OK"));
|
||||
assertThat("Response body content", response, containsString("HttpServletMapping{matchValue=, pattern=, servletName=, mappingMatch=null}"));
|
||||
_server.stop();
|
||||
|
||||
ServletPathSpec spec = new ServletPathSpec("");
|
||||
handler = new PathMappingHandler(spec, spec.getPathMatch("foo"), "Something");
|
||||
_server.setHandler(handler);
|
||||
_server.start();
|
||||
response = _connector.getResponse(request);
|
||||
assertTrue(response.startsWith("HTTP/1.1 200 OK"));
|
||||
assertThat("Response body content", response, containsString("HttpServletMapping{matchValue=, pattern=, servletName=Something, mappingMatch=CONTEXT_ROOT}"));
|
||||
_server.stop();
|
||||
|
||||
spec = new ServletPathSpec("/");
|
||||
handler = new PathMappingHandler(spec, "", "Default");
|
||||
_server.setHandler(handler);
|
||||
_server.start();
|
||||
response = _connector.getResponse(request);
|
||||
assertTrue(response.startsWith("HTTP/1.1 200 OK"));
|
||||
assertThat("Response body content", response, containsString("HttpServletMapping{matchValue=/, pattern=/, servletName=Default, mappingMatch=DEFAULT}"));
|
||||
_server.stop();
|
||||
|
||||
spec = new ServletPathSpec("/foo/*");
|
||||
handler = new PathMappingHandler(spec, spec.getPathMatch("/foo/bar"), "BarServlet");
|
||||
_server.setHandler(handler);
|
||||
_server.start();
|
||||
response = _connector.getResponse(request);
|
||||
assertTrue(response.startsWith("HTTP/1.1 200 OK"));
|
||||
assertThat("Response body content", response, containsString("HttpServletMapping{matchValue=/foo, pattern=/foo/*, servletName=BarServlet, mappingMatch=PATH}"));
|
||||
_server.stop();
|
||||
|
||||
spec = new ServletPathSpec("*.jsp");
|
||||
handler = new PathMappingHandler(spec, spec.getPathMatch("/foo/bar.jsp"), "JspServlet");
|
||||
_server.setHandler(handler);
|
||||
_server.start();
|
||||
response = _connector.getResponse(request);
|
||||
assertTrue(response.startsWith("HTTP/1.1 200 OK"));
|
||||
assertThat("Response body content", response, containsString("HttpServletMapping{matchValue=/foo/bar, pattern=*.jsp, servletName=JspServlet, mappingMatch=EXTENSION}"));
|
||||
_server.stop();
|
||||
|
||||
spec = new ServletPathSpec("/catalog");
|
||||
handler = new PathMappingHandler(spec, spec.getPathMatch("/catalog"), "CatalogServlet");
|
||||
_server.setHandler(handler);
|
||||
_server.start();
|
||||
response = _connector.getResponse(request);
|
||||
assertTrue(response.startsWith("HTTP/1.1 200 OK"));
|
||||
assertThat("Response body content", response, containsString("HttpServletMapping{matchValue=catalog, pattern=/catalog, servletName=CatalogServlet, mappingMatch=EXACT}"));
|
||||
_server.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCookies() throws Exception
|
||||
{
|
||||
|
@ -1915,4 +1981,68 @@ public class RequestTest
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class TestUserIdentityScope implements UserIdentity.Scope
|
||||
{
|
||||
private ContextHandler _handler;
|
||||
private String _contextPath;
|
||||
private String _name;
|
||||
|
||||
public TestUserIdentityScope(ContextHandler handler, String contextPath, String name)
|
||||
{
|
||||
_handler = handler;
|
||||
_contextPath = contextPath;
|
||||
_name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContextHandler getContextHandler()
|
||||
{
|
||||
return _handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContextPath()
|
||||
{
|
||||
return _contextPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getRoleRefMap()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private class PathMappingHandler extends AbstractHandler
|
||||
{
|
||||
private ServletPathSpec _spec;
|
||||
private String _servletPath;
|
||||
private String _servletName;
|
||||
|
||||
public PathMappingHandler(ServletPathSpec spec, String servletPath, String servletName)
|
||||
{
|
||||
_spec = spec;
|
||||
_servletPath = servletPath;
|
||||
_servletName = servletName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
((Request)request).setHandled(true);
|
||||
baseRequest.setServletPath(_servletPath);
|
||||
baseRequest.setPathSpec(_spec);
|
||||
if (_servletName != null)
|
||||
baseRequest.setUserIdentityScope(new TestUserIdentityScope(null, null, _servletName));
|
||||
HttpServletMapping mapping = baseRequest.getHttpServletMapping();
|
||||
response.getWriter().println(mapping);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,6 @@ import java.util.ArrayList;
|
|||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
|
@ -36,6 +35,7 @@ import org.eclipse.jetty.server.Connector;
|
|||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
|
@ -67,7 +67,6 @@ public class InetAccessHandlerTest
|
|||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
|
||||
throws IOException, ServletException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
response.setStatus(HttpStatus.OK_200);
|
||||
|
@ -85,7 +84,7 @@ public class InetAccessHandlerTest
|
|||
|
||||
@ParameterizedTest
|
||||
@MethodSource("data")
|
||||
public void testHandler(String include, String exclude, String includeConnectors, String excludeConnectors, String code)
|
||||
public void testHandler(String path, String include, String exclude, String includeConnectors, String excludeConnectors, String code)
|
||||
throws Exception
|
||||
{
|
||||
_handler.clear();
|
||||
|
@ -107,14 +106,14 @@ public class InetAccessHandlerTest
|
|||
{
|
||||
if (inc.length() > 0)
|
||||
{
|
||||
_handler.includeConnector(inc);
|
||||
_handler.include(inc + "@");
|
||||
}
|
||||
}
|
||||
for (String exc : excludeConnectors.split(";", -1))
|
||||
{
|
||||
if (exc.length() > 0)
|
||||
{
|
||||
_handler.excludeConnector(exc);
|
||||
_handler.exclude(exc + "@");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -127,11 +126,11 @@ public class InetAccessHandlerTest
|
|||
}
|
||||
}
|
||||
|
||||
testConnector(_connector1.getLocalPort(), include, exclude, includeConnectors, excludeConnectors, codePerConnector.get(0));
|
||||
testConnector(_connector2.getLocalPort(), include, exclude, includeConnectors, excludeConnectors, codePerConnector.get(1));
|
||||
testConnector(_connector1.getLocalPort(), path, include, exclude, includeConnectors, excludeConnectors, codePerConnector.get(0));
|
||||
testConnector(_connector2.getLocalPort(), path, include, exclude, includeConnectors, excludeConnectors, codePerConnector.get(1));
|
||||
}
|
||||
|
||||
private void testConnector(int port, String include, String exclude, String includeConnectors, String excludeConnectors, String code) throws IOException
|
||||
private void testConnector(int port, String path, String include, String exclude, String includeConnectors, String excludeConnectors, String code) throws IOException
|
||||
{
|
||||
try (Socket socket = new Socket("127.0.0.1", port);)
|
||||
{
|
||||
|
@ -139,7 +138,7 @@ public class InetAccessHandlerTest
|
|||
|
||||
HttpTester.Request request = HttpTester.newRequest();
|
||||
request.setMethod("GET");
|
||||
request.setURI("/path");
|
||||
request.setURI(StringUtil.isEmpty(path) ? "/" : path);
|
||||
request.setHeader("Host", "127.0.0.1");
|
||||
request.setVersion(HttpVersion.HTTP_1_0);
|
||||
|
||||
|
@ -166,60 +165,81 @@ public class InetAccessHandlerTest
|
|||
Object[][] data = new Object[][]
|
||||
{
|
||||
// Empty lists 1
|
||||
{"", "", "", "", "200;200"},
|
||||
{"", "", "", "", "", "200;200"},
|
||||
|
||||
// test simple filters
|
||||
{"127.0.0.1", "", "", "", "200;200"},
|
||||
{"127.0.0.1-127.0.0.254", "", "", "", "200;200"},
|
||||
{"192.0.0.1", "", "", "", "403;403"},
|
||||
{"192.0.0.1-192.0.0.254", "", "", "", "403;403"},
|
||||
{"", "127.0.0.1", "", "", "", "200;200"},
|
||||
{"", "127.0.0.1-127.0.0.254", "", "", "", "200;200"},
|
||||
{"", "127.0.0.1-127.0.0.254", "", "", "", "200;200"},
|
||||
{"", "192.0.0.1", "", "", "", "403;403"},
|
||||
{"", "192.0.0.1-192.0.0.254", "", "", "", "403;403"},
|
||||
|
||||
// test includeConnector
|
||||
{"127.0.0.1", "", "http_connector1", "", "200;200"},
|
||||
{"127.0.0.1-127.0.0.254", "", "http_connector1", "", "200;200"},
|
||||
{"192.0.0.1", "", "http_connector1", "", "403;200"},
|
||||
{"192.0.0.1-192.0.0.254", "", "http_connector1", "", "403;200"},
|
||||
{"192.0.0.1", "", "http_connector2", "", "200;403"},
|
||||
{"192.0.0.1-192.0.0.254", "", "http_connector2", "", "200;403"},
|
||||
{"", "127.0.0.1", "", "http_connector1", "", "200;200"},
|
||||
{"", "127.0.0.1-127.0.0.254", "", "http_connector1", "", "200;200"},
|
||||
{"", "192.0.0.1", "", "http_connector1", "", "200;403"},
|
||||
{"", "192.0.0.1-192.0.0.254", "", "http_connector1", "", "200;403"},
|
||||
{"", "192.0.0.1", "", "http_connector2", "", "403;200"},
|
||||
{"", "192.0.0.1-192.0.0.254", "", "http_connector2", "", "403;200"},
|
||||
|
||||
// test includeConnector names where none of them match
|
||||
{"127.0.0.1", "", "nothttp", "", "200;200"},
|
||||
{"127.0.0.1-127.0.0.254", "", "nothttp", "", "200;200"},
|
||||
{"192.0.0.1", "", "nothttp", "", "200;200"},
|
||||
{"192.0.0.1-192.0.0.254", "", "nothttp", "", "200;200"},
|
||||
{"", "127.0.0.1", "", "nothttp", "", "200;200"},
|
||||
{"", "127.0.0.1-127.0.0.254", "", "nothttp", "", "200;200"},
|
||||
{"", "192.0.0.1", "", "nothttp", "", "403;403"},
|
||||
{"", "192.0.0.1-192.0.0.254", "", "nothttp", "", "403;403"},
|
||||
|
||||
// text excludeConnector
|
||||
{"127.0.0.1", "", "", "http_connector1", "200;200"},
|
||||
{"127.0.0.1-127.0.0.254", "", "", "http_connector1", "200;200"},
|
||||
{"192.0.0.1", "", "", "http_connector1", "200;403"},
|
||||
{"192.0.0.1-192.0.0.254", "", "", "http_connector1", "200;403"},
|
||||
{"192.0.0.1", "", "", "http_connector2", "403;200"},
|
||||
{"192.0.0.1-192.0.0.254", "", "", "http_connector2", "403;200"},
|
||||
{"", "127.0.0.1", "", "", "http_connector1", "403;200"},
|
||||
{"", "127.0.0.1-127.0.0.254", "", "", "http_connector1", "403;200"},
|
||||
{"", "192.0.0.1", "", "", "http_connector1", "403;403"},
|
||||
{"", "192.0.0.1-192.0.0.254", "", "", "http_connector1", "403;403"},
|
||||
{"", "192.0.0.1", "", "", "http_connector2", "403;403"},
|
||||
{"", "192.0.0.1-192.0.0.254", "", "", "http_connector2", "403;403"},
|
||||
|
||||
// test excludeConnector where none of them match.
|
||||
{"127.0.0.1", "", "", "nothttp", "200;200"},
|
||||
{"127.0.0.1-127.0.0.254", "", "", "nothttp", "200;200"},
|
||||
{"192.0.0.1", "", "", "nothttp", "403;403"},
|
||||
{"192.0.0.1-192.0.0.254", "", "", "nothttp", "403;403"},
|
||||
{"", "127.0.0.1", "", "", "nothttp", "200;200"},
|
||||
{"", "127.0.0.1-127.0.0.254", "", "", "nothttp", "200;200"},
|
||||
{"", "192.0.0.1", "", "", "nothttp", "403;403"},
|
||||
{"", "192.0.0.1-192.0.0.254", "", "", "nothttp", "403;403"},
|
||||
|
||||
// both connectors are excluded
|
||||
{"127.0.0.1", "", "", "http_connector1;http_connector2", "200;200"},
|
||||
{"127.0.0.1-127.0.0.254", "", "", "http_connector1;http_connector2", "200;200"},
|
||||
{"192.0.0.1", "", "", "http_connector1;http_connector2", "200;200"},
|
||||
{"192.0.0.1-192.0.0.254", "", "", "http_connector1;http_connector2", "200;200"},
|
||||
{"", "127.0.0.1", "", "", "http_connector1;http_connector2", "403;403"},
|
||||
{"", "127.0.0.1-127.0.0.254", "", "", "http_connector1;http_connector2", "403;403"},
|
||||
{"", "192.0.0.1", "", "", "http_connector1;http_connector2", "403;403"},
|
||||
{"", "192.0.0.1-192.0.0.254", "", "", "http_connector1;http_connector2", "403;403"},
|
||||
|
||||
// both connectors are included
|
||||
{"127.0.0.1", "", "http_connector1;http_connector2", "", "200;200"},
|
||||
{"127.0.0.1-127.0.0.254", "", "http_connector1;http_connector2", "", "200;200"},
|
||||
{"192.0.0.1", "", "http_connector1;http_connector2", "", "403;403"},
|
||||
{"192.0.0.1-192.0.0.254", "", "http_connector1;http_connector2", "", "403;403"},
|
||||
{"", "127.0.0.1", "", "http_connector1;http_connector2", "", "200;200"},
|
||||
{"", "127.0.0.1-127.0.0.254", "", "http_connector1;http_connector2", "", "200;200"},
|
||||
{"", "192.0.0.1", "", "http_connector1;http_connector2", "", "200;200"},
|
||||
{"", "192.0.0.1-192.0.0.254", "", "http_connector1;http_connector2", "", "200;200"},
|
||||
{"", "", "127.0.0.1", "http_connector1;http_connector2", "", "403;403"},
|
||||
|
||||
// exclude takes precedence over include
|
||||
{"127.0.0.1", "", "http_connector1;http_connector2", "http_connector1;http_connector2", "200;200"},
|
||||
{"127.0.0.1-127.0.0.254", "", "http_connector1;http_connector2", "http_connector1;http_connector2", "200;200"},
|
||||
{"192.0.0.1", "", "http_connector1;http_connector2", "http_connector1;http_connector2", "200;200"},
|
||||
{"192.0.0.1-192.0.0.254", "", "http_connector1;http_connector2", "http_connector1;http_connector2", "200;200"}
|
||||
{"", "127.0.0.1", "", "http_connector1;http_connector2", "http_connector1;http_connector2", "403;403"},
|
||||
{"", "127.0.0.1-127.0.0.254", "", "http_connector1;http_connector2", "http_connector1;http_connector2", "403;403"},
|
||||
{"", "192.0.0.1", "", "http_connector1;http_connector2", "http_connector1;http_connector2", "403;403"},
|
||||
{"", "192.0.0.1-192.0.0.254", "", "http_connector1;http_connector2", "http_connector1;http_connector2", "403;403"},
|
||||
|
||||
// Test path specific include/exclude.
|
||||
{"/testPath", "", "", "http_connector1", "", "200;403"},
|
||||
{"/", "127.0.0.1", "127.0.0.1|/testPath", "http_connector1", "", "200;200"},
|
||||
{"/testPath", "127.0.0.1", "127.0.0.1|/testPath", "http_connector1", "", "403;403"},
|
||||
{"/notTestPath", "127.0.1.11|/testPath", "", "http_connector1", "", "200;403"},
|
||||
{"/testPath", "127.0.1.11|/testPath", "", "http_connector1", "", "200;403"},
|
||||
{"/testPath", "127.0.0.13|/testPath;127.0.0.1|/testPath", "", "http_connector1", "", "200;200"},
|
||||
{"/testPath", "127.0.0.1", "127.0.0.1|/testPath", "http_connector1", "", "403;403"},
|
||||
{"/", "127.0.0.1", "127.0.0.1|/testPath", "http_connector1", "", "200;200"},
|
||||
{"/a/b", "", "127.0.0.1|/a/*", "", "", "403;403"},
|
||||
{"/b/a", "", "127.0.0.1|/a/*", "", "", "200;200"},
|
||||
{"/org/eclipse/jetty/test.html", "127.0.0.1|*.html", "127.0.0.1|*.xml", "", "", "200;200"},
|
||||
{"/org/eclipse/jetty/test.xml", "127.0.0.1|*.html", "127.0.0.1|*.xml", "", "", "403;403"},
|
||||
{"/org/eclipse/jetty/test.pdf", "127.0.0.1|*.html", "127.0.0.1|*.xml", "", "", "403;403"},
|
||||
{"/a/test.html", "", "127.0.0.1|*.html;127.0.0.10|/a/*", "", "", "403;403"},
|
||||
{"/foo/bar/test.xml", "127.0.0.1|/foo/*", "127.0.0.0-127.0.0.2|*.html", "", "", "200;200"},
|
||||
{"/foo/bar/test.html", "127.0.0.1|/foo/*", "127.0.0.0-127.0.0.2|*.html", "", "", "403;403"},
|
||||
{"/foo/bar/test.xml", "127.0.0.1|/foo/bar/*", "127.0.0.1|/foo/*", "", "", "403;403"},
|
||||
};
|
||||
return Arrays.asList(data).stream().map(Arguments::of);
|
||||
return Arrays.stream(data).map(Arguments::of);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,10 +19,12 @@
|
|||
package org.eclipse.jetty.servlet;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.UnavailableException;
|
||||
|
||||
import org.eclipse.jetty.server.handler.ContextHandler;
|
||||
import org.eclipse.jetty.server.handler.ContextHandler.Context;
|
||||
import org.eclipse.jetty.util.Loader;
|
||||
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||
import org.eclipse.jetty.util.component.AbstractLifeCycle;
|
||||
|
@ -177,6 +179,40 @@ public abstract class BaseHolder<T> extends AbstractLifeCycle implements Dumpabl
|
|||
return _instance;
|
||||
}
|
||||
|
||||
protected synchronized T createInstance() throws Exception
|
||||
{
|
||||
ServletContext ctx = getServletContext();
|
||||
if (ctx == null)
|
||||
return getHeldClass().getDeclaredConstructor().newInstance();
|
||||
|
||||
if (ServletContextHandler.Context.class.isAssignableFrom(ctx.getClass()))
|
||||
return ((ServletContextHandler.Context)ctx).createInstance(this);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public ServletContext getServletContext()
|
||||
{
|
||||
ServletContext scontext = null;
|
||||
|
||||
//try the ServletHandler first
|
||||
if (getServletHandler() != null)
|
||||
scontext = getServletHandler().getServletContext();
|
||||
|
||||
if (scontext != null)
|
||||
return scontext;
|
||||
|
||||
//try the ContextHandler next
|
||||
Context ctx = ContextHandler.getCurrentContext();
|
||||
if (ctx != null)
|
||||
{
|
||||
ContextHandler contextHandler = ctx.getContextHandler();
|
||||
if (contextHandler != null)
|
||||
return contextHandler.getServletContext();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if this holder was created for a specific instance.
|
||||
*/
|
||||
|
|
|
@ -24,6 +24,7 @@ import java.util.Arrays;
|
|||
import java.util.Collection;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.DispatcherType;
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterConfig;
|
||||
|
@ -113,10 +114,7 @@ public class FilterHolder extends Holder<Filter>
|
|||
{
|
||||
try
|
||||
{
|
||||
ServletContext context = getServletHandler().getServletContext();
|
||||
_filter = (context != null)
|
||||
? context.createFilter(getHeldClass())
|
||||
: getHeldClass().getDeclaredConstructor().newInstance();
|
||||
_filter = createInstance();
|
||||
}
|
||||
catch (ServletException ex)
|
||||
{
|
||||
|
@ -135,6 +133,19 @@ public class FilterHolder extends Holder<Filter>
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized Filter createInstance() throws Exception
|
||||
{
|
||||
Filter filter = super.createInstance();
|
||||
if (filter == null)
|
||||
{
|
||||
ServletContext context = getServletContext();
|
||||
if (context != null)
|
||||
filter = context.createFilter(getHeldClass());
|
||||
}
|
||||
return filter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doStop()
|
||||
throws Exception
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
package org.eclipse.jetty.servlet;
|
||||
|
||||
import java.util.EventListener;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
|
@ -88,10 +89,7 @@ public class ListenerHolder extends BaseHolder<EventListener>
|
|||
//create an instance of the listener and decorate it
|
||||
try
|
||||
{
|
||||
ServletContext context = contextHandler.getServletContext();
|
||||
_listener = (context != null)
|
||||
? context.createListener(getHeldClass())
|
||||
: getHeldClass().getDeclaredConstructor().newInstance();
|
||||
_listener = createInstance();
|
||||
}
|
||||
catch (ServletException ex)
|
||||
{
|
||||
|
@ -107,6 +105,20 @@ public class ListenerHolder extends BaseHolder<EventListener>
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized EventListener createInstance() throws Exception
|
||||
{
|
||||
|
||||
EventListener listener = super.createInstance();
|
||||
if (listener == null)
|
||||
{
|
||||
ServletContext ctx = getServletContext();
|
||||
if (ctx != null)
|
||||
listener = ctx.createListener(getHeldClass());
|
||||
}
|
||||
return listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doStop() throws Exception
|
||||
{
|
||||
|
|
|
@ -1261,6 +1261,21 @@ public class ServletContextHandler extends ContextHandler
|
|||
return _objFactory.decorate(super.createInstance(clazz));
|
||||
}
|
||||
|
||||
public <T> T createInstance(BaseHolder<T> holder) throws ServletException
|
||||
{
|
||||
try
|
||||
{
|
||||
//set a thread local
|
||||
DecoratedObjectFactory.associateInfo(holder);
|
||||
return createInstance(holder.getHeldClass());
|
||||
}
|
||||
finally
|
||||
{
|
||||
//unset the thread local
|
||||
DecoratedObjectFactory.disassociateInfo();
|
||||
}
|
||||
}
|
||||
|
||||
public <T extends Filter> void destroyFilter(T f)
|
||||
{
|
||||
_objFactory.destroy(f);
|
||||
|
|
|
@ -449,6 +449,7 @@ public class ServletHandler extends ScopedHandler
|
|||
{
|
||||
baseRequest.setAttribute(RequestDispatcher.INCLUDE_SERVLET_PATH, servletPath);
|
||||
baseRequest.setAttribute(RequestDispatcher.INCLUDE_PATH_INFO, pathInfo);
|
||||
baseRequest.setAttribute(RequestDispatcher.INCLUDE_MAPPING, Request.getServletMapping(pathSpec, servletPath, servletHolder.getName()));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -33,6 +33,7 @@ import java.util.Map;
|
|||
import java.util.Set;
|
||||
import java.util.Stack;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.servlet.GenericServlet;
|
||||
import javax.servlet.MultipartConfigElement;
|
||||
import javax.servlet.Servlet;
|
||||
|
@ -1110,30 +1111,22 @@ public class ServletHolder extends Holder<Servlet> implements UserIdentity.Scope
|
|||
* @throws NoSuchMethodException if creating new instance resulted in error
|
||||
* @throws InvocationTargetException If creating new instance throws an exception
|
||||
*/
|
||||
protected Servlet newInstance() throws ServletException, IllegalAccessException, InstantiationException,
|
||||
NoSuchMethodException, InvocationTargetException
|
||||
protected Servlet newInstance() throws Exception
|
||||
{
|
||||
try
|
||||
{
|
||||
ServletContext ctx = getServletHandler().getServletContext();
|
||||
if (ctx != null)
|
||||
return ctx.createServlet(getHeldClass());
|
||||
return getHeldClass().getDeclaredConstructor().newInstance();
|
||||
return createInstance();
|
||||
}
|
||||
|
||||
}
|
||||
catch (ServletException ex)
|
||||
@Override
|
||||
protected synchronized Servlet createInstance() throws Exception
|
||||
{
|
||||
Throwable cause = ex.getRootCause();
|
||||
if (cause instanceof InstantiationException)
|
||||
throw (InstantiationException)cause;
|
||||
if (cause instanceof IllegalAccessException)
|
||||
throw (IllegalAccessException)cause;
|
||||
if (cause instanceof NoSuchMethodException)
|
||||
throw (NoSuchMethodException)cause;
|
||||
if (cause instanceof InvocationTargetException)
|
||||
throw (InvocationTargetException)cause;
|
||||
throw ex;
|
||||
Servlet servlet = super.createInstance();
|
||||
if (servlet == null)
|
||||
{
|
||||
ServletContext ctx = getServletContext();
|
||||
if (ctx != null)
|
||||
servlet = ctx.createServlet(getHeldClass());
|
||||
}
|
||||
return servlet;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -33,7 +33,6 @@ public class Source
|
|||
EMBEDDED, JAVAX_API, DESCRIPTOR, ANNOTATION
|
||||
}
|
||||
|
||||
;
|
||||
public Origin _origin;
|
||||
public String _resource;
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ import javax.servlet.DispatcherType;
|
|||
import javax.servlet.RequestDispatcher;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletMapping;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpServletResponseWrapper;
|
||||
|
@ -224,6 +225,10 @@ public class AsyncContextTest
|
|||
assertThat("query string attr is correct", responseBody, containsString("async:run:attr:queryString:dispatch=true"));
|
||||
assertThat("context path attr is correct", responseBody, containsString("async:run:attr:contextPath:/ctx"));
|
||||
assertThat("request uri attr is correct", responseBody, containsString("async:run:attr:requestURI:/ctx/servletPath"));
|
||||
assertThat("http servlet mapping matchValue is correct", responseBody, containsString("async:run:attr:mapping:matchValue:servletPath"));
|
||||
assertThat("http servlet mapping pattern is correct", responseBody, containsString("async:run:attr:mapping:pattern:/servletPath"));
|
||||
assertThat("http servlet mapping servletName is correct", responseBody, containsString("async:run:attr:mapping:servletName:"));
|
||||
assertThat("http servlet mapping mappingMatch is correct", responseBody, containsString("async:run:attr:mapping:mappingMatch:EXACT"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -257,6 +262,10 @@ public class AsyncContextTest
|
|||
assertThat("async run attr query string", responseBody, containsString("async:run:attr:queryString:dispatch=true"));
|
||||
assertThat("async run context path", responseBody, containsString("async:run:attr:contextPath:/ctx"));
|
||||
assertThat("async run request uri has correct encoding", responseBody, containsString("async:run:attr:requestURI:/ctx/test/hello%2fthere"));
|
||||
assertThat("http servlet mapping matchValue is correct", responseBody, containsString("async:run:attr:mapping:matchValue:/test"));
|
||||
assertThat("http servlet mapping pattern is correct", responseBody, containsString("async:run:attr:mapping:pattern:/test/*"));
|
||||
assertThat("http servlet mapping servletName is correct", responseBody, containsString("async:run:attr:mapping:servletName:"));
|
||||
assertThat("http servlet mapping mappingMatch is correct", responseBody, containsString("async:run:attr:mapping:mappingMatch:PATH"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -296,6 +305,10 @@ public class AsyncContextTest
|
|||
assertThat("query string attr is correct", responseBody, containsString("async:run:attr:queryString:dispatch=true&queryStringWithEncoding=space%20space"));
|
||||
assertThat("context path attr is correct", responseBody, containsString("async:run:attr:contextPath:/ctx"));
|
||||
assertThat("request uri attr is correct", responseBody, containsString("async:run:attr:requestURI:/ctx/path%20with%20spaces/servletPath"));
|
||||
assertThat("http servlet mapping matchValue is correct", responseBody, containsString("async:run:attr:mapping:matchValue:path with spaces/servletPath"));
|
||||
assertThat("http servlet mapping pattern is correct", responseBody, containsString("async:run:attr:mapping:pattern:/path with spaces/servletPath"));
|
||||
assertThat("http servlet mapping servletName is correct", responseBody, containsString("async:run:attr:mapping:servletName:"));
|
||||
assertThat("http servlet mapping mappingMatch is correct", responseBody, containsString("async:run:attr:mapping:mappingMatch:EXACT"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -338,6 +351,10 @@ public class AsyncContextTest
|
|||
assertThat("query string attr is correct", responseBody, containsString("async:run:attr:queryString:dispatch=true"));
|
||||
assertThat("context path attr is correct", responseBody, containsString("async:run:attr:contextPath:/ctx"));
|
||||
assertThat("request uri attr is correct", responseBody, containsString("async:run:attr:requestURI:/ctx/servletPath"));
|
||||
assertThat("http servlet mapping matchValue is correct", responseBody, containsString("async:run:attr:mapping:matchValue:servletPath"));
|
||||
assertThat("http servlet mapping pattern is correct", responseBody, containsString("async:run:attr:mapping:pattern:/servletPath"));
|
||||
assertThat("http servlet mapping servletName is correct", responseBody, containsString("async:run:attr:mapping:servletName:"));
|
||||
assertThat("http servlet mapping mappingMatch is correct", responseBody, containsString("async:run:attr:mapping:mappingMatch:EXACT"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -687,6 +704,14 @@ public class AsyncContextTest
|
|||
_context.getResponse().getOutputStream().print("async:run:attr:queryString:" + req.getAttribute(AsyncContext.ASYNC_QUERY_STRING) + "\n");
|
||||
_context.getResponse().getOutputStream().print("async:run:attr:contextPath:" + req.getAttribute(AsyncContext.ASYNC_CONTEXT_PATH) + "\n");
|
||||
_context.getResponse().getOutputStream().print("async:run:attr:requestURI:" + req.getAttribute(AsyncContext.ASYNC_REQUEST_URI) + "\n");
|
||||
HttpServletMapping mapping = (HttpServletMapping)req.getAttribute(AsyncContext.ASYNC_MAPPING);
|
||||
if (mapping != null)
|
||||
{
|
||||
_context.getResponse().getOutputStream().print("async:run:attr:mapping:matchValue:" + mapping.getMatchValue() + "\n");
|
||||
_context.getResponse().getOutputStream().print("async:run:attr:mapping:pattern:" + mapping.getPattern() + "\n");
|
||||
_context.getResponse().getOutputStream().print("async:run:attr:mapping:servletName:" + mapping.getServletName() + "\n");
|
||||
_context.getResponse().getOutputStream().print("async:run:attr:mapping:mappingMatch:" + mapping.getMappingMatch() + "\n");
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
|
|
|
@ -41,6 +41,7 @@ import javax.servlet.ServletResponse;
|
|||
import javax.servlet.ServletResponseWrapper;
|
||||
import javax.servlet.WriteListener;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletMapping;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletRequestWrapper;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
@ -728,9 +729,12 @@ public class DispatcherTest
|
|||
assertEquals("/ForwardServlet", request.getAttribute(Dispatcher.FORWARD_SERVLET_PATH));
|
||||
assertEquals(null, request.getAttribute(Dispatcher.FORWARD_PATH_INFO));
|
||||
assertEquals("do=assertforward&do=more&test=1", request.getAttribute(Dispatcher.FORWARD_QUERY_STRING));
|
||||
HttpServletMapping fwdMapping = (HttpServletMapping)request.getAttribute(Dispatcher.FORWARD_MAPPING);
|
||||
assertNotNull(fwdMapping);
|
||||
assertEquals("/ForwardServlet", fwdMapping.getMatchValue());
|
||||
|
||||
List<String> expectedAttributeNames = Arrays.asList(Dispatcher.FORWARD_REQUEST_URI, Dispatcher.FORWARD_CONTEXT_PATH,
|
||||
Dispatcher.FORWARD_SERVLET_PATH, Dispatcher.FORWARD_QUERY_STRING);
|
||||
Dispatcher.FORWARD_SERVLET_PATH, Dispatcher.FORWARD_QUERY_STRING, Dispatcher.FORWARD_MAPPING);
|
||||
List<String> requestAttributeNames = Collections.list(request.getAttributeNames());
|
||||
assertTrue(requestAttributeNames.containsAll(expectedAttributeNames));
|
||||
|
||||
|
@ -760,9 +764,12 @@ public class DispatcherTest
|
|||
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));
|
||||
HttpServletMapping fwdMapping = (HttpServletMapping)request.getAttribute(Dispatcher.FORWARD_MAPPING);
|
||||
assertNotNull(fwdMapping);
|
||||
assertEquals("/ForwardServlet", fwdMapping.getMatchValue());
|
||||
|
||||
List<String> expectedAttributeNames = Arrays.asList(Dispatcher.FORWARD_REQUEST_URI, Dispatcher.FORWARD_CONTEXT_PATH,
|
||||
Dispatcher.FORWARD_SERVLET_PATH, Dispatcher.FORWARD_QUERY_STRING);
|
||||
Dispatcher.FORWARD_SERVLET_PATH, Dispatcher.FORWARD_QUERY_STRING, Dispatcher.FORWARD_MAPPING);
|
||||
List<String> requestAttributeNames = Collections.list(request.getAttributeNames());
|
||||
assertTrue(requestAttributeNames.containsAll(expectedAttributeNames));
|
||||
|
||||
|
@ -806,9 +813,12 @@ public class DispatcherTest
|
|||
assertEquals("/AssertIncludeServlet", request.getAttribute(Dispatcher.INCLUDE_SERVLET_PATH));
|
||||
assertEquals(null, request.getAttribute(Dispatcher.INCLUDE_PATH_INFO));
|
||||
assertEquals("do=end&do=the", request.getAttribute(Dispatcher.INCLUDE_QUERY_STRING));
|
||||
HttpServletMapping incMapping = (HttpServletMapping)request.getAttribute(Dispatcher.INCLUDE_MAPPING);
|
||||
assertNotNull(incMapping);
|
||||
assertEquals("/AssertIncludeServlet", incMapping.getMatchValue());
|
||||
|
||||
List expectedAttributeNames = Arrays.asList(Dispatcher.INCLUDE_REQUEST_URI, Dispatcher.INCLUDE_CONTEXT_PATH,
|
||||
Dispatcher.INCLUDE_SERVLET_PATH, Dispatcher.INCLUDE_QUERY_STRING);
|
||||
Dispatcher.INCLUDE_SERVLET_PATH, Dispatcher.INCLUDE_QUERY_STRING, Dispatcher.INCLUDE_MAPPING);
|
||||
List requestAttributeNames = Collections.list(request.getAttributeNames());
|
||||
assertTrue(requestAttributeNames.containsAll(expectedAttributeNames));
|
||||
|
||||
|
@ -836,17 +846,23 @@ public class DispatcherTest
|
|||
assertEquals("/ForwardServlet", request.getAttribute(Dispatcher.FORWARD_SERVLET_PATH));
|
||||
assertEquals("/forwardpath", request.getAttribute(Dispatcher.FORWARD_PATH_INFO));
|
||||
assertEquals("do=include", request.getAttribute(Dispatcher.FORWARD_QUERY_STRING));
|
||||
HttpServletMapping fwdMapping = (HttpServletMapping)request.getAttribute(Dispatcher.FORWARD_MAPPING);
|
||||
assertNotNull(fwdMapping);
|
||||
assertEquals("/ForwardServlet", fwdMapping.getMatchValue());
|
||||
|
||||
assertEquals("/context/AssertForwardIncludeServlet/assertpath", request.getAttribute(Dispatcher.INCLUDE_REQUEST_URI));
|
||||
assertEquals("/context", request.getAttribute(Dispatcher.INCLUDE_CONTEXT_PATH));
|
||||
assertEquals("/AssertForwardIncludeServlet", request.getAttribute(Dispatcher.INCLUDE_SERVLET_PATH));
|
||||
assertEquals("/assertpath", request.getAttribute(Dispatcher.INCLUDE_PATH_INFO));
|
||||
assertEquals("do=end", request.getAttribute(Dispatcher.INCLUDE_QUERY_STRING));
|
||||
HttpServletMapping incMapping = (HttpServletMapping)request.getAttribute(Dispatcher.INCLUDE_MAPPING);
|
||||
assertNotNull(incMapping);
|
||||
assertEquals("/AssertForwardIncludeServlet", incMapping.getMatchValue());
|
||||
|
||||
List expectedAttributeNames = Arrays.asList(Dispatcher.FORWARD_REQUEST_URI, Dispatcher.FORWARD_CONTEXT_PATH, Dispatcher.FORWARD_SERVLET_PATH,
|
||||
Dispatcher.FORWARD_PATH_INFO, Dispatcher.FORWARD_QUERY_STRING,
|
||||
Dispatcher.FORWARD_PATH_INFO, Dispatcher.FORWARD_QUERY_STRING, Dispatcher.FORWARD_MAPPING,
|
||||
Dispatcher.INCLUDE_REQUEST_URI, Dispatcher.INCLUDE_CONTEXT_PATH, Dispatcher.INCLUDE_SERVLET_PATH,
|
||||
Dispatcher.INCLUDE_PATH_INFO, Dispatcher.INCLUDE_QUERY_STRING);
|
||||
Dispatcher.INCLUDE_PATH_INFO, Dispatcher.INCLUDE_QUERY_STRING, Dispatcher.INCLUDE_MAPPING);
|
||||
List requestAttributeNames = Collections.list(request.getAttributeNames());
|
||||
assertTrue(requestAttributeNames.containsAll(expectedAttributeNames));
|
||||
|
||||
|
@ -874,15 +890,19 @@ public class DispatcherTest
|
|||
assertEquals(null, request.getAttribute(Dispatcher.INCLUDE_SERVLET_PATH));
|
||||
assertEquals(null, request.getAttribute(Dispatcher.INCLUDE_PATH_INFO));
|
||||
assertEquals(null, request.getAttribute(Dispatcher.INCLUDE_QUERY_STRING));
|
||||
assertEquals(null, request.getAttribute(Dispatcher.INCLUDE_MAPPING));
|
||||
|
||||
assertEquals("/context/IncludeServlet/includepath", request.getAttribute(Dispatcher.FORWARD_REQUEST_URI));
|
||||
assertEquals("/context", request.getAttribute(Dispatcher.FORWARD_CONTEXT_PATH));
|
||||
assertEquals("/IncludeServlet", request.getAttribute(Dispatcher.FORWARD_SERVLET_PATH));
|
||||
assertEquals("/includepath", request.getAttribute(Dispatcher.FORWARD_PATH_INFO));
|
||||
assertEquals("do=forward", request.getAttribute(Dispatcher.FORWARD_QUERY_STRING));
|
||||
HttpServletMapping fwdMapping = (HttpServletMapping)request.getAttribute(Dispatcher.FORWARD_MAPPING);
|
||||
assertNotNull(fwdMapping);
|
||||
assertEquals("/IncludeServlet", fwdMapping.getMatchValue());
|
||||
|
||||
List expectedAttributeNames = Arrays.asList(Dispatcher.FORWARD_REQUEST_URI, Dispatcher.FORWARD_CONTEXT_PATH, Dispatcher.FORWARD_SERVLET_PATH,
|
||||
Dispatcher.FORWARD_PATH_INFO, Dispatcher.FORWARD_QUERY_STRING);
|
||||
Dispatcher.FORWARD_PATH_INFO, Dispatcher.FORWARD_QUERY_STRING, Dispatcher.FORWARD_MAPPING);
|
||||
List requestAttributeNames = Collections.list(request.getAttributeNames());
|
||||
assertTrue(requestAttributeNames.containsAll(expectedAttributeNames));
|
||||
|
||||
|
|
|
@ -27,10 +27,12 @@ import javax.servlet.ServletException;
|
|||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.util.log.StacklessLogging;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
/**
|
||||
|
@ -38,6 +40,22 @@ import static org.junit.jupiter.api.Assertions.fail;
|
|||
*/
|
||||
public class FilterHolderTest
|
||||
{
|
||||
public static class DummyFilter implements Filter
|
||||
{
|
||||
public DummyFilter()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) throws ServletException
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitialize()
|
||||
|
@ -95,4 +113,28 @@ public class FilterHolderTest
|
|||
fh.initialize();
|
||||
assertEquals(2, counter.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateInstance() throws Exception
|
||||
{
|
||||
try (StacklessLogging ignore = new StacklessLogging(ServletHandler.class, ServletContextHandler.class))
|
||||
{
|
||||
//test without a ServletContextHandler or current ContextHandler
|
||||
FilterHolder holder = new FilterHolder();
|
||||
holder.setName("foo");
|
||||
holder.setHeldClass(DummyFilter.class);
|
||||
Filter filter = holder.createInstance();
|
||||
assertNotNull(filter);
|
||||
|
||||
//test with a ServletContextHandler
|
||||
Server server = new Server();
|
||||
ServletContextHandler context = new ServletContextHandler();
|
||||
server.setHandler(context);
|
||||
ServletHandler handler = context.getServletHandler();
|
||||
handler.addFilter(holder);
|
||||
holder.setServletHandler(handler);
|
||||
context.start();
|
||||
assertNotNull(holder.getFilter());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under
|
||||
// the terms of the Eclipse Public License 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// This Source Code may also be made available under the following
|
||||
// Secondary Licenses when the conditions for such availability set
|
||||
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||
// the Apache License v2.0 which is available at
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.servlet;
|
||||
|
||||
import java.util.EventListener;
|
||||
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.util.log.StacklessLogging;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
public class ListenerHolderTest
|
||||
{
|
||||
public static class DummyListener implements EventListener
|
||||
{
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateInstance() throws Exception
|
||||
{
|
||||
try (StacklessLogging ignore = new StacklessLogging(ServletHandler.class, ServletContextHandler.class))
|
||||
{
|
||||
//test without a ServletContextHandler or current ContextHandler
|
||||
ListenerHolder holder = new ListenerHolder();
|
||||
holder.setHeldClass(DummyListener.class);
|
||||
EventListener listener = holder.createInstance();
|
||||
assertNotNull(listener);
|
||||
|
||||
//test with a ServletContextHandler
|
||||
Server server = new Server();
|
||||
ServletContextHandler context = new ServletContextHandler();
|
||||
server.setHandler(context);
|
||||
ServletHandler handler = context.getServletHandler();
|
||||
handler.addListener(holder);
|
||||
holder.setServletHandler(handler);
|
||||
context.start();
|
||||
assertNotNull(holder.getListener());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,10 +20,13 @@ package org.eclipse.jetty.servlet;
|
|||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.Servlet;
|
||||
import javax.servlet.ServletRegistration;
|
||||
import javax.servlet.UnavailableException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.handler.ContextHandler;
|
||||
import org.eclipse.jetty.util.MultiException;
|
||||
import org.eclipse.jetty.util.log.StacklessLogging;
|
||||
|
@ -33,6 +36,7 @@ import static org.hamcrest.MatcherAssert.assertThat;
|
|||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
@ -114,6 +118,30 @@ public class ServletHolderTest
|
|||
assertEquals("org.apache.jsp.a.b.c.blah_jsp", h.getClassNameForJsp("a/b/c/blah.jsp"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateInstance() throws Exception
|
||||
{
|
||||
try (StacklessLogging ignore = new StacklessLogging(ServletHandler.class, ContextHandler.class, ServletContextHandler.class))
|
||||
{
|
||||
//test without a ServletContextHandler or current ContextHandler
|
||||
ServletHolder holder = new ServletHolder();
|
||||
holder.setName("foo");
|
||||
holder.setHeldClass(FakeServlet.class);
|
||||
Servlet servlet = holder.createInstance();
|
||||
assertNotNull(servlet);
|
||||
|
||||
//test with a ServletContextHandler
|
||||
Server server = new Server();
|
||||
ServletContextHandler context = new ServletContextHandler();
|
||||
server.setHandler(context);
|
||||
ServletHandler handler = context.getServletHandler();
|
||||
handler.addServlet(holder);
|
||||
holder.setServletHandler(handler);
|
||||
context.start();
|
||||
assertNotNull(holder.getServlet());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoClassName() throws Exception
|
||||
{
|
||||
|
|
|
@ -154,12 +154,12 @@ public class Module implements Comparable<Module>
|
|||
private final List<String> _license = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Dependencies
|
||||
* Dependencies from {@code [depends]} section
|
||||
*/
|
||||
private final List<String> _depends = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Optional
|
||||
* Optional dependencies from {@code [optional]} section are structural in nature.
|
||||
*/
|
||||
private final Set<String> _optional = new HashSet<>();
|
||||
|
||||
|
@ -196,6 +196,18 @@ public class Module implements Comparable<Module>
|
|||
process(basehome);
|
||||
}
|
||||
|
||||
public static boolean isRequiredDependency(String depends)
|
||||
{
|
||||
return (depends != null) && (depends.charAt(0) != '?');
|
||||
}
|
||||
|
||||
public static String normalizeModuleName(String name)
|
||||
{
|
||||
if (!isRequiredDependency(name))
|
||||
return name.substring(1);
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getName()
|
||||
{
|
||||
return _name;
|
||||
|
|
|
@ -247,6 +247,7 @@ public class ModuleGraphWriter
|
|||
{
|
||||
for (String depends : module.getDepends())
|
||||
{
|
||||
depends = Module.normalizeModuleName(depends);
|
||||
out.printf(" \"%s\" -> \"%s\";%n", module.getName(), depends);
|
||||
}
|
||||
for (String optional : module.getOptional())
|
||||
|
|
|
@ -20,6 +20,7 @@ package org.eclipse.jetty.start;
|
|||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
@ -132,7 +133,12 @@ public class Modules implements Iterable<Module>
|
|||
label = " Depend: %s";
|
||||
for (String parent : module.getDepends())
|
||||
{
|
||||
parent = Module.normalizeModuleName(parent);
|
||||
System.out.printf(label, parent);
|
||||
if (!Module.isRequiredDependency(parent))
|
||||
{
|
||||
System.out.print(" [not-required]");
|
||||
}
|
||||
label = ", %s";
|
||||
}
|
||||
System.out.println();
|
||||
|
@ -353,45 +359,59 @@ public class Modules implements Iterable<Module>
|
|||
|
||||
// Process module dependencies (always processed as may be dynamic)
|
||||
StartLog.debug("Enabled module %s depends on %s", module.getName(), module.getDepends());
|
||||
for (String dependsOn : module.getDepends())
|
||||
for (String dependsOnRaw : module.getDepends())
|
||||
{
|
||||
// Look for modules that provide that dependency
|
||||
Set<Module> providers = getAvailableProviders(dependsOn);
|
||||
boolean isRequired = Module.isRequiredDependency(dependsOnRaw);
|
||||
// Final to allow lambda's below to use name
|
||||
final String dependentModule = Module.normalizeModuleName(dependsOnRaw);
|
||||
|
||||
StartLog.debug("Module %s depends on %s provided by %s", module, dependsOn, providers);
|
||||
// Look for modules that provide that dependency
|
||||
Set<Module> providers = getAvailableProviders(dependentModule);
|
||||
|
||||
StartLog.debug("Module %s depends on %s provided by %s", module, dependentModule, providers);
|
||||
|
||||
// If there are no known providers of the module
|
||||
if (providers.isEmpty())
|
||||
{
|
||||
// look for a dynamic module
|
||||
if (dependsOn.contains("/"))
|
||||
if (dependentModule.contains("/"))
|
||||
{
|
||||
Path file = _baseHome.getPath("modules/" + dependentModule + ".mod");
|
||||
if (isRequired || Files.exists(file))
|
||||
{
|
||||
Path file = _baseHome.getPath("modules/" + dependsOn + ".mod");
|
||||
registerModule(file).expandDependencies(_args.getProperties());
|
||||
providers = _provided.get(dependsOn);
|
||||
providers = _provided.get(dependentModule);
|
||||
if (providers == null || providers.isEmpty())
|
||||
throw new UsageException("Module %s does not provide %s", _baseHome.toShortForm(file), dependsOn);
|
||||
throw new UsageException("Module %s does not provide %s", _baseHome.toShortForm(file), dependentModule);
|
||||
|
||||
enable(newlyEnabled, providers.stream().findFirst().get(), "dynamic dependency of " + module.getName(), true);
|
||||
continue;
|
||||
}
|
||||
throw new UsageException("No module found to provide %s for %s", dependsOn, module);
|
||||
}
|
||||
// is this a non-required module
|
||||
if (!isRequired)
|
||||
{
|
||||
StartLog.debug("Skipping non-required module [%s]: doesn't exist", dependentModule);
|
||||
continue;
|
||||
}
|
||||
// throw an exception (not a dynamic module and a required dependency)
|
||||
throw new UsageException("No module found to provide %s for %s", dependentModule, module);
|
||||
}
|
||||
|
||||
// If a provider is already enabled, then add a transitive enable
|
||||
if (providers.stream().filter(Module::isEnabled).count() > 0)
|
||||
providers.stream().filter(m -> m.isEnabled() && !m.equals(module)).forEach(m -> enable(newlyEnabled, m, "transitive provider of " + dependsOn + " for " + module.getName(), true));
|
||||
if (providers.stream().anyMatch(Module::isEnabled))
|
||||
providers.stream().filter(m -> m.isEnabled() && !m.equals(module)).forEach(m -> enable(newlyEnabled, m, "transitive provider of " + dependentModule + " for " + module.getName(), true));
|
||||
else
|
||||
{
|
||||
// Is there an obvious default?
|
||||
Optional<Module> dftProvider = (providers.size() == 1)
|
||||
? providers.stream().findFirst()
|
||||
: providers.stream().filter(m -> m.getName().equals(dependsOn)).findFirst();
|
||||
: providers.stream().filter(m -> m.getName().equals(dependentModule)).findFirst();
|
||||
|
||||
if (dftProvider.isPresent())
|
||||
enable(newlyEnabled, dftProvider.get(), "transitive provider of " + dependsOn + " for " + module.getName(), true);
|
||||
enable(newlyEnabled, dftProvider.get(), "transitive provider of " + dependentModule + " for " + module.getName(), true);
|
||||
else if (StartLog.isDebugEnabled())
|
||||
StartLog.debug("Module %s requires a %s implementation from one of %s", module, dependsOn, providers);
|
||||
StartLog.debug("Module %s requires a %s implementation from one of %s", module, dependentModule, providers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,6 +36,8 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
|||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
|
||||
@ExtendWith(WorkDirExtension.class)
|
||||
public class ModulesTest
|
||||
|
@ -202,6 +204,152 @@ public class ModulesTest
|
|||
assertThat("Resolved XMLs: " + actualXmls, actualXmls, contains(expectedXmls.toArray()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResolveNotRequiredModuleNotFound() throws IOException
|
||||
{
|
||||
// Test Env
|
||||
File homeDir = MavenTestingUtils.getTestResourceDir("non-required-deps");
|
||||
File baseDir = testdir.getEmptyPathDir().toFile();
|
||||
String[] cmdLine = new String[]{"bar.type=cannot-find-me"};
|
||||
|
||||
// Configuration
|
||||
CommandLineConfigSource cmdLineSource = new CommandLineConfigSource(cmdLine);
|
||||
ConfigSources config = new ConfigSources();
|
||||
config.add(cmdLineSource);
|
||||
config.add(new JettyHomeConfigSource(homeDir.toPath()));
|
||||
config.add(new JettyBaseConfigSource(baseDir.toPath()));
|
||||
|
||||
// Initialize
|
||||
BaseHome basehome = new BaseHome(config);
|
||||
|
||||
StartArgs args = new StartArgs(basehome);
|
||||
args.parse(config);
|
||||
|
||||
// Test Modules
|
||||
Modules modules = new Modules(basehome, args);
|
||||
modules.registerAll();
|
||||
|
||||
// Enable module
|
||||
modules.enable("bar", TEST_SOURCE);
|
||||
|
||||
// Collect active module list
|
||||
List<Module> active = modules.getEnabled();
|
||||
|
||||
// Assert names are correct, and in the right order
|
||||
List<String> expectedNames = new ArrayList<>();
|
||||
expectedNames.add("foo");
|
||||
expectedNames.add("bar");
|
||||
|
||||
List<String> actualNames = new ArrayList<>();
|
||||
for (Module actual : active)
|
||||
{
|
||||
actualNames.add(actual.getName());
|
||||
}
|
||||
|
||||
assertThat("Resolved Names: " + actualNames, actualNames, contains(expectedNames.toArray()));
|
||||
|
||||
Props props = args.getProperties();
|
||||
assertThat(props.getString("bar.name"), is(nullValue()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResolveNotRequiredModuleFound() throws IOException
|
||||
{
|
||||
// Test Env
|
||||
File homeDir = MavenTestingUtils.getTestResourceDir("non-required-deps");
|
||||
File baseDir = testdir.getEmptyPathDir().toFile();
|
||||
String[] cmdLine = new String[]{"bar.type=dive"};
|
||||
|
||||
// Configuration
|
||||
CommandLineConfigSource cmdLineSource = new CommandLineConfigSource(cmdLine);
|
||||
ConfigSources config = new ConfigSources();
|
||||
config.add(cmdLineSource);
|
||||
config.add(new JettyHomeConfigSource(homeDir.toPath()));
|
||||
config.add(new JettyBaseConfigSource(baseDir.toPath()));
|
||||
|
||||
// Initialize
|
||||
BaseHome basehome = new BaseHome(config);
|
||||
|
||||
StartArgs args = new StartArgs(basehome);
|
||||
args.parse(config);
|
||||
|
||||
// Test Modules
|
||||
Modules modules = new Modules(basehome, args);
|
||||
modules.registerAll();
|
||||
|
||||
// Enable module
|
||||
modules.enable("bar", TEST_SOURCE);
|
||||
|
||||
// Collect active module list
|
||||
List<Module> active = modules.getEnabled();
|
||||
|
||||
// Assert names are correct, and in the right order
|
||||
List<String> expectedNames = new ArrayList<>();
|
||||
expectedNames.add("foo");
|
||||
expectedNames.add("bar");
|
||||
expectedNames.add("bar-dive");
|
||||
|
||||
List<String> actualNames = new ArrayList<>();
|
||||
for (Module actual : active)
|
||||
{
|
||||
actualNames.add(actual.getName());
|
||||
}
|
||||
|
||||
assertThat("Resolved Names: " + actualNames, actualNames, contains(expectedNames.toArray()));
|
||||
|
||||
Props props = args.getProperties();
|
||||
assertThat(props.getString("bar.name"), is("dive"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResolveNotRequiredModuleFoundDynamic() throws IOException
|
||||
{
|
||||
// Test Env
|
||||
File homeDir = MavenTestingUtils.getTestResourceDir("non-required-deps");
|
||||
File baseDir = testdir.getEmptyPathDir().toFile();
|
||||
String[] cmdLine = new String[]{"bar.type=dynamic"};
|
||||
|
||||
// Configuration
|
||||
CommandLineConfigSource cmdLineSource = new CommandLineConfigSource(cmdLine);
|
||||
ConfigSources config = new ConfigSources();
|
||||
config.add(cmdLineSource);
|
||||
config.add(new JettyHomeConfigSource(homeDir.toPath()));
|
||||
config.add(new JettyBaseConfigSource(baseDir.toPath()));
|
||||
|
||||
// Initialize
|
||||
BaseHome basehome = new BaseHome(config);
|
||||
|
||||
StartArgs args = new StartArgs(basehome);
|
||||
args.parse(config);
|
||||
|
||||
// Test Modules
|
||||
Modules modules = new Modules(basehome, args);
|
||||
modules.registerAll();
|
||||
|
||||
// Enable module
|
||||
modules.enable("bar", TEST_SOURCE);
|
||||
|
||||
// Collect active module list
|
||||
List<Module> active = modules.getEnabled();
|
||||
|
||||
// Assert names are correct, and in the right order
|
||||
List<String> expectedNames = new ArrayList<>();
|
||||
expectedNames.add("foo");
|
||||
expectedNames.add("bar");
|
||||
expectedNames.add("impls/bar-dynamic");
|
||||
|
||||
List<String> actualNames = new ArrayList<>();
|
||||
for (Module actual : active)
|
||||
{
|
||||
actualNames.add(actual.getName());
|
||||
}
|
||||
|
||||
assertThat("Resolved Names: " + actualNames, actualNames, contains(expectedNames.toArray()));
|
||||
|
||||
Props props = args.getProperties();
|
||||
assertThat(props.getString("bar.name"), is("dynamic"));
|
||||
}
|
||||
|
||||
private List<String> normalizeLibs(List<Module> active)
|
||||
{
|
||||
List<String> libs = new ArrayList<>();
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
[ini]
|
||||
bar.name=dive
|
|
@ -0,0 +1,6 @@
|
|||
# Top level mod
|
||||
|
||||
[depends]
|
||||
foo
|
||||
?bar-${bar.type}
|
||||
?impls/bar-${bar.type}
|
|
@ -0,0 +1 @@
|
|||
# nothing here
|
|
@ -0,0 +1,2 @@
|
|||
[ini]
|
||||
bar.name=dynamic
|
|
@ -239,7 +239,7 @@ public class DateCache
|
|||
// recheck the tick, to save multiple formats
|
||||
if (tick == null || tick._seconds != seconds)
|
||||
{
|
||||
String s = ZonedDateTime.ofInstant(Instant.now(), _zoneId).format(_tzFormat);
|
||||
String s = ZonedDateTime.ofInstant(Instant.ofEpochMilli(now), _zoneId).format(_tzFormat);
|
||||
_tick = new Tick(seconds, s);
|
||||
tick = _tick;
|
||||
}
|
||||
|
|
|
@ -46,8 +46,25 @@ public class DecoratedObjectFactory implements Iterable<Decorator>
|
|||
*/
|
||||
public static final String ATTR = DecoratedObjectFactory.class.getName();
|
||||
|
||||
private static final ThreadLocal<Object> decoratorInfo = new ThreadLocal<>();
|
||||
|
||||
private List<Decorator> decorators = new ArrayList<>();
|
||||
|
||||
public static void associateInfo(Object info)
|
||||
{
|
||||
decoratorInfo.set(info);
|
||||
}
|
||||
|
||||
public static void disassociateInfo()
|
||||
{
|
||||
decoratorInfo.set(null);
|
||||
}
|
||||
|
||||
public static Object getAssociatedInfo()
|
||||
{
|
||||
return decoratorInfo.get();
|
||||
}
|
||||
|
||||
public void addDecorator(Decorator decorator)
|
||||
{
|
||||
LOG.debug("Adding Decorator: {}", decorator);
|
||||
|
@ -70,7 +87,7 @@ public class DecoratedObjectFactory implements Iterable<Decorator>
|
|||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
LOG.debug("Creating Instance: " + clazz);
|
||||
LOG.debug("Creating Instance: {}", clazz);
|
||||
}
|
||||
T o = clazz.getDeclaredConstructor().newInstance();
|
||||
return decorate(o);
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
package org.eclipse.jetty.util;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
@ -145,10 +146,7 @@ public class IncludeExcludeSet<T, P> implements Predicate<P>
|
|||
|
||||
public void include(T... element)
|
||||
{
|
||||
for (T e : element)
|
||||
{
|
||||
_includes.add(e);
|
||||
}
|
||||
_includes.addAll(Arrays.asList(element));
|
||||
}
|
||||
|
||||
public void exclude(T element)
|
||||
|
@ -158,10 +156,7 @@ public class IncludeExcludeSet<T, P> implements Predicate<P>
|
|||
|
||||
public void exclude(T... element)
|
||||
{
|
||||
for (T e : element)
|
||||
{
|
||||
_excludes.add(e);
|
||||
}
|
||||
_excludes.addAll(Arrays.asList(element));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -233,34 +228,4 @@ public class IncludeExcludeSet<T, P> implements Predicate<P>
|
|||
{
|
||||
return _includes.isEmpty() && _excludes.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Match items in combined IncludeExcludeSets.
|
||||
* @param item1 The item to match against set1
|
||||
* @param set1 A IncludeExcludeSet to match item1 against
|
||||
* @param item2 The item to match against set2
|
||||
* @param set2 A IncludeExcludeSet to match item2 against
|
||||
* @param <T1> The type of item1
|
||||
* @param <T2> The type of item2
|
||||
* @return True IFF <ul>
|
||||
* <li>Neither item is excluded from their respective sets</li>
|
||||
* <li>Both sets have no includes OR at least one of the items is included in its respective set</li>
|
||||
* </ul>
|
||||
*/
|
||||
public static <T1,T2> boolean matchCombined(T1 item1, IncludeExcludeSet<?,T1> set1, T2 item2, IncludeExcludeSet<?,T2> set2)
|
||||
{
|
||||
Boolean match1 = set1.isIncludedAndNotExcluded(item1);
|
||||
Boolean match2 = set2.isIncludedAndNotExcluded(item2);
|
||||
|
||||
// if we are excluded from either set, then we do not match
|
||||
if (match1 == Boolean.FALSE || match2 == Boolean.FALSE)
|
||||
return false;
|
||||
|
||||
// If either set has any includes, then we must be included by one of them
|
||||
if (set1.hasIncludes() || set2.hasIncludes())
|
||||
return match1 == Boolean.TRUE || match2 == Boolean.TRUE;
|
||||
|
||||
// If not excluded and no includes, then we match
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,282 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under
|
||||
// the terms of the Eclipse Public License 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// This Source Code may also be made available under the following
|
||||
// Secondary Licenses when the conditions for such availability set
|
||||
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||
// the Apache License v2.0 which is available at
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.util;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* A pattern representing a single or range of {@link InetAddress}. To create a pattern use
|
||||
* the {@link InetAddressPattern#from(String)} method, which will create a pattern given a
|
||||
* string conforming to one of the following formats.
|
||||
*
|
||||
* <dl>
|
||||
* <dt>InetAddress</dt>
|
||||
* <dd>A single InetAddress either in hostname or address format.
|
||||
* All formats supported by {@link InetAddress} are accepted. Not ethat using hostname
|
||||
* matches may force domain lookups. eg. "[::1]", "1.2.3.4", "::ffff:127.0.0.1"</dd>
|
||||
* <dt>InetAddress/CIDR</dt>
|
||||
* <dd>An InetAddress with a integer number of bits to indicate
|
||||
* the significant prefix. eg. "192.168.0.0/16" will match from "192.168.0.0" to
|
||||
* "192.168.255.255" </dd>
|
||||
* <dt>InetAddress-InetAddress</dt>
|
||||
* <dd>An inclusive range of InetAddresses.
|
||||
* eg. "[a000::1]-[afff::]", "192.168.128.0-192.168.128.255"</dd>
|
||||
* <dt>Legacy format</dt>
|
||||
* <dd>The legacy format used for IPv4 only.
|
||||
* eg. "10.10.10-14.0-128"</dd>
|
||||
* </dl>
|
||||
*/
|
||||
public abstract class InetAddressPattern implements Predicate<InetAddress>
|
||||
{
|
||||
protected final String _pattern;
|
||||
|
||||
public static InetAddressPattern from(String pattern)
|
||||
{
|
||||
if (pattern == null)
|
||||
return null;
|
||||
|
||||
int slash = pattern.lastIndexOf('/');
|
||||
int dash = pattern.lastIndexOf('-');
|
||||
try
|
||||
{
|
||||
if (slash >= 0)
|
||||
return new CidrInetAddressRange(pattern, InetAddress.getByName(pattern.substring(0, slash).trim()), StringUtil.toInt(pattern, slash + 1));
|
||||
|
||||
if (dash >= 0)
|
||||
return new MinMaxInetAddressRange(pattern, InetAddress.getByName(pattern.substring(0, dash).trim()), InetAddress.getByName(pattern.substring(dash + 1).trim()));
|
||||
|
||||
return new SingletonInetAddressRange(pattern, InetAddress.getByName(pattern));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (slash < 0 && dash > 0)
|
||||
return new LegacyInetAddressRange(pattern);
|
||||
}
|
||||
catch (Exception ex2)
|
||||
{
|
||||
e.addSuppressed(ex2);
|
||||
}
|
||||
throw new IllegalArgumentException("Bad pattern: " + pattern, e);
|
||||
}
|
||||
}
|
||||
|
||||
public InetAddressPattern(String pattern)
|
||||
{
|
||||
_pattern = pattern;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return _pattern;
|
||||
}
|
||||
|
||||
static class SingletonInetAddressRange extends InetAddressPattern
|
||||
{
|
||||
final InetAddress _address;
|
||||
|
||||
public SingletonInetAddressRange(String pattern, InetAddress address)
|
||||
{
|
||||
super(pattern);
|
||||
_address = address;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(InetAddress address)
|
||||
{
|
||||
return _address.equals(address);
|
||||
}
|
||||
}
|
||||
|
||||
static class MinMaxInetAddressRange extends InetAddressPattern
|
||||
{
|
||||
final int[] _min;
|
||||
final int[] _max;
|
||||
|
||||
public MinMaxInetAddressRange(String pattern, InetAddress min, InetAddress max)
|
||||
{
|
||||
super(pattern);
|
||||
|
||||
byte[] rawMin = min.getAddress();
|
||||
byte[] rawMax = max.getAddress();
|
||||
if (rawMin.length != rawMax.length)
|
||||
throw new IllegalArgumentException("Cannot mix IPv4 and IPv6: " + pattern);
|
||||
|
||||
if (rawMin.length == 4)
|
||||
{
|
||||
// there must be 6 '.' or this is likely to be a legacy pattern
|
||||
int count = 0;
|
||||
for (char c : pattern.toCharArray())
|
||||
{
|
||||
if (c == '.')
|
||||
count++;
|
||||
}
|
||||
if (count != 6)
|
||||
throw new IllegalArgumentException("Legacy pattern: " + pattern);
|
||||
}
|
||||
|
||||
_min = new int[rawMin.length];
|
||||
_max = new int[rawMin.length];
|
||||
|
||||
for (int i = 0; i < _min.length; i++)
|
||||
{
|
||||
_min[i] = 0xff & rawMin[i];
|
||||
_max[i] = 0xff & rawMax[i];
|
||||
}
|
||||
|
||||
for (int i = 0; i < _min.length; i++)
|
||||
{
|
||||
if (_min[i] > _max[i])
|
||||
throw new IllegalArgumentException("min is greater than max: " + pattern);
|
||||
if (_min[i] < _max[i])
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(InetAddress address)
|
||||
{
|
||||
byte[] raw = address.getAddress();
|
||||
if (raw.length != _min.length)
|
||||
return false;
|
||||
|
||||
boolean minOk = false;
|
||||
boolean maxOk = false;
|
||||
|
||||
for (int i = 0; i < _min.length; i++)
|
||||
{
|
||||
int r = 0xff & raw[i];
|
||||
if (!minOk)
|
||||
{
|
||||
if (r < _min[i])
|
||||
return false;
|
||||
if (r > _min[i])
|
||||
minOk = true;
|
||||
}
|
||||
if (!maxOk)
|
||||
{
|
||||
if (r > _max[i])
|
||||
return false;
|
||||
if (r < _max[i])
|
||||
maxOk = true;
|
||||
}
|
||||
|
||||
if (minOk && maxOk)
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static class CidrInetAddressRange extends InetAddressPattern
|
||||
{
|
||||
final byte[] _raw;
|
||||
final int _octets;
|
||||
final int _mask;
|
||||
final int _masked;
|
||||
|
||||
public CidrInetAddressRange(String pattern, InetAddress address, int cidr)
|
||||
{
|
||||
super(pattern);
|
||||
_raw = address.getAddress();
|
||||
_octets = cidr / 8;
|
||||
_mask = 0xff & (0xff << (8 - cidr % 8));
|
||||
_masked = _mask == 0 ? 0 : _raw[_octets] & _mask;
|
||||
|
||||
if (cidr > (_raw.length * 8))
|
||||
throw new IllegalArgumentException("CIDR too large: " + pattern);
|
||||
|
||||
if (_mask != 0 && (0xff & _raw[_octets]) != _masked)
|
||||
throw new IllegalArgumentException("CIDR bits non zero: " + pattern);
|
||||
|
||||
for (int o = _octets + (_mask == 0 ? 0 : 1); o < _raw.length; o++)
|
||||
{
|
||||
if (_raw[o] != 0)
|
||||
throw new IllegalArgumentException("CIDR bits non zero: " + pattern);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(InetAddress address)
|
||||
{
|
||||
byte[] raw = address.getAddress();
|
||||
if (raw.length != _raw.length)
|
||||
return false;
|
||||
|
||||
for (int o = 0; o < _octets; o++)
|
||||
{
|
||||
if (_raw[o] != raw[o])
|
||||
return false;
|
||||
}
|
||||
|
||||
return _mask == 0 || (raw[_octets] & _mask) == _masked;
|
||||
}
|
||||
}
|
||||
|
||||
static class LegacyInetAddressRange extends InetAddressPattern
|
||||
{
|
||||
int[] _min = new int[4];
|
||||
int[] _max = new int[4];
|
||||
|
||||
public LegacyInetAddressRange(String pattern)
|
||||
{
|
||||
super(pattern);
|
||||
|
||||
String[] parts = pattern.split("\\.");
|
||||
if (parts.length != 4)
|
||||
throw new IllegalArgumentException("Bad legacy pattern: " + pattern);
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
String part = parts[i].trim();
|
||||
int dash = part.indexOf('-');
|
||||
if (dash < 0)
|
||||
_min[i] = _max[i] = Integer.parseInt(part);
|
||||
else
|
||||
{
|
||||
_min[i] = (dash == 0) ? 0 : StringUtil.toInt(part, 0);
|
||||
_max[i] = (dash == part.length() - 1) ? 255 : StringUtil.toInt(part, dash + 1);
|
||||
}
|
||||
|
||||
if (_min[i] < 0 || _min[i] > _max[i] || _max[i] > 255)
|
||||
throw new IllegalArgumentException("Bad legacy pattern: " + pattern);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(InetAddress address)
|
||||
{
|
||||
byte[] raw = address.getAddress();
|
||||
if (raw.length != 4)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
if ((0xff & raw[i]) < _min[i] || (0xff & raw[i]) > _max[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -30,62 +30,20 @@ import java.util.function.Predicate;
|
|||
* A set of InetAddress patterns.
|
||||
* <p>This is a {@link Set} of String patterns that are used to match
|
||||
* a {@link Predicate} over InetAddress for containment semantics.
|
||||
* The patterns that may be set are:
|
||||
* The patterns that may be set are defined in {@link InetAddressPattern}.
|
||||
* </p>
|
||||
* <dl>
|
||||
* <dt>InetAddress</dt><dd>A single InetAddress either in hostname or address format.
|
||||
* All formats supported by {@link InetAddress} are accepted. Not ethat using hostname
|
||||
* matches may force domain lookups. eg. "[::1]", "1.2.3.4", "::ffff:127.0.0.1"</dd>
|
||||
* <dt>InetAddress/CIDR</dt><dd>An InetAddress with a integer number of bits to indicate
|
||||
* the significant prefix. eg. "192.168.0.0/16" will match from "192.168.0.0" to
|
||||
* "192.168.255.255" </dd>
|
||||
* <dt>InetAddress-InetAddress</dt><dd>An inclusive range of InetAddresses.
|
||||
* eg. "[a000::1]-[afff::]", "192.168.128.0-192.168.128.255"</dd>
|
||||
* </dl>
|
||||
* <p>This class is designed to work with {@link IncludeExcludeSet}</p>
|
||||
*
|
||||
* @see IncludeExcludeSet
|
||||
*/
|
||||
public class InetAddressSet extends AbstractSet<String> implements Set<String>, Predicate<InetAddress>
|
||||
{
|
||||
private Map<String, InetPattern> _patterns = new HashMap<>();
|
||||
private Map<String, InetAddressPattern> _patterns = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public boolean add(String pattern)
|
||||
{
|
||||
return _patterns.put(pattern, newInetRange(pattern)) == null;
|
||||
}
|
||||
|
||||
private InetPattern newInetRange(String pattern)
|
||||
{
|
||||
if (pattern == null)
|
||||
return null;
|
||||
|
||||
int slash = pattern.lastIndexOf('/');
|
||||
int dash = pattern.lastIndexOf('-');
|
||||
try
|
||||
{
|
||||
if (slash >= 0)
|
||||
return new CidrInetRange(pattern, InetAddress.getByName(pattern.substring(0, slash).trim()), StringUtil.toInt(pattern, slash + 1));
|
||||
|
||||
if (dash >= 0)
|
||||
return new MinMaxInetRange(pattern, InetAddress.getByName(pattern.substring(0, dash).trim()), InetAddress.getByName(pattern.substring(dash + 1).trim()));
|
||||
|
||||
return new SingletonInetRange(pattern, InetAddress.getByName(pattern));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (slash < 0 && dash > 0)
|
||||
return new LegacyInetRange(pattern);
|
||||
}
|
||||
catch (Exception e2)
|
||||
{
|
||||
e.addSuppressed(e2);
|
||||
}
|
||||
throw new IllegalArgumentException("Bad pattern: " + pattern, e);
|
||||
}
|
||||
return _patterns.put(pattern, InetAddressPattern.from(pattern)) == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -111,219 +69,11 @@ public class InetAddressSet extends AbstractSet<String> implements Set<String>,
|
|||
{
|
||||
if (address == null)
|
||||
return false;
|
||||
byte[] raw = address.getAddress();
|
||||
for (InetPattern pattern : _patterns.values())
|
||||
for (InetAddressPattern pattern : _patterns.values())
|
||||
{
|
||||
if (pattern.test(address, raw))
|
||||
if (pattern.test(address))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
abstract static class InetPattern
|
||||
{
|
||||
final String _pattern;
|
||||
|
||||
InetPattern(String pattern)
|
||||
{
|
||||
_pattern = pattern;
|
||||
}
|
||||
|
||||
abstract boolean test(InetAddress address, byte[] raw);
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return _pattern;
|
||||
}
|
||||
}
|
||||
|
||||
static class SingletonInetRange extends InetPattern
|
||||
{
|
||||
final InetAddress _address;
|
||||
|
||||
public SingletonInetRange(String pattern, InetAddress address)
|
||||
{
|
||||
super(pattern);
|
||||
_address = address;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(InetAddress address, byte[] raw)
|
||||
{
|
||||
return _address.equals(address);
|
||||
}
|
||||
}
|
||||
|
||||
static class MinMaxInetRange extends InetPattern
|
||||
{
|
||||
final int[] _min;
|
||||
final int[] _max;
|
||||
|
||||
public MinMaxInetRange(String pattern, InetAddress min, InetAddress max)
|
||||
{
|
||||
super(pattern);
|
||||
|
||||
byte[] rawMin = min.getAddress();
|
||||
byte[] rawMax = max.getAddress();
|
||||
if (rawMin.length != rawMax.length)
|
||||
throw new IllegalArgumentException("Cannot mix IPv4 and IPv6: " + pattern);
|
||||
|
||||
if (rawMin.length == 4)
|
||||
{
|
||||
// there must be 6 '.' or this is likely to be a legacy pattern
|
||||
int count = 0;
|
||||
for (char c : pattern.toCharArray())
|
||||
{
|
||||
if (c == '.')
|
||||
count++;
|
||||
}
|
||||
if (count != 6)
|
||||
throw new IllegalArgumentException("Legacy pattern: " + pattern);
|
||||
}
|
||||
|
||||
_min = new int[rawMin.length];
|
||||
_max = new int[rawMin.length];
|
||||
|
||||
for (int i = 0; i < _min.length; i++)
|
||||
{
|
||||
_min[i] = 0xff & rawMin[i];
|
||||
_max[i] = 0xff & rawMax[i];
|
||||
}
|
||||
|
||||
for (int i = 0; i < _min.length; i++)
|
||||
{
|
||||
if (_min[i] > _max[i])
|
||||
throw new IllegalArgumentException("min is greater than max: " + pattern);
|
||||
if (_min[i] < _max[i])
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(InetAddress item, byte[] raw)
|
||||
{
|
||||
if (raw.length != _min.length)
|
||||
return false;
|
||||
|
||||
boolean minOk = false;
|
||||
boolean maxOk = false;
|
||||
|
||||
for (int i = 0; i < _min.length; i++)
|
||||
{
|
||||
int r = 0xff & raw[i];
|
||||
if (!minOk)
|
||||
{
|
||||
if (r < _min[i])
|
||||
return false;
|
||||
if (r > _min[i])
|
||||
minOk = true;
|
||||
}
|
||||
if (!maxOk)
|
||||
{
|
||||
if (r > _max[i])
|
||||
return false;
|
||||
if (r < _max[i])
|
||||
maxOk = true;
|
||||
}
|
||||
|
||||
if (minOk && maxOk)
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static class CidrInetRange extends InetPattern
|
||||
{
|
||||
final byte[] _raw;
|
||||
final int _octets;
|
||||
final int _mask;
|
||||
final int _masked;
|
||||
|
||||
public CidrInetRange(String pattern, InetAddress address, int cidr)
|
||||
{
|
||||
super(pattern);
|
||||
_raw = address.getAddress();
|
||||
_octets = cidr / 8;
|
||||
_mask = 0xff & (0xff << (8 - cidr % 8));
|
||||
_masked = _mask == 0 ? 0 : _raw[_octets] & _mask;
|
||||
|
||||
if (cidr > (_raw.length * 8))
|
||||
throw new IllegalArgumentException("CIDR too large: " + pattern);
|
||||
|
||||
if (_mask != 0 && (0xff & _raw[_octets]) != _masked)
|
||||
throw new IllegalArgumentException("CIDR bits non zero: " + pattern);
|
||||
|
||||
for (int o = _octets + (_mask == 0 ? 0 : 1); o < _raw.length; o++)
|
||||
{
|
||||
if (_raw[o] != 0)
|
||||
throw new IllegalArgumentException("CIDR bits non zero: " + pattern);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(InetAddress item, byte[] raw)
|
||||
{
|
||||
if (raw.length != _raw.length)
|
||||
return false;
|
||||
|
||||
for (int o = 0; o < _octets; o++)
|
||||
{
|
||||
if (_raw[o] != raw[o])
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_mask != 0 && (raw[_octets] & _mask) != _masked)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static class LegacyInetRange extends InetPattern
|
||||
{
|
||||
int[] _min = new int[4];
|
||||
int[] _max = new int[4];
|
||||
|
||||
public LegacyInetRange(String pattern)
|
||||
{
|
||||
super(pattern);
|
||||
|
||||
String[] parts = pattern.split("\\.");
|
||||
if (parts.length != 4)
|
||||
throw new IllegalArgumentException("Bad legacy pattern: " + pattern);
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
String part = parts[i].trim();
|
||||
int dash = part.indexOf('-');
|
||||
if (dash < 0)
|
||||
_min[i] = _max[i] = Integer.parseInt(part);
|
||||
else
|
||||
{
|
||||
_min[i] = (dash == 0) ? 0 : StringUtil.toInt(part, 0);
|
||||
_max[i] = (dash == part.length() - 1) ? 255 : StringUtil.toInt(part, dash + 1);
|
||||
}
|
||||
|
||||
if (_min[i] < 0 || _min[i] > _max[i] || _max[i] > 255)
|
||||
throw new IllegalArgumentException("Bad legacy pattern: " + pattern);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(InetAddress item, byte[] raw)
|
||||
{
|
||||
if (raw.length != 4)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
if ((0xff & raw[i]) < _min[i] || (0xff & raw[i]) > _max[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,14 +63,13 @@ import org.eclipse.jetty.util.Loader;
|
|||
*/
|
||||
public class JavaUtilLog extends AbstractLogger
|
||||
{
|
||||
private static final String THIS_CLASS = JavaUtilLog.class.getName();
|
||||
private static final boolean __source =
|
||||
Boolean.parseBoolean(Log.__props.getProperty("org.eclipse.jetty.util.log.SOURCE",
|
||||
Log.__props.getProperty("org.eclipse.jetty.util.log.javautil.SOURCE", "true")));
|
||||
private static final boolean SOURCE =
|
||||
Boolean.parseBoolean(Log.getProperty("org.eclipse.jetty.util.log.SOURCE",
|
||||
Log.getProperty("org.eclipse.jetty.util.log.javautil.SOURCE", "true")));
|
||||
|
||||
private static boolean _initialized = false;
|
||||
private static boolean __initialized = false;
|
||||
|
||||
private Level configuredLevel;
|
||||
private Level _configuredLevel;
|
||||
private java.util.logging.Logger _logger;
|
||||
|
||||
public JavaUtilLog()
|
||||
|
@ -82,11 +81,11 @@ public class JavaUtilLog extends AbstractLogger
|
|||
{
|
||||
synchronized (JavaUtilLog.class)
|
||||
{
|
||||
if (!_initialized)
|
||||
if (!__initialized)
|
||||
{
|
||||
_initialized = true;
|
||||
__initialized = true;
|
||||
|
||||
final String properties = Log.__props.getProperty("org.eclipse.jetty.util.log.javautil.PROPERTIES", null);
|
||||
final String properties = Log.getProperty("org.eclipse.jetty.util.log.javautil.PROPERTIES", null);
|
||||
if (properties != null)
|
||||
{
|
||||
AccessController.doPrivileged(new PrivilegedAction<Object>()
|
||||
|
@ -115,7 +114,7 @@ public class JavaUtilLog extends AbstractLogger
|
|||
|
||||
_logger = java.util.logging.Logger.getLogger(name);
|
||||
|
||||
switch (lookupLoggingLevel(Log.__props, name))
|
||||
switch (lookupLoggingLevel(Log.getProperties(), name))
|
||||
{
|
||||
case LEVEL_ALL:
|
||||
_logger.setLevel(Level.ALL);
|
||||
|
@ -137,7 +136,7 @@ public class JavaUtilLog extends AbstractLogger
|
|||
break;
|
||||
}
|
||||
|
||||
configuredLevel = _logger.getLevel();
|
||||
_configuredLevel = _logger.getLevel();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -152,13 +151,13 @@ public class JavaUtilLog extends AbstractLogger
|
|||
if (thrown != null)
|
||||
record.setThrown(thrown);
|
||||
record.setLoggerName(_logger.getName());
|
||||
if (__source)
|
||||
if (SOURCE)
|
||||
{
|
||||
StackTraceElement[] stack = new Throwable().getStackTrace();
|
||||
for (int i = 0; i < stack.length; i++)
|
||||
{
|
||||
StackTraceElement e = stack[i];
|
||||
if (!e.getClassName().equals(THIS_CLASS))
|
||||
if (!e.getClassName().equals(JavaUtilLog.class.getName()))
|
||||
{
|
||||
record.setSourceClassName(e.getClassName());
|
||||
record.setSourceMethodName(e.getMethodName());
|
||||
|
@ -222,12 +221,12 @@ public class JavaUtilLog extends AbstractLogger
|
|||
{
|
||||
if (enabled)
|
||||
{
|
||||
configuredLevel = _logger.getLevel();
|
||||
_configuredLevel = _logger.getLevel();
|
||||
_logger.setLevel(Level.FINE);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.setLevel(configuredLevel);
|
||||
_logger.setLevel(_configuredLevel);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue