Merge branch 'jetty-9.4.x' of https://github.com/eclipse/jetty.project into issues/644
This commit is contained in:
commit
ba8c9ed178
14
VERSION.txt
14
VERSION.txt
|
@ -225,6 +225,20 @@ jetty-9.4.0.M0 - 03 June 2016
|
|||
+ 609 ignore failing test
|
||||
+ 610 Ignore failing test
|
||||
|
||||
jetty-9.3.13.M0 - 30 September 2016
|
||||
+ 277 Proxy servlet does not handle HTTP status 100 correctly
|
||||
+ 870 TLS protocol exclusion broken for SslContextFactory(String)
|
||||
+ 915 The jetty-maven-plugin:stop goal doesn't stop everything completely
|
||||
+ 918 Support certificates hot reload
|
||||
+ 930 Add module instructions to SSL section
|
||||
+ 943 Docs: Error in 'Embedding Jetty' page - example 'FileServer'
|
||||
+ 948 9.4.0.RC0 jetty-distribution invalid config etc/jetty-http2c.xml
|
||||
+ 955 Response listeners not invoked when using Connection.send()
|
||||
+ 959 CompleteListener invoked twice for HTTP/2 transport and response content
|
||||
+ 960 Async I/O spin when reading early EOF
|
||||
+ 965 Link from High Load docs to Garbage Collection Tuning is broken
|
||||
+ 966 Remove usages of ConcurrentArrayQueue
|
||||
|
||||
jetty-9.3.12.v20160915 - 15 September 2016
|
||||
+ 56 Fix authn issues in LdapLoginModule
|
||||
+ 131 Improve Connector Statistic names and values
|
||||
|
|
|
@ -21,7 +21,6 @@ package org.eclipse.jetty.annotations;
|
|||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
@ -48,6 +47,7 @@ import org.eclipse.jetty.plus.annotation.ContainerInitializer;
|
|||
import org.eclipse.jetty.util.ConcurrentHashSet;
|
||||
import org.eclipse.jetty.util.MultiException;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.TypeUtil;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
|
@ -81,8 +81,6 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
protected List<ContainerInitializerAnnotationHandler> _containerInitializerAnnotationHandlers = new ArrayList<ContainerInitializerAnnotationHandler>();
|
||||
|
||||
protected List<ParserTask> _parserTasks;
|
||||
protected WebAppClassNameResolver _webAppClassNameResolver;
|
||||
protected ContainerClassNameResolver _containerClassNameResolver;
|
||||
|
||||
protected CounterStatistic _containerPathStats;
|
||||
protected CounterStatistic _webInfLibStats;
|
||||
|
@ -138,15 +136,13 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
protected Exception _exception;
|
||||
protected final AnnotationParser _parser;
|
||||
protected final Set<? extends Handler> _handlers;
|
||||
protected final ClassNameResolver _resolver;
|
||||
protected final Resource _resource;
|
||||
protected TimeStatistic _stat;
|
||||
|
||||
public ParserTask (AnnotationParser parser, Set<? extends Handler>handlers, Resource resource, ClassNameResolver resolver)
|
||||
public ParserTask (AnnotationParser parser, Set<? extends Handler>handlers, Resource resource)
|
||||
{
|
||||
_parser = parser;
|
||||
_handlers = handlers;
|
||||
_resolver = resolver;
|
||||
_resource = resource;
|
||||
}
|
||||
|
||||
|
@ -160,7 +156,7 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
if (_stat != null)
|
||||
_stat.start();
|
||||
if (_parser != null)
|
||||
_parser.parse(_handlers, _resource, _resolver);
|
||||
_parser.parse(_handlers, _resource);
|
||||
if (_stat != null)
|
||||
_stat.end();
|
||||
return null;
|
||||
|
@ -174,83 +170,7 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
public Resource getResource()
|
||||
{
|
||||
return _resource;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* WebAppClassNameResolver
|
||||
*
|
||||
* Checks to see if a classname belongs to hidden or visible packages when scanning,
|
||||
* and whether a classname that is a duplicate should override a previously
|
||||
* scanned classname.
|
||||
*
|
||||
* This is analogous to the management of classes that the WebAppClassLoader is doing,
|
||||
* however we don't want to load the classes at this point so we are doing it on
|
||||
* the name only.
|
||||
*
|
||||
*/
|
||||
public class WebAppClassNameResolver implements ClassNameResolver
|
||||
{
|
||||
private WebAppContext _context;
|
||||
|
||||
public WebAppClassNameResolver (WebAppContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public boolean isExcluded (String name)
|
||||
{
|
||||
if (_context.isSystemClass(name)) return true;
|
||||
if (_context.isServerClass(name)) return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean shouldOverride (String name)
|
||||
{
|
||||
//looking at webapp classpath, found already-parsed class
|
||||
//of same name - did it come from system or duplicate in webapp?
|
||||
if (_context.isParentLoaderPriority())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ContainerClassNameResolver
|
||||
*
|
||||
* Checks to see if a classname belongs to a hidden or visible package
|
||||
* when scanning for annotations and thus whether it should be excluded from
|
||||
* consideration or not.
|
||||
*
|
||||
* This is analogous to the management of classes that the WebAppClassLoader is doing,
|
||||
* however we don't want to load the classes at this point so we are doing it on
|
||||
* the name only.
|
||||
*
|
||||
*/
|
||||
public class ContainerClassNameResolver implements ClassNameResolver
|
||||
{
|
||||
private WebAppContext _context;
|
||||
|
||||
public ContainerClassNameResolver (WebAppContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
public boolean isExcluded (String name)
|
||||
{
|
||||
if (_context.isSystemClass(name)) return false;
|
||||
if (_context.isServerClass(name)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean shouldOverride (String name)
|
||||
{
|
||||
//visiting the container classpath,
|
||||
if (_context.isParentLoaderPriority())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -390,8 +310,6 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
@Override
|
||||
public void preConfigure(final WebAppContext context) throws Exception
|
||||
{
|
||||
_webAppClassNameResolver = new WebAppClassNameResolver(context);
|
||||
_containerClassNameResolver = new ContainerClassNameResolver(context);
|
||||
String tmp = (String)context.getAttribute(SERVLET_CONTAINER_INITIALIZER_EXCLUSION_PATTERN);
|
||||
_sciExcludePattern = (tmp==null?null:Pattern.compile(tmp));
|
||||
}
|
||||
|
@ -445,15 +363,15 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
|
||||
if (!_discoverableAnnotationHandlers.isEmpty() || _classInheritanceHandler != null || !_containerInitializerAnnotationHandlers.isEmpty())
|
||||
scanForAnnotations(context);
|
||||
|
||||
|
||||
// Resolve container initializers
|
||||
List<ContainerInitializer> initializers =
|
||||
(List<ContainerInitializer>)context.getAttribute(AnnotationConfiguration.CONTAINER_INITIALIZERS);
|
||||
(List<ContainerInitializer>)context.getAttribute(AnnotationConfiguration.CONTAINER_INITIALIZERS);
|
||||
if (initializers != null && initializers.size()>0)
|
||||
{
|
||||
Map<String, Set<String>> map = ( Map<String, Set<String>>) context.getAttribute(AnnotationConfiguration.CLASS_INHERITANCE_MAP);
|
||||
for (ContainerInitializer i : initializers)
|
||||
i.resolveClasses(context,map);
|
||||
i.resolveClasses(context,map);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -723,23 +641,7 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
public Resource getJarFor (ServletContainerInitializer service)
|
||||
throws MalformedURLException, IOException
|
||||
{
|
||||
//try the thread context classloader to get the jar that loaded the class
|
||||
URL jarURL = Thread.currentThread().getContextClassLoader().getResource(service.getClass().getName().replace('.','/')+".class");
|
||||
|
||||
//if for some reason that failed (eg we're in osgi and the TCCL does not know about the service) try the classloader that
|
||||
//loaded the class
|
||||
if (jarURL == null)
|
||||
jarURL = service.getClass().getClassLoader().getResource(service.getClass().getName().replace('.','/')+".class");
|
||||
|
||||
String loadingJarName = jarURL.toString();
|
||||
|
||||
int i = loadingJarName.indexOf(".jar");
|
||||
if (i < 0)
|
||||
return null; //not from a jar
|
||||
|
||||
loadingJarName = loadingJarName.substring(0,i+4);
|
||||
loadingJarName = (loadingJarName.startsWith("jar:")?loadingJarName.substring(4):loadingJarName);
|
||||
return Resource.newResource(loadingJarName);
|
||||
return TypeUtil.getLoadedFrom(service.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -760,26 +662,41 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
if (context == null)
|
||||
throw new IllegalArgumentException("WebAppContext null");
|
||||
|
||||
if (LOG.isDebugEnabled()) LOG.debug("Checking {} for jar exclusion", sci);
|
||||
|
||||
|
||||
//A ServletContainerInitializer that came from the container's classpath cannot be excluded by an ordering
|
||||
//of WEB-INF/lib jars
|
||||
if (isFromContainerClassPath(context, sci))
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("!Excluded {} from container classpath", sci);
|
||||
return false;
|
||||
}
|
||||
|
||||
//If no ordering, nothing is excluded
|
||||
if (context.getMetaData().getOrdering() == null)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("!Excluded {} no ordering", sci);
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
List<Resource> orderedJars = context.getMetaData().getOrderedWebInfJars();
|
||||
|
||||
//there is an ordering, but there are no jars resulting from the ordering, everything excluded
|
||||
if (orderedJars.isEmpty())
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Excluded {} empty ordering", sci);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sciResource == null)
|
||||
return false; //not from a jar therefore not from WEB-INF so not excludable
|
||||
{
|
||||
//not from a jar therefore not from WEB-INF so not excludable
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("!Excluded {} not from jar", sci);
|
||||
return false;
|
||||
}
|
||||
|
||||
URI loadingJarURI = sciResource.getURI();
|
||||
boolean found = false;
|
||||
|
@ -790,10 +707,11 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
found = r.getURI().equals(loadingJarURI);
|
||||
}
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{}Excluded {} found={}",found?"!":"",sci,found);
|
||||
return !found;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test if the ServletContainerIntializer is excluded by the
|
||||
* o.e.j.containerInitializerExclusionPattern
|
||||
|
@ -808,7 +726,8 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
return false;
|
||||
|
||||
//test if name of class matches the regex
|
||||
if (LOG.isDebugEnabled()) LOG.debug("Checking {} against containerInitializerExclusionPattern",sci.getClass().getName());
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Checking {} against containerInitializerExclusionPattern",sci.getClass().getName());
|
||||
return _sciExcludePattern.matcher(sci.getClass().getName()).matches();
|
||||
}
|
||||
|
||||
|
@ -866,17 +785,18 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
//because containerInitializerOrdering omits it
|
||||
for (ServletContainerInitializer sci:_loadedInitializers)
|
||||
{
|
||||
|
||||
if (matchesExclusionPattern(sci))
|
||||
{
|
||||
if (LOG.isDebugEnabled()) LOG.debug("{} excluded by pattern", sci);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{} excluded by pattern", sci);
|
||||
continue;
|
||||
}
|
||||
|
||||
Resource sciResource = getJarFor(sci);
|
||||
if (isFromExcludedJar(context, sci, sciResource))
|
||||
{
|
||||
if (LOG.isDebugEnabled()) LOG.debug("{} is from excluded jar", sci);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{} is from excluded jar", sci);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -885,7 +805,8 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
if (initializerOrdering != null
|
||||
&& (!initializerOrdering.hasWildcard() && initializerOrdering.getIndexOf(name) < 0))
|
||||
{
|
||||
if (LOG.isDebugEnabled()) LOG.debug("{} is excluded by ordering", sci);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{} is excluded by ordering", sci);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -912,12 +833,14 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
//no web.xml ordering defined, add SCIs in any order
|
||||
if (context.getMetaData().getOrdering() == null)
|
||||
{
|
||||
if (LOG.isDebugEnabled()) LOG.debug("No web.xml ordering, ServletContainerInitializers in random order");
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("No web.xml ordering, ServletContainerInitializers in random order");
|
||||
nonExcludedInitializers.addAll(sciResourceMap.keySet());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (LOG.isDebugEnabled()) LOG.debug("Ordering ServletContainerInitializers with ordering {}",context.getMetaData().getOrdering());
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Ordering ServletContainerInitializers with ordering {}",context.getMetaData().getOrdering());
|
||||
for (Map.Entry<ServletContainerInitializer, Resource> entry:sciResourceMap.entrySet())
|
||||
{
|
||||
//add in SCIs from the container classpath
|
||||
|
@ -992,7 +915,7 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
//queue it up for scanning if using multithreaded mode
|
||||
if (_parserTasks != null)
|
||||
{
|
||||
ParserTask task = new ParserTask(parser, handlers, r, _containerClassNameResolver);
|
||||
ParserTask task = new ParserTask(parser, handlers, r);
|
||||
_parserTasks.add(task);
|
||||
_containerPathStats.increment();
|
||||
if (LOG.isDebugEnabled())
|
||||
|
@ -1054,7 +977,7 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
|
||||
if (_parserTasks != null)
|
||||
{
|
||||
ParserTask task = new ParserTask(parser, handlers,r, _webAppClassNameResolver);
|
||||
ParserTask task = new ParserTask(parser, handlers,r);
|
||||
_parserTasks.add (task);
|
||||
_webInfLibStats.increment();
|
||||
if (LOG.isDebugEnabled())
|
||||
|
@ -1087,7 +1010,7 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
{
|
||||
if (_parserTasks != null)
|
||||
{
|
||||
ParserTask task = new ParserTask(parser, handlers, dir, _webAppClassNameResolver);
|
||||
ParserTask task = new ParserTask(parser, handlers, dir);
|
||||
_parserTasks.add(task);
|
||||
_webInfClassesStats.increment();
|
||||
if (LOG.isDebugEnabled())
|
||||
|
|
|
@ -544,28 +544,24 @@ public class AnnotationParser
|
|||
*
|
||||
* @param handlers the set of handlers to find class
|
||||
* @param className the class name to parse
|
||||
* @param resolver the class name resolver to use
|
||||
* @throws Exception if unable to parse
|
||||
*/
|
||||
public void parse (Set<? extends Handler> handlers, String className, ClassNameResolver resolver)
|
||||
public void parse (Set<? extends Handler> handlers, String className)
|
||||
throws Exception
|
||||
{
|
||||
if (className == null)
|
||||
return;
|
||||
|
||||
if (!resolver.isExcluded(className))
|
||||
if (!isParsed(className))
|
||||
{
|
||||
if (!isParsed(className) || resolver.shouldOverride(className))
|
||||
className = className.replace('.', '/')+".class";
|
||||
URL resource = Loader.getResource(className);
|
||||
if (resource!= null)
|
||||
{
|
||||
className = className.replace('.', '/')+".class";
|
||||
URL resource = Loader.getResource(className);
|
||||
if (resource!= null)
|
||||
Resource r = Resource.newResource(resource);
|
||||
try (InputStream is = r.getInputStream())
|
||||
{
|
||||
Resource r = Resource.newResource(resource);
|
||||
try (InputStream is = r.getInputStream())
|
||||
{
|
||||
scanClass(handlers, null, is);
|
||||
}
|
||||
scanClass(handlers, null, is);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -578,33 +574,30 @@ public class AnnotationParser
|
|||
*
|
||||
* @param handlers the handlers to look for class in
|
||||
* @param clazz the class to look for
|
||||
* @param resolver the resolver to look up class with
|
||||
* @param visitSuperClasses if true, also visit super classes for parse
|
||||
* @throws Exception if unable to parse class
|
||||
*/
|
||||
public void parse (Set<? extends Handler> handlers, Class<?> clazz, ClassNameResolver resolver, boolean visitSuperClasses)
|
||||
public void parse (Set<? extends Handler> handlers, Class<?> clazz, boolean visitSuperClasses)
|
||||
throws Exception
|
||||
{
|
||||
Class<?> cz = clazz;
|
||||
while (cz != null)
|
||||
{
|
||||
if (!resolver.isExcluded(cz.getName()))
|
||||
if (!isParsed(cz.getName()))
|
||||
{
|
||||
if (!isParsed(cz.getName()) || resolver.shouldOverride(cz.getName()))
|
||||
String nameAsResource = cz.getName().replace('.', '/')+".class";
|
||||
URL resource = Loader.getResource(nameAsResource);
|
||||
if (resource!= null)
|
||||
{
|
||||
String nameAsResource = cz.getName().replace('.', '/')+".class";
|
||||
URL resource = Loader.getResource(nameAsResource);
|
||||
if (resource!= null)
|
||||
Resource r = Resource.newResource(resource);
|
||||
try (InputStream is = r.getInputStream())
|
||||
{
|
||||
Resource r = Resource.newResource(resource);
|
||||
try (InputStream is = r.getInputStream())
|
||||
{
|
||||
scanClass(handlers, null, is);
|
||||
}
|
||||
scanClass(handlers, null, is);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (visitSuperClasses)
|
||||
cz = cz.getSuperclass();
|
||||
else
|
||||
|
@ -619,16 +612,15 @@ public class AnnotationParser
|
|||
*
|
||||
* @param handlers the set of handlers to look for class in
|
||||
* @param classNames the class name
|
||||
* @param resolver the class name resolver
|
||||
* @throws Exception if unable to parse
|
||||
*/
|
||||
public void parse (Set<? extends Handler> handlers, String[] classNames, ClassNameResolver resolver)
|
||||
public void parse (Set<? extends Handler> handlers, String[] classNames)
|
||||
throws Exception
|
||||
{
|
||||
if (classNames == null)
|
||||
return;
|
||||
|
||||
parse(handlers, Arrays.asList(classNames), resolver);
|
||||
parse(handlers, Arrays.asList(classNames));
|
||||
}
|
||||
|
||||
|
||||
|
@ -637,10 +629,9 @@ public class AnnotationParser
|
|||
*
|
||||
* @param handlers the set of handlers to look for class in
|
||||
* @param classNames the class names
|
||||
* @param resolver the class name resolver
|
||||
* @throws Exception if unable to parse
|
||||
*/
|
||||
public void parse (Set<? extends Handler> handlers, List<String> classNames, ClassNameResolver resolver)
|
||||
public void parse (Set<? extends Handler> handlers, List<String> classNames)
|
||||
throws Exception
|
||||
{
|
||||
MultiException me = new MultiException();
|
||||
|
@ -649,7 +640,7 @@ public class AnnotationParser
|
|||
{
|
||||
try
|
||||
{
|
||||
if ((resolver == null) || (!resolver.isExcluded(s) && (!isParsed(s) || resolver.shouldOverride(s))))
|
||||
if (!isParsed(s))
|
||||
{
|
||||
s = s.replace('.', '/')+".class";
|
||||
URL resource = Loader.getResource(s);
|
||||
|
@ -677,10 +668,9 @@ public class AnnotationParser
|
|||
*
|
||||
* @param handlers the set of handlers to look for classes in
|
||||
* @param dir the resource directory to look for classes
|
||||
* @param resolver the class name resolver
|
||||
* @throws Exception if unable to parse
|
||||
*/
|
||||
protected void parseDir (Set<? extends Handler> handlers, Resource dir, ClassNameResolver resolver)
|
||||
protected void parseDir (Set<? extends Handler> handlers, Resource dir)
|
||||
throws Exception
|
||||
{
|
||||
// skip dirs whose name start with . (ie hidden)
|
||||
|
@ -696,7 +686,7 @@ public class AnnotationParser
|
|||
{
|
||||
Resource res = dir.addPath(files[f]);
|
||||
if (res.isDirectory())
|
||||
parseDir(handlers, res, resolver);
|
||||
parseDir(handlers, res);
|
||||
else
|
||||
{
|
||||
//we've already verified the directories, so just verify the class file name
|
||||
|
@ -706,7 +696,7 @@ public class AnnotationParser
|
|||
try
|
||||
{
|
||||
String name = res.getName();
|
||||
if ((resolver == null)|| (!resolver.isExcluded(name) && (!isParsed(name) || resolver.shouldOverride(name))))
|
||||
if (!isParsed(name))
|
||||
{
|
||||
Resource r = Resource.newResource(res.getURL());
|
||||
if (LOG.isDebugEnabled()) {LOG.debug("Scanning class {}", r);};
|
||||
|
@ -741,10 +731,9 @@ public class AnnotationParser
|
|||
* @param loader the classloader for the classes
|
||||
* @param visitParents if true, visit parent classloaders too
|
||||
* @param nullInclusive if true, an empty pattern means all names match, if false, none match
|
||||
* @param resolver the class name resolver
|
||||
* @throws Exception if unable to parse
|
||||
*/
|
||||
public void parse (final Set<? extends Handler> handlers, ClassLoader loader, boolean visitParents, boolean nullInclusive, final ClassNameResolver resolver)
|
||||
public void parse (final Set<? extends Handler> handlers, ClassLoader loader, boolean visitParents, boolean nullInclusive)
|
||||
throws Exception
|
||||
{
|
||||
if (loader==null)
|
||||
|
@ -762,7 +751,7 @@ public class AnnotationParser
|
|||
{
|
||||
try
|
||||
{
|
||||
parseJarEntry(handlers, Resource.newResource(jarUri), entry, resolver);
|
||||
parseJarEntry(handlers, Resource.newResource(jarUri), entry);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@ -782,10 +771,9 @@ public class AnnotationParser
|
|||
*
|
||||
* @param handlers the handlers to look for classes in
|
||||
* @param uris the uris for the jars
|
||||
* @param resolver the class name resolver
|
||||
* @throws Exception if unable to parse
|
||||
*/
|
||||
public void parse (final Set<? extends Handler> handlers, final URI[] uris, final ClassNameResolver resolver)
|
||||
public void parse (final Set<? extends Handler> handlers, final URI[] uris)
|
||||
throws Exception
|
||||
{
|
||||
if (uris==null)
|
||||
|
@ -797,7 +785,7 @@ public class AnnotationParser
|
|||
{
|
||||
try
|
||||
{
|
||||
parse(handlers, uri, resolver);
|
||||
parse(handlers, uri);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@ -812,16 +800,15 @@ public class AnnotationParser
|
|||
*
|
||||
* @param handlers the handlers to look for classes in
|
||||
* @param uri the uri for the jar
|
||||
* @param resolver the class name resolver
|
||||
* @throws Exception if unable to parse
|
||||
*/
|
||||
public void parse (final Set<? extends Handler> handlers, URI uri, final ClassNameResolver resolver)
|
||||
public void parse (final Set<? extends Handler> handlers, URI uri)
|
||||
throws Exception
|
||||
{
|
||||
if (uri == null)
|
||||
return;
|
||||
|
||||
parse (handlers, Resource.newResource(uri), resolver);
|
||||
parse (handlers, Resource.newResource(uri));
|
||||
}
|
||||
|
||||
|
||||
|
@ -830,10 +817,9 @@ public class AnnotationParser
|
|||
*
|
||||
* @param handlers the handlers to look for classes in
|
||||
* @param r the resource to parse
|
||||
* @param resolver the class name resolver
|
||||
* @throws Exception if unable to parse
|
||||
*/
|
||||
public void parse (final Set<? extends Handler> handlers, Resource r, final ClassNameResolver resolver)
|
||||
public void parse (final Set<? extends Handler> handlers, Resource r)
|
||||
throws Exception
|
||||
{
|
||||
if (r == null)
|
||||
|
@ -841,14 +827,14 @@ public class AnnotationParser
|
|||
|
||||
if (r.exists() && r.isDirectory())
|
||||
{
|
||||
parseDir(handlers, r, resolver);
|
||||
parseDir(handlers, r);
|
||||
return;
|
||||
}
|
||||
|
||||
String fullname = r.toString();
|
||||
if (fullname.endsWith(".jar"))
|
||||
{
|
||||
parseJar(handlers, r, resolver);
|
||||
parseJar(handlers, r);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -872,10 +858,9 @@ public class AnnotationParser
|
|||
*
|
||||
* @param handlers the handlers to look for classes in
|
||||
* @param jarResource the jar resource to parse
|
||||
* @param resolver the class name resolver
|
||||
* @throws Exception if unable to parse
|
||||
*/
|
||||
protected void parseJar (Set<? extends Handler> handlers, Resource jarResource, final ClassNameResolver resolver)
|
||||
protected void parseJar (Set<? extends Handler> handlers, Resource jarResource)
|
||||
throws Exception
|
||||
{
|
||||
if (jarResource == null)
|
||||
|
@ -899,7 +884,7 @@ public class AnnotationParser
|
|||
{
|
||||
try
|
||||
{
|
||||
parseJarEntry(handlers, jarResource, entry, resolver);
|
||||
parseJarEntry(handlers, jarResource, entry);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@ -927,10 +912,9 @@ public class AnnotationParser
|
|||
* @param handlers the handlers to look for classes in
|
||||
* @param jar the jar resource to parse
|
||||
* @param entry the entry in the jar resource to parse
|
||||
* @param resolver the class name resolver
|
||||
* @throws Exception if unable to parse
|
||||
*/
|
||||
protected void parseJarEntry (Set<? extends Handler> handlers, Resource jar, JarEntry entry, final ClassNameResolver resolver)
|
||||
protected void parseJarEntry (Set<? extends Handler> handlers, Resource jar, JarEntry entry)
|
||||
throws Exception
|
||||
{
|
||||
if (jar == null || entry == null)
|
||||
|
@ -947,9 +931,7 @@ public class AnnotationParser
|
|||
{
|
||||
String shortName = name.replace('/', '.').substring(0,name.length()-6);
|
||||
|
||||
if ((resolver == null)
|
||||
||
|
||||
(!resolver.isExcluded(shortName) && (!isParsed(shortName) || resolver.shouldOverride(shortName))))
|
||||
if (!isParsed(shortName))
|
||||
{
|
||||
Resource clazz = Resource.newResource("jar:"+jar.getURI()+"!/"+name);
|
||||
if (LOG.isDebugEnabled()) {LOG.debug("Scanning class from jar {}", clazz);};
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.annotations;
|
||||
|
||||
|
||||
|
||||
public interface ClassNameResolver
|
||||
{
|
||||
/**
|
||||
* Based on the execution context, should the class represented
|
||||
* by "name" be excluded from consideration?
|
||||
* @param name the name to test
|
||||
* @return true if classname is excluded
|
||||
*/
|
||||
public boolean isExcluded (String name);
|
||||
|
||||
|
||||
/**
|
||||
* Based on the execution context, if a duplicate class
|
||||
* represented by "name" is detected, should the existing
|
||||
* one be overridden or not?
|
||||
* @param name the name to test
|
||||
* @return true if name should be overridden
|
||||
*/
|
||||
public boolean shouldOverride (String name);
|
||||
}
|
|
@ -74,6 +74,12 @@ public class TestAnnotationInheritance
|
|||
return;
|
||||
annotatedMethods.add(info.getClassInfo().getClassName()+"."+info.getMethodName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return annotatedClassNames.toString()+annotatedMethods+annotatedFields;
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
|
@ -93,18 +99,7 @@ public class TestAnnotationInheritance
|
|||
|
||||
SampleHandler handler = new SampleHandler();
|
||||
AnnotationParser parser = new AnnotationParser();
|
||||
parser.parse(Collections.singleton(handler), classNames, new ClassNameResolver ()
|
||||
{
|
||||
public boolean isExcluded(String name)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean shouldOverride(String name)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
});
|
||||
parser.parse(Collections.singleton(handler), classNames);
|
||||
|
||||
//check we got 2 class annotations
|
||||
assertEquals(2, handler.annotatedClassNames.size());
|
||||
|
@ -129,18 +124,7 @@ public class TestAnnotationInheritance
|
|||
{
|
||||
SampleHandler handler = new SampleHandler();
|
||||
AnnotationParser parser = new AnnotationParser();
|
||||
parser.parse(Collections.singleton(handler), ClassB.class, new ClassNameResolver ()
|
||||
{
|
||||
public boolean isExcluded(String name)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean shouldOverride(String name)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}, true);
|
||||
parser.parse(Collections.singleton(handler), ClassB.class, true);
|
||||
|
||||
//check we got 2 class annotations
|
||||
assertEquals(2, handler.annotatedClassNames.size());
|
||||
|
@ -160,46 +144,6 @@ public class TestAnnotationInheritance
|
|||
assertEquals("org.eclipse.jetty.annotations.ClassA.m", handler.annotatedFields.get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExclusions() throws Exception
|
||||
{
|
||||
AnnotationParser parser = new AnnotationParser();
|
||||
SampleHandler handler = new SampleHandler();
|
||||
parser.parse(Collections.singleton(handler), ClassA.class.getName(), new ClassNameResolver()
|
||||
{
|
||||
public boolean isExcluded(String name)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean shouldOverride(String name)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
});
|
||||
assertEquals (0, handler.annotatedClassNames.size());
|
||||
assertEquals (0, handler.annotatedFields.size());
|
||||
assertEquals (0, handler.annotatedMethods.size());
|
||||
|
||||
handler.annotatedClassNames.clear();
|
||||
handler.annotatedFields.clear();
|
||||
handler.annotatedMethods.clear();
|
||||
|
||||
parser.parse (Collections.singleton(handler), ClassA.class.getName(), new ClassNameResolver()
|
||||
{
|
||||
public boolean isExcluded(String name)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean shouldOverride(String name)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
});
|
||||
assertEquals (1, handler.annotatedClassNames.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTypeInheritanceHandling() throws Exception
|
||||
{
|
||||
|
@ -218,7 +162,7 @@ public class TestAnnotationInheritance
|
|||
classNames.add(InterfaceD.class.getName());
|
||||
classNames.add(Foo.class.getName());
|
||||
|
||||
parser.parse(Collections.singleton(handler), classNames, null);
|
||||
parser.parse(Collections.singleton(handler), classNames);
|
||||
|
||||
assertNotNull(map);
|
||||
assertFalse(map.isEmpty());
|
||||
|
|
|
@ -111,19 +111,7 @@ public class TestAnnotationParser
|
|||
}
|
||||
|
||||
//long start = System.currentTimeMillis();
|
||||
parser.parse(Collections.singleton(new SampleAnnotationHandler()), classNames,new ClassNameResolver()
|
||||
{
|
||||
public boolean isExcluded(String name)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean shouldOverride(String name)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
});
|
||||
parser.parse(Collections.singleton(new SampleAnnotationHandler()), classNames);
|
||||
//long end = System.currentTimeMillis();
|
||||
|
||||
//System.err.println("Time to parse class: " + ((end - start)));
|
||||
|
@ -162,7 +150,7 @@ public class TestAnnotationParser
|
|||
}
|
||||
}
|
||||
|
||||
parser.parse(Collections.singleton(new MultiAnnotationHandler()), classNames,null);
|
||||
parser.parse(Collections.singleton(new MultiAnnotationHandler()), classNames);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -171,7 +159,7 @@ public class TestAnnotationParser
|
|||
File badClassesJar = MavenTestingUtils.getTestResourceFile("bad-classes.jar");
|
||||
AnnotationParser parser = new AnnotationParser();
|
||||
Set<Handler> emptySet = Collections.emptySet();
|
||||
parser.parse(emptySet, badClassesJar.toURI(),null);
|
||||
parser.parse(emptySet, badClassesJar.toURI());
|
||||
// only the valid classes inside bad-classes.jar should be parsed. If any invalid classes are parsed and exception would be thrown here
|
||||
}
|
||||
|
||||
|
@ -196,7 +184,7 @@ public class TestAnnotationParser
|
|||
AnnotationParser parser = new AnnotationParser();
|
||||
|
||||
// Parse
|
||||
parser.parse(Collections.singleton(tracker), basedir.toURI(),null);
|
||||
parser.parse(Collections.singleton(tracker), basedir.toURI());
|
||||
|
||||
// Validate
|
||||
Assert.assertThat("Found Class", tracker.foundClasses, contains(ClassA.class.getName()));
|
||||
|
|
|
@ -73,18 +73,7 @@ public class TestServletAnnotations
|
|||
|
||||
TestWebServletAnnotationHandler handler = new TestWebServletAnnotationHandler(wac, results);
|
||||
|
||||
parser.parse(Collections.singleton(handler), classes, new ClassNameResolver ()
|
||||
{
|
||||
public boolean isExcluded(String name)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean shouldOverride(String name)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
});
|
||||
parser.parse(Collections.singleton(handler), classes);
|
||||
|
||||
|
||||
assertEquals(1, results.size());
|
||||
|
|
|
@ -18,26 +18,13 @@
|
|||
|
||||
package org.eclipse.jetty.client;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.util.zip.DataFormatException;
|
||||
import java.util.zip.Inflater;
|
||||
import java.util.zip.ZipException;
|
||||
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
|
||||
/**
|
||||
* {@link ContentDecoder} for the "gzip" encoding.
|
||||
*
|
||||
*/
|
||||
public class GZIPContentDecoder implements ContentDecoder
|
||||
public class GZIPContentDecoder extends org.eclipse.jetty.http.GZIPContentDecoder implements ContentDecoder
|
||||
{
|
||||
private final Inflater inflater = new Inflater(true);
|
||||
private final byte[] bytes;
|
||||
private byte[] output;
|
||||
private State state;
|
||||
private int size;
|
||||
private int value;
|
||||
private byte flags;
|
||||
|
||||
public GZIPContentDecoder()
|
||||
{
|
||||
|
@ -46,285 +33,7 @@ public class GZIPContentDecoder implements ContentDecoder
|
|||
|
||||
public GZIPContentDecoder(int bufferSize)
|
||||
{
|
||||
this.bytes = new byte[bufferSize];
|
||||
reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>If the decoding did not produce any output, for example because it consumed gzip header
|
||||
* or trailer bytes, it returns a buffer with zero capacity.</p>
|
||||
* <p>This method never returns null.</p>
|
||||
* <p>The given {@code buffer}'s position will be modified to reflect the bytes consumed during
|
||||
* the decoding.</p>
|
||||
* <p>The decoding may be finished without consuming the buffer completely if the buffer contains
|
||||
* gzip bytes plus other bytes (either plain or gzipped).</p>
|
||||
*/
|
||||
@Override
|
||||
public ByteBuffer decode(ByteBuffer buffer)
|
||||
{
|
||||
try
|
||||
{
|
||||
while (buffer.hasRemaining())
|
||||
{
|
||||
byte currByte = buffer.get();
|
||||
switch (state)
|
||||
{
|
||||
case INITIAL:
|
||||
{
|
||||
buffer.position(buffer.position() - 1);
|
||||
state = State.ID;
|
||||
break;
|
||||
}
|
||||
case ID:
|
||||
{
|
||||
value += (currByte & 0xFF) << 8 * size;
|
||||
++size;
|
||||
if (size == 2)
|
||||
{
|
||||
if (value != 0x8B1F)
|
||||
throw new ZipException("Invalid gzip bytes");
|
||||
state = State.CM;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CM:
|
||||
{
|
||||
if ((currByte & 0xFF) != 0x08)
|
||||
throw new ZipException("Invalid gzip compression method");
|
||||
state = State.FLG;
|
||||
break;
|
||||
}
|
||||
case FLG:
|
||||
{
|
||||
flags = currByte;
|
||||
state = State.MTIME;
|
||||
size = 0;
|
||||
value = 0;
|
||||
break;
|
||||
}
|
||||
case MTIME:
|
||||
{
|
||||
// Skip the 4 MTIME bytes
|
||||
++size;
|
||||
if (size == 4)
|
||||
state = State.XFL;
|
||||
break;
|
||||
}
|
||||
case XFL:
|
||||
{
|
||||
// Skip XFL
|
||||
state = State.OS;
|
||||
break;
|
||||
}
|
||||
case OS:
|
||||
{
|
||||
// Skip OS
|
||||
state = State.FLAGS;
|
||||
break;
|
||||
}
|
||||
case FLAGS:
|
||||
{
|
||||
buffer.position(buffer.position() - 1);
|
||||
if ((flags & 0x04) == 0x04)
|
||||
{
|
||||
state = State.EXTRA_LENGTH;
|
||||
size = 0;
|
||||
value = 0;
|
||||
}
|
||||
else if ((flags & 0x08) == 0x08)
|
||||
state = State.NAME;
|
||||
else if ((flags & 0x10) == 0x10)
|
||||
state = State.COMMENT;
|
||||
else if ((flags & 0x2) == 0x2)
|
||||
{
|
||||
state = State.HCRC;
|
||||
size = 0;
|
||||
value = 0;
|
||||
}
|
||||
else
|
||||
state = State.DATA;
|
||||
break;
|
||||
}
|
||||
case EXTRA_LENGTH:
|
||||
{
|
||||
value += (currByte & 0xFF) << 8 * size;
|
||||
++size;
|
||||
if (size == 2)
|
||||
state = State.EXTRA;
|
||||
break;
|
||||
}
|
||||
case EXTRA:
|
||||
{
|
||||
// Skip EXTRA bytes
|
||||
--value;
|
||||
if (value == 0)
|
||||
{
|
||||
// Clear the EXTRA flag and loop on the flags
|
||||
flags &= ~0x04;
|
||||
state = State.FLAGS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NAME:
|
||||
{
|
||||
// Skip NAME bytes
|
||||
if (currByte == 0)
|
||||
{
|
||||
// Clear the NAME flag and loop on the flags
|
||||
flags &= ~0x08;
|
||||
state = State.FLAGS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case COMMENT:
|
||||
{
|
||||
// Skip COMMENT bytes
|
||||
if (currByte == 0)
|
||||
{
|
||||
// Clear the COMMENT flag and loop on the flags
|
||||
flags &= ~0x10;
|
||||
state = State.FLAGS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case HCRC:
|
||||
{
|
||||
// Skip HCRC
|
||||
++size;
|
||||
if (size == 2)
|
||||
{
|
||||
// Clear the HCRC flag and loop on the flags
|
||||
flags &= ~0x02;
|
||||
state = State.FLAGS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DATA:
|
||||
{
|
||||
buffer.position(buffer.position() - 1);
|
||||
while (true)
|
||||
{
|
||||
int decoded = inflate(bytes);
|
||||
if (decoded == 0)
|
||||
{
|
||||
if (inflater.needsInput())
|
||||
{
|
||||
if (buffer.hasRemaining())
|
||||
{
|
||||
byte[] input = new byte[buffer.remaining()];
|
||||
buffer.get(input);
|
||||
inflater.setInput(input);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (output != null)
|
||||
{
|
||||
ByteBuffer result = ByteBuffer.wrap(output);
|
||||
output = null;
|
||||
return result;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (inflater.finished())
|
||||
{
|
||||
int remaining = inflater.getRemaining();
|
||||
buffer.position(buffer.limit() - remaining);
|
||||
state = State.CRC;
|
||||
size = 0;
|
||||
value = 0;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ZipException("Invalid inflater state");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (output == null)
|
||||
{
|
||||
// Save the inflated bytes and loop to see if we have finished
|
||||
output = Arrays.copyOf(bytes, decoded);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Accumulate inflated bytes and loop to see if we have finished
|
||||
byte[] newOutput = Arrays.copyOf(output, output.length + decoded);
|
||||
System.arraycopy(bytes, 0, newOutput, output.length, decoded);
|
||||
output = newOutput;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CRC:
|
||||
{
|
||||
value += (currByte & 0xFF) << 8 * size;
|
||||
++size;
|
||||
if (size == 4)
|
||||
{
|
||||
// From RFC 1952, compliant decoders need not to verify the CRC
|
||||
state = State.ISIZE;
|
||||
size = 0;
|
||||
value = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ISIZE:
|
||||
{
|
||||
value += (currByte & 0xFF) << 8 * size;
|
||||
++size;
|
||||
if (size == 4)
|
||||
{
|
||||
if (value != inflater.getBytesWritten())
|
||||
throw new ZipException("Invalid input size");
|
||||
|
||||
ByteBuffer result = output == null ? BufferUtil.EMPTY_BUFFER : ByteBuffer.wrap(output);
|
||||
reset();
|
||||
return result;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new ZipException();
|
||||
}
|
||||
}
|
||||
return BufferUtil.EMPTY_BUFFER;
|
||||
}
|
||||
catch (ZipException x)
|
||||
{
|
||||
throw new RuntimeException(x);
|
||||
}
|
||||
}
|
||||
|
||||
private int inflate(byte[] bytes) throws ZipException
|
||||
{
|
||||
try
|
||||
{
|
||||
return inflater.inflate(bytes);
|
||||
}
|
||||
catch (DataFormatException x)
|
||||
{
|
||||
throw new ZipException(x.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void reset()
|
||||
{
|
||||
inflater.reset();
|
||||
Arrays.fill(bytes, (byte)0);
|
||||
output = null;
|
||||
state = State.INITIAL;
|
||||
size = 0;
|
||||
value = 0;
|
||||
flags = 0;
|
||||
}
|
||||
|
||||
protected boolean isFinished()
|
||||
{
|
||||
return state == State.INITIAL;
|
||||
super(null,bufferSize);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -351,9 +60,4 @@ public class GZIPContentDecoder implements ContentDecoder
|
|||
return new GZIPContentDecoder(bufferSize);
|
||||
}
|
||||
}
|
||||
|
||||
private enum State
|
||||
{
|
||||
INITIAL, ID, CM, FLG, MTIME, XFL, OS, FLAGS, EXTRA_LENGTH, EXTRA, NAME, COMMENT, HCRC, DATA, CRC, ISIZE
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@ import org.eclipse.jetty.http.HttpStatus;
|
|||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.CountingCallback;
|
||||
import org.eclipse.jetty.util.component.Destroyable;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
|
@ -470,6 +471,7 @@ public abstract class HttpReceiver
|
|||
*/
|
||||
protected void reset()
|
||||
{
|
||||
destroyDecoder(decoder);
|
||||
decoder = null;
|
||||
}
|
||||
|
||||
|
@ -482,9 +484,18 @@ public abstract class HttpReceiver
|
|||
*/
|
||||
protected void dispose()
|
||||
{
|
||||
destroyDecoder(decoder);
|
||||
decoder = null;
|
||||
}
|
||||
|
||||
private static void destroyDecoder(ContentDecoder decoder)
|
||||
{
|
||||
if (decoder instanceof Destroyable)
|
||||
{
|
||||
((Destroyable)decoder).destroy();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean abort(HttpExchange exchange, Throwable failure)
|
||||
{
|
||||
// Update the state to avoid more response processing.
|
||||
|
|
|
@ -33,6 +33,7 @@ import org.eclipse.jetty.toolchain.test.TestTracker;
|
|||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
@Deprecated
|
||||
public class GZIPContentDecoderTest
|
||||
{
|
||||
@Rule
|
||||
|
|
|
@ -329,7 +329,7 @@
|
|||
<arguments>
|
||||
<argument>jetty.home=${assembly-directory}</argument>
|
||||
<argument>jetty.base=${assembly-directory}</argument>
|
||||
<argument>--add-to-start=deploy,websocket,ext,resources,jsp,jstl,http</argument>
|
||||
<argument>--add-to-start=server,deploy,websocket,ext,resources,jsp,jstl,http</argument>
|
||||
</arguments>
|
||||
</configuration>
|
||||
<goals>
|
||||
|
|
|
@ -31,18 +31,24 @@ Not only does the `quickstart-web.xml` contain all the discovered Servlets, Filt
|
|||
|
||||
With the quickstart mechanism, Jetty is able to entirely bypass all scanning and discovery modes and start a webapp in a predictable and fast way.
|
||||
Tests have shown that webapps that took many seconds to scan and deploy can now be deployed in a few hundred milliseconds.
|
||||
Additionally, if debug logging is enabled, the generated quickstart information is tagged with the origin of every element, which can be useful for debugging purposes.
|
||||
|
||||
==== Setting up Quickstart
|
||||
|
||||
To use quickstart the module has to be available to the Jetty instance.
|
||||
In a standard Jetty distribution it can be configured with the following command:
|
||||
===== Prerequisites
|
||||
|
||||
====== Jetty Distribution
|
||||
|
||||
In a standard Jetty distribution the quickstart module can be configured with the following command:
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
----
|
||||
$ java -jar $JETTY_HOME/start.jar --add-to-start=quickstart
|
||||
----
|
||||
|
||||
In a Maven project this is done by adding a dependency on the artifact ID `jetty-quickstart`.
|
||||
====== Embedded
|
||||
|
||||
In a Maven project you add a dependency on the artifact `jetty-quickstart`.
|
||||
|
||||
[source, xml, subs="{sub-order}"]
|
||||
----
|
||||
|
@ -53,26 +59,62 @@ In a Maven project this is done by adding a dependency on the artifact ID `jetty
|
|||
</dependency>
|
||||
----
|
||||
|
||||
Additionally, for those using Maven, the link:#get-up-and-running[Jetty Maven Plugin] has a goal, link:#jetty-effective-web-xml[`jetty:effective-web-xml`], which performs quickstart operations.
|
||||
It should be noted, however, that the Jetty Maven Plugin also includes additional items on it's classpath which may not be needed by the webapp.
|
||||
|
||||
Deployed webapps need to be instances of link:{JDURL}/org/eclipse/jetty/quickstart/QuickStartWebApp.html[`org.eclipse.jetty.quickstart.QuickStartWebApp`] rather than the normal `org.eclipse.jetty.webapp.WebAppContext`.
|
||||
If a web application already has a `webapps/myapp.xml` file, simply change the class in the `Configure` element.
|
||||
Otherwise, create a `webapps/myapp.xml` file as follows:
|
||||
|
||||
===== Configuration
|
||||
|
||||
Webapps need to be instances of link:{JDURL}/org/eclipse/jetty/quickstart/QuickStartWebApp.html[`org.eclipse.jetty.quickstart.QuickStartWebApp`] rather than the normal `org.eclipse.jetty.webapp.WebAppContext`.
|
||||
|
||||
`org.eclipse.jetty.quickstart.QuickStartWebApp` instances offer the same setters as the familiar `org.eclipse.jetty.webapp.WebAppContext`, with the addition of:
|
||||
|
||||
autoPreconfigure::
|
||||
(true/false).
|
||||
If true, the first time the webapp is run, the WEB-INF/quickstart-web.xml is generated BEFORE the webapp is deployed.
|
||||
Subsequent runs use the previously generated quickstart file.
|
||||
|
||||
====== In XML
|
||||
If a web application already has a context xml file, eg `webapps/myapp.xml` file, simply change the class in the `Configure` element.
|
||||
Otherwise, create a context xml file with the following information (in addition to the usual setting of contextPath, war etc):
|
||||
|
||||
[source, xml, subs="{sub-order}"]
|
||||
----
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
|
||||
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
|
||||
<Configure class="org.eclipse.jetty.quickstart.QuickStartWebApp">
|
||||
<Set name="war"><Property name="jetty.webapps" default="."/>/benchmark.war</Set>
|
||||
<Set name="contextPath">/benchmark</Set>
|
||||
<Set name="autoPreconfigure">true</Set>
|
||||
</Configure>
|
||||
----
|
||||
|
||||
For embedded implementations of Jetty, invoking the link:{JDURL}/org/eclipse/jetty/quickstart/PreconfigureQuickStartWar.html[`org.eclipse.jetty.quickstart.PreconfigureQuickStartWar`] class can be used to configure war files for quickstart deployment.
|
||||
This will create the `quickstart-web.xml` before the first deployment.
|
||||
====== In Code
|
||||
|
||||
Create an instance of link:{JDURL}/org/eclipse/jetty/quickstart/QuickStartWebApp.html[`org.eclipse.jetty.quickstart.QuickStartWebApp`] rather than the normal `org.eclipse.jetty.webapp.WebAppContext`. You then use the QuickStartWebApp instance in exactly the same way that you would a WebAppContext.
|
||||
|
||||
Here's a snippet:
|
||||
|
||||
[source, java]
|
||||
----
|
||||
QuickStartWebApp webapp = new QuickStartWebApp();
|
||||
webapp.setAutoPreconfigure(true);
|
||||
----
|
||||
|
||||
|
||||
====== Pre-generating the quickstart-web.xml file
|
||||
|
||||
Rather than use the `autoPreconfigure` feature of the QuickStartWebApp - which lazily generates the `quickstart-web.xml` file - you can eagerly pre-generate it for an existing war by invoking as a main class link:{JDURL}/org/eclipse/jetty/quickstart/PreconfigureQuickStartWar.html[`org.eclipse.jetty.quickstart.PreconfigureQuickStartWar`].
|
||||
Note that you will need to provide all necessary jetty jars on the command line classpath.
|
||||
This will unpack the war if necessary, and create the `quickstart-web.xml` before the first deployment:
|
||||
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
----
|
||||
$ java -cp [jetty classpath] org.eclipse.jetty.quickstart.PreconfigureQuickStartWar myapp.war
|
||||
----
|
||||
|
||||
Run the class with no arguments to see other runtime options.
|
||||
|
||||
Alternatively, you could use the link:#get-up-and-running[Jetty Maven Plugin] goal link:#jetty-effective-web-xml[`jetty:effective-web-xml`]: this will generate quickstart information, but print it to stderr.
|
||||
The goal provides a configuration option to save the output to a file, which you can then copy into your webapp's WEB-INF dir.
|
||||
Note that as the Jetty Maven Plugin is a general tool for running webapps, it may have more jars on its classpath than are needed by your application, and thus may generate extra quickstart information: we recommend that you use this goal only as a quick guide to the type of information that quickstart generates.
|
||||
|
||||
// ==== Preconfiguring the web application
|
||||
//
|
||||
|
@ -81,14 +123,8 @@ This will create the `quickstart-web.xml` before the first deployment.
|
|||
//
|
||||
// It is also possible to preconfigure a war file manually by running the class link:{JDURL}/org/eclipse/jetty/quickstart/PreconfigureQuickStartWar.html[org.eclipse.jetty.quickstart.PreconfigureQuickStartWar] with the jetty-all-uber (aggregate) jar:
|
||||
//
|
||||
// [source, screen, subs="{sub-order}"]
|
||||
// ----
|
||||
// $ java -cp jetty-all-{VERSION}-uber.jar org.eclipse.jetty.quickstart.PreconfigureQuickStartWar myapp.war
|
||||
// ----
|
||||
//
|
||||
// This will create the `quickstart-web.xml` file before the first deployment.
|
||||
// Note that this can also be a good debugging tool for discovered configuration and if run with debug turned on the origin of every element is included in the `quickstart-web.xml` file.
|
||||
// Run the class with no arguments to see other runtime options.
|
||||
|
||||
==== Avoiding TLD Scans with precompiled JSPs
|
||||
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
[description]
|
||||
Enables GCloud Datastore API and implementation
|
||||
|
||||
[Tags]
|
||||
3rdparty
|
||||
gcloud
|
||||
|
||||
[depends]
|
||||
gcloud
|
||||
jcl-api
|
||||
jcl-impl
|
||||
|
||||
[files]
|
||||
maven://com.google.cloud/google-cloud-datastore/0.3.0|lib/gcloud/google-cloud-datastore-0.3.0.jar
|
||||
maven://com.google.cloud/google-cloud-core/0.3.0|lib/gcloud/google-cloud-core-0.3.0.jar
|
||||
maven://com.google.auth/google-auth-library-credentials/0.3.1|lib/gcloud/google-auth-library-credentials-0.3.1.jar
|
||||
maven://com.google.auth/google-auth-library-oauth2-http/0.3.1|lib/gcloud/google-auth-library-oauth2-http-0.3.1.jar
|
||||
maven://com.google.http-client/google-http-client-jackson2/1.19.0|lib/gcloud/google-http-client-jackson2-1.19.0.jar
|
||||
maven://com.fasterxml.jackson.core/jackson-core/2.1.3|lib/gcloud/jackson-core-2.1.3.jar
|
||||
maven://com.google.http-client/google-http-client/1.21.0|lib/gcloud/google-http-client-1.21.0.jar
|
||||
maven://com.google.code.findbugs/jsr305/1.3.9|lib/gcloud/jsr305-1.3.9.jar
|
||||
maven://org.apache.httpcomponents/httpclient/4.0.1|lib/gcloud/httpclient-4.0.1.jar
|
||||
maven://org.apache.httpcomponents/httpcore/4.0.1|lib/gcloud/httpcore-4.0.1.jar
|
||||
maven://commons-codec/commons-codec/1.3|lib/gcloud/commons-codec-1.3.jar
|
||||
maven://com.google.oauth-client/google-oauth-client/1.21.0|lib/gcloud/google-oauth-client-1.21.0.jar
|
||||
maven://com.google.guava/guava/19.0|lib/gcloud/guava-19.0.jar
|
||||
maven://com.google.api-client/google-api-client-appengine/1.21.0|lib/gcloud/google-api-client-appengine-1.21.0.jar
|
||||
maven://com.google.oauth-client/google-oauth-client-appengine/1.21.0|lib/gcloud/google-oauth-client-appengine-1.21.0.jar
|
||||
maven://com.google.oauth-client/google-oauth-client-servlet/1.21.0|lib/gcloud/google-oauth-client-servlet-1.21.0.jar
|
||||
maven://com.google.http-client/google-http-client-jdo/1.21.0|lib/gcloud/google-http-client-jdo-1.21.0.jar
|
||||
maven://com.google.api-client/google-api-client-servlet/1.21.0|lib/gcloud/google-api-client-servlet-1.21.0.jar
|
||||
maven://javax.jdo/jdo2-api/2.3-eb|lib/gcloud/jdo2-api-2.3-eb.jar
|
||||
maven://javax.transaction/transaction-api/1.1|lib/gcloud/transaction-api-1.1.jar
|
||||
maven://com.google.http-client/google-http-client-appengine/1.21.0|lib/gcloud/google-http-client-appengine-1.21.0.jar
|
||||
maven://com.google.http-client/google-http-client-jackson/1.21.0|lib/gcloud/google-http-client-jackson-1.21.0.jar
|
||||
maven://org.codehaus.jackson/jackson-core-asl/1.9.11|lib/gcloud/jackson-core-asl-1.9.11.jar
|
||||
maven://joda-time/joda-time/2.9.2|lib/gcloud/joda-time-2.9.2.jar
|
||||
maven://com.google.protobuf/protobuf-java/3.0.0-beta-3|lib/gcloud/protobuf-java-3.0.0-beta-3.jar
|
||||
maven://com.google.api/gax/0.0.16|lib/gcloud/gax-0.0.16.jar
|
||||
maven://io.grpc/grpc-all/0.15.0|lib/gcloud/grpc-all-0.15.0.jar
|
||||
maven://io.grpc/grpc-auth/0.15.0|lib/gcloud/grpc-auth-0.15.0.jar
|
||||
maven://io.grpc/grpc-netty/0.15.0|lib/gcloud/grpc-netty-0.15.0.jar
|
||||
maven://io.netty/netty-codec-http2/4.1.1.Final|lib/gcloud/netty-codec-http2-4.1.1.jar
|
||||
maven://io.netty/netty-codec-http/4.1.1.Final|lib/gcloud/netty-codec-http-4.1.1.Final.jar
|
||||
maven://io.netty/netty-codec/4.1.1.Final|lib/gcloud/netty-codec-4.1.1.Final.jar
|
||||
maven://io.netty/netty-handler/4.1.1.Final|lib/gcloud/netty-handler-4.1.1.Final.jar
|
||||
maven://io.netty/netty-buffer/4.1.1.Final|lib/gcloud/netty-buffer-4.1.1.Final.jar
|
||||
maven://io.netty/netty-common/4.1.1.Final|lib/gcloud/netty-common-4.1.1.Final.jar
|
||||
maven://io.netty/netty-transport/4.1.1.Final|lib/gcloud/netty-transport-4.1.1.Final.jar
|
||||
maven://io.netty/netty-resolver/4.1.1.Final|lib/gcloud/netty-resolver-4.1.1.Final.jar
|
||||
maven://io.grpc/grpc-okhttp/0.15.0|lib/gcloud/grpc-okhttp-0.15.0.jar
|
||||
maven://com.squareup.okio/okio/1.6.0|lib/gcloud/okio-1.6.0.jar
|
||||
maven://com.squareup.okhttp/okhttp/2.5.0|lib/gcloud/okhttp-2.5.0.jar
|
||||
maven://io.grpc/grpc-protobuf-nano/0.15.0|lib/gcloud/grpc-protobuf-nano-0.15.0.jar
|
||||
maven://com.google.protobuf.nano/protobuf-javanano/3.0.0-alpha-5|lib/gcloud/protobuf-javanano-3.0.0-alpha-5.jar
|
||||
maven://io.grpc/grpc-stub/0.15.0|lib/gcloud/grpc-stub-0.15.0.jar
|
||||
maven://io.grpc/grpc-protobuf/0.15.0|lib/gcloud/grpc-protobuf-0.15.0.jar
|
||||
maven://com.google.protobuf/protobuf-java-util/3.0.0-beta-3|lib/gcloud/protobuf-java-util-3.0.0-beta-3.jar
|
||||
maven://com.google.code.gson/gson/2.3|lib/gcloud/gson-2.3.jar
|
||||
maven://io.grpc/grpc-protobuf-lite/0.15.0|lib/gcloud/grpc-protobuf-lite-0.15.0.jar
|
||||
maven://io.grpc/grpc-core/0.15.0|lib/gcloud/grpc-core-0.15.0.jar
|
||||
maven://com.google.auto.value/auto-value/1.1|lib/gcloud/auto-value-1.1.jar
|
||||
maven://com.google.inject/guice/4.0|lib/gcloud/guice-4.0.jar
|
||||
maven://javax.inject/javax.inject/1|lib/gcloud/javax.inject-1.jar
|
||||
maven://aopalliance/aopalliance/1.0|lib/gcloud/aopalliance-1.0.jar
|
||||
maven://com.google.api.grpc/grpc-google-common-protos/0.0.7|lib/gcloud/grpc-google-common-protos-0.0.7.jar
|
||||
maven://org.json/json/20151123|lib/gcloud/json-20151123.jar
|
||||
maven://com.google.cloud.datastore/datastore-v1-protos/1.0.1|lib/gcloud/datastore-v1-protos-1.0.1-beta.jar
|
||||
maven://com.google.cloud.datastore/datastore-v1-proto-client/1.1.0|lib/gcloud/datastore-v1-proto-client-1.1.0.jar
|
||||
maven://com.google.http-client/google-http-client-protobuf/1.20.0|lib/gcloud/google-http-client-protobuf-1.20.0.jar
|
||||
maven://com.google.api-client/google-api-client/1.20.0|lib/gcloud/google-api-client-1.20.0.jar
|
||||
maven://com.google.guava/guava-jdk5/13.0|lib/gcloud/guava-jdk5-13.0.jar
|
|
@ -0,0 +1,18 @@
|
|||
[description]
|
||||
Control GCloud API classpath
|
||||
|
||||
[Tags]
|
||||
3rdparty
|
||||
gcloud
|
||||
|
||||
[lib]
|
||||
lib/gcloud/*.jar
|
||||
|
||||
[license]
|
||||
GCloudDatastore is an open source project hosted on Github and released under the Apache 2.0 license.
|
||||
https://github.com/GoogleCloudPlatform/gcloud-java
|
||||
http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
[ini-template]
|
||||
## Hide the gcloud libraries from deployed webapps
|
||||
jetty.webapp.addServerClasses,=file:${jetty.base}/lib/gcloud/
|
|
@ -1,92 +1,28 @@
|
|||
[description]
|
||||
Enables GCloudDatastore session management.
|
||||
|
||||
[Tags]
|
||||
session
|
||||
gcloud
|
||||
|
||||
[provides]
|
||||
session-store
|
||||
|
||||
[depends]
|
||||
gcloud-datastore
|
||||
annotations
|
||||
webapp
|
||||
sessions
|
||||
jcl-api
|
||||
jcl-impl
|
||||
|
||||
[files]
|
||||
basehome:etc/sessions/gcloud/index.yaml|etc/index.yaml
|
||||
maven://com.google.cloud/google-cloud-datastore/0.3.0|lib/gcloud/google-cloud-datastore-0.3.0.jar
|
||||
maven://com.google.cloud/google-cloud-core/0.3.0|lib/gcloud/google-cloud-core-0.3.0.jar
|
||||
maven://com.google.auth/google-auth-library-credentials/0.3.1|lib/gcloud/google-auth-library-credentials-0.3.1.jar
|
||||
maven://com.google.auth/google-auth-library-oauth2-http/0.3.1|lib/gcloud/google-auth-library-oauth2-http-0.3.1.jar
|
||||
maven://com.google.http-client/google-http-client-jackson2/1.19.0|lib/gcloud/google-http-client-jackson2-1.19.0.jar
|
||||
maven://com.fasterxml.jackson.core/jackson-core/2.1.3|lib/gcloud/jackson-core-2.1.3.jar
|
||||
maven://com.google.http-client/google-http-client/1.21.0|lib/gcloud/google-http-client-1.21.0.jar
|
||||
maven://com.google.code.findbugs/jsr305/1.3.9|lib/gcloud/jsr305-1.3.9.jar
|
||||
maven://org.apache.httpcomponents/httpclient/4.0.1|lib/gcloud/httpclient-4.0.1.jar
|
||||
maven://org.apache.httpcomponents/httpcore/4.0.1|lib/gcloud/httpcore-4.0.1.jar
|
||||
maven://commons-codec/commons-codec/1.3|lib/gcloud/commons-codec-1.3.jar
|
||||
maven://com.google.oauth-client/google-oauth-client/1.21.0|lib/gcloud/google-oauth-client-1.21.0.jar
|
||||
maven://com.google.guava/guava/19.0|lib/gcloud/guava-19.0.jar
|
||||
maven://com.google.api-client/google-api-client-appengine/1.21.0|lib/gcloud/google-api-client-appengine-1.21.0.jar
|
||||
maven://com.google.oauth-client/google-oauth-client-appengine/1.21.0|lib/gcloud/google-oauth-client-appengine-1.21.0.jar
|
||||
maven://com.google.oauth-client/google-oauth-client-servlet/1.21.0|lib/gcloud/google-oauth-client-servlet-1.21.0.jar
|
||||
maven://com.google.http-client/google-http-client-jdo/1.21.0|lib/gcloud/google-http-client-jdo-1.21.0.jar
|
||||
maven://com.google.api-client/google-api-client-servlet/1.21.0|lib/gcloud/google-api-client-servlet-1.21.0.jar
|
||||
maven://javax.jdo/jdo2-api/2.3-eb|lib/gcloud/jdo2-api-2.3-eb.jar
|
||||
maven://javax.transaction/transaction-api/1.1|lib/gcloud/transaction-api-1.1.jar
|
||||
maven://com.google.http-client/google-http-client-appengine/1.21.0|lib/gcloud/google-http-client-appengine-1.21.0.jar
|
||||
maven://com.google.http-client/google-http-client-jackson/1.21.0|lib/gcloud/google-http-client-jackson-1.21.0.jar
|
||||
maven://org.codehaus.jackson/jackson-core-asl/1.9.11|lib/gcloud/jackson-core-asl-1.9.11.jar
|
||||
maven://joda-time/joda-time/2.9.2|lib/gcloud/joda-time-2.9.2.jar
|
||||
maven://com.google.protobuf/protobuf-java/3.0.0-beta-3|lib/gcloud/protobuf-java-3.0.0-beta-3.jar
|
||||
maven://com.google.api/gax/0.0.16|lib/gcloud/gax-0.0.16.jar
|
||||
maven://io.grpc/grpc-all/0.15.0|lib/gcloud/grpc-all-0.15.0.jar
|
||||
maven://io.grpc/grpc-auth/0.15.0|lib/gcloud/grpc-auth-0.15.0.jar
|
||||
maven://io.grpc/grpc-netty/0.15.0|lib/gcloud/grpc-netty-0.15.0.jar
|
||||
maven://io.netty/netty-codec-http2/4.1.1.Final|lib/gcloud/netty-codec-http2-4.1.1.jar
|
||||
maven://io.netty/netty-codec-http/4.1.1.Final|lib/gcloud/netty-codec-http-4.1.1.Final.jar
|
||||
maven://io.netty/netty-codec/4.1.1.Final|lib/gcloud/netty-codec-4.1.1.Final.jar
|
||||
maven://io.netty/netty-handler/4.1.1.Final|lib/gcloud/netty-handler-4.1.1.Final.jar
|
||||
maven://io.netty/netty-buffer/4.1.1.Final|lib/gcloud/netty-buffer-4.1.1.Final.jar
|
||||
maven://io.netty/netty-common/4.1.1.Final|lib/gcloud/netty-common-4.1.1.Final.jar
|
||||
maven://io.netty/netty-transport/4.1.1.Final|lib/gcloud/netty-transport-4.1.1.Final.jar
|
||||
maven://io.netty/netty-resolver/4.1.1.Final|lib/gcloud/netty-resolver-4.1.1.Final.jar
|
||||
maven://io.grpc/grpc-okhttp/0.15.0|lib/gcloud/grpc-okhttp-0.15.0.jar
|
||||
maven://com.squareup.okio/okio/1.6.0|lib/gcloud/okio-1.6.0.jar
|
||||
maven://com.squareup.okhttp/okhttp/2.5.0|lib/gcloud/okhttp-2.5.0.jar
|
||||
maven://io.grpc/grpc-protobuf-nano/0.15.0|lib/gcloud/grpc-protobuf-nano-0.15.0.jar
|
||||
maven://com.google.protobuf.nano/protobuf-javanano/3.0.0-alpha-5|lib/gcloud/protobuf-javanano-3.0.0-alpha-5.jar
|
||||
maven://io.grpc/grpc-stub/0.15.0|lib/gcloud/grpc-stub-0.15.0.jar
|
||||
maven://io.grpc/grpc-protobuf/0.15.0|lib/gcloud/grpc-protobuf-0.15.0.jar
|
||||
maven://com.google.protobuf/protobuf-java-util/3.0.0-beta-3|lib/gcloud/protobuf-java-util-3.0.0-beta-3.jar
|
||||
maven://com.google.code.gson/gson/2.3|lib/gcloud/gson-2.3.jar
|
||||
maven://io.grpc/grpc-protobuf-lite/0.15.0|lib/gcloud/grpc-protobuf-lite-0.15.0.jar
|
||||
maven://io.grpc/grpc-core/0.15.0|lib/gcloud/grpc-core-0.15.0.jar
|
||||
maven://com.google.auto.value/auto-value/1.1|lib/gcloud/auto-value-1.1.jar
|
||||
maven://com.google.inject/guice/4.0|lib/gcloud/guice-4.0.jar
|
||||
maven://javax.inject/javax.inject/1|lib/gcloud/javax.inject-1.jar
|
||||
maven://aopalliance/aopalliance/1.0|lib/gcloud/aopalliance-1.0.jar
|
||||
maven://com.google.api.grpc/grpc-google-common-protos/0.0.7|lib/gcloud/grpc-google-common-protos-0.0.7.jar
|
||||
maven://org.json/json/20151123|lib/gcloud/json-20151123.jar
|
||||
maven://com.google.cloud.datastore/datastore-v1-protos/1.0.1|lib/gcloud/datastore-v1-protos-1.0.1-beta.jar
|
||||
maven://com.google.cloud.datastore/datastore-v1-proto-client/1.1.0|lib/gcloud/datastore-v1-proto-client-1.1.0.jar
|
||||
maven://com.google.http-client/google-http-client-protobuf/1.20.0|lib/gcloud/google-http-client-protobuf-1.20.0.jar
|
||||
maven://com.google.api-client/google-api-client/1.20.0|lib/gcloud/google-api-client-1.20.0.jar
|
||||
maven://com.google.guava/guava-jdk5/13.0|lib/gcloud/guava-jdk5-13.0.jar
|
||||
|
||||
|
||||
basehome:modules/gcloud/index.yaml|etc/index.yaml
|
||||
|
||||
[lib]
|
||||
lib/jetty-gcloud-session-manager-${jetty.version}.jar
|
||||
lib/gcloud/*.jar
|
||||
|
||||
[xml]
|
||||
etc/sessions/gcloud/session-store.xml
|
||||
|
||||
[license]
|
||||
GCloudDatastore is an open source project hosted on Github and released under the Apache 2.0 license.
|
||||
https://github.com/GoogleCloudPlatform/gcloud-java
|
||||
http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
[ini-template]
|
||||
|
||||
## GCloudDatastore Session config
|
||||
|
|
|
@ -808,6 +808,9 @@ public class GCloudSessionDataStore extends AbstractSessionDataStore
|
|||
{
|
||||
//need to assume that the problem is the index doesn't exist, because there
|
||||
//is no specific code for that
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Check for indexes", e);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<name>Jetty :: GCloud</name>
|
||||
|
||||
<properties>
|
||||
<gcloud.version>0.3.0</gcloud.version>
|
||||
<gcloud.version>0.4.0</gcloud.version>
|
||||
</properties>
|
||||
|
||||
<modules>
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
[description]
|
||||
Deploys the Hawtio console as a webapplication.
|
||||
|
||||
[Tags]
|
||||
3rdparty
|
||||
|
||||
[depend]
|
||||
stats
|
||||
deploy
|
||||
|
@ -13,6 +16,7 @@ etc/hawtio.xml
|
|||
etc/hawtio/
|
||||
lib/hawtio/
|
||||
https://oss.sonatype.org/content/repositories/public/io/hawt/hawtio-default/1.4.16/hawtio-default-1.4.16.war|lib/hawtio/hawtio.war
|
||||
basehome:modules/hawtio/hawtio.xml|etc/hawtio.xml
|
||||
|
||||
[license]
|
||||
Hawtio is a redhat JBoss project released under the Apache License, v2.0
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
[description]
|
||||
Deploys the JAMon webapplication
|
||||
|
||||
[Tags]
|
||||
3rdparty
|
||||
|
||||
[depend]
|
||||
stats
|
||||
deploy
|
||||
|
@ -14,6 +17,7 @@ etc/jamon.xml
|
|||
lib/jamon/
|
||||
maven://com.jamonapi/jamon/2.81|lib/jamon/jamon-2.81.jar
|
||||
maven://com.jamonapi/jamon_war/2.81/war|lib/jamon/jamon.war
|
||||
basehome:modules/jamon/jamon.xml|etc/jamon.xml
|
||||
|
||||
[lib]
|
||||
lib/jamon/**.jar
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
[description]
|
||||
Deploys the Jminix JMX Console within the server
|
||||
|
||||
[Tags]
|
||||
3rdparty
|
||||
|
||||
[depend]
|
||||
stats
|
||||
jmx
|
||||
|
@ -26,6 +29,7 @@ maven://commons-collections/commons-collections/3.2|lib/jminix/commons-collectio
|
|||
maven://net.sf.ezmorph/ezmorph/1.0.6|lib/jminix/ezmorph-1.0.6.jar
|
||||
maven://org.jgroups/jgroups/2.12.1.3.Final|lib/jminix/jgroups-2.12.1.3.Final.jar
|
||||
maven://org.jasypt/jasypt/1.8|lib/jminix/jasypt-1.8.jar
|
||||
basehome:modules/jminix/jminix.xml|etc/jminix.xml
|
||||
|
||||
[lib]
|
||||
lib/jminix/**.jar
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
[description]
|
||||
Deploys the Jolokia console as a web application.
|
||||
|
||||
[Tags]
|
||||
3rdparty
|
||||
|
||||
[depend]
|
||||
stats
|
||||
deploy
|
||||
|
@ -11,6 +14,7 @@ etc/jolokia.xml
|
|||
|
||||
[files]
|
||||
maven://org.jolokia/jolokia-war/1.2.2/war|lib/jolokia/jolokia.war
|
||||
basehome:modules/jolokia/jolokia.xml|etc/jolokia.xml
|
||||
|
||||
[license]
|
||||
Jolokia is released under the Apache License 2.0
|
||||
|
|
|
@ -18,6 +18,11 @@
|
|||
<artifactId>jetty-util</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-io</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.toolchain</groupId>
|
||||
<artifactId>jetty-test-helper</artifactId>
|
||||
|
|
|
@ -0,0 +1,423 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.http;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.zip.DataFormatException;
|
||||
import java.util.zip.Inflater;
|
||||
import java.util.zip.ZipException;
|
||||
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.component.Destroyable;
|
||||
|
||||
/**
|
||||
* Decoder for the "gzip" encoding.
|
||||
* <p>
|
||||
* A decoder that inflates gzip compressed data that has been
|
||||
* optimized for async usage with minimal data copies.
|
||||
*/
|
||||
public class GZIPContentDecoder implements Destroyable
|
||||
{
|
||||
private final Inflater _inflater = new Inflater(true);
|
||||
private final ByteBufferPool _pool;
|
||||
private final int _bufferSize;
|
||||
private State _state;
|
||||
private int _size;
|
||||
private int _value;
|
||||
private byte _flags;
|
||||
private ByteBuffer _inflated;
|
||||
|
||||
public GZIPContentDecoder()
|
||||
{
|
||||
this(null,2048);
|
||||
}
|
||||
|
||||
public GZIPContentDecoder(int bufferSize)
|
||||
{
|
||||
this(null,bufferSize);
|
||||
}
|
||||
|
||||
public GZIPContentDecoder(ByteBufferPool pool, int bufferSize)
|
||||
{
|
||||
_bufferSize = bufferSize;
|
||||
_pool = pool;
|
||||
reset();
|
||||
}
|
||||
|
||||
/** Inflate compressed data from a buffer.
|
||||
*
|
||||
* @param compressed Buffer containing compressed data.
|
||||
* @return Buffer containing inflated data.
|
||||
*/
|
||||
public ByteBuffer decode(ByteBuffer compressed)
|
||||
{
|
||||
decodeChunks(compressed);
|
||||
if (BufferUtil.isEmpty(_inflated) || _state==State.CRC || _state==State.ISIZE )
|
||||
return BufferUtil.EMPTY_BUFFER;
|
||||
|
||||
ByteBuffer result = _inflated;
|
||||
_inflated = null;
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Called when a chunk of data is inflated.
|
||||
* <p>The default implementation aggregates all the chunks
|
||||
* into a single buffer returned from {@link #decode(ByteBuffer)}.
|
||||
* Derived implementations may choose to consume chunks individually
|
||||
* and return false to prevent further inflation until a subsequent
|
||||
* call to {@link #decode(ByteBuffer)} or {@link #decodeChunks(ByteBuffer)}.
|
||||
*
|
||||
* @param chunk The inflated chunk of data
|
||||
* @return False if inflating should continue, or True if the call
|
||||
* to {@link #decodeChunks(ByteBuffer)} or {@link #decode(ByteBuffer)}
|
||||
* should return, allowing back pressure of compressed data.
|
||||
*/
|
||||
protected boolean decodedChunk(ByteBuffer chunk)
|
||||
{
|
||||
if (_inflated==null)
|
||||
{
|
||||
_inflated=chunk;
|
||||
}
|
||||
else
|
||||
{
|
||||
int size = _inflated.remaining() + chunk.remaining();
|
||||
if (size<=_inflated.capacity())
|
||||
{
|
||||
BufferUtil.append(_inflated,chunk);
|
||||
BufferUtil.put(chunk,_inflated);
|
||||
release(chunk);
|
||||
}
|
||||
else
|
||||
{
|
||||
ByteBuffer bigger=acquire(size);
|
||||
int pos=BufferUtil.flipToFill(bigger);
|
||||
BufferUtil.put(_inflated,bigger);
|
||||
BufferUtil.put(chunk,bigger);
|
||||
BufferUtil.flipToFlush(bigger,pos);
|
||||
release(_inflated);
|
||||
release(chunk);
|
||||
_inflated = bigger;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflate compressed data.
|
||||
* <p>Inflation continues until the compressed block end is reached, there is no
|
||||
* more compressed data or a call to {@link #decodedChunk(ByteBuffer)} returns true.
|
||||
* @param compressed Buffer of compressed data to inflate
|
||||
*/
|
||||
protected void decodeChunks(ByteBuffer compressed)
|
||||
{
|
||||
ByteBuffer buffer = null;
|
||||
try
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
switch (_state)
|
||||
{
|
||||
case INITIAL:
|
||||
{
|
||||
_state = State.ID;
|
||||
break;
|
||||
}
|
||||
|
||||
case FLAGS:
|
||||
{
|
||||
if ((_flags & 0x04) == 0x04)
|
||||
{
|
||||
_state = State.EXTRA_LENGTH;
|
||||
_size = 0;
|
||||
_value = 0;
|
||||
}
|
||||
else if ((_flags & 0x08) == 0x08)
|
||||
_state = State.NAME;
|
||||
else if ((_flags & 0x10) == 0x10)
|
||||
_state = State.COMMENT;
|
||||
else if ((_flags & 0x2) == 0x2)
|
||||
{
|
||||
_state = State.HCRC;
|
||||
_size = 0;
|
||||
_value = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
_state = State.DATA;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA:
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (buffer==null)
|
||||
buffer = acquire(_bufferSize);
|
||||
|
||||
try
|
||||
{
|
||||
int length = _inflater.inflate(buffer.array(),buffer.arrayOffset(),buffer.capacity());
|
||||
buffer.limit(length);
|
||||
}
|
||||
catch (DataFormatException x)
|
||||
{
|
||||
throw new ZipException(x.getMessage());
|
||||
}
|
||||
|
||||
if (buffer.hasRemaining())
|
||||
{
|
||||
ByteBuffer chunk = buffer;
|
||||
buffer = null;
|
||||
if (decodedChunk(chunk))
|
||||
return;
|
||||
}
|
||||
else if (_inflater.needsInput())
|
||||
{
|
||||
if (!compressed.hasRemaining())
|
||||
return;
|
||||
if (compressed.hasArray())
|
||||
{
|
||||
_inflater.setInput(compressed.array(),compressed.arrayOffset()+compressed.position(),compressed.remaining());
|
||||
compressed.position(compressed.limit());
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO use the pool
|
||||
byte[] input = new byte[compressed.remaining()];
|
||||
compressed.get(input);
|
||||
_inflater.setInput(input);
|
||||
}
|
||||
}
|
||||
else if (_inflater.finished())
|
||||
{
|
||||
int remaining = _inflater.getRemaining();
|
||||
compressed.position(compressed.limit() - remaining);
|
||||
_state = State.CRC;
|
||||
_size = 0;
|
||||
_value = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!compressed.hasRemaining())
|
||||
break;
|
||||
|
||||
byte currByte = compressed.get();
|
||||
switch (_state)
|
||||
{
|
||||
case ID:
|
||||
{
|
||||
_value += (currByte & 0xFF) << 8 * _size;
|
||||
++_size;
|
||||
if (_size == 2)
|
||||
{
|
||||
if (_value != 0x8B1F)
|
||||
throw new ZipException("Invalid gzip bytes");
|
||||
_state = State.CM;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CM:
|
||||
{
|
||||
if ((currByte & 0xFF) != 0x08)
|
||||
throw new ZipException("Invalid gzip compression method");
|
||||
_state = State.FLG;
|
||||
break;
|
||||
}
|
||||
case FLG:
|
||||
{
|
||||
_flags = currByte;
|
||||
_state = State.MTIME;
|
||||
_size = 0;
|
||||
_value = 0;
|
||||
break;
|
||||
}
|
||||
case MTIME:
|
||||
{
|
||||
// Skip the 4 MTIME bytes
|
||||
++_size;
|
||||
if (_size == 4)
|
||||
_state = State.XFL;
|
||||
break;
|
||||
}
|
||||
case XFL:
|
||||
{
|
||||
// Skip XFL
|
||||
_state = State.OS;
|
||||
break;
|
||||
}
|
||||
case OS:
|
||||
{
|
||||
// Skip OS
|
||||
_state = State.FLAGS;
|
||||
break;
|
||||
}
|
||||
case EXTRA_LENGTH:
|
||||
{
|
||||
_value += (currByte & 0xFF) << 8 * _size;
|
||||
++_size;
|
||||
if (_size == 2)
|
||||
_state = State.EXTRA;
|
||||
break;
|
||||
}
|
||||
case EXTRA:
|
||||
{
|
||||
// Skip EXTRA bytes
|
||||
--_value;
|
||||
if (_value == 0)
|
||||
{
|
||||
// Clear the EXTRA flag and loop on the flags
|
||||
_flags &= ~0x04;
|
||||
_state = State.FLAGS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NAME:
|
||||
{
|
||||
// Skip NAME bytes
|
||||
if (currByte == 0)
|
||||
{
|
||||
// Clear the NAME flag and loop on the flags
|
||||
_flags &= ~0x08;
|
||||
_state = State.FLAGS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case COMMENT:
|
||||
{
|
||||
// Skip COMMENT bytes
|
||||
if (currByte == 0)
|
||||
{
|
||||
// Clear the COMMENT flag and loop on the flags
|
||||
_flags &= ~0x10;
|
||||
_state = State.FLAGS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case HCRC:
|
||||
{
|
||||
// Skip HCRC
|
||||
++_size;
|
||||
if (_size == 2)
|
||||
{
|
||||
// Clear the HCRC flag and loop on the flags
|
||||
_flags &= ~0x02;
|
||||
_state = State.FLAGS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CRC:
|
||||
{
|
||||
_value += (currByte & 0xFF) << 8 * _size;
|
||||
++_size;
|
||||
if (_size == 4)
|
||||
{
|
||||
// From RFC 1952, compliant decoders need not to verify the CRC
|
||||
_state = State.ISIZE;
|
||||
_size = 0;
|
||||
_value = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ISIZE:
|
||||
{
|
||||
_value += (currByte & 0xFF) << 8 * _size;
|
||||
++_size;
|
||||
if (_size == 4)
|
||||
{
|
||||
if (_value != _inflater.getBytesWritten())
|
||||
throw new ZipException("Invalid input size");
|
||||
|
||||
// TODO ByteBuffer result = output == null ? BufferUtil.EMPTY_BUFFER : ByteBuffer.wrap(output);
|
||||
reset();
|
||||
return ;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new ZipException();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ZipException x)
|
||||
{
|
||||
throw new RuntimeException(x);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (buffer!=null)
|
||||
release(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
private void reset()
|
||||
{
|
||||
_inflater.reset();
|
||||
_state = State.INITIAL;
|
||||
_size = 0;
|
||||
_value = 0;
|
||||
_flags = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy()
|
||||
{
|
||||
_inflater.end();
|
||||
}
|
||||
|
||||
public boolean isFinished()
|
||||
{
|
||||
return _state == State.INITIAL;
|
||||
}
|
||||
|
||||
private enum State
|
||||
{
|
||||
INITIAL, ID, CM, FLG, MTIME, XFL, OS, FLAGS, EXTRA_LENGTH, EXTRA, NAME, COMMENT, HCRC, DATA, CRC, ISIZE
|
||||
}
|
||||
|
||||
/**
|
||||
* @return An indirect buffer of the configured buffersize either from the pool or freshly allocated.
|
||||
*/
|
||||
public ByteBuffer acquire(int capacity)
|
||||
{
|
||||
return _pool==null?BufferUtil.allocate(capacity):_pool.acquire(capacity,false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Release an allocated buffer.
|
||||
* <p>This method will called {@link ByteBufferPool#release(ByteBuffer)} if a buffer pool has
|
||||
* been configured. This method should be called once for all buffers returned from {@link #decode(ByteBuffer)}
|
||||
* or passed to {@link #decodedChunk(ByteBuffer)}.
|
||||
* @param buffer The buffer to release.
|
||||
*/
|
||||
public void release(ByteBuffer buffer)
|
||||
{
|
||||
if (_pool!=null && buffer!=BufferUtil.EMPTY_BUFFER)
|
||||
_pool.release(buffer);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,343 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.http;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
import org.eclipse.jetty.io.ArrayByteBufferPool;
|
||||
import org.eclipse.jetty.toolchain.test.TestTracker;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
|
||||
public class GZIPContentDecoderTest
|
||||
{
|
||||
@Rule
|
||||
public final TestTracker tracker = new TestTracker();
|
||||
|
||||
|
||||
ArrayByteBufferPool pool;
|
||||
AtomicInteger buffers = new AtomicInteger(0);
|
||||
|
||||
@Before
|
||||
public void beforeClass() throws Exception
|
||||
{
|
||||
buffers.set(0);
|
||||
pool = new ArrayByteBufferPool()
|
||||
{
|
||||
|
||||
@Override
|
||||
public ByteBuffer acquire(int size, boolean direct)
|
||||
{
|
||||
buffers.incrementAndGet();
|
||||
return super.acquire(size,direct);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release(ByteBuffer buffer)
|
||||
{
|
||||
buffers.decrementAndGet();
|
||||
super.release(buffer);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@After
|
||||
public void afterClass() throws Exception
|
||||
{
|
||||
assertEquals(0,buffers.get());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testStreamNoBlocks() throws Exception
|
||||
{
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
GZIPOutputStream output = new GZIPOutputStream(baos);
|
||||
output.close();
|
||||
byte[] bytes = baos.toByteArray();
|
||||
|
||||
GZIPInputStream input = new GZIPInputStream(new ByteArrayInputStream(bytes), 1);
|
||||
int read = input.read();
|
||||
assertEquals(-1, read);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStreamBigBlockOneByteAtATime() throws Exception
|
||||
{
|
||||
String data = "0123456789ABCDEF";
|
||||
for (int i = 0; i < 10; ++i)
|
||||
data += data;
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
GZIPOutputStream output = new GZIPOutputStream(baos);
|
||||
output.write(data.getBytes(StandardCharsets.UTF_8));
|
||||
output.close();
|
||||
byte[] bytes = baos.toByteArray();
|
||||
|
||||
baos = new ByteArrayOutputStream();
|
||||
GZIPInputStream input = new GZIPInputStream(new ByteArrayInputStream(bytes), 1);
|
||||
int read;
|
||||
while ((read = input.read()) >= 0)
|
||||
baos.write(read);
|
||||
assertEquals(data, new String(baos.toByteArray(), StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoBlocks() throws Exception
|
||||
{
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
GZIPOutputStream output = new GZIPOutputStream(baos);
|
||||
output.close();
|
||||
byte[] bytes = baos.toByteArray();
|
||||
|
||||
GZIPContentDecoder decoder = new GZIPContentDecoder(pool,2048);
|
||||
ByteBuffer decoded = decoder.decode(ByteBuffer.wrap(bytes));
|
||||
assertEquals(0, decoded.remaining());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSmallBlock() throws Exception
|
||||
{
|
||||
String data = "0";
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
GZIPOutputStream output = new GZIPOutputStream(baos);
|
||||
output.write(data.getBytes(StandardCharsets.UTF_8));
|
||||
output.close();
|
||||
byte[] bytes = baos.toByteArray();
|
||||
|
||||
GZIPContentDecoder decoder = new GZIPContentDecoder(pool,2048);
|
||||
ByteBuffer decoded = decoder.decode(ByteBuffer.wrap(bytes));
|
||||
assertEquals(data, StandardCharsets.UTF_8.decode(decoded).toString());
|
||||
decoder.release(decoded);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSmallBlockWithGZIPChunkedAtBegin() throws Exception
|
||||
{
|
||||
String data = "0";
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
GZIPOutputStream output = new GZIPOutputStream(baos);
|
||||
output.write(data.getBytes(StandardCharsets.UTF_8));
|
||||
output.close();
|
||||
byte[] bytes = baos.toByteArray();
|
||||
|
||||
// The header is 10 bytes, chunk at 11 bytes
|
||||
byte[] bytes1 = new byte[11];
|
||||
System.arraycopy(bytes, 0, bytes1, 0, bytes1.length);
|
||||
byte[] bytes2 = new byte[bytes.length - bytes1.length];
|
||||
System.arraycopy(bytes, bytes1.length, bytes2, 0, bytes2.length);
|
||||
|
||||
GZIPContentDecoder decoder = new GZIPContentDecoder(pool,2048);
|
||||
ByteBuffer decoded = decoder.decode(ByteBuffer.wrap(bytes1));
|
||||
assertEquals(0, decoded.capacity());
|
||||
decoded = decoder.decode(ByteBuffer.wrap(bytes2));
|
||||
assertEquals(data, StandardCharsets.UTF_8.decode(decoded).toString());
|
||||
decoder.release(decoded);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSmallBlockWithGZIPChunkedAtEnd() throws Exception
|
||||
{
|
||||
String data = "0";
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
GZIPOutputStream output = new GZIPOutputStream(baos);
|
||||
output.write(data.getBytes(StandardCharsets.UTF_8));
|
||||
output.close();
|
||||
byte[] bytes = baos.toByteArray();
|
||||
|
||||
// The trailer is 8 bytes, chunk the last 9 bytes
|
||||
byte[] bytes1 = new byte[bytes.length - 9];
|
||||
System.arraycopy(bytes, 0, bytes1, 0, bytes1.length);
|
||||
byte[] bytes2 = new byte[bytes.length - bytes1.length];
|
||||
System.arraycopy(bytes, bytes1.length, bytes2, 0, bytes2.length);
|
||||
|
||||
GZIPContentDecoder decoder = new GZIPContentDecoder(pool,2048);
|
||||
ByteBuffer decoded = decoder.decode(ByteBuffer.wrap(bytes1));
|
||||
assertEquals(data, StandardCharsets.UTF_8.decode(decoded).toString());
|
||||
assertFalse(decoder.isFinished());
|
||||
decoder.release(decoded);
|
||||
decoded = decoder.decode(ByteBuffer.wrap(bytes2));
|
||||
assertEquals(0, decoded.remaining());
|
||||
assertTrue(decoder.isFinished());
|
||||
decoder.release(decoded);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSmallBlockWithGZIPTrailerChunked() throws Exception
|
||||
{
|
||||
String data = "0";
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
GZIPOutputStream output = new GZIPOutputStream(baos);
|
||||
output.write(data.getBytes(StandardCharsets.UTF_8));
|
||||
output.close();
|
||||
byte[] bytes = baos.toByteArray();
|
||||
|
||||
// The trailer is 4+4 bytes, chunk the last 3 bytes
|
||||
byte[] bytes1 = new byte[bytes.length - 3];
|
||||
System.arraycopy(bytes, 0, bytes1, 0, bytes1.length);
|
||||
byte[] bytes2 = new byte[bytes.length - bytes1.length];
|
||||
System.arraycopy(bytes, bytes1.length, bytes2, 0, bytes2.length);
|
||||
|
||||
GZIPContentDecoder decoder = new GZIPContentDecoder(pool,2048);
|
||||
ByteBuffer decoded = decoder.decode(ByteBuffer.wrap(bytes1));
|
||||
assertEquals(0, decoded.capacity());
|
||||
decoder.release(decoded);
|
||||
decoded = decoder.decode(ByteBuffer.wrap(bytes2));
|
||||
assertEquals(data, StandardCharsets.UTF_8.decode(decoded).toString());
|
||||
decoder.release(decoded);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTwoSmallBlocks() throws Exception
|
||||
{
|
||||
String data1 = "0";
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
GZIPOutputStream output = new GZIPOutputStream(baos);
|
||||
output.write(data1.getBytes(StandardCharsets.UTF_8));
|
||||
output.close();
|
||||
byte[] bytes1 = baos.toByteArray();
|
||||
|
||||
String data2 = "1";
|
||||
baos = new ByteArrayOutputStream();
|
||||
output = new GZIPOutputStream(baos);
|
||||
output.write(data2.getBytes(StandardCharsets.UTF_8));
|
||||
output.close();
|
||||
byte[] bytes2 = baos.toByteArray();
|
||||
|
||||
byte[] bytes = new byte[bytes1.length + bytes2.length];
|
||||
System.arraycopy(bytes1, 0, bytes, 0, bytes1.length);
|
||||
System.arraycopy(bytes2, 0, bytes, bytes1.length, bytes2.length);
|
||||
|
||||
GZIPContentDecoder decoder = new GZIPContentDecoder(pool,2048);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(bytes);
|
||||
ByteBuffer decoded = decoder.decode(buffer);
|
||||
assertEquals(data1, StandardCharsets.UTF_8.decode(decoded).toString());
|
||||
assertTrue(decoder.isFinished());
|
||||
assertTrue(buffer.hasRemaining());
|
||||
decoder.release(decoded);
|
||||
decoded = decoder.decode(buffer);
|
||||
assertEquals(data2, StandardCharsets.UTF_8.decode(decoded).toString());
|
||||
assertTrue(decoder.isFinished());
|
||||
assertFalse(buffer.hasRemaining());
|
||||
decoder.release(decoded);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBigBlock() throws Exception
|
||||
{
|
||||
String data = "0123456789ABCDEF";
|
||||
for (int i = 0; i < 10; ++i)
|
||||
data += data;
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
GZIPOutputStream output = new GZIPOutputStream(baos);
|
||||
output.write(data.getBytes(StandardCharsets.UTF_8));
|
||||
output.close();
|
||||
byte[] bytes = baos.toByteArray();
|
||||
|
||||
String result = "";
|
||||
GZIPContentDecoder decoder = new GZIPContentDecoder(pool,2048);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(bytes);
|
||||
while (buffer.hasRemaining())
|
||||
{
|
||||
ByteBuffer decoded = decoder.decode(buffer);
|
||||
result += StandardCharsets.UTF_8.decode(decoded).toString();
|
||||
decoder.release(decoded);
|
||||
}
|
||||
assertEquals(data, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBigBlockOneByteAtATime() throws Exception
|
||||
{
|
||||
String data = "0123456789ABCDEF";
|
||||
for (int i = 0; i < 10; ++i)
|
||||
data += data;
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
GZIPOutputStream output = new GZIPOutputStream(baos);
|
||||
output.write(data.getBytes(StandardCharsets.UTF_8));
|
||||
output.close();
|
||||
byte[] bytes = baos.toByteArray();
|
||||
|
||||
String result = "";
|
||||
GZIPContentDecoder decoder = new GZIPContentDecoder(64);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(bytes);
|
||||
while (buffer.hasRemaining())
|
||||
{
|
||||
ByteBuffer decoded = decoder.decode(ByteBuffer.wrap(new byte[]{buffer.get()}));
|
||||
if (decoded.hasRemaining())
|
||||
result += StandardCharsets.UTF_8.decode(decoded).toString();
|
||||
decoder.release(decoded);
|
||||
}
|
||||
assertEquals(data, result);
|
||||
assertTrue(decoder.isFinished());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBigBlockWithExtraBytes() throws Exception
|
||||
{
|
||||
String data1 = "0123456789ABCDEF";
|
||||
for (int i = 0; i < 10; ++i)
|
||||
data1 += data1;
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
GZIPOutputStream output = new GZIPOutputStream(baos);
|
||||
output.write(data1.getBytes(StandardCharsets.UTF_8));
|
||||
output.close();
|
||||
byte[] bytes1 = baos.toByteArray();
|
||||
|
||||
String data2 = "HELLO";
|
||||
byte[] bytes2 = data2.getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
byte[] bytes = new byte[bytes1.length + bytes2.length];
|
||||
System.arraycopy(bytes1, 0, bytes, 0, bytes1.length);
|
||||
System.arraycopy(bytes2, 0, bytes, bytes1.length, bytes2.length);
|
||||
|
||||
String result = "";
|
||||
GZIPContentDecoder decoder = new GZIPContentDecoder(64);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(bytes);
|
||||
while (buffer.hasRemaining())
|
||||
{
|
||||
ByteBuffer decoded = decoder.decode(buffer);
|
||||
if (decoded.hasRemaining())
|
||||
result += StandardCharsets.UTF_8.decode(decoded).toString();
|
||||
decoder.release(decoded);
|
||||
if (decoder.isFinished())
|
||||
break;
|
||||
}
|
||||
assertEquals(data1, result);
|
||||
assertTrue(buffer.hasRemaining());
|
||||
assertEquals(data2, StandardCharsets.UTF_8.decode(buffer).toString());
|
||||
}
|
||||
}
|
|
@ -118,6 +118,14 @@ public class SessionFailureTest extends AbstractTest
|
|||
Assert.assertTrue(writeLatch.await(5, TimeUnit.SECONDS));
|
||||
Assert.assertTrue(serverFailureLatch.await(5, TimeUnit.SECONDS));
|
||||
Assert.assertTrue(clientFailureLatch.await(5, TimeUnit.SECONDS));
|
||||
Assert.assertFalse(((HTTP2Session)session).getEndPoint().isOpen());
|
||||
long start = System.nanoTime();
|
||||
long now = System.nanoTime();
|
||||
while (((HTTP2Session)session).getEndPoint().isOpen())
|
||||
{
|
||||
if (TimeUnit.NANOSECONDS.toSeconds(now-start)>5)
|
||||
Assert.fail();
|
||||
Thread.sleep(10);
|
||||
now = System.nanoTime();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,12 @@
|
|||
Enables HTTP2 protocol support on the TLS(SSL) Connector,
|
||||
using the ALPN extension to select which protocol to use.
|
||||
|
||||
[Tags]
|
||||
connector
|
||||
http2
|
||||
http
|
||||
ssl
|
||||
|
||||
[depend]
|
||||
ssl
|
||||
alpn
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
Enables the HTTP2C protocol on the HTTP Connector
|
||||
The connector will accept both HTTP/1 and HTTP/2 connections.
|
||||
|
||||
[Tags]
|
||||
connector
|
||||
http2
|
||||
http
|
||||
|
||||
[depend]
|
||||
http
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
[description]
|
||||
Enables session data store in a local Infinispan cache
|
||||
|
||||
[Tags]
|
||||
session
|
||||
|
||||
[provides]
|
||||
session-store
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
[description]
|
||||
Enables session data store in a remote Infinispan cache
|
||||
|
||||
[Tags]
|
||||
session
|
||||
|
||||
[provides]
|
||||
session-store
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
[description]
|
||||
Memcache cache for SessionData
|
||||
|
||||
[Tags]
|
||||
session
|
||||
|
||||
[depends]
|
||||
session-store
|
||||
slf4j-api
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
[description]
|
||||
Enables NoSql session management with a MongoDB driver.
|
||||
|
||||
[Tags]
|
||||
session
|
||||
|
||||
[provides]
|
||||
session-store
|
||||
|
||||
|
|
|
@ -18,11 +18,14 @@
|
|||
|
||||
package org.eclipse.jetty.osgi.annotations;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.ServletContainerInitializer;
|
||||
|
||||
import org.eclipse.jetty.annotations.AnnotationParser.Handler;
|
||||
import org.eclipse.jetty.annotations.ClassNameResolver;
|
||||
import org.eclipse.jetty.osgi.boot.OSGiWebInfConfiguration;
|
||||
import org.eclipse.jetty.osgi.boot.OSGiWebappConstants;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
|
@ -44,9 +47,9 @@ public class AnnotationConfiguration extends org.eclipse.jetty.annotations.Annot
|
|||
public class BundleParserTask extends ParserTask
|
||||
{
|
||||
|
||||
public BundleParserTask (AnnotationParser parser, Set<? extends Handler>handlers, Resource resource, ClassNameResolver resolver)
|
||||
public BundleParserTask (AnnotationParser parser, Set<? extends Handler>handlers, Resource resource)
|
||||
{
|
||||
super(parser, handlers, resource, resolver);
|
||||
super(parser, handlers, resource);
|
||||
}
|
||||
|
||||
public Void call() throws Exception
|
||||
|
@ -57,7 +60,7 @@ public class AnnotationConfiguration extends org.eclipse.jetty.annotations.Annot
|
|||
Bundle bundle = osgiAnnotationParser.getBundle(_resource);
|
||||
if (_stat != null)
|
||||
_stat.start();
|
||||
osgiAnnotationParser.parse(_handlers, bundle, _resolver);
|
||||
osgiAnnotationParser.parse(_handlers, bundle);
|
||||
if (_stat != null)
|
||||
_stat.end();
|
||||
}
|
||||
|
@ -79,6 +82,17 @@ public class AnnotationConfiguration extends org.eclipse.jetty.annotations.Annot
|
|||
return new AnnotationParser();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resource getJarFor(ServletContainerInitializer service) throws MalformedURLException, IOException
|
||||
{
|
||||
Resource resource = super.getJarFor(service);
|
||||
// TODO This is not correct, but implemented like this to be bug for bug compatible
|
||||
// with previous implementation that could only handle actual jars and not bundles.
|
||||
if (resource!=null && !resource.toString().endsWith(".jar"))
|
||||
return null;
|
||||
return resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Here is the order in which jars and osgi artifacts are scanned for discoverable annotations.
|
||||
* <ol>
|
||||
|
@ -195,47 +209,13 @@ public class AnnotationConfiguration extends org.eclipse.jetty.annotations.Annot
|
|||
handlers.add(_classInheritanceHandler);
|
||||
handlers.addAll(_containerInitializerAnnotationHandlers);
|
||||
|
||||
ClassNameResolver classNameResolver = createClassNameResolver(context);
|
||||
if (_parserTasks != null)
|
||||
{
|
||||
BundleParserTask task = new BundleParserTask(parser, handlers, bundleRes, classNameResolver);
|
||||
BundleParserTask task = new BundleParserTask(parser, handlers, bundleRes);
|
||||
_parserTasks.add(task);
|
||||
if (LOG.isDebugEnabled())
|
||||
task.setStatistic(new TimeStatistic());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the same classname resolver than for the webInfjar scanner
|
||||
* @param context the web app context
|
||||
* @return the class name resolver
|
||||
*/
|
||||
protected ClassNameResolver createClassNameResolver(final WebAppContext context)
|
||||
{
|
||||
return createClassNameResolver(context,true,false,false,false);
|
||||
}
|
||||
|
||||
protected ClassNameResolver createClassNameResolver(final WebAppContext context,
|
||||
final boolean excludeSysClass, final boolean excludeServerClass, final boolean excludeEverythingElse,
|
||||
final boolean overrideIsParenLoaderIsPriority)
|
||||
{
|
||||
return new ClassNameResolver ()
|
||||
{
|
||||
public boolean isExcluded (String name)
|
||||
{
|
||||
if (context.isSystemClass(name)) return excludeSysClass;
|
||||
if (context.isServerClass(name)) return excludeServerClass;
|
||||
return excludeEverythingElse;
|
||||
}
|
||||
|
||||
public boolean shouldOverride (String name)
|
||||
{
|
||||
//looking at system classpath
|
||||
if (context.isParentLoaderPriority())
|
||||
return overrideIsParenLoaderIsPriority;
|
||||
return !overrideIsParenLoaderIsPriority;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -29,7 +29,6 @@ import java.util.StringTokenizer;
|
|||
import java.util.TreeSet;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.eclipse.jetty.annotations.ClassNameResolver;
|
||||
import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelperFactory;
|
||||
import org.eclipse.jetty.util.ConcurrentHashSet;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
|
@ -85,7 +84,7 @@ public class AnnotationParser extends org.eclipse.jetty.annotations.AnnotationPa
|
|||
*
|
||||
*/
|
||||
@Override
|
||||
public void parse (Set<? extends Handler> handlers, URI[] uris, ClassNameResolver resolver)
|
||||
public void parse (Set<? extends Handler> handlers, URI[] uris)
|
||||
throws Exception
|
||||
{
|
||||
for (URI uri : uris)
|
||||
|
@ -99,16 +98,16 @@ public class AnnotationParser extends org.eclipse.jetty.annotations.AnnotationPa
|
|||
}
|
||||
//a jar in WEB-INF/lib or the WEB-INF/classes
|
||||
//use the behavior of the super class for a standard jar.
|
||||
super.parse(handlers, new URI[] {uri},resolver);
|
||||
super.parse(handlers, new URI[] {uri});
|
||||
}
|
||||
else
|
||||
{
|
||||
parse(handlers, associatedBundle,resolver);
|
||||
parse(handlers, associatedBundle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void parse(Set<? extends Handler> handlers, Bundle bundle, ClassNameResolver resolver)
|
||||
protected void parse(Set<? extends Handler> handlers, Bundle bundle)
|
||||
throws Exception
|
||||
{
|
||||
URI uri = _bundleToUri.get(bundle);
|
||||
|
@ -205,7 +204,7 @@ public class AnnotationParser extends org.eclipse.jetty.annotations.AnnotationPa
|
|||
}
|
||||
//transform into a classname to pass to the resolver
|
||||
String shortName = name.replace('/', '.').substring(0,name.length()-6);
|
||||
if ((resolver == null) || (!resolver.isExcluded(shortName) && (!isParsed(shortName) || resolver.shouldOverride(shortName))))
|
||||
if (!isParsed(shortName))
|
||||
{
|
||||
try (InputStream classInputStream = classUrl.openStream())
|
||||
{
|
||||
|
|
|
@ -47,18 +47,16 @@ public abstract class AbstractOSGiApp extends App
|
|||
private static final Logger LOG = Log.getLogger(AbstractOSGiApp.class);
|
||||
|
||||
protected Bundle _bundle;
|
||||
protected Dictionary _properties;
|
||||
protected Dictionary<?,?> _properties;
|
||||
protected ServiceRegistration _registration;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public AbstractOSGiApp(DeploymentManager manager, AppProvider provider, Bundle bundle, String originId)
|
||||
{
|
||||
super(manager, provider, originId);
|
||||
_properties = bundle.getHeaders();
|
||||
_bundle = bundle;
|
||||
this (manager, provider, bundle, bundle.getHeaders(), originId);
|
||||
}
|
||||
/* ------------------------------------------------------------ */
|
||||
public AbstractOSGiApp(DeploymentManager manager, AppProvider provider, Bundle bundle, Dictionary properties, String originId)
|
||||
public AbstractOSGiApp(DeploymentManager manager, AppProvider provider, Bundle bundle, Dictionary<?,?> properties, String originId)
|
||||
{
|
||||
super(manager, provider, originId);
|
||||
_properties = properties;
|
||||
|
|
|
@ -38,6 +38,7 @@ import org.eclipse.jetty.util.log.Logger;
|
|||
import org.eclipse.jetty.util.resource.JarResource;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.webapp.Configuration;
|
||||
import org.eclipse.jetty.webapp.WebAppClassLoader;
|
||||
import org.eclipse.jetty.webapp.WebAppContext;
|
||||
import org.eclipse.jetty.xml.XmlConfiguration;
|
||||
import org.osgi.framework.Bundle;
|
||||
|
@ -436,18 +437,23 @@ public abstract class AbstractWebAppProvider extends AbstractLifeCycle implement
|
|||
}
|
||||
}
|
||||
}
|
||||
if (contextXmlUrl == null) return;
|
||||
if (contextXmlUrl == null)
|
||||
return;
|
||||
|
||||
// Apply it just as the standard jetty ContextProvider would do
|
||||
LOG.info("Applying " + contextXmlUrl + " to " + _webApp);
|
||||
|
||||
XmlConfiguration xmlConfiguration = new XmlConfiguration(contextXmlUrl);
|
||||
HashMap properties = new HashMap();
|
||||
properties.put("Server", getDeploymentManager().getServer());
|
||||
properties.put(OSGiWebappConstants.JETTY_BUNDLE_ROOT, rootResource.toString());
|
||||
properties.put(OSGiServerConstants.JETTY_HOME, getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME));
|
||||
xmlConfiguration.getProperties().putAll(properties);
|
||||
xmlConfiguration.configure(_webApp);
|
||||
WebAppClassLoader.runWithServerClassAccess(()->
|
||||
{
|
||||
HashMap<String,String> properties = new HashMap<>();
|
||||
xmlConfiguration.getIdMap().put("Server",getDeploymentManager().getServer());
|
||||
properties.put(OSGiWebappConstants.JETTY_BUNDLE_ROOT, rootResource.toString());
|
||||
properties.put(OSGiServerConstants.JETTY_HOME, (String)getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME));
|
||||
xmlConfiguration.getProperties().putAll(properties);
|
||||
xmlConfiguration.configure(_webApp);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
|
|
@ -175,7 +175,7 @@ public class BundleWebAppProvider extends AbstractWebAppProvider implements Bund
|
|||
String contextPath = null;
|
||||
try
|
||||
{
|
||||
Dictionary headers = bundle.getHeaders();
|
||||
Dictionary<String,String> headers = bundle.getHeaders();
|
||||
|
||||
//does the bundle have a OSGiWebappConstants.JETTY_WAR_FOLDER_PATH
|
||||
String resourcePath = Util.getManifestHeaderValue(OSGiWebappConstants.JETTY_WAR_FOLDER_PATH, OSGiWebappConstants.JETTY_WAR_RESOURCE_PATH, headers);
|
||||
|
@ -217,7 +217,7 @@ public class BundleWebAppProvider extends AbstractWebAppProvider implements Bund
|
|||
{
|
||||
//Could be a static webapp with no web.xml
|
||||
String base = ".";
|
||||
contextPath = (String)headers.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH);
|
||||
contextPath = headers.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH);
|
||||
String originId = getOriginId(bundle,base);
|
||||
|
||||
OSGiApp app = new OSGiApp(getDeploymentManager(), this, bundle, originId);
|
||||
|
|
|
@ -78,8 +78,6 @@ public class OSGiWebappClassLoader extends WebAppClassLoader implements BundleRe
|
|||
|
||||
private Bundle _contributor;
|
||||
|
||||
private boolean _lookInOsgiFirst = true;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @param parent The parent classloader.
|
||||
|
@ -96,11 +94,26 @@ public class OSGiWebappClassLoader extends WebAppClassLoader implements BundleRe
|
|||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public Class<?> loadClass(String name) throws ClassNotFoundException
|
||||
{
|
||||
return super.loadClass(name);
|
||||
try
|
||||
{
|
||||
return _osgiBundleClassLoader.loadClass(name);
|
||||
}
|
||||
catch (ClassNotFoundException cne)
|
||||
{
|
||||
try
|
||||
{
|
||||
return super.loadClass(name);
|
||||
}
|
||||
catch (ClassNotFoundException cne2)
|
||||
{
|
||||
throw cne;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -121,36 +134,18 @@ public class OSGiWebappClassLoader extends WebAppClassLoader implements BundleRe
|
|||
{
|
||||
Enumeration<URL> osgiUrls = _osgiBundleClassLoader.getResources(name);
|
||||
Enumeration<URL> urls = super.getResources(name);
|
||||
if (_lookInOsgiFirst)
|
||||
{
|
||||
return Collections.enumeration(toList(osgiUrls, urls));
|
||||
}
|
||||
else
|
||||
{
|
||||
return Collections.enumeration(toList(urls, osgiUrls));
|
||||
}
|
||||
List<URL> resources = toList(osgiUrls, urls);
|
||||
return Collections.enumeration(resources);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public URL getResource(String name)
|
||||
{
|
||||
if (_lookInOsgiFirst)
|
||||
{
|
||||
URL url = _osgiBundleClassLoader.getResource(name);
|
||||
return url != null ? url : super.getResource(name);
|
||||
}
|
||||
else
|
||||
{
|
||||
URL url = super.getResource(name);
|
||||
return url != null ? url : _osgiBundleClassLoader.getResource(name);
|
||||
}
|
||||
URL url = _osgiBundleClassLoader.getResource(name);
|
||||
return url != null ? url : super.getResource(name);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
private List<URL> toList(Enumeration<URL> e, Enumeration<URL> e2)
|
||||
{
|
||||
|
@ -160,30 +155,7 @@ public class OSGiWebappClassLoader extends WebAppClassLoader implements BundleRe
|
|||
while (e2 != null && e2.hasMoreElements())
|
||||
list.add(e2.nextElement());
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
protected Class<?> findClass(String name) throws ClassNotFoundException
|
||||
{
|
||||
try
|
||||
{
|
||||
return _lookInOsgiFirst ? _osgiBundleClassLoader.loadClass(name) : super.findClass(name);
|
||||
}
|
||||
catch (ClassNotFoundException cne)
|
||||
{
|
||||
try
|
||||
{
|
||||
return _lookInOsgiFirst ? super.findClass(name) : _osgiBundleClassLoader.loadClass(name);
|
||||
}
|
||||
catch (ClassNotFoundException cne2)
|
||||
{
|
||||
throw cne;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
<Set name="minGzipSize"><Property name="jetty.gzip.minGzipSize" deprecated="gzip.minGzipSize" default="2048"/></Set>
|
||||
<Set name="checkGzExists"><Property name="jetty.gzip.checkGzExists" deprecated="gzip.checkGzExists" default="false"/></Set>
|
||||
<Set name="compressionLevel"><Property name="jetty.gzip.compressionLevel" deprecated="gzip.compressionLevel" default="-1"/></Set>
|
||||
<Set name="inflateBufferSize"><Property name="jetty.gzip.inflateBufferSize" default="0/></Set>
|
||||
|
||||
<Set name="excludedAgentPatterns">
|
||||
<Array type="String">
|
||||
<Item><Property name="jetty.gzip.excludedUserAgent" deprecated="gzip.excludedUserAgent" default=".*MSIE.6\.0.*"/></Item>
|
||||
|
|
|
@ -12,7 +12,18 @@
|
|||
<Arg>
|
||||
<Ref refid="Server"/>
|
||||
</Arg>
|
||||
<Set name="workerName"><Property name="jetty.sessionIdManager.workerName"><Default>node<Env name="JETTY_WORKER_INSTANCE" default="0"/></Default></Property></Set>
|
||||
<Set name="workerName">
|
||||
<Property name="jetty.sessionIdManager.workerName">
|
||||
<Default>node<Env name="JETTY_WORKER_INSTANCE">
|
||||
<Default>
|
||||
<Env name="GAE_MODULE_INSTANCE">
|
||||
<Default>0</Default>
|
||||
</Env>
|
||||
</Default>
|
||||
</Env>
|
||||
</Default>
|
||||
</Property>
|
||||
</Set>
|
||||
|
||||
<!-- ===================================================================== -->
|
||||
<!-- Configure a session housekeeper to help with scavenging -->
|
||||
|
|
|
@ -3,6 +3,9 @@ Enables the DebugListener to generate additional
|
|||
logging regarding detailed request handling events.
|
||||
Renames threads to include request URI.
|
||||
|
||||
[Tags]
|
||||
debug
|
||||
|
||||
[depend]
|
||||
deploy
|
||||
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
Deprecated Debug Log using the DebugHandle.
|
||||
Replaced with the debug module.
|
||||
|
||||
[Tags]
|
||||
debug
|
||||
|
||||
[depend]
|
||||
server
|
||||
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
Adds all jar files discovered in $JETTY_HOME/lib/ext
|
||||
and $JETTY_BASE/lib/ext to the servers classpath.
|
||||
|
||||
[Tags]
|
||||
classpath
|
||||
|
||||
[lib]
|
||||
lib/ext/**.jar
|
||||
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
Enable GzipHandler for dynamic gzip compression
|
||||
for the entire server.
|
||||
|
||||
[Tags]
|
||||
handler
|
||||
|
||||
[depend]
|
||||
server
|
||||
|
||||
|
@ -20,3 +23,6 @@ etc/jetty-gzip.xml
|
|||
|
||||
## User agents for which gzip is disabled
|
||||
# jetty.gzip.excludedUserAgent=.*MSIE.6\.0.*
|
||||
|
||||
## Inflate request buffer size, or 0 for no request inflation
|
||||
# jetty.gzip.inflateBufferSize=0
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
Adds a forwarded request customizer to the HTTP Connector
|
||||
to process forwarded-for style headers from a proxy.
|
||||
|
||||
[Tags]
|
||||
connector
|
||||
|
||||
[depend]
|
||||
http
|
||||
|
||||
|
|
|
@ -3,6 +3,10 @@ Enables a HTTP connector on the server.
|
|||
By default HTTP/1 is support, but HTTP2C can
|
||||
be added to the connector with the http2c module.
|
||||
|
||||
[Tags]
|
||||
connector
|
||||
http
|
||||
|
||||
[depend]
|
||||
server
|
||||
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
[description]
|
||||
Adds HTTPS protocol support to the TLS(SSL) Connector
|
||||
|
||||
[Tags]
|
||||
connector
|
||||
https
|
||||
http
|
||||
ssl
|
||||
|
||||
[depend]
|
||||
ssl
|
||||
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
Enable the ipaccess handler to apply a white/black list
|
||||
control of the remote IP of requests.
|
||||
|
||||
[Tags]
|
||||
handler
|
||||
|
||||
[depend]
|
||||
server
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
[description]
|
||||
A noop module that creates an ini template useful for
|
||||
setting JVM arguments (eg -Xmx )
|
||||
|
||||
[ini-template]
|
||||
## JVM Configuration
|
||||
## If JVM args are include in an ini file then --exec is needed
|
||||
|
@ -16,7 +17,7 @@ setting JVM arguments (eg -Xmx )
|
|||
# -XX:+CMSClassUnloadingEnabled
|
||||
# -XX:+UseCMSCompactAtFullCollection
|
||||
# -XX:CMSInitiatingOccupancyFraction=80
|
||||
# -verbose:gc
|
||||
# -internal:gc
|
||||
# -XX:+PrintGCDateStamps
|
||||
# -XX:+PrintGCTimeStamps
|
||||
# -XX:+PrintGCDetails
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
[description]
|
||||
Enables logback request log.
|
||||
|
||||
[Tags]
|
||||
requestlog
|
||||
logging
|
||||
logback
|
||||
|
||||
[depend]
|
||||
server
|
||||
logback-core
|
||||
|
|
|
@ -5,6 +5,10 @@ This allows a Proxy operating in TCP mode to transport
|
|||
details of the proxied connection to the server.
|
||||
Both V1 and V2 versions of the protocol are supported.
|
||||
|
||||
[Tags]
|
||||
connector
|
||||
ssl
|
||||
|
||||
[depend]
|
||||
ssl
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
[description]
|
||||
Enables a NCSA style request log.
|
||||
|
||||
[Tags]
|
||||
requestlog
|
||||
|
||||
[depend]
|
||||
server
|
||||
|
||||
|
@ -36,4 +39,4 @@ logs/
|
|||
# jetty.requestlog.timezone=GMT
|
||||
|
||||
## Whether to log LogLatency
|
||||
# jetty.requestlog.loglatency=false
|
||||
# jetty.requestlog.loglatency=false
|
||||
|
|
|
@ -3,6 +3,9 @@ Adds the $JETTY_HOME/resources and/or $JETTY_BASE/resources
|
|||
directory to the server classpath. Useful for configuration
|
||||
property files (eg jetty-logging.properties)
|
||||
|
||||
[Tags]
|
||||
classpath
|
||||
|
||||
[lib]
|
||||
resources/
|
||||
|
||||
|
|
|
@ -4,6 +4,9 @@ If not enabled, sessions will use a HashSessionCache by default, so enabling
|
|||
via this module is only needed if the configuration properties need to be
|
||||
changed.
|
||||
|
||||
[Tags]
|
||||
session
|
||||
|
||||
[provides]
|
||||
session-cache
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
[description]
|
||||
A trivial SessionCache that does not actually cache sessions.
|
||||
|
||||
[Tags]
|
||||
session
|
||||
|
||||
[provides]
|
||||
session-cache
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
[description]
|
||||
Enables caching of SessionData in front of a SessionDataStore.
|
||||
|
||||
[Tags]
|
||||
session
|
||||
|
||||
|
||||
[depend]
|
||||
session-store
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
[description]
|
||||
Enables session persistent storage in files.
|
||||
|
||||
[Tags]
|
||||
session
|
||||
|
||||
[provides]
|
||||
session-store
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
[description]
|
||||
Enables JDBC peristent/distributed session storage.
|
||||
|
||||
[Tags]
|
||||
session
|
||||
|
||||
[provides]
|
||||
session-store
|
||||
|
||||
|
|
|
@ -5,6 +5,9 @@ created or by enabling other session-cache or session-store
|
|||
modules. Without this module enabled, the server may still
|
||||
use sessions, but their management cannot be configured.
|
||||
|
||||
[Tags]
|
||||
session
|
||||
|
||||
[depends]
|
||||
server
|
||||
|
||||
|
|
|
@ -3,6 +3,10 @@ Enables a TLS(SSL) Connector on the server.
|
|||
This may be used for HTTPS and/or HTTP2 by enabling
|
||||
the associated support modules.
|
||||
|
||||
[Tags]
|
||||
connector
|
||||
ssl
|
||||
|
||||
[depend]
|
||||
server
|
||||
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
Enable detailed statistics collection for the server,
|
||||
available via JMX.
|
||||
|
||||
[Tags]
|
||||
handler
|
||||
|
||||
[depend]
|
||||
server
|
||||
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
# Applies ThreadLimiteHandler to entire server
|
||||
#
|
||||
|
||||
[Tags]
|
||||
handler
|
||||
|
||||
[depend]
|
||||
server
|
||||
|
||||
|
|
|
@ -37,25 +37,94 @@ import org.eclipse.jetty.io.EofException;
|
|||
import org.eclipse.jetty.io.RuntimeIOException;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.component.Destroyable;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.util.thread.Invocable.InvocationType;
|
||||
|
||||
/**
|
||||
* {@link HttpInput} provides an implementation of {@link ServletInputStream} for {@link HttpChannel}.
|
||||
* <p>
|
||||
* Content may arrive in patterns such as [content(), content(), messageComplete()] so that this class
|
||||
* maintains two states: the content state that tells whether there is content to consume and the EOF
|
||||
* state that tells whether an EOF has arrived.
|
||||
* Only once the content has been consumed the content state is moved to the EOF state.
|
||||
* Content may arrive in patterns such as [content(), content(), messageComplete()] so that this class maintains two states: the content state that tells
|
||||
* whether there is content to consume and the EOF state that tells whether an EOF has arrived. Only once the content has been consumed the content state is
|
||||
* moved to the EOF state.
|
||||
*/
|
||||
public class HttpInput extends ServletInputStream implements Runnable
|
||||
{
|
||||
/**
|
||||
* An interceptor for HTTP Request input.
|
||||
* <p>
|
||||
* Unlike inputstream wrappers that can be applied by filters, an interceptor
|
||||
* is entirely transparent and works with async IO APIs.
|
||||
* <p>
|
||||
* An Interceptor may consume data from the passed content and the interceptor
|
||||
* will continue to be called for the same content until the interceptor returns
|
||||
* null or an empty content. Thus even if the passed content is completely consumed
|
||||
* the interceptor will be called with the same content until it can no longer
|
||||
* produce more content.
|
||||
* @see HttpInput#setInterceptor(Interceptor)
|
||||
* @see HttpInput#addInterceptor(Interceptor)
|
||||
*/
|
||||
public interface Interceptor
|
||||
{
|
||||
/**
|
||||
* @param content The content to be intercepted (may be empty or a {@link SentinelContent}.
|
||||
* The content will be modified with any data the interceptor consumes, but there is no requirement
|
||||
* that all the data is consumed by the interceptor.
|
||||
* @return The intercepted content or null if interception is completed for that content.
|
||||
*/
|
||||
Content readFrom(Content content);
|
||||
}
|
||||
|
||||
/**
|
||||
* An {@link Interceptor} that chains two other {@link Interceptor}s together.
|
||||
* The {@link #readFrom(Content)} calls the previous {@link Interceptor}'s
|
||||
* {@link #readFrom(Content)} and then passes any {@link Content} returned
|
||||
* to the next {@link Interceptor}.
|
||||
*/
|
||||
public static class ChainedInterceptor implements Interceptor, Destroyable
|
||||
{
|
||||
private final Interceptor _prev;
|
||||
private final Interceptor _next;
|
||||
|
||||
public ChainedInterceptor(Interceptor prev, Interceptor next)
|
||||
{
|
||||
_prev = prev;
|
||||
_next = next;
|
||||
}
|
||||
|
||||
public Interceptor getPrev()
|
||||
{
|
||||
return _prev;
|
||||
}
|
||||
|
||||
public Interceptor getNext()
|
||||
{
|
||||
return _next;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Content readFrom(Content content)
|
||||
{
|
||||
return getNext().readFrom(getPrev().readFrom(content));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy()
|
||||
{
|
||||
if (_prev instanceof Destroyable)
|
||||
((Destroyable)_prev).destroy();
|
||||
if (_next instanceof Destroyable)
|
||||
((Destroyable)_next).destroy();
|
||||
}
|
||||
}
|
||||
|
||||
private final static Logger LOG = Log.getLogger(HttpInput.class);
|
||||
private final static Content EOF_CONTENT = new EofContent("EOF");
|
||||
private final static Content EARLY_EOF_CONTENT = new EofContent("EARLY_EOF");
|
||||
|
||||
private final byte[] _oneByteBuffer = new byte[1];
|
||||
private Content _content;
|
||||
private Content _intercepted;
|
||||
private final Deque<Content> _inputQ = new ArrayDeque<>();
|
||||
private final HttpChannelState _channelState;
|
||||
private ReadListener _listener;
|
||||
|
@ -64,6 +133,7 @@ public class HttpInput extends ServletInputStream implements Runnable
|
|||
private long _contentArrived;
|
||||
private long _contentConsumed;
|
||||
private long _blockUntil;
|
||||
private Interceptor _interceptor;
|
||||
|
||||
public HttpInput(HttpChannelState state)
|
||||
{
|
||||
|
@ -79,6 +149,9 @@ public class HttpInput extends ServletInputStream implements Runnable
|
|||
{
|
||||
synchronized (_inputQ)
|
||||
{
|
||||
if (_content!=null)
|
||||
_content.failed(null);
|
||||
_content = null;
|
||||
Content item = _inputQ.poll();
|
||||
while (item != null)
|
||||
{
|
||||
|
@ -91,9 +164,42 @@ public class HttpInput extends ServletInputStream implements Runnable
|
|||
_contentConsumed = 0;
|
||||
_firstByteTimeStamp = -1;
|
||||
_blockUntil = 0;
|
||||
if (_interceptor instanceof Destroyable)
|
||||
((Destroyable)_interceptor).destroy();
|
||||
_interceptor = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The current Interceptor, or null if none set
|
||||
*/
|
||||
public Interceptor getInterceptor()
|
||||
{
|
||||
return _interceptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the interceptor.
|
||||
* @param interceptor The interceptor to use.
|
||||
*/
|
||||
public void setInterceptor(Interceptor interceptor)
|
||||
{
|
||||
_interceptor = interceptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link Interceptor}, using a {@link ChainedInterceptor} if
|
||||
* an {@link Interceptor} is already set.
|
||||
* @param interceptor the next {@link Interceptor} in a chain
|
||||
*/
|
||||
public void addInterceptor(Interceptor interceptor)
|
||||
{
|
||||
if (_interceptor == null)
|
||||
_interceptor = interceptor;
|
||||
else
|
||||
_interceptor = new ChainedInterceptor(_interceptor,interceptor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int available()
|
||||
{
|
||||
|
@ -101,8 +207,9 @@ public class HttpInput extends ServletInputStream implements Runnable
|
|||
boolean woken = false;
|
||||
synchronized (_inputQ)
|
||||
{
|
||||
Content content = _inputQ.peek();
|
||||
if (content == null)
|
||||
if (_content == null)
|
||||
_content = _inputQ.poll();
|
||||
if (_content == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -112,11 +219,12 @@ public class HttpInput extends ServletInputStream implements Runnable
|
|||
{
|
||||
woken = failed(e);
|
||||
}
|
||||
content = _inputQ.peek();
|
||||
if (_content == null)
|
||||
_content = _inputQ.poll();
|
||||
}
|
||||
|
||||
if (content != null)
|
||||
available = remaining(content);
|
||||
if (_content != null)
|
||||
available = _content.remaining();
|
||||
}
|
||||
|
||||
if (woken)
|
||||
|
@ -139,10 +247,10 @@ public class HttpInput extends ServletInputStream implements Runnable
|
|||
@Override
|
||||
public int read() throws IOException
|
||||
{
|
||||
int read = read(_oneByteBuffer, 0, 1);
|
||||
int read = read(_oneByteBuffer,0,1);
|
||||
if (read == 0)
|
||||
throw new IllegalStateException("unready read=0");
|
||||
return read < 0 ? -1 : _oneByteBuffer[0] & 0xFF;
|
||||
return read < 0?-1:_oneByteBuffer[0] & 0xFF;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -168,7 +276,7 @@ public class HttpInput extends ServletInputStream implements Runnable
|
|||
{
|
||||
long minimum_data = minRequestDataRate * TimeUnit.NANOSECONDS.toMillis(period) / TimeUnit.SECONDS.toMillis(1);
|
||||
if (_contentArrived < minimum_data)
|
||||
throw new BadMessageException(HttpStatus.REQUEST_TIMEOUT_408, String.format("Request data rate < %d B/s", minRequestDataRate));
|
||||
throw new BadMessageException(HttpStatus.REQUEST_TIMEOUT_408,String.format("Request data rate < %d B/s",minRequestDataRate));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -177,11 +285,12 @@ public class HttpInput extends ServletInputStream implements Runnable
|
|||
Content item = nextContent();
|
||||
if (item != null)
|
||||
{
|
||||
int l = get(item, b, off, len);
|
||||
int l = get(item,b,off,len);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{} read {} from {}", this, l, item);
|
||||
LOG.debug("{} read {} from {}",this,l,item);
|
||||
|
||||
consumeNonContent();
|
||||
// Consume any following poison pills
|
||||
pollReadableContent();
|
||||
|
||||
return l;
|
||||
}
|
||||
|
@ -193,191 +302,207 @@ public class HttpInput extends ServletInputStream implements Runnable
|
|||
}
|
||||
|
||||
/**
|
||||
* Called when derived implementations should attempt to
|
||||
* produce more Content and add it via {@link #addContent(Content)}.
|
||||
* For protocols that are constantly producing (eg HTTP2) this can
|
||||
* be left as a noop;
|
||||
* Called when derived implementations should attempt to produce more Content and add it via {@link #addContent(Content)}. For protocols that are constantly
|
||||
* producing (eg HTTP2) this can be left as a noop;
|
||||
*
|
||||
* @throws IOException if unable to produce content
|
||||
* @throws IOException
|
||||
* if unable to produce content
|
||||
*/
|
||||
protected void produceContent() throws IOException
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next content from the inputQ, calling {@link #produceContent()}
|
||||
* if need be. EOF is processed and state changed.
|
||||
* Get the next content from the inputQ, calling {@link #produceContent()} if need be. EOF is processed and state changed.
|
||||
*
|
||||
* @return the content or null if none available.
|
||||
* @throws IOException if retrieving the content fails
|
||||
* @throws IOException
|
||||
* if retrieving the content fails
|
||||
*/
|
||||
protected Content nextContent() throws IOException
|
||||
{
|
||||
Content content = pollContent();
|
||||
Content content = pollNonEmptyContent();
|
||||
if (content == null && !isFinished())
|
||||
{
|
||||
produceContent();
|
||||
content = pollContent();
|
||||
content = pollNonEmptyContent();
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Poll the inputQ for Content.
|
||||
* Consumed buffers and {@link PoisonPillContent}s are removed and
|
||||
* EOF state updated if need be.
|
||||
* Poll the inputQ for Content. Consumed buffers and {@link SentinelContent}s are removed and EOF state updated if need be.
|
||||
*
|
||||
* @return Content or null
|
||||
*/
|
||||
protected Content pollContent()
|
||||
protected Content pollNonEmptyContent()
|
||||
{
|
||||
// Items are removed only when they are fully consumed.
|
||||
Content content = _inputQ.peek();
|
||||
// Skip consumed items at the head of the queue.
|
||||
while (content != null && remaining(content) == 0)
|
||||
{
|
||||
_inputQ.poll();
|
||||
content.succeeded();
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{} consumed {}", this, content);
|
||||
|
||||
if (content == EOF_CONTENT)
|
||||
while (true)
|
||||
{
|
||||
// Get the next content (or EOF)
|
||||
Content content = pollReadableContent();
|
||||
|
||||
// If it is EOF, consume it here
|
||||
if (content instanceof SentinelContent)
|
||||
{
|
||||
if (_listener == null)
|
||||
_state = EOF;
|
||||
else
|
||||
if (content == EARLY_EOF_CONTENT)
|
||||
_state = EARLY_EOF;
|
||||
else if (content instanceof EofContent)
|
||||
{
|
||||
_state = AEOF;
|
||||
boolean woken = _channelState.onReadReady(); // force callback?
|
||||
if (woken)
|
||||
wake();
|
||||
if (_listener == null)
|
||||
_state = EOF;
|
||||
else
|
||||
{
|
||||
_state = AEOF;
|
||||
boolean woken = _channelState.onReadReady(); // force callback?
|
||||
if (woken)
|
||||
wake();
|
||||
}
|
||||
}
|
||||
|
||||
// Consume the EOF content, either if it was original content
|
||||
// or if it was produced by interception
|
||||
content.succeeded();
|
||||
if (_content==content)
|
||||
_content = null;
|
||||
else if (_intercepted==content)
|
||||
_intercepted = null;
|
||||
continue;
|
||||
}
|
||||
else if (content == EARLY_EOF_CONTENT)
|
||||
_state = EARLY_EOF;
|
||||
|
||||
content = _inputQ.peek();
|
||||
return content;
|
||||
}
|
||||
|
||||
return content;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Poll the inputQ for Content or EOF. Consumed buffers and non EOF {@link SentinelContent}s are removed. EOF state is not updated.
|
||||
* Interception is done within this method.
|
||||
* @return Content with remaining, a {@link SentinelContent}, or null
|
||||
*/
|
||||
protected void consumeNonContent()
|
||||
protected Content pollReadableContent()
|
||||
{
|
||||
// Items are removed only when they are fully consumed.
|
||||
Content content = _inputQ.peek();
|
||||
// Skip consumed items at the head of the queue.
|
||||
while (content != null && remaining(content) == 0)
|
||||
// If we have a chunk produced by interception
|
||||
if (_intercepted!=null)
|
||||
{
|
||||
// Defer EOF until read
|
||||
if (content instanceof EofContent)
|
||||
break;
|
||||
|
||||
// Consume all other empty content
|
||||
_inputQ.poll();
|
||||
content.succeeded();
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{} consumed {}", this, content);
|
||||
content = _inputQ.peek();
|
||||
// Use it if it has any remaining content
|
||||
if (_intercepted.hasContent())
|
||||
return _intercepted;
|
||||
|
||||
// succeed the chunk
|
||||
_intercepted.succeeded();
|
||||
_intercepted=null;
|
||||
}
|
||||
|
||||
// If we don't have a Content under consideration, get
|
||||
// the next one off the input Q.
|
||||
if (_content == null)
|
||||
_content = _inputQ.poll();
|
||||
|
||||
// While we have content to consider.
|
||||
while (_content!=null)
|
||||
{
|
||||
// Are we intercepting?
|
||||
if (_interceptor!=null)
|
||||
{
|
||||
// Intercept the current content (may be called several
|
||||
// times for the same content
|
||||
_intercepted = _interceptor.readFrom(_content);
|
||||
|
||||
// If interception produced new content
|
||||
if (_intercepted!=null && _intercepted!=_content)
|
||||
{
|
||||
// if it is not empty use it
|
||||
if (_intercepted.hasContent())
|
||||
return _intercepted;
|
||||
_intercepted.succeeded();
|
||||
}
|
||||
|
||||
// intercepted content consumed
|
||||
_intercepted=null;
|
||||
|
||||
// fall through so that the unintercepted _content is
|
||||
// considered for any remaining content, for EOF and to
|
||||
// succeed it if it is entirely consumed.
|
||||
}
|
||||
|
||||
// If the content has content or is an EOF marker, use it
|
||||
if (_content.hasContent() || _content instanceof SentinelContent)
|
||||
return _content;
|
||||
|
||||
// The content is consumed, so get the next one. Note that EOF
|
||||
// content is never consumed here, but in #pollContent
|
||||
_content.succeeded();
|
||||
_content = _inputQ.poll();
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the next readable from the inputQ, calling {@link #produceContent()}
|
||||
* if need be. EOF is NOT processed and state is not changed.
|
||||
* Get the next readable from the inputQ, calling {@link #produceContent()} if need be. EOF is NOT processed and state is not changed.
|
||||
*
|
||||
* @return the content or EOF or null if none available.
|
||||
* @throws IOException if retrieving the content fails
|
||||
* @throws IOException
|
||||
* if retrieving the content fails
|
||||
*/
|
||||
protected Content nextReadable() throws IOException
|
||||
{
|
||||
Content content = pollReadable();
|
||||
Content content = pollReadableContent();
|
||||
if (content == null && !isFinished())
|
||||
{
|
||||
produceContent();
|
||||
content = pollReadable();
|
||||
content = pollReadableContent();
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Poll the inputQ for Content or EOF.
|
||||
* Consumed buffers and non EOF {@link PoisonPillContent}s are removed.
|
||||
* EOF state is not updated.
|
||||
*
|
||||
* @return Content, EOF or null
|
||||
*/
|
||||
protected Content pollReadable()
|
||||
{
|
||||
// Items are removed only when they are fully consumed.
|
||||
Content content = _inputQ.peek();
|
||||
|
||||
// Skip consumed items at the head of the queue except EOF
|
||||
while (content != null)
|
||||
{
|
||||
if (content == EOF_CONTENT || content == EARLY_EOF_CONTENT || remaining(content) > 0)
|
||||
return content;
|
||||
|
||||
_inputQ.poll();
|
||||
content.succeeded();
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{} consumed {}", this, content);
|
||||
content = _inputQ.peek();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param item the content
|
||||
* @return how many bytes remain in the given content
|
||||
*/
|
||||
protected int remaining(Content item)
|
||||
{
|
||||
return item.remaining();
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the given content into the given byte buffer.
|
||||
*
|
||||
* @param content the content to copy from
|
||||
* @param buffer the buffer to copy into
|
||||
* @param offset the buffer offset to start copying from
|
||||
* @param length the space available in the buffer
|
||||
* @param content
|
||||
* the content to copy from
|
||||
* @param buffer
|
||||
* the buffer to copy into
|
||||
* @param offset
|
||||
* the buffer offset to start copying from
|
||||
* @param length
|
||||
* the space available in the buffer
|
||||
* @return the number of bytes actually copied
|
||||
*/
|
||||
protected int get(Content content, byte[] buffer, int offset, int length)
|
||||
{
|
||||
int l = Math.min(content.remaining(), length);
|
||||
content.getContent().get(buffer, offset, l);
|
||||
int l = content.get(buffer,offset,length);
|
||||
_contentConsumed += l;
|
||||
return l;
|
||||
}
|
||||
|
||||
/**
|
||||
* Consumes the given content.
|
||||
* Calls the content succeeded if all content consumed.
|
||||
* Consumes the given content. Calls the content succeeded if all content consumed.
|
||||
*
|
||||
* @param content the content to consume
|
||||
* @param length the number of bytes to consume
|
||||
* @param content
|
||||
* the content to consume
|
||||
* @param length
|
||||
* the number of bytes to consume
|
||||
*/
|
||||
protected void skip(Content content, int length)
|
||||
{
|
||||
int l = Math.min(content.remaining(), length);
|
||||
ByteBuffer buffer = content.getContent();
|
||||
buffer.position(buffer.position() + l);
|
||||
int l = content.skip(length);
|
||||
|
||||
_contentConsumed += l;
|
||||
if (l > 0 && !content.hasContent())
|
||||
pollContent(); // hungry succeed
|
||||
if (l > 0 && content.isEmpty())
|
||||
pollNonEmptyContent(); // hungry succeed
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Blocks until some content or some end-of-file event arrives.
|
||||
*
|
||||
* @throws IOException if the wait is interrupted
|
||||
* @throws IOException
|
||||
* if the wait is interrupted
|
||||
*/
|
||||
protected void blockForContent() throws IOException
|
||||
{
|
||||
|
@ -392,7 +517,7 @@ public class HttpInput extends ServletInputStream implements Runnable
|
|||
}
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{} blocking for content timeout={}", this, timeout);
|
||||
LOG.debug("{} blocking for content timeout={}",this,timeout);
|
||||
if (timeout > 0)
|
||||
_inputQ.wait(timeout);
|
||||
else
|
||||
|
@ -402,7 +527,7 @@ public class HttpInput extends ServletInputStream implements Runnable
|
|||
// TODO: so spurious wakeups are not handled correctly.
|
||||
|
||||
if (_blockUntil != 0 && TimeUnit.NANOSECONDS.toMillis(_blockUntil - System.nanoTime()) <= 0)
|
||||
throw new TimeoutException(String.format("Blocking timeout %d ms", getBlockingTimeout()));
|
||||
throw new TimeoutException(String.format("Blocking timeout %d ms",getBlockingTimeout()));
|
||||
}
|
||||
catch (Throwable e)
|
||||
{
|
||||
|
@ -412,11 +537,12 @@ public class HttpInput extends ServletInputStream implements Runnable
|
|||
|
||||
/**
|
||||
* Adds some content to the start of this input stream.
|
||||
* <p>Typically used to push back content that has
|
||||
* been read, perhaps mutated. The bytes prepended are
|
||||
* deducted for the contentConsumed total</p>
|
||||
* <p>
|
||||
* Typically used to push back content that has been read, perhaps mutated. The bytes prepended are deducted for the contentConsumed total
|
||||
* </p>
|
||||
*
|
||||
* @param item the content to add
|
||||
* @param item
|
||||
* the content to add
|
||||
* @return true if content channel woken for read
|
||||
*/
|
||||
public boolean prependContent(Content item)
|
||||
|
@ -424,44 +550,54 @@ public class HttpInput extends ServletInputStream implements Runnable
|
|||
boolean woken = false;
|
||||
synchronized (_inputQ)
|
||||
{
|
||||
_inputQ.push(item);
|
||||
if (_content != null)
|
||||
_inputQ.push(_content);
|
||||
_content = item;
|
||||
_contentConsumed -= item.remaining();
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{} prependContent {}", this, item);
|
||||
LOG.debug("{} prependContent {}",this,item);
|
||||
|
||||
if (_listener == null)
|
||||
_inputQ.notify();
|
||||
else
|
||||
woken = _channelState.onReadPossible();
|
||||
}
|
||||
|
||||
return woken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds some content to this input stream.
|
||||
*
|
||||
* @param item the content to add
|
||||
* @param content
|
||||
* the content to add
|
||||
* @return true if content channel woken for read
|
||||
*/
|
||||
public boolean addContent(Content item)
|
||||
public boolean addContent(Content content)
|
||||
{
|
||||
boolean woken = false;
|
||||
synchronized (_inputQ)
|
||||
{
|
||||
if (_firstByteTimeStamp == -1)
|
||||
_firstByteTimeStamp = System.nanoTime();
|
||||
_contentArrived += item.remaining();
|
||||
_inputQ.offer(item);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{} addContent {}", this, item);
|
||||
|
||||
if (_listener == null)
|
||||
_inputQ.notify();
|
||||
_contentArrived += content.remaining();
|
||||
|
||||
if (_content==null && _inputQ.isEmpty())
|
||||
_content=content;
|
||||
else
|
||||
woken = _channelState.onReadPossible();
|
||||
}
|
||||
_inputQ.offer(content);
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{} addContent {}",this,content);
|
||||
|
||||
if (pollReadableContent()!=null)
|
||||
{
|
||||
if (_listener == null)
|
||||
_inputQ.notify();
|
||||
else
|
||||
woken = _channelState.onReadPossible();
|
||||
}
|
||||
}
|
||||
return woken;
|
||||
}
|
||||
|
||||
|
@ -469,7 +605,7 @@ public class HttpInput extends ServletInputStream implements Runnable
|
|||
{
|
||||
synchronized (_inputQ)
|
||||
{
|
||||
return _inputQ.size() > 0;
|
||||
return _content!=null || _inputQ.size() > 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -490,11 +626,9 @@ public class HttpInput extends ServletInputStream implements Runnable
|
|||
}
|
||||
|
||||
/**
|
||||
* This method should be called to signal that an EOF has been
|
||||
* detected before all the expected content arrived.
|
||||
* This method should be called to signal that an EOF has been detected before all the expected content arrived.
|
||||
* <p>
|
||||
* Typically this will result in an EOFException being thrown
|
||||
* from a subsequent read rather than a -1 return.
|
||||
* Typically this will result in an EOFException being thrown from a subsequent read rather than a -1 return.
|
||||
*
|
||||
* @return true if content channel woken for read
|
||||
*/
|
||||
|
@ -504,8 +638,7 @@ public class HttpInput extends ServletInputStream implements Runnable
|
|||
}
|
||||
|
||||
/**
|
||||
* This method should be called to signal that all the expected
|
||||
* content arrived.
|
||||
* This method should be called to signal that all the expected content arrived.
|
||||
*
|
||||
* @return true if content channel woken for read
|
||||
*/
|
||||
|
@ -526,7 +659,7 @@ public class HttpInput extends ServletInputStream implements Runnable
|
|||
if (item == null)
|
||||
break; // Let's not bother blocking
|
||||
|
||||
skip(item, remaining(item));
|
||||
skip(item,item.remaining());
|
||||
}
|
||||
return isFinished() && !isError();
|
||||
}
|
||||
|
@ -641,11 +774,8 @@ public class HttpInput extends ServletInputStream implements Runnable
|
|||
}
|
||||
|
||||
/*
|
||||
* <p>
|
||||
* While this class is-a Runnable, it should never be dispatched in it's own thread. It is a
|
||||
* runnable only so that the calling thread can use {@link ContextHandler#handle(Runnable)}
|
||||
* to setup classloaders etc.
|
||||
* </p>
|
||||
* <p> While this class is-a Runnable, it should never be dispatched in it's own thread. It is a runnable only so that the calling thread can use {@link
|
||||
* ContextHandler#handle(Runnable)} to setup classloaders etc. </p>
|
||||
*/
|
||||
@Override
|
||||
public void run()
|
||||
|
@ -666,7 +796,7 @@ public class HttpInput extends ServletInputStream implements Runnable
|
|||
}
|
||||
|
||||
listener = _listener;
|
||||
error = _state instanceof ErrorState ? ((ErrorState)_state).getError() : null;
|
||||
error = _state instanceof ErrorState?((ErrorState)_state).getError():null;
|
||||
}
|
||||
|
||||
try
|
||||
|
@ -721,19 +851,24 @@ public class HttpInput extends ServletInputStream implements Runnable
|
|||
content = _inputQ.peekFirst();
|
||||
}
|
||||
return String.format("%s@%x[c=%d,q=%d,[0]=%s,s=%s]",
|
||||
getClass().getSimpleName(),
|
||||
hashCode(),
|
||||
consumed,
|
||||
q,
|
||||
content,
|
||||
state);
|
||||
getClass().getSimpleName(),
|
||||
hashCode(),
|
||||
consumed,
|
||||
q,
|
||||
content,
|
||||
state);
|
||||
}
|
||||
|
||||
public static class PoisonPillContent extends Content
|
||||
/**
|
||||
* A Sentinel Content, which has zero length content but
|
||||
* indicates some other event in the input stream (eg EOF)
|
||||
*
|
||||
*/
|
||||
public static class SentinelContent extends Content
|
||||
{
|
||||
private final String _name;
|
||||
|
||||
public PoisonPillContent(String name)
|
||||
public SentinelContent(String name)
|
||||
{
|
||||
super(BufferUtil.EMPTY_BUFFER);
|
||||
_name = name;
|
||||
|
@ -746,7 +881,7 @@ public class HttpInput extends ServletInputStream implements Runnable
|
|||
}
|
||||
}
|
||||
|
||||
public static class EofContent extends PoisonPillContent
|
||||
public static class EofContent extends SentinelContent
|
||||
{
|
||||
EofContent(String name)
|
||||
{
|
||||
|
@ -756,22 +891,36 @@ public class HttpInput extends ServletInputStream implements Runnable
|
|||
|
||||
public static class Content implements Callback
|
||||
{
|
||||
private final ByteBuffer _content;
|
||||
protected final ByteBuffer _content;
|
||||
|
||||
public Content(ByteBuffer content)
|
||||
{
|
||||
_content = content;
|
||||
}
|
||||
|
||||
public ByteBuffer getByteBuffer()
|
||||
{
|
||||
return _content;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InvocationType getInvocationType()
|
||||
{
|
||||
return InvocationType.NON_BLOCKING;
|
||||
}
|
||||
|
||||
public ByteBuffer getContent()
|
||||
public int get(byte[] buffer, int offset, int length)
|
||||
{
|
||||
return _content;
|
||||
length = Math.min(_content.remaining(),length);
|
||||
_content.get(buffer,offset,length);
|
||||
return length;
|
||||
}
|
||||
|
||||
public int skip(int length)
|
||||
{
|
||||
length = Math.min(_content.remaining(),length);
|
||||
_content.position(_content.position() + length);
|
||||
return length;
|
||||
}
|
||||
|
||||
public boolean hasContent()
|
||||
|
@ -783,15 +932,19 @@ public class HttpInput extends ServletInputStream implements Runnable
|
|||
{
|
||||
return _content.remaining();
|
||||
}
|
||||
|
||||
public boolean isEmpty()
|
||||
{
|
||||
return !_content.hasRemaining();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("Content@%x{%s}", hashCode(), BufferUtil.toDetailString(_content));
|
||||
return String.format("Content@%x{%s}",hashCode(),BufferUtil.toDetailString(_content));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected static abstract class State
|
||||
{
|
||||
public boolean blockForContent(HttpInput in) throws IOException
|
||||
|
|
|
@ -175,9 +175,9 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
return _interceptor;
|
||||
}
|
||||
|
||||
public void setInterceptor(Interceptor filter)
|
||||
public void setInterceptor(Interceptor interceptor)
|
||||
{
|
||||
_interceptor = filter;
|
||||
_interceptor = interceptor;
|
||||
}
|
||||
|
||||
public boolean isWritten()
|
||||
|
|
|
@ -133,7 +133,7 @@ public class BufferedResponseHandler extends HandlerWrapper
|
|||
}
|
||||
|
||||
// If not a supported method - no Vary because no matter what client, this URI is always excluded
|
||||
if (!_methods.matches(baseRequest.getMethod()))
|
||||
if (!_methods.test(baseRequest.getMethod()))
|
||||
{
|
||||
LOG.debug("{} excluded by method {}",this,request);
|
||||
_handler.handle(target,baseRequest, request, response);
|
||||
|
@ -173,7 +173,7 @@ public class BufferedResponseHandler extends HandlerWrapper
|
|||
/* ------------------------------------------------------------ */
|
||||
protected boolean isMimeTypeBufferable(String mimetype)
|
||||
{
|
||||
return _mimeTypes.matches(mimetype);
|
||||
return _mimeTypes.test(mimetype);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -182,7 +182,7 @@ public class BufferedResponseHandler extends HandlerWrapper
|
|||
if (requestURI == null)
|
||||
return true;
|
||||
|
||||
return _paths.matches(requestURI);
|
||||
return _paths.test(requestURI);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
|
|
@ -65,6 +65,7 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
|
|||
private int _compressionLevel=Deflater.DEFAULT_COMPRESSION;
|
||||
private boolean _checkGzExists = true;
|
||||
private boolean _syncFlush = false;
|
||||
private int _inflateBufferSize = -1;
|
||||
|
||||
// non-static, as other GzipHandler instances may have different configurations
|
||||
private final ThreadLocal<Deflater> _deflater = new ThreadLocal<>();
|
||||
|
@ -77,6 +78,7 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
|
|||
private HttpField _vary;
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Instantiates a new gzip handler.
|
||||
|
@ -398,6 +400,24 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
|
|||
return _vary;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @return size in bytes of the buffer to inflate compressed request, or 0 for no inflation.
|
||||
*/
|
||||
public int getInflateBufferSize()
|
||||
{
|
||||
return _inflateBufferSize;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @param size size in bytes of the buffer to inflate compressed request, or 0 for no inflation.
|
||||
*/
|
||||
public void setInflateBufferSize(int size)
|
||||
{
|
||||
_inflateBufferSize = size;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @see org.eclipse.jetty.server.handler.HandlerWrapper#handle(java.lang.String, org.eclipse.jetty.server.Request, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
|
||||
|
@ -409,6 +429,19 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
|
|||
String path = context==null?baseRequest.getRequestURI():URIUtil.addPaths(baseRequest.getServletPath(),baseRequest.getPathInfo());
|
||||
LOG.debug("{} handle {} in {}",this,baseRequest,context);
|
||||
|
||||
// Handle request inflation
|
||||
if (_inflateBufferSize>0)
|
||||
{
|
||||
HttpField ce = baseRequest.getHttpFields().getField(HttpHeader.CONTENT_ENCODING);
|
||||
if (ce!=null && "gzip".equalsIgnoreCase(ce.getValue()))
|
||||
{
|
||||
// TODO should check ce.contains and then remove just the gzip encoding
|
||||
baseRequest.getHttpFields().remove(HttpHeader.CONTENT_ENCODING);
|
||||
baseRequest.getHttpFields().add(new HttpField("X-Content-Encoding",ce.getValue()));
|
||||
baseRequest.getHttpInput().addInterceptor(new GzipHttpInputInterceptor(baseRequest.getHttpChannel().getByteBufferPool(),_inflateBufferSize));
|
||||
}
|
||||
}
|
||||
|
||||
HttpOutput out = baseRequest.getResponse().getHttpOutput();
|
||||
// Are we already being gzipped?
|
||||
HttpOutput.Interceptor interceptor = out.getInterceptor();
|
||||
|
@ -424,7 +457,7 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
|
|||
}
|
||||
|
||||
// If not a supported method - no Vary because no matter what client, this URI is always excluded
|
||||
if (!_methods.matches(baseRequest.getMethod()))
|
||||
if (!_methods.test(baseRequest.getMethod()))
|
||||
{
|
||||
LOG.debug("{} excluded by method {}",this,request);
|
||||
_handler.handle(target,baseRequest, request, response);
|
||||
|
@ -516,14 +549,14 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
|
|||
if (ua == null)
|
||||
return false;
|
||||
|
||||
return _agentPatterns.matches(ua);
|
||||
return _agentPatterns.test(ua);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public boolean isMimeTypeGzipable(String mimetype)
|
||||
{
|
||||
return _mimeTypes.matches(mimetype);
|
||||
return _mimeTypes.test(mimetype);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -539,7 +572,7 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
|
|||
if (requestURI == null)
|
||||
return true;
|
||||
|
||||
return _paths.matches(requestURI);
|
||||
return _paths.test(requestURI);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.server.handler.gzip;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.eclipse.jetty.http.GZIPContentDecoder;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.server.HttpInput;
|
||||
import org.eclipse.jetty.server.HttpInput.Content;
|
||||
import org.eclipse.jetty.util.component.Destroyable;
|
||||
|
||||
/**
|
||||
* A HttpInput Interceptor that inflates GZIP encoded request content.
|
||||
*/
|
||||
public class GzipHttpInputInterceptor implements HttpInput.Interceptor, Destroyable
|
||||
{
|
||||
private final Decoder _decoder;
|
||||
private ByteBuffer _chunk;
|
||||
|
||||
public GzipHttpInputInterceptor(ByteBufferPool pool, int bufferSize)
|
||||
{
|
||||
_decoder = new Decoder(pool, bufferSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Content readFrom(Content content)
|
||||
{
|
||||
_decoder.decodeChunks(content.getByteBuffer());
|
||||
final ByteBuffer chunk = _chunk;
|
||||
|
||||
if (chunk == null)
|
||||
return null;
|
||||
|
||||
return new Content(chunk)
|
||||
{
|
||||
@Override
|
||||
public void succeeded()
|
||||
{
|
||||
_decoder.release(chunk);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy()
|
||||
{
|
||||
_decoder.destroy();
|
||||
}
|
||||
|
||||
private class Decoder extends GZIPContentDecoder
|
||||
{
|
||||
private Decoder(ByteBufferPool pool, int bufferSize)
|
||||
{
|
||||
super(pool, bufferSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean decodedChunk(final ByteBuffer chunk)
|
||||
{
|
||||
_chunk = chunk;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decodeChunks(ByteBuffer compressed)
|
||||
{
|
||||
_chunk = null;
|
||||
super.decodeChunks(compressed);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -336,9 +336,9 @@ public class DefaultSessionIdManager extends AbstractLifeCycle implements Sessio
|
|||
{
|
||||
String inst = System.getenv("JETTY_WORKER_INSTANCE");
|
||||
_workerName = "node"+ (inst==null?"0":inst);
|
||||
LOG.warn("No workerName configured for DefaultSessionIdManager, using {}",_workerName);
|
||||
}
|
||||
|
||||
LOG.info("DefaultSessionIdManager workerName={}",_workerName);
|
||||
_workerAttr=(_workerName!=null && _workerName.startsWith("$"))?_workerName.substring(1):null;
|
||||
|
||||
if (_houseKeeper == null)
|
||||
|
|
|
@ -354,6 +354,7 @@ public class AsyncRequestReadTest
|
|||
for (int i=read;i-->0;)
|
||||
{
|
||||
int c=in.read();
|
||||
// System.err.println("in="+c);
|
||||
if (c<0)
|
||||
break;
|
||||
out.write(c);
|
||||
|
|
|
@ -0,0 +1,265 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.server.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.http.HttpTester;
|
||||
import org.eclipse.jetty.http.HttpVersion;
|
||||
import org.eclipse.jetty.server.Handler;
|
||||
import org.eclipse.jetty.server.HttpConfiguration;
|
||||
import org.eclipse.jetty.server.HttpConnectionFactory;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.SecureRequestCustomizer;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.server.SslConnectionFactory;
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
|
||||
import org.eclipse.jetty.util.thread.Scheduler;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class SslContextFactoryReloadTest
|
||||
{
|
||||
public static final String KEYSTORE_1 = "src/test/resources/reload_keystore_1.jks";
|
||||
public static final String KEYSTORE_2 = "src/test/resources/reload_keystore_2.jks";
|
||||
|
||||
private Server server;
|
||||
private SslContextFactory sslContextFactory;
|
||||
private ServerConnector connector;
|
||||
|
||||
private void start(Handler handler) throws Exception
|
||||
{
|
||||
server = new Server();
|
||||
|
||||
sslContextFactory = new SslContextFactory();
|
||||
sslContextFactory.setKeyStorePath(KEYSTORE_1);
|
||||
sslContextFactory.setKeyStorePassword("storepwd");
|
||||
sslContextFactory.setKeyStoreType("JKS");
|
||||
sslContextFactory.setKeyStoreProvider(null);
|
||||
|
||||
HttpConfiguration httpsConfig = new HttpConfiguration();
|
||||
httpsConfig.addCustomizer(new SecureRequestCustomizer());
|
||||
connector = new ServerConnector(server,
|
||||
new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()),
|
||||
new HttpConnectionFactory(httpsConfig));
|
||||
server.addConnector(connector);
|
||||
|
||||
server.setHandler(handler);
|
||||
|
||||
server.start();
|
||||
}
|
||||
|
||||
@After
|
||||
public void dispose() throws Exception
|
||||
{
|
||||
if (server != null)
|
||||
server.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReload() throws Exception
|
||||
{
|
||||
start(new EchoHandler());
|
||||
|
||||
SSLContext ctx = SSLContext.getInstance("TLSv1.2");
|
||||
ctx.init(null, SslContextFactory.TRUST_ALL_CERTS, null);
|
||||
SSLSocketFactory socketFactory = ctx.getSocketFactory();
|
||||
try (SSLSocket client1 = (SSLSocket)socketFactory.createSocket("localhost", connector.getLocalPort()))
|
||||
{
|
||||
String serverDN1 = client1.getSession().getPeerPrincipal().getName();
|
||||
Assert.assertThat(serverDN1, Matchers.startsWith("CN=localhost1"));
|
||||
|
||||
String request = "" +
|
||||
"GET / HTTP/1.1\r\n" +
|
||||
"Host: localhost\r\n" +
|
||||
"\r\n";
|
||||
|
||||
OutputStream output1 = client1.getOutputStream();
|
||||
output1.write(request.getBytes(StandardCharsets.UTF_8));
|
||||
output1.flush();
|
||||
|
||||
HttpTester.Response response1 = HttpTester.parseResponse(HttpTester.from(client1.getInputStream()));
|
||||
Assert.assertNotNull(response1);
|
||||
Assert.assertThat(response1.getStatus(), Matchers.equalTo(HttpStatus.OK_200));
|
||||
|
||||
// Reconfigure SslContextFactory.
|
||||
sslContextFactory.reload(sslContextFactory ->
|
||||
{
|
||||
sslContextFactory.setKeyStorePath(KEYSTORE_2);
|
||||
sslContextFactory.setKeyStorePassword("storepwd");
|
||||
});
|
||||
|
||||
// New connection should use the new keystore.
|
||||
try (SSLSocket client2 = (SSLSocket)socketFactory.createSocket("localhost", connector.getLocalPort()))
|
||||
{
|
||||
String serverDN2 = client2.getSession().getPeerPrincipal().getName();
|
||||
Assert.assertThat(serverDN2, Matchers.startsWith("CN=localhost2"));
|
||||
|
||||
OutputStream output2 = client1.getOutputStream();
|
||||
output2.write(request.getBytes(StandardCharsets.UTF_8));
|
||||
output2.flush();
|
||||
|
||||
HttpTester.Response response2 = HttpTester.parseResponse(HttpTester.from(client1.getInputStream()));
|
||||
Assert.assertNotNull(response2);
|
||||
Assert.assertThat(response2.getStatus(), Matchers.equalTo(HttpStatus.OK_200));
|
||||
}
|
||||
|
||||
// Must still be possible to make requests with the first connection.
|
||||
output1.write(request.getBytes(StandardCharsets.UTF_8));
|
||||
output1.flush();
|
||||
|
||||
response1 = HttpTester.parseResponse(HttpTester.from(client1.getInputStream()));
|
||||
Assert.assertNotNull(response1);
|
||||
Assert.assertThat(response1.getStatus(), Matchers.equalTo(HttpStatus.OK_200));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReloadWhileServing() throws Exception
|
||||
{
|
||||
start(new EchoHandler());
|
||||
|
||||
Scheduler scheduler = new ScheduledExecutorScheduler();
|
||||
scheduler.start();
|
||||
try
|
||||
{
|
||||
SSLContext ctx = SSLContext.getInstance("TLSv1.2");
|
||||
ctx.init(null, SslContextFactory.TRUST_ALL_CERTS, null);
|
||||
SSLSocketFactory socketFactory = ctx.getSocketFactory();
|
||||
|
||||
// Perform 4 reloads while connections are being served.
|
||||
AtomicInteger reloads = new AtomicInteger(4);
|
||||
long reloadPeriod = 500;
|
||||
AtomicBoolean running = new AtomicBoolean(true);
|
||||
scheduler.schedule(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
if (reloads.decrementAndGet() == 0)
|
||||
{
|
||||
running.set(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
sslContextFactory.reload(sslContextFactory ->
|
||||
{
|
||||
if (sslContextFactory.getKeyStorePath().endsWith(KEYSTORE_1))
|
||||
sslContextFactory.setKeyStorePath(KEYSTORE_2);
|
||||
else
|
||||
sslContextFactory.setKeyStorePath(KEYSTORE_1);
|
||||
});
|
||||
scheduler.schedule(this, reloadPeriod, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
catch (Exception x)
|
||||
{
|
||||
running.set(false);
|
||||
reloads.set(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, reloadPeriod, TimeUnit.MILLISECONDS);
|
||||
|
||||
byte[] content = new byte[16 * 1024];
|
||||
while (running.get())
|
||||
{
|
||||
try (SSLSocket client = (SSLSocket)socketFactory.createSocket("localhost", connector.getLocalPort()))
|
||||
{
|
||||
// We need to invalidate the session every time we open a new SSLSocket.
|
||||
// This is because when the client uses session resumption, it caches
|
||||
// the server certificates and then checks that it is the same during
|
||||
// a new TLS handshake. If the SslContextFactory is reloaded during the
|
||||
// TLS handshake, the client will see the new certificate and blow up.
|
||||
// Note that browsers can handle this case better: they will just not
|
||||
// use session resumption and fallback to the normal TLS handshake.
|
||||
client.getSession().invalidate();
|
||||
|
||||
String request1 = "" +
|
||||
"POST / HTTP/1.1\r\n" +
|
||||
"Host: localhost\r\n" +
|
||||
"Content-Length: " + content.length + "\r\n" +
|
||||
"\r\n";
|
||||
OutputStream outputStream = client.getOutputStream();
|
||||
outputStream.write(request1.getBytes(StandardCharsets.UTF_8));
|
||||
outputStream.write(content);
|
||||
outputStream.flush();
|
||||
|
||||
InputStream inputStream = client.getInputStream();
|
||||
HttpTester.Response response1 = HttpTester.parseResponse(HttpTester.from(inputStream));
|
||||
Assert.assertNotNull(response1);
|
||||
Assert.assertThat(response1.getStatus(), Matchers.equalTo(HttpStatus.OK_200));
|
||||
|
||||
String request2 = "" +
|
||||
"GET / HTTP/1.1\r\n" +
|
||||
"Host: localhost\r\n" +
|
||||
"Connection: close\r\n" +
|
||||
"\r\n";
|
||||
outputStream.write(request2.getBytes(StandardCharsets.UTF_8));
|
||||
outputStream.flush();
|
||||
|
||||
HttpTester.Response response2 = HttpTester.parseResponse(HttpTester.from(inputStream));
|
||||
Assert.assertNotNull(response2);
|
||||
Assert.assertThat(response2.getStatus(), Matchers.equalTo(HttpStatus.OK_200));
|
||||
}
|
||||
}
|
||||
|
||||
Assert.assertEquals(0, reloads.get());
|
||||
}
|
||||
finally
|
||||
{
|
||||
scheduler.stop();
|
||||
}
|
||||
}
|
||||
|
||||
private static class EchoHandler extends AbstractHandler
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
if (HttpMethod.POST.is(request.getMethod()))
|
||||
IO.copy(request.getInputStream(), response.getOutputStream());
|
||||
else
|
||||
response.setContentLength(0);
|
||||
}
|
||||
}
|
||||
}
|
Binary file not shown.
Binary file not shown.
|
@ -33,8 +33,10 @@ import java.io.IOException;
|
|||
import java.io.InputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
|
@ -45,6 +47,7 @@ import org.eclipse.jetty.http.HttpTester;
|
|||
import org.eclipse.jetty.server.LocalConnector;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.handler.gzip.GzipHandler;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.After;
|
||||
|
@ -86,6 +89,7 @@ public class GzipHandlerTest
|
|||
GzipHandler gzipHandler = new GzipHandler();
|
||||
gzipHandler.setExcludedAgentPatterns();
|
||||
gzipHandler.setMinGzipSize(16);
|
||||
gzipHandler.setInflateBufferSize(4096);
|
||||
|
||||
ServletContextHandler context = new ServletContextHandler(gzipHandler,"/ctx");
|
||||
ServletHandler servlets = context.getServletHandler();
|
||||
|
@ -97,6 +101,7 @@ public class GzipHandlerTest
|
|||
servlets.addServletWithMapping(TestServlet.class,"/content");
|
||||
servlets.addServletWithMapping(ForwardServlet.class,"/forward");
|
||||
servlets.addServletWithMapping(IncludeServlet.class,"/include");
|
||||
servlets.addServletWithMapping(EchoServlet.class,"/echo/*");
|
||||
|
||||
_server.start();
|
||||
}
|
||||
|
@ -147,6 +152,21 @@ public class GzipHandlerTest
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class EchoServlet extends HttpServlet
|
||||
{
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse response) throws ServletException, IOException
|
||||
{
|
||||
response.setContentType(req.getContentType());
|
||||
IO.copy(req.getInputStream(),response.getOutputStream());
|
||||
}
|
||||
@Override
|
||||
protected void doPost(HttpServletRequest req, HttpServletResponse response) throws ServletException, IOException
|
||||
{
|
||||
doGet(req,response);
|
||||
}
|
||||
}
|
||||
|
||||
public static class ForwardServlet extends HttpServlet
|
||||
{
|
||||
|
@ -392,4 +412,68 @@ public class GzipHandlerTest
|
|||
assertThat("Included Paths.size", includedPaths.length, is(2));
|
||||
assertThat("Included Paths", Arrays.asList(includedPaths), contains("/foo","^/bar.*$"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testGzipRequest() throws Exception
|
||||
{
|
||||
String data = "Hello Nice World! ";
|
||||
for (int i = 0; i < 10; ++i)
|
||||
data += data;
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
GZIPOutputStream output = new GZIPOutputStream(baos);
|
||||
output.write(data.getBytes(StandardCharsets.UTF_8));
|
||||
output.close();
|
||||
byte[] bytes = baos.toByteArray();
|
||||
|
||||
// generated and parsed test
|
||||
HttpTester.Request request = HttpTester.newRequest();
|
||||
HttpTester.Response response;
|
||||
|
||||
request.setMethod("POST");
|
||||
request.setURI("/ctx/echo");
|
||||
request.setVersion("HTTP/1.0");
|
||||
request.setHeader("Host","tester");
|
||||
request.setHeader("Content-Type","text/plain");
|
||||
request.setHeader("Content-Encoding","gzip");
|
||||
request.setContent(bytes);
|
||||
|
||||
response = HttpTester.parseResponse(_connector.getResponse(request.generate()));
|
||||
|
||||
assertThat(response.getStatus(),is(200));
|
||||
assertThat(response.getContent(),is(data));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGzipBomb() throws Exception
|
||||
{
|
||||
byte[] data = new byte[512*1024];
|
||||
Arrays.fill(data,(byte)'X');
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
GZIPOutputStream output = new GZIPOutputStream(baos);
|
||||
output.write(data);
|
||||
output.close();
|
||||
byte[] bytes = baos.toByteArray();
|
||||
|
||||
// generated and parsed test
|
||||
HttpTester.Request request = HttpTester.newRequest();
|
||||
HttpTester.Response response;
|
||||
|
||||
request.setMethod("POST");
|
||||
request.setURI("/ctx/echo");
|
||||
request.setVersion("HTTP/1.0");
|
||||
request.setHeader("Host","tester");
|
||||
request.setHeader("Content-Type","text/plain");
|
||||
request.setHeader("Content-Encoding","gzip");
|
||||
request.setContent(bytes);
|
||||
|
||||
response = HttpTester.parseResponse(_connector.getResponse(request.generate()));
|
||||
// TODO need to test back pressure works
|
||||
|
||||
assertThat(response.getStatus(),is(200));
|
||||
assertThat(response.getContentBytes().length,is(512*1024));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -50,11 +50,10 @@ public class BaseBuilder
|
|||
*
|
||||
* @param module
|
||||
* the module to add
|
||||
* @return true if module was added, false if module was not added
|
||||
* (because that module already exists)
|
||||
* @return The ini file if module was added, null if module was not added.
|
||||
* @throws IOException if unable to add the module
|
||||
*/
|
||||
public boolean addModule(Module module) throws IOException;
|
||||
public String addModule(Module module) throws IOException;
|
||||
}
|
||||
|
||||
private static final String EXITING_LICENSE_NOT_ACKNOWLEDGED = "Exiting: license not acknowledged!";
|
||||
|
@ -181,7 +180,6 @@ public class BaseBuilder
|
|||
|
||||
if (!newly_added.isEmpty())
|
||||
{
|
||||
|
||||
if (Files.exists(startini) && Files.exists(startd))
|
||||
StartLog.warn("Use both %s and %s is deprecated",getBaseHome().toShortForm(startd),getBaseHome().toShortForm(startini));
|
||||
|
||||
|
@ -189,6 +187,7 @@ public class BaseBuilder
|
|||
builder.set(useStartD?new StartDirBuilder(this):new StartIniBuilder(this));
|
||||
newly_added.stream().map(n->modules.get(n)).forEach(module ->
|
||||
{
|
||||
String ini=null;
|
||||
try
|
||||
{
|
||||
if (module.isSkipFilesValidation())
|
||||
|
@ -197,8 +196,13 @@ public class BaseBuilder
|
|||
}
|
||||
else
|
||||
{
|
||||
if (builder.get().addModule(module))
|
||||
modified.set(true);
|
||||
// if (explictly added and ini file modified)
|
||||
if (startArgs.getStartModules().contains(module.getName()))
|
||||
{
|
||||
ini=builder.get().addModule(module);
|
||||
if (ini!=null)
|
||||
modified.set(true);
|
||||
}
|
||||
for (String file : module.getFiles())
|
||||
files.add(new FileArg(module,startArgs.getProperties().expand(file)));
|
||||
}
|
||||
|
@ -207,6 +211,26 @@ public class BaseBuilder
|
|||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
if (module.isDynamic())
|
||||
{
|
||||
for (String s:module.getEnableSources())
|
||||
StartLog.info("%-15s %s",module.getName(),s);
|
||||
}
|
||||
else if (module.isTransitive())
|
||||
{
|
||||
if (module.hasIniTemplate())
|
||||
StartLog.info("%-15s transitively enabled, ini template available with --add-to-start=%s",
|
||||
module.getName(),
|
||||
module.getName());
|
||||
else
|
||||
StartLog.info("%-15s transitively enabled",module.getName());
|
||||
}
|
||||
else
|
||||
StartLog.info("%-15s initialized in %s",
|
||||
module.getName(),
|
||||
ini);
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -243,16 +243,19 @@ public class Main
|
|||
|
||||
public void listModules(StartArgs args)
|
||||
{
|
||||
List<String> tags = args.getListModules();
|
||||
|
||||
StartLog.endStartLog();
|
||||
System.out.println();
|
||||
System.out.println("Jetty All Available Modules:");
|
||||
System.out.println("----------------------------");
|
||||
args.getAllModules().dump();
|
||||
System.out.println("Available Modules:");
|
||||
System.out.println("==================");
|
||||
System.out.println("tags: "+tags);
|
||||
args.getAllModules().dump(tags);
|
||||
|
||||
// Dump Enabled Modules
|
||||
System.out.println();
|
||||
System.out.println("Jetty Selected Module Ordering:");
|
||||
System.out.println("-------------------------------");
|
||||
System.out.println("Enabled Modules:");
|
||||
System.out.println("================");
|
||||
Modules modules = args.getAllModules();
|
||||
modules.dumpEnabled();
|
||||
}
|
||||
|
@ -381,7 +384,7 @@ public class Main
|
|||
}
|
||||
|
||||
// Show modules
|
||||
if (args.isListModules())
|
||||
if (args.getListModules()!=null)
|
||||
{
|
||||
listModules(args);
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ import java.util.stream.Collectors;
|
|||
* A module may be enabled, either directly by name or transiently via a dependency
|
||||
* from another module by name or provided capability.
|
||||
*/
|
||||
public class Module
|
||||
public class Module implements Comparable<Module>
|
||||
{
|
||||
private static final String VERSION_UNSPECIFIED = "9.2";
|
||||
private static Pattern MOD_NAME = Pattern.compile("^(.*)\\.mod",Pattern.CASE_INSENSITIVE);
|
||||
|
@ -96,6 +96,9 @@ public class Module
|
|||
/** List of provides for this Module */
|
||||
private final Set<String> _provides=new HashSet<>();
|
||||
|
||||
/** List of tags for this Module */
|
||||
private final List<String> _tags=new ArrayList<>();
|
||||
|
||||
/** Boolean true if directly enabled, false if all selections are transitive */
|
||||
private boolean _notTransitive;
|
||||
|
||||
|
@ -328,6 +331,10 @@ public class Module
|
|||
case "FILES":
|
||||
_files.add(line);
|
||||
break;
|
||||
case "TAG":
|
||||
case "TAGS":
|
||||
_tags.add(line);
|
||||
break;
|
||||
case "DEFAULTS": // old name introduced in 9.2.x
|
||||
case "INI": // new name for 9.3+
|
||||
_defaultConfig.add(line);
|
||||
|
@ -446,6 +453,16 @@ public class Module
|
|||
return _description;
|
||||
}
|
||||
|
||||
public List<String> getTags()
|
||||
{
|
||||
return _tags;
|
||||
}
|
||||
|
||||
public String getPrimaryTag()
|
||||
{
|
||||
return _tags.isEmpty()?"*":_tags.get(0);
|
||||
}
|
||||
|
||||
public boolean isEnabled()
|
||||
{
|
||||
return !_enables.isEmpty();
|
||||
|
@ -504,4 +521,13 @@ public class Module
|
|||
out.println();
|
||||
out.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Module m)
|
||||
{
|
||||
int by_tag = getPrimaryTag().compareTo(m.getPrimaryTag());
|
||||
if (by_tag!=0)
|
||||
return by_tag;
|
||||
return getName().compareTo(m.getName());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@ package org.eclipse.jetty.start;
|
|||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
|
@ -30,6 +29,7 @@ import java.util.Map;
|
|||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
@ -57,59 +57,92 @@ public class Modules implements Iterable<Module>
|
|||
}
|
||||
}
|
||||
|
||||
public void dump()
|
||||
public void dump(List<String> tags)
|
||||
{
|
||||
List<String> ordered = _modules.stream().map(m->{return m.getName();}).collect(Collectors.toList());
|
||||
Collections.sort(ordered);
|
||||
ordered.stream().map(n->{return get(n);}).forEach(module->
|
||||
{
|
||||
String status = "[ ]";
|
||||
if (module.isTransitive())
|
||||
Set<String> exclude = tags.stream().filter(t->t.startsWith("-")).map(t->t.substring(1)).collect(Collectors.toSet());
|
||||
Set<String> include = tags.stream().filter(t->!t.startsWith("-")).collect(Collectors.toSet());
|
||||
boolean all = include.contains("*") || include.isEmpty();
|
||||
AtomicReference<String> tag = new AtomicReference<>();
|
||||
|
||||
_modules.stream()
|
||||
.filter(m->
|
||||
{
|
||||
status = "[t]";
|
||||
}
|
||||
else if (module.isEnabled())
|
||||
boolean included = all || m.getTags().stream().anyMatch(t->include.contains(t));
|
||||
boolean excluded = m.getTags().stream().anyMatch(t->exclude.contains(t));
|
||||
return included && !excluded;
|
||||
})
|
||||
.sorted()
|
||||
.forEach(module->
|
||||
{
|
||||
status = "[x]";
|
||||
}
|
||||
|
||||
System.out.printf("%n %s Module: %s%n",status,module.getName());
|
||||
if (module.getProvides().size()>1)
|
||||
{
|
||||
System.out.printf(" Provides: %s%n",module.getProvides());
|
||||
}
|
||||
for (String description : module.getDescription())
|
||||
{
|
||||
System.out.printf(" : %s%n",description);
|
||||
}
|
||||
for (String parent : module.getDepends())
|
||||
{
|
||||
System.out.printf(" Depend: %s%n",parent);
|
||||
}
|
||||
for (String optional : module.getOptional())
|
||||
{
|
||||
System.out.printf(" Optional: %s%n",optional);
|
||||
}
|
||||
for (String lib : module.getLibs())
|
||||
{
|
||||
System.out.printf(" LIB: %s%n",lib);
|
||||
}
|
||||
for (String xml : module.getXmls())
|
||||
{
|
||||
System.out.printf(" XML: %s%n",xml);
|
||||
}
|
||||
for (String jvm : module.getJvmArgs())
|
||||
{
|
||||
System.out.printf(" JVM: %s%n",jvm);
|
||||
}
|
||||
if (module.isEnabled())
|
||||
{
|
||||
for (String selection : module.getEnableSources())
|
||||
if (!module.getPrimaryTag().equals(tag.get()))
|
||||
{
|
||||
System.out.printf(" Enabled: %s%n",selection);
|
||||
tag.set(module.getPrimaryTag());
|
||||
System.out.printf("%nModules for tag '%s':%n",module.getPrimaryTag());
|
||||
System.out.print("-------------------");
|
||||
for (int i=module.getPrimaryTag().length();i-->0;)
|
||||
System.out.print("-");
|
||||
System.out.println();
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
String label;
|
||||
Set<String> provides = module.getProvides();
|
||||
provides.remove(module.getName());
|
||||
System.out.printf("%n Module: %s %s%n",module.getName(),provides.size()>0?provides:"");
|
||||
for (String description : module.getDescription())
|
||||
{
|
||||
System.out.printf(" : %s%n",description);
|
||||
}
|
||||
if (!module.getTags().isEmpty())
|
||||
{
|
||||
label=" Tags: %s";
|
||||
for (String t : module.getTags())
|
||||
{
|
||||
System.out.printf(label,t);
|
||||
label=", %s";
|
||||
}
|
||||
System.out.println();
|
||||
}
|
||||
if (!module.getDepends().isEmpty())
|
||||
{
|
||||
label=" Depend: %s";
|
||||
for (String parent : module.getDepends())
|
||||
{
|
||||
System.out.printf(label,parent);
|
||||
label=", %s";
|
||||
}
|
||||
System.out.println();
|
||||
}
|
||||
if (!module.getOptional().isEmpty())
|
||||
{
|
||||
label=" Optional: %s";
|
||||
for (String parent : module.getOptional())
|
||||
{
|
||||
System.out.printf(label,parent);
|
||||
label=", %s";
|
||||
}
|
||||
System.out.println();
|
||||
}
|
||||
for (String lib : module.getLibs())
|
||||
{
|
||||
System.out.printf(" LIB: %s%n",lib);
|
||||
}
|
||||
for (String xml : module.getXmls())
|
||||
{
|
||||
System.out.printf(" XML: %s%n",xml);
|
||||
}
|
||||
for (String jvm : module.getJvmArgs())
|
||||
{
|
||||
System.out.printf(" JVM: %s%n",jvm);
|
||||
}
|
||||
if (module.isEnabled())
|
||||
{
|
||||
for (String selection : module.getEnableSources())
|
||||
{
|
||||
System.out.printf(" Enabled: %s%n",selection);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void dumpEnabled()
|
||||
|
@ -125,6 +158,8 @@ public class Modules implements Iterable<Module>
|
|||
index="";
|
||||
name="";
|
||||
}
|
||||
if (module.isTransitive() && module.hasIniTemplate())
|
||||
System.out.printf(" init template available with --add-to-start=%s%n",module.getName());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -279,9 +314,6 @@ public class Modules implements Iterable<Module>
|
|||
m.expandProperties(_args.getProperties());
|
||||
}
|
||||
}
|
||||
else if (module.isTransitive() && module.hasIniTemplate())
|
||||
newlyEnabled.add(module.getName());
|
||||
|
||||
|
||||
// Process module dependencies (always processed as may be dynamic)
|
||||
for(String dependsOn:module.getDepends())
|
||||
|
@ -318,7 +350,7 @@ public class Modules implements Iterable<Module>
|
|||
// Is there an obvious default?
|
||||
Optional<Module> dftProvider = providers.stream().filter(m->m.getName().equals(dependsOn)).findFirst();
|
||||
if (dftProvider.isPresent())
|
||||
enable(newlyEnabled,dftProvider.get(),"default provider of "+dependsOn+" for "+module.getName(),true);
|
||||
enable(newlyEnabled,dftProvider.get(),"transitive provider of "+dependsOn+" for "+module.getName(),true);
|
||||
else if (StartLog.isDebugEnabled())
|
||||
StartLog.debug("Module %s requires %s from one of %s",module,dependsOn,providers);
|
||||
}
|
||||
|
|
|
@ -170,7 +170,7 @@ public class StartArgs
|
|||
|
||||
private boolean help = false;
|
||||
private boolean stopCommand = false;
|
||||
private boolean listModules = false;
|
||||
private List<String> listModules = null;
|
||||
private boolean listClasspath = false;
|
||||
private boolean listConfig = false;
|
||||
private boolean version = false;
|
||||
|
@ -364,7 +364,7 @@ public class StartArgs
|
|||
}
|
||||
else
|
||||
{
|
||||
System.out.printf(" %s = %s%n",key,properties.expand(prop.value));
|
||||
System.out.printf(" %s = %s%n",key,prop.value);
|
||||
if (StartLog.isDebugEnabled())
|
||||
{
|
||||
System.out.printf(" origin: %s%n",prop.origin);
|
||||
|
@ -372,7 +372,7 @@ public class StartArgs
|
|||
{
|
||||
prop = prop.overrides;
|
||||
System.out.printf(" (overrides)%n");
|
||||
System.out.printf(" %s = %s%n",key,properties.expand(prop.value));
|
||||
System.out.printf(" %s = %s%n",key,prop.value);
|
||||
System.out.printf(" origin: %s%n",prop.origin);
|
||||
}
|
||||
}
|
||||
|
@ -398,7 +398,7 @@ public class StartArgs
|
|||
for (String key : sortedKeys)
|
||||
{
|
||||
String value = System.getProperty(key);
|
||||
System.out.printf(" %s = %s%n",key,properties.expand(value));
|
||||
System.out.printf(" %s = %s%n",key,value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -750,7 +750,7 @@ public class StartArgs
|
|||
return listConfig;
|
||||
}
|
||||
|
||||
public boolean isListModules()
|
||||
public List<String> getListModules()
|
||||
{
|
||||
return listModules;
|
||||
}
|
||||
|
@ -873,7 +873,7 @@ public class StartArgs
|
|||
if (arg.equals("--create-files"))
|
||||
{
|
||||
run = false;
|
||||
download = true;
|
||||
download = true;boolean
|
||||
licenseCheckRequired = true;
|
||||
return;
|
||||
}
|
||||
|
@ -938,10 +938,25 @@ public class StartArgs
|
|||
return;
|
||||
}
|
||||
|
||||
// Module Management
|
||||
if ("--list-all-modules".equals(arg))
|
||||
{
|
||||
listModules = Collections.singletonList("*");
|
||||
run = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Module Management
|
||||
if ("--list-modules".equals(arg))
|
||||
{
|
||||
listModules = true;
|
||||
listModules = Collections.singletonList("-internal");
|
||||
run = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (arg.startsWith("--list-modules="))
|
||||
{
|
||||
listModules = Props.getValues(arg);
|
||||
run = false;
|
||||
return;
|
||||
}
|
||||
|
@ -1035,13 +1050,33 @@ public class StartArgs
|
|||
}
|
||||
|
||||
// Is this a raw property declaration?
|
||||
int idx = arg.indexOf('=');
|
||||
if (idx >= 0)
|
||||
int equals = arg.indexOf('=');
|
||||
if (equals >= 0)
|
||||
{
|
||||
String key = arg.substring(0,idx);
|
||||
String value = arg.substring(idx + 1);
|
||||
String key = arg.substring(0,equals);
|
||||
String value = arg.substring(equals + 1);
|
||||
|
||||
if (replaceProps)
|
||||
if (key.endsWith("+"))
|
||||
{
|
||||
key = key.substring(0,key.length()-1);
|
||||
String orig = getProperties().getString(key);
|
||||
if (orig != null && !orig.isEmpty())
|
||||
{
|
||||
value=orig+value;
|
||||
source=propertySource.get(key)+","+source;
|
||||
}
|
||||
}
|
||||
else if (key.endsWith(","))
|
||||
{
|
||||
key = key.substring(0,key.length()-1);
|
||||
String orig = getProperties().getString(key);
|
||||
if (orig != null && !orig.isEmpty())
|
||||
{
|
||||
value=value.isEmpty()?orig:(orig+","+value);
|
||||
source=propertySource.get(key)+","+source;
|
||||
}
|
||||
}
|
||||
else if (replaceProps)
|
||||
{
|
||||
if (propertySource.containsKey(key))
|
||||
{
|
||||
|
@ -1050,21 +1085,10 @@ public class StartArgs
|
|||
propertySource.put(key,source);
|
||||
}
|
||||
|
||||
if ("OPTION".equals(key) || "OPTIONS".equals(key))
|
||||
{
|
||||
StringBuilder warn = new StringBuilder();
|
||||
warn.append("The behavior of the argument ");
|
||||
warn.append(arg).append(" (seen in ").append(source);
|
||||
warn.append(") has changed, and is now considered a normal property. ");
|
||||
warn.append(key).append(" no longer controls what libraries are on your classpath,");
|
||||
warn.append(" use --module instead. See --help for details.");
|
||||
StartLog.warn(warn.toString());
|
||||
}
|
||||
|
||||
setProperty(key,value,source,replaceProps);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Is this an xml file?
|
||||
if (FS.isXml(arg))
|
||||
{
|
||||
|
@ -1169,7 +1193,9 @@ public class StartArgs
|
|||
return;
|
||||
}
|
||||
|
||||
if (replaceProp || (!properties.containsKey(key)))
|
||||
if (value==null || value.isEmpty())
|
||||
properties.remove(key,value,source);
|
||||
else if (replaceProp || (!properties.containsKey(key)))
|
||||
{
|
||||
properties.setProperty(key,value,source);
|
||||
if(key.equals("java.version"))
|
||||
|
|
|
@ -50,7 +50,7 @@ public class StartDirBuilder implements BaseBuilder.Config
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean addModule(Module module) throws IOException
|
||||
public String addModule(Module module) throws IOException
|
||||
{
|
||||
if (module.isDynamic())
|
||||
{
|
||||
|
@ -59,28 +59,20 @@ public class StartDirBuilder implements BaseBuilder.Config
|
|||
// warn
|
||||
StartLog.warn("%-15s not adding [ini-template] from dynamic module",module.getName());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
String mode = "";
|
||||
if (module.isTransitive())
|
||||
{
|
||||
mode = "(transitively) ";
|
||||
return null;
|
||||
}
|
||||
|
||||
if (module.hasIniTemplate() || !module.isTransitive())
|
||||
{
|
||||
// Create start.d/{name}.ini
|
||||
Path ini = startDir.resolve(module.getName() + ".ini");
|
||||
StartLog.info("%-15s initialised %sin %s",module.getName(),mode,baseHome.toShortForm(ini));
|
||||
|
||||
try (BufferedWriter writer = Files.newBufferedWriter(ini,StandardCharsets.UTF_8,StandardOpenOption.CREATE,StandardOpenOption.TRUNCATE_EXISTING))
|
||||
{
|
||||
module.writeIniSection(writer);
|
||||
}
|
||||
return true;
|
||||
return baseHome.toShortForm(ini);
|
||||
}
|
||||
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@ package org.eclipse.jetty.start.builders;
|
|||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
@ -86,13 +85,13 @@ public class StartIniBuilder implements BaseBuilder.Config
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean addModule(Module module) throws IOException
|
||||
public String addModule(Module module) throws IOException
|
||||
{
|
||||
if (modulesPresent.contains(module.getName()))
|
||||
{
|
||||
StartLog.info("%-15s already initialised in %s",module.getName(),baseHome.toShortForm(startIni));
|
||||
// skip, already present
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (module.isDynamic())
|
||||
|
@ -102,27 +101,19 @@ public class StartIniBuilder implements BaseBuilder.Config
|
|||
// warn
|
||||
StartLog.warn("%-15s not adding [ini-template] from dynamic module",module.getName());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
String mode = "";
|
||||
if (module.isTransitive())
|
||||
{
|
||||
mode = "(transitively) ";
|
||||
return null;
|
||||
}
|
||||
|
||||
if (module.hasIniTemplate() || !module.isTransitive())
|
||||
{
|
||||
StartLog.info("%-15s initialised %sin %s",module.getName(),mode,baseHome.toShortForm(startIni));
|
||||
|
||||
// Append to start.ini
|
||||
try (BufferedWriter writer = Files.newBufferedWriter(startIni,StandardCharsets.UTF_8,StandardOpenOption.APPEND,StandardOpenOption.CREATE))
|
||||
{
|
||||
module.writeIniSection(writer);
|
||||
}
|
||||
return true;
|
||||
return baseHome.toShortForm(startIni);
|
||||
}
|
||||
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ Debug and Start Logging:
|
|||
Module Management:
|
||||
------------------
|
||||
|
||||
--list-modules List all modules defined by the system.
|
||||
--list-modules List non verbose modules defined by the system.
|
||||
Looking for module files in ${jetty.base}/modules/*.mod and
|
||||
then ${jetty.home}/modules/*.mod
|
||||
Will also list enabled state based on information
|
||||
|
@ -67,6 +67,13 @@ Module Management:
|
|||
o The command line
|
||||
o The ${jetty.base}/start.ini
|
||||
o The ${jetty.base}/start.d/*.ini files
|
||||
|
||||
--list-modules=<tag>(,<tag>)*
|
||||
List modules by tag. Use '*' for all tags. Prefix a tag
|
||||
with '-' to exclude the tag.
|
||||
|
||||
--list-all-modules
|
||||
List all modules.
|
||||
|
||||
--module=<modulename>(,<modulename>)*
|
||||
Temporarily enable a module from the command line.
|
||||
|
@ -140,6 +147,15 @@ Startup / Shutdown Command Line:
|
|||
|
||||
Properties:
|
||||
|
||||
name=value
|
||||
Set a property that can be expanded in XML files with the <Property> element.
|
||||
|
||||
name+=value
|
||||
Add to an existing property.
|
||||
|
||||
name,=value
|
||||
Add to an existing property as a comma separated list.
|
||||
|
||||
STOP.HOST=[string]
|
||||
The host to use to stop the running Jetty server (defaults to 127.0.0.1)
|
||||
Required along with STOP.PORT if you want to use the --stop option above.
|
||||
|
|
|
@ -12,3 +12,5 @@ PROP|main.prop=value0
|
|||
PROP|port=9090
|
||||
PROP|other=value
|
||||
PROP|jetty.http.port=9090
|
||||
PROP|add=beginningmiddleend
|
||||
PROP|list=one,two,three
|
||||
|
|
|
@ -1,2 +1,8 @@
|
|||
other=value
|
||||
port=9090
|
||||
add+=beginning
|
||||
add+=middle
|
||||
add+=end
|
||||
list,=one
|
||||
list,=two
|
||||
list,=three
|
||||
|
|
|
@ -14,7 +14,7 @@ LIB|${jetty.home}/lib/extra/extra1.jar
|
|||
|
||||
# The Properties we expect (order is irrelevant)
|
||||
PROP|extra.prop=value0
|
||||
PROP|main.prop=valueT
|
||||
PROP|main.prop=value0
|
||||
PROP|optional.prop=value0
|
||||
|
||||
# Files / Directories to create
|
||||
|
|
|
@ -14,12 +14,11 @@ LIB|${jetty.home}/lib/extra/extra1.jar
|
|||
|
||||
# The Properties we expect (order is irrelevant)
|
||||
PROP|extra.prop=value0
|
||||
PROP|main.prop=valueT
|
||||
PROP|main.prop=value0
|
||||
PROP|optional.prop=value0
|
||||
|
||||
# Files / Directories to create
|
||||
EXISTS|maindir/
|
||||
EXISTS|start.d/main.ini
|
||||
EXISTS|start.d/extra.ini
|
||||
EXISTS|start.d/optional.ini
|
||||
|
||||
|
|
|
@ -14,12 +14,11 @@ LIB|${jetty.home}/lib/extra/extra1.jar
|
|||
|
||||
# The Properties we expect (order is irrelevant)
|
||||
PROP|extra.prop=value0
|
||||
PROP|main.prop=valueT
|
||||
PROP|main.prop=value0
|
||||
PROP|optional.prop=value0
|
||||
|
||||
# Files / Directories to create
|
||||
EXISTS|maindir/
|
||||
EXISTS|start.d/
|
||||
EXISTS|start.d/main.ini
|
||||
EXISTS|start.d/extra.ini
|
||||
EXISTS|start.d/optional.ini
|
||||
|
|
|
@ -4,5 +4,8 @@ etc/t.xml
|
|||
[optional]
|
||||
main
|
||||
|
||||
[ini]
|
||||
transient.option=transient
|
||||
|
||||
[ini-template]
|
||||
transient.option=transient
|
||||
|
|
|
@ -4,6 +4,9 @@ by the Unix Domain Socket connector, for use when behind a proxy operating
|
|||
in HTTP mode that adds forwarded-for style HTTP headers. Typically this
|
||||
is an alternate to the Proxy Protocol used mostly for TCP mode.
|
||||
|
||||
[Tags]
|
||||
connector
|
||||
|
||||
[depend]
|
||||
unixsocket-http
|
||||
|
||||
|
|
|
@ -4,6 +4,10 @@ It should be used when a proxy is forwarding either HTTP or decrypted
|
|||
HTTPS traffic to the connector and may be used with the
|
||||
unix-socket-http2c modules to upgrade to HTTP/2.
|
||||
|
||||
[Tags]
|
||||
connector
|
||||
http
|
||||
|
||||
[depend]
|
||||
unixsocket
|
||||
|
||||
|
|
|
@ -3,6 +3,10 @@ Adds a HTTP2C connetion factory to the Unix Domain Socket Connector
|
|||
It can be used when either the proxy forwards direct
|
||||
HTTP/2C (unecrypted) or decrypted HTTP/2 traffic.
|
||||
|
||||
[Tags]
|
||||
connector
|
||||
http2
|
||||
|
||||
[depend]
|
||||
unixsocket-http
|
||||
|
||||
|
|
|
@ -8,6 +8,9 @@ SSL properties may be interpreted by the unixsocket-secure
|
|||
module to indicate secure HTTPS traffic. Typically this
|
||||
is an alternate to the forwarded module.
|
||||
|
||||
[Tags]
|
||||
connector
|
||||
|
||||
[depend]
|
||||
unixsocket
|
||||
|
||||
|
|
|
@ -5,6 +5,9 @@ This looks for a secure scheme transported either by the
|
|||
unixsocket-forwarded, unixsocket-proxy-protocol or in a
|
||||
HTTP2 request.
|
||||
|
||||
[Tags]
|
||||
connector
|
||||
|
||||
[depend]
|
||||
unixsocket-http
|
||||
|
||||
|
|
|
@ -7,6 +7,9 @@ needless fragmentation and have better dispatch behaviours.
|
|||
When enabled with corresponding support modules, the connector can
|
||||
accept HTTP, HTTPS or HTTP2C traffic.
|
||||
|
||||
[Tags]
|
||||
connector
|
||||
|
||||
[depend]
|
||||
server
|
||||
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
Provides a Java Commons Logging implementation.
|
||||
To receive jetty logs the jetty-slf4j and slf4j-jcl must also be enabled.
|
||||
|
||||
[tags]
|
||||
logging
|
||||
jcl
|
||||
internal
|
||||
|
||||
[depends]
|
||||
|
||||
[provides]
|
|
@ -1,6 +1,11 @@
|
|||
[description]
|
||||
Provides a Java Commons Logging implementation that logs to the SLF4J API.
|
||||
Requires another module that provides and SLF4J implementation.
|
||||
Provides a Java Commons Logging (JCL) to SLF4J logging bridge.
|
||||
|
||||
[tags]
|
||||
logging
|
||||
jcl
|
||||
slf4j
|
||||
internal
|
||||
|
||||
[depends]
|
||||
slf4j-api
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
[description]
|
||||
Provides a Jetty Logging implementation that logs to the Java Util Logging API.
|
||||
Requires another module that provides a Java Util Logging implementation.
|
||||
|
||||
[provide]
|
||||
logging
|
||||
|
||||
[exec]
|
||||
-Dorg.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.JavaUtilLog
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue