Merge branch 'jetty-9.1' into release-9.1

This commit is contained in:
Jesse McConnell 2013-10-24 16:09:18 -05:00
commit 9e8d07337b
229 changed files with 6196 additions and 4733 deletions

View File

@ -191,6 +191,62 @@ jetty-9.1.0.M0 - 16 September 2013
+ 417225 added Container.addEventListener method
+ 417260 Protected targets matched as true URI path segments
jetty-9.0.6.v20130930 - 30 September 2013
+ 411069 better set compiler defaults to 1.7, including webdefault.xml for jsp
+ 411934 War overlay configuration assumes src/main/webapp exists
+ 413484 setAttribute in nosql session management better handles _dirty status
+ 413684 deprecated unsafe alias checkers
+ 413737 hide stacktrace in ReferrerPushStrategyTest
+ 414431 Avoid debug NPE race
+ 414898 Only upgrade v0 to v1 cookies on dquote , ; backslash space and tab
in the value
+ 415192 <jsp-file> maps to JspPropertyGroupServlet instead of JspServlet
+ 415194 Deployer gives management of context to context collection
+ 415302
+ 415330 Avoid multiple callbacks at EOF
+ 415401 Add initalizeDefaults call to SpringConfigurationProcessor
+ 415548 migrate ProxyHTTPToSPDYTest to use HttpClient to avoid intermittent
NPE part 2
+ 415605 fix status code logging for async requests
+ 415999 Fix some of FindBugs warnings
+ 416015 Handle null Accept-Language and other headers
+ 416096 DefaultServlet leaves open file descriptors with file sizes greater
than response buffer
+ 416102 Clean up of async sendContent process
+ 416103 Added AllowSymLinkAliasChecker.java
+ 416251 ProxyHTTPToSPDYConnection now sends a 502 to the client if it
receives a rst frame from the upstream spdy server
+ 416266 HttpServletResponse.encodeURL() encodes on first request when only
SessionTrackingMode.COOKIE is used
+ 416314 jetty async client wrong behaviour for HEAD Method + Redirect.
+ 416321 handle failure during blocked committing write
+ 416453 Add comments to embedded SplitFileServer example
+ 416477 Improved consumeAll error handling
+ 416568 Simplified servlet exception logging
+ 416577 enhanced shutdown handler to send shutdown at startup
+ 416585 WebInfConfiguration examines webapp classloader first instead of its
parent when looking for container jars
+ 416597 Allow classes and jars on the webappcontext extraclasspath to be
scanned for annotations
+ 416663 Content-length set by resourcehandler
+ 416674 run all jetty-ant tests on random ports
+ 416679 Change warning to debug if no transaction manager present
+ 416787 StringIndexOutOfBounds with a pathMap of ""
+ 416940 avoid download of spring-beans.dtd
+ 416990 JMX names statically unique
+ 417110 Demo / html body end tag missing in authfail.html
+ 417225 added Container.addEventListener method
+ 417260 Protected targets matched as true URI path segments
+ 417289 SPDY replace use of direct buffers with indirect buffers or make it
configurable
+ 417475 Do not null context Trie during dynamic deploy
+ 417574 Setting options with _JAVA_OPTIONS breaks run-forked with
<waitForChild>true</waitForChild>
+ 417831 Remove jetty-logging.properties from distro/resources
+ 418014 Handle NTFS canonical exceptions during alias check
+ 418212 org.eclipse.jetty.spdy.server.http.SSLExternalServerTest hangs
+ 418227 Null cookie value test
jetty-9.0.5.v20130815 - 15 August 2013
+ 414898 Only upgrade v0 to v1 cookies on dquote , ; backslash space and tab
in the value

View File

@ -18,11 +18,17 @@
package org.eclipse.jetty.annotations;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.concurrent.Callable;
@ -41,7 +47,6 @@ import org.eclipse.jetty.util.MultiException;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.webapp.AbstractConfiguration;
import org.eclipse.jetty.webapp.FragmentDescriptor;
import org.eclipse.jetty.webapp.MetaDataComplete;
@ -57,6 +62,7 @@ public class AnnotationConfiguration extends AbstractConfiguration
{
private static final Logger LOG = Log.getLogger(AnnotationConfiguration.class);
public static final String SERVLET_CONTAINER_INITIALIZER_ORDER = "org.eclipse.jetty.containerInitializerOrder";
public static final String CLASS_INHERITANCE_MAP = "org.eclipse.jetty.classInheritanceMap";
public static final String CONTAINER_INITIALIZERS = "org.eclipse.jetty.containerInitializers";
public static final String CONTAINER_INITIALIZER_STARTER = "org.eclipse.jetty.containerInitializerStarter";
@ -76,6 +82,12 @@ public class AnnotationConfiguration extends AbstractConfiguration
/**
* ParserTask
*
* Task to executing scanning of a resource for annotations.
*
*/
public class ParserTask implements Callable<Void>
{
protected Exception _exception;
@ -176,6 +188,143 @@ public class AnnotationConfiguration extends AbstractConfiguration
}
}
/**
* ServletContainerInitializerOrdering
*
* A list of classnames of ServletContainerInitializers in the order in which
* they are to be called back. One name only in the list can be "*", which is a
* wildcard which matches any other ServletContainerInitializer name not already
* matched.
*/
public class ServletContainerInitializerOrdering
{
private Map<String, Integer> _indexMap = new HashMap<String, Integer>();
private Integer _star = null;
private String _ordering = null;
public ServletContainerInitializerOrdering (String ordering)
{
if (ordering != null)
{
_ordering = ordering;
String[] tmp = ordering.split(",");
for (int i=0; i<tmp.length; i++)
{
String s = tmp[i].trim();
_indexMap.put(s, Integer.valueOf(i));
if ("*".equals(s))
{
if (_star != null)
throw new IllegalArgumentException("Duplicate wildcards in ServletContainerInitializer ordering "+ordering);
_star = Integer.valueOf(i);
}
}
}
}
/**
* True if "*" is one of the values.
* @return
*/
public boolean hasWildcard()
{
return _star != null;
}
/**
* Get the index of the "*" element, if it is specified. -1 otherwise.
* @return
*/
public int getWildcardIndex()
{
if (!hasWildcard())
return -1;
return _star.intValue();
}
/**
* True if the ordering contains a single value of "*"
* @return
*/
public boolean isDefaultOrder ()
{
return (getSize() == 1 && hasWildcard());
}
/**
* Get the order index of the given classname
* @param name
* @return
*/
public int getIndexOf (String name)
{
Integer i = _indexMap.get(name);
if (i == null)
return -1;
return i.intValue();
}
/**
* Get the number of elements of the ordering
* @return
*/
public int getSize()
{
return _indexMap.size();
}
public String toString()
{
if (_ordering == null)
return "";
return _ordering;
}
}
/**
* ServletContainerInitializerComparator
*
* Comparator impl that orders a set of ServletContainerInitializers according to the
* list of classnames (optionally containing a "*" wildcard character) established in a
* ServletContainerInitializerOrdering.
* @see ServletContainerInitializerOrdering
*/
public class ServletContainerInitializerComparator implements Comparator<ServletContainerInitializer>
{
private ServletContainerInitializerOrdering _ordering;
public ServletContainerInitializerComparator (ServletContainerInitializerOrdering ordering)
{
_ordering = ordering;
}
@Override
public int compare(ServletContainerInitializer sci1, ServletContainerInitializer sci2)
{
String c1 = (sci1 != null? sci1.getClass().getName() : null);
String c2 = (sci2 != null? sci2.getClass().getName() : null);
if (c1 == null && c2 == null)
return 0;
int i1 = _ordering.getIndexOf(c1);
if (i1 < 0 && _ordering.hasWildcard())
i1 = _ordering.getWildcardIndex();
int i2 = _ordering.getIndexOf(c2);
if (i2 < 0 && _ordering.hasWildcard())
i2 = _ordering.getWildcardIndex();
return Integer.compare(i1, i2);
}
}
@Override
public void preConfigure(final WebAppContext context) throws Exception
{
@ -278,24 +427,18 @@ public class AnnotationConfiguration extends AbstractConfiguration
throws Exception
{
AnnotationParser parser = createAnnotationParser();
boolean multiThreadedScan = isUseMultiThreading(context);
int maxScanWait = 0;
if (multiThreadedScan)
{
_parserTasks = new ArrayList<ParserTask>();
maxScanWait = getMaxScanWait(context);
}
_parserTasks = new ArrayList<ParserTask>();
long start = 0;
if (LOG.isDebugEnabled())
{
start = System.nanoTime();
LOG.debug("Scanning for annotations: webxml={}, metadatacomplete={}, configurationDiscovered={}, multiThreaded={}",
LOG.debug("Scanning for annotations: webxml={}, metadatacomplete={}, configurationDiscovered={}, multiThreaded={}, maxScanWait={}",
context.getServletContext().getEffectiveMajorVersion(),
context.getMetaData().isMetaDataComplete(),
context.isConfigurationDiscovered(),
multiThreadedScan);
isUseMultiThreading(context),
getMaxScanWait(context));
}
parseContainerPath(context, parser);
@ -307,23 +450,15 @@ public class AnnotationConfiguration extends AbstractConfiguration
parseWebInfClasses(context, parser);
parseWebInfLib (context, parser);
if (!multiThreadedScan)
{
if (LOG.isDebugEnabled())
{
long end = System.nanoTime();
LOG.debug("Annotation parsing millisec={}",(TimeUnit.MILLISECONDS.convert(end-start, TimeUnit.NANOSECONDS)));
}
return;
}
if (LOG.isDebugEnabled())
start = System.nanoTime();
//execute scan asynchronously using jetty's thread pool
//execute scan, either effectively synchronously (1 thread only), or asychronously (limited by number of processors available)
final Semaphore task_limit = (isUseMultiThreading(context)? new Semaphore(Runtime.getRuntime().availableProcessors()):new Semaphore(1));
final CountDownLatch latch = new CountDownLatch(_parserTasks.size());
final MultiException me = new MultiException();
final Semaphore task_limit=new Semaphore(Runtime.getRuntime().availableProcessors());
for (final ParserTask p:_parserTasks)
{
task_limit.acquire();
@ -349,13 +484,10 @@ public class AnnotationConfiguration extends AbstractConfiguration
});
}
boolean timeout = !latch.await(maxScanWait, TimeUnit.SECONDS);
boolean timeout = !latch.await(getMaxScanWait(context), TimeUnit.SECONDS);
if (LOG.isDebugEnabled())
{
long end = System.nanoTime();
LOG.debug("Annotation parsing millisec={}",(TimeUnit.MILLISECONDS.convert(end-start, TimeUnit.NANOSECONDS)));
}
LOG.debug("Annotation parsing millisec={}",(TimeUnit.MILLISECONDS.convert(System.nanoTime()-start, TimeUnit.NANOSECONDS)));
if (timeout)
me.add(new Exception("Timeout scanning annotations"));
@ -507,18 +639,42 @@ public class AnnotationConfiguration extends AbstractConfiguration
context.addBean(starter, true);
}
public Resource getJarFor (ServletContainerInitializer service)
throws MalformedURLException, IOException
{
String loadingJarName = Thread.currentThread().getContextClassLoader().getResource(service.getClass().getName().replace('.','/')+".class").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);
}
/**
* Check to see if the ServletContainerIntializer loaded via the ServiceLoader came
* from a jar that is excluded by the fragment ordering. See ServletSpec 3.0 p.85.
* @param context
* @param service
* @param sci
* @return true if excluded
*/
public boolean isFromExcludedJar (WebAppContext context, ServletContainerInitializer service)
public boolean isFromExcludedJar (WebAppContext context, ServletContainerInitializer sci, Resource sciResource)
throws Exception
{
if (sci == null)
throw new IllegalArgumentException("ServletContainerInitializer null");
if (context == null)
throw new IllegalArgumentException("WebAppContext null");
//A ServletContainerInitializer that came from the container's classpath cannot be excluded by an ordering
//of WEB-INF/lib jars
if (sci.getClass().getClassLoader()==context.getClassLoader().getParent())
return false;
List<Resource> orderedJars = context.getMetaData().getOrderedWebInfJars();
//If no ordering, nothing is excluded
@ -529,15 +685,10 @@ public class AnnotationConfiguration extends AbstractConfiguration
if (orderedJars.isEmpty())
return true;
String loadingJarName = Thread.currentThread().getContextClassLoader().getResource(service.getClass().getName().replace('.','/')+".class").toString();
int i = loadingJarName.indexOf(".jar");
if (i < 0)
if (sciResource == null)
return false; //not from a jar therefore not from WEB-INF so not excludable
loadingJarName = loadingJarName.substring(0,i+4);
loadingJarName = (loadingJarName.startsWith("jar:")?loadingJarName.substring(4):loadingJarName);
URI loadingJarURI = Resource.newResource(loadingJarName).getURI();
URI loadingJarURI = sciResource.getURI();
boolean found = false;
Iterator<Resource> itor = orderedJars.iterator();
while (!found && itor.hasNext())
@ -559,8 +710,9 @@ public class AnnotationConfiguration extends AbstractConfiguration
public List<ServletContainerInitializer> getNonExcludedInitializers (WebAppContext context)
throws Exception
{
List<ServletContainerInitializer> nonExcludedInitializers = new ArrayList<ServletContainerInitializer>();
ArrayList<ServletContainerInitializer> nonExcludedInitializers = new ArrayList<ServletContainerInitializer>();
//We use the ServiceLoader mechanism to find the ServletContainerInitializer classes to inspect
long start = 0;
if (LOG.isDebugEnabled())
@ -569,18 +721,104 @@ public class AnnotationConfiguration extends AbstractConfiguration
if (LOG.isDebugEnabled())
LOG.debug("Service loaders found in {}ms", (TimeUnit.MILLISECONDS.convert((System.nanoTime()-start), TimeUnit.NANOSECONDS)));
if (loadedInitializers != null)
//no ServletContainerInitializers found
if (loadedInitializers == null)
return Collections.emptyList();
ServletContainerInitializerOrdering initializerOrdering = getInitializerOrdering(context);
if (initializerOrdering != null && !initializerOrdering.isDefaultOrder())
{
for (ServletContainerInitializer service : loadedInitializers)
if (LOG.isDebugEnabled())
LOG.debug("Ordering ServletContainerInitializers with "+initializerOrdering);
//There is an ordering that is not just "*".
//Arrange ServletContainerInitializers according to the ordering of classnames given, irrespective of coming from container or webapp classpaths
for (ServletContainerInitializer sci:loadedInitializers)
{
if (!isFromExcludedJar(context, service))
nonExcludedInitializers.add(service);
Resource sciResource = getJarFor(sci);
if (!isFromExcludedJar(context, sci, sciResource))
{
String name = sci.getClass().getName();
if (initializerOrdering.getIndexOf(name) >= 0 || initializerOrdering.hasWildcard())
nonExcludedInitializers.add(sci);
}
}
//apply the ordering
Collections.sort(nonExcludedInitializers, new ServletContainerInitializerComparator(initializerOrdering));
}
else
{
//No ordering specified, or just the wildcard value "*" specified.
//Fallback to ordering the ServletContainerInitializers according to:
//container classpath first, WEB-INF/clases then WEB-INF/lib (obeying any web.xml jar ordering)
if (LOG.isDebugEnabled())
LOG.debug("Ordering ServletContainerInitializers as container path, webapp path");
Map<ServletContainerInitializer,Resource> webappPathInitializerResourceMap = new HashMap<ServletContainerInitializer,Resource>();
for (ServletContainerInitializer sci : loadedInitializers)
{
//if its on the container's classpath then add it
if (sci.getClass().getClassLoader() == context.getClassLoader().getParent())
{
nonExcludedInitializers.add(sci);
}
else
{
//if on the webapp's classpath then check the containing jar is not excluded from consideration
Resource sciResource = getJarFor(sci);
if (!isFromExcludedJar(context, sci, sciResource))
{
webappPathInitializerResourceMap.put(sci, sciResource);
}
}
}
//add the webapp classpath ones according to any web.xml ordering
if (context.getMetaData().getOrdering() == null)
nonExcludedInitializers.addAll(webappPathInitializerResourceMap.keySet()); //no ordering, just add them
else
{
//add in any ServletContainerInitializers which are not in a jar, as they must be from WEB-INF/classes
for (Map.Entry<ServletContainerInitializer, Resource> entry:webappPathInitializerResourceMap.entrySet())
{
if (entry.getValue() == null)
nonExcludedInitializers.add(entry.getKey());
}
//add ServletContainerInitializers according to the ordering of its containing jar
for (Resource webInfJar:context.getMetaData().getOrderedWebInfJars())
{
for (Map.Entry<ServletContainerInitializer, Resource> entry:webappPathInitializerResourceMap.entrySet())
{
if (webInfJar.equals(entry.getValue()))
nonExcludedInitializers.add(entry.getKey());
}
}
}
}
return nonExcludedInitializers;
}
/**
* Jetty-specific extension that allows an ordering to be applied across ALL ServletContainerInitializers.
*
* @return
*/
public ServletContainerInitializerOrdering getInitializerOrdering (WebAppContext context)
{
if (context == null)
return null;
String tmp = (String)context.getAttribute(SERVLET_CONTAINER_INITIALIZER_ORDER);
if (tmp == null || "".equals(tmp.trim()))
return null;
return new ServletContainerInitializerOrdering(tmp);
}
/**
@ -607,9 +845,6 @@ public class AnnotationConfiguration extends AbstractConfiguration
//queue it up for scanning if using multithreaded mode
if (_parserTasks != null)
_parserTasks.add(new ParserTask(parser, handlers, r, _containerClassNameResolver));
else
//just scan it now
parser.parse(handlers, r, _containerClassNameResolver);
}
}
@ -664,8 +899,6 @@ public class AnnotationConfiguration extends AbstractConfiguration
if (_parserTasks != null)
_parserTasks.add (new ParserTask(parser, handlers,r, _webAppClassNameResolver));
else
parser.parse(handlers, r, _webAppClassNameResolver);
}
}
@ -694,8 +927,6 @@ public class AnnotationConfiguration extends AbstractConfiguration
{
if (_parserTasks != null)
_parserTasks.add(new ParserTask(parser, handlers, dir, _webAppClassNameResolver));
else
parser.parse(handlers, dir, _webAppClassNameResolver);
}
}

View File

@ -39,6 +39,7 @@ import java.util.jar.JarInputStream;
import org.eclipse.jetty.util.ConcurrentHashSet;
import org.eclipse.jetty.util.Loader;
import org.eclipse.jetty.util.MultiException;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.Resource;
@ -370,15 +371,6 @@ public class AnnotationParser
final MethodInfo _mi;
final Set<? extends Handler> _handlers;
/**
* @param classname
* @param access
* @param name
* @param methodDesc
* @param signature
* @param exceptions
*/
public MyMethodVisitor(final Set<? extends Handler> handlers,
final ClassInfo classInfo,
final int access,
@ -423,9 +415,6 @@ public class AnnotationParser
final Set<? extends Handler> _handlers;
/**
* @param classname
*/
public MyFieldVisitor(final Set<? extends Handler> handlers,
final ClassInfo classInfo,
final int access,
@ -614,6 +603,7 @@ public class AnnotationParser
}
}
}
if (visitSuperClasses)
cz = cz.getSuperclass();
else
@ -650,19 +640,29 @@ public class AnnotationParser
public void parse (Set<? extends Handler> handlers, List<String> classNames, ClassNameResolver resolver)
throws Exception
{
MultiException me = new MultiException();
for (String s:classNames)
{
if ((resolver == null) || (!resolver.isExcluded(s) && (!isParsed(s) || resolver.shouldOverride(s))))
try
{
s = s.replace('.', '/')+".class";
URL resource = Loader.getResource(this.getClass(), s);
if (resource!= null)
if ((resolver == null) || (!resolver.isExcluded(s) && (!isParsed(s) || resolver.shouldOverride(s))))
{
Resource r = Resource.newResource(resource);
scanClass(handlers, null, r.getInputStream());
s = s.replace('.', '/')+".class";
URL resource = Loader.getResource(this.getClass(), s);
if (resource!= null)
{
Resource r = Resource.newResource(resource);
scanClass(handlers, null, r.getInputStream());
}
}
}
catch (Exception e)
{
me.add(new RuntimeException("Error scanning class "+s, e));
}
}
me.ifExceptionThrow();
}
@ -682,25 +682,22 @@ public class AnnotationParser
if (LOG.isDebugEnabled()) {LOG.debug("Scanning dir {}", dir);};
MultiException me = new MultiException();
String[] files=dir.list();
for (int f=0;files!=null && f<files.length;f++)
{
try
Resource res = dir.addPath(files[f]);
if (res.isDirectory())
parseDir(handlers, res, resolver);
else
{
Resource res = dir.addPath(files[f]);
if (res.isDirectory())
parseDir(handlers, res, resolver);
else
System.err.println("TRYING TO SCAN "+res);
//we've already verified the directories, so just verify the class file name
File file = res.getFile();
if (isValidClassFileName((file==null?null:file.getName())))
{
//we've already verified the directories, so just verify the class file name
boolean valid = true;
File file = res.getFile();
if (file == null)
LOG.warn("Unable to validate class file name for {}", res);
else
valid = isValidClassFileName(file.getName());
if (valid)
try
{
String name = res.getName();
if ((resolver == null)|| (!resolver.isExcluded(name) && (!isParsed(name) || resolver.shouldOverride(name))))
@ -709,15 +706,20 @@ public class AnnotationParser
if (LOG.isDebugEnabled()) {LOG.debug("Scanning class {}", r);};
scanClass(handlers, dir, r.getInputStream());
}
}
catch (Exception ex)
{
me.add(new RuntimeException("Error scanning file "+files[f],ex));
}
}
}
catch (Exception ex)
{
LOG.warn(Log.EXCEPTION,ex);
else
{
if (LOG.isDebugEnabled()) LOG.debug("Skipping scan on invalid file {}", res);
}
}
}
me.ifExceptionThrow();
}
@ -740,6 +742,8 @@ public class AnnotationParser
if (!(loader instanceof URLClassLoader))
return; //can't extract classes?
final MultiException me = new MultiException();
JarScanner scanner = new JarScanner()
{
@Override
@ -751,13 +755,14 @@ public class AnnotationParser
}
catch (Exception e)
{
LOG.warn("Problem parsing jar entry: {}", entry.getName());
me.add(new RuntimeException("Error parsing entry "+entry.getName()+" from jar "+ jarUri, e));
}
}
};
scanner.scan(null, loader, nullInclusive, visitParents);
me.ifExceptionThrow();
}
@ -774,6 +779,8 @@ public class AnnotationParser
if (uris==null)
return;
MultiException me = new MultiException();
for (URI uri:uris)
{
try
@ -782,10 +789,10 @@ public class AnnotationParser
}
catch (Exception e)
{
LOG.warn("Problem parsing classes from {}", uri);
me.add(new RuntimeException("Problem parsing classes from "+ uri, e));
}
}
me.ifExceptionThrow();
}
/**
@ -801,8 +808,6 @@ public class AnnotationParser
return;
parse (handlers, Resource.newResource(uri), resolver);
}
@ -895,13 +900,22 @@ public class AnnotationParser
if (in==null)
return;
MultiException me = new MultiException();
JarInputStream jar_in = new JarInputStream(in);
try
{
JarEntry entry = jar_in.getNextJarEntry();
while (entry!=null)
{
parseJarEntry(handlers, jarResource, entry, resolver);
try
{
parseJarEntry(handlers, jarResource, entry, resolver);
}
catch (Exception e)
{
me.add(new RuntimeException("Error scanning entry "+entry.getName()+" from jar "+jarResource, e));
}
entry = jar_in.getNextJarEntry();
}
}
@ -909,7 +923,8 @@ public class AnnotationParser
{
jar_in.close();
}
}
me.ifExceptionThrow();
}
}
/**

View File

@ -50,8 +50,9 @@ public class ContainerInitializerAnnotationHandler extends AbstractHandler
/**
* Handle finding a class that is annotated with the annotation we were constructed with.
* @see org.eclipse.jetty.annotations.AnnotationParser.DiscoverableAnnotationHandler#handle(ClassInfo)
* */
*
* @see org.eclipse.jetty.annotations.AnnotationParser.Handler#handle(ClassInfo, String)
*/
public void handle(ClassInfo info, String annotationName)
{
if (annotationName == null || !_annotation.getName().equals(annotationName))
@ -63,7 +64,7 @@ public class ContainerInitializerAnnotationHandler extends AbstractHandler
/**
* Handle finding a field that is annotated with the annotation we were constructed with.
*
* @see org.eclipse.jetty.annotations.AnnotationParser.DiscoverableAnnotationHandler#handle(org.eclipse.jetty.annotations.AnnotationParser.FieldAnnotationInfo)
* @see org.eclipse.jetty.annotations.AnnotationParser.Handler#handle(FieldInfo, String)
*/
public void handle(FieldInfo info, String annotationName)
{
@ -75,7 +76,7 @@ public class ContainerInitializerAnnotationHandler extends AbstractHandler
/**
* Handle finding a method that is annotated with the annotation we were constructed with.
*
* @see org.eclipse.jetty.annotations.AnnotationParser.DiscoverableAnnotationHandler#handle(org.eclipse.jetty.annotations.AnnotationParser.MethodAnnotationInfo)
* @see org.eclipse.jetty.annotations.AnnotationParser.Handler#handle(MethodInfo, String)
*/
public void handle(MethodInfo info, String annotationName)
{

View File

@ -18,14 +18,12 @@
package org.eclipse.jetty.annotations;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.jetty.plus.annotation.ContainerInitializer;
import org.eclipse.jetty.util.ConcurrentHashSet;
import org.eclipse.jetty.util.MultiMap;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@ -71,6 +69,8 @@ public class ServletContainerInitializersStarter extends AbstractLifeCycle
//instantiate ServletContainerInitializers, call doStart
try
{
if (LOG.isDebugEnabled())
LOG.debug("Calling ServletContainerInitializer "+i.getTarget().getClass().getName());
i.callStartup(_context);
}
catch (Exception e)

View File

@ -36,7 +36,7 @@ public class WebListenerAnnotationHandler extends AbstractDiscoverableAnnotation
/**
* @see org.eclipse.jetty.annotations.AnnotationParser.DiscoverableAnnotationHandler#handle(ClassAnnotationInfo)
* @see org.eclipse.jetty.annotations.AnnotationParser.Handler#handle(ClassInfo, String)
*/
public void handle(ClassInfo info, String annotationName)
{

View File

@ -18,14 +18,11 @@
package org.eclipse.jetty.annotations;
import java.util.List;
import org.eclipse.jetty.annotations.AnnotationParser.ClassInfo;
import org.eclipse.jetty.annotations.AnnotationParser.FieldInfo;
import org.eclipse.jetty.annotations.AnnotationParser.MethodInfo;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.webapp.DiscoveredAnnotation;
import org.eclipse.jetty.webapp.WebAppContext;
/**
@ -47,8 +44,7 @@ public class WebServletAnnotationHandler extends AbstractDiscoverableAnnotationH
/**
* Handle discovering a WebServlet annotation.
*
*
* @see org.eclipse.jetty.annotations.AnnotationParser.DiscoverableAnnotationHandler#handleClass(java.lang.String, int, int, java.lang.String, java.lang.String, java.lang.String[], java.lang.String, java.util.List)
* @see org.eclipse.jetty.annotations.AnnotationParser.Handler#handle(ClassInfo, String)
*/
@Override
public void handle(ClassInfo info, String annotationName)

View File

@ -124,18 +124,5 @@
<artifactId>jetty-test-helper</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.ning</groupId>
<artifactId>async-http-client</artifactId>
<version>1.7.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.2.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -3,5 +3,4 @@
#
[lib]
# Client jars
lib/jetty-client-${jetty.version}.jar

View File

@ -19,24 +19,20 @@
package org.eclipse.jetty.client;
import java.io.IOException;
import java.net.ConnectException;
import java.net.SocketAddress;
import java.net.SocketException;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import javax.net.ssl.SSLEngine;
import java.util.Map;
import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.SelectChannelEndPoint;
import org.eclipse.jetty.io.SelectorManager;
import org.eclipse.jetty.io.ssl.SslConnection;
import org.eclipse.jetty.io.ssl.SslClientConnectionFactory;
import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.ssl.SslContextFactory;
public abstract class AbstractHttpClientTransport extends ContainerLifeCycle implements HttpClientTransport
{
@ -72,12 +68,20 @@ public abstract class AbstractHttpClientTransport extends ContainerLifeCycle imp
}
@Override
public void connect(HttpDestination destination, SocketAddress address, Promise<org.eclipse.jetty.client.api.Connection> promise)
protected void doStop() throws Exception
{
super.doStop();
removeBean(selectorManager);
}
@Override
public void connect(SocketAddress address, Map<String, Object> context)
{
SocketChannel channel = null;
try
{
channel = SocketChannel.open();
HttpDestination destination = (HttpDestination)context.get(HTTP_DESTINATION_CONTEXT_KEY);
HttpClient client = destination.getHttpClient();
SocketAddress bindAddress = client.getBindAddress();
if (bindAddress != null)
@ -86,8 +90,9 @@ public abstract class AbstractHttpClientTransport extends ContainerLifeCycle imp
channel.configureBlocking(false);
channel.connect(address);
ConnectionCallback callback = new ConnectionCallback(destination, promise);
selectorManager.connect(channel, callback);
context.put(SslClientConnectionFactory.SSL_PEER_HOST_CONTEXT_KEY, destination.getHost());
context.put(SslClientConnectionFactory.SSL_PEER_PORT_CONTEXT_KEY, destination.getPort());
selectorManager.connect(channel, context);
}
// Must catch all exceptions, since some like
// UnresolvedAddressException are not IOExceptions.
@ -104,12 +109,14 @@ public abstract class AbstractHttpClientTransport extends ContainerLifeCycle imp
}
finally
{
@SuppressWarnings("unchecked")
Promise<Connection> promise = (Promise<Connection>)context.get(HTTP_CONNECTION_PROMISE_CONTEXT_KEY);
promise.failed(x);
}
}
}
protected void configure(HttpClient client, SocketChannel channel) throws SocketException
protected void configure(HttpClient client, SocketChannel channel) throws IOException
{
channel.socket().setTcpNoDelay(client.isTCPNoDelay());
}
@ -119,40 +126,6 @@ public abstract class AbstractHttpClientTransport extends ContainerLifeCycle imp
return new ClientSelectorManager(client, selectors);
}
protected SslConnection createSslConnection(EndPoint endPoint, HttpDestination destination)
{
HttpClient httpClient = destination.getHttpClient();
SslContextFactory sslContextFactory = httpClient.getSslContextFactory();
SSLEngine engine = sslContextFactory.newSSLEngine(destination.getHost(), destination.getPort());
engine.setUseClientMode(true);
SslConnection sslConnection = newSslConnection(httpClient, endPoint, engine);
sslConnection.setRenegotiationAllowed(sslContextFactory.isRenegotiationAllowed());
endPoint.setConnection(sslConnection);
EndPoint appEndPoint = sslConnection.getDecryptedEndPoint();
Connection connection = newConnection(appEndPoint, destination);
appEndPoint.setConnection(connection);
return sslConnection;
}
protected SslConnection newSslConnection(HttpClient httpClient, EndPoint endPoint, SSLEngine engine)
{
return new SslConnection(httpClient.getByteBufferPool(), httpClient.getExecutor(), endPoint, engine);
}
protected abstract Connection newConnection(EndPoint endPoint, HttpDestination destination);
protected org.eclipse.jetty.client.api.Connection tunnel(EndPoint endPoint, HttpDestination destination, org.eclipse.jetty.client.api.Connection connection)
{
SslConnection sslConnection = createSslConnection(endPoint, destination);
Connection result = sslConnection.getDecryptedEndPoint().getConnection();
selectorManager.connectionClosed((Connection)connection);
selectorManager.connectionOpened(sslConnection);
LOG.debug("Tunnelled {} over {}", connection, result);
return (org.eclipse.jetty.client.api.Connection)result;
}
protected class ClientSelectorManager extends SelectorManager
{
private final HttpClient client;
@ -170,63 +143,21 @@ public abstract class AbstractHttpClientTransport extends ContainerLifeCycle imp
}
@Override
public Connection newConnection(SocketChannel channel, EndPoint endPoint, Object attachment) throws IOException
public org.eclipse.jetty.io.Connection newConnection(SocketChannel channel, EndPoint endPoint, Object attachment) throws IOException
{
ConnectionCallback callback = (ConnectionCallback)attachment;
HttpDestination destination = callback.destination;
SslContextFactory sslContextFactory = client.getSslContextFactory();
if (!destination.isProxied() && HttpScheme.HTTPS.is(destination.getScheme()))
{
if (sslContextFactory == null)
{
IOException failure = new ConnectException("Missing " + SslContextFactory.class.getSimpleName() + " for " + destination.getScheme() + " requests");
callback.failed(failure);
throw failure;
}
else
{
SslConnection sslConnection = createSslConnection(endPoint, destination);
callback.succeeded((org.eclipse.jetty.client.api.Connection)sslConnection.getDecryptedEndPoint().getConnection());
return sslConnection;
}
}
else
{
Connection connection = AbstractHttpClientTransport.this.newConnection(endPoint, destination);
callback.succeeded((org.eclipse.jetty.client.api.Connection)connection);
return connection;
}
@SuppressWarnings("unchecked")
Map<String, Object> context = (Map<String, Object>)attachment;
HttpDestination destination = (HttpDestination)context.get(HTTP_DESTINATION_CONTEXT_KEY);
return destination.getClientConnectionFactory().newConnection(endPoint, context);
}
@Override
protected void connectionFailed(SocketChannel channel, Throwable ex, Object attachment)
{
ConnectionCallback callback = (ConnectionCallback)attachment;
callback.failed(ex);
}
}
private class ConnectionCallback implements Promise<org.eclipse.jetty.client.api.Connection>
{
private final HttpDestination destination;
private final Promise<org.eclipse.jetty.client.api.Connection> promise;
private ConnectionCallback(HttpDestination destination, Promise<org.eclipse.jetty.client.api.Connection> promise)
{
this.destination = destination;
this.promise = promise;
}
@Override
public void succeeded(org.eclipse.jetty.client.api.Connection result)
{
promise.succeeded(result);
}
@Override
public void failed(Throwable x)
protected void connectionFailed(SocketChannel channel, Throwable x, Object attachment)
{
@SuppressWarnings("unchecked")
Map<String, Object> context = (Map<String, Object>)attachment;
@SuppressWarnings("unchecked")
Promise<Connection> promise = (Promise<Connection>)context.get(HTTP_CONNECTION_PROMISE_CONTEXT_KEY);
promise.failed(x);
}
}

View File

@ -109,16 +109,19 @@ public abstract class AuthenticationProtocolHandler implements ProtocolHandler
return;
}
URI uri = getAuthenticationURI(request);
Authentication authentication = null;
Authentication.HeaderInfo headerInfo = null;
for (Authentication.HeaderInfo element : headerInfos)
URI uri = getAuthenticationURI(request);
if (uri != null)
{
authentication = client.getAuthenticationStore().findAuthentication(element.getType(), uri, element.getRealm());
if (authentication != null)
for (Authentication.HeaderInfo element : headerInfos)
{
headerInfo = element;
break;
authentication = client.getAuthenticationStore().findAuthentication(element.getType(), uri, element.getRealm());
if (authentication != null)
{
headerInfo = element;
break;
}
}
}
if (authentication == null)

View File

@ -16,7 +16,7 @@
// ========================================================================
//
package org.eclipse.jetty.client.http;
package org.eclipse.jetty.client;
import java.io.IOException;
import java.util.concurrent.BlockingDeque;
@ -33,9 +33,9 @@ import org.eclipse.jetty.util.component.Dumpable;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
public class HttpConnectionPool implements Dumpable
public class ConnectionPool implements Dumpable
{
private static final Logger LOG = Log.getLogger(HttpConnectionPool.class);
private static final Logger LOG = Log.getLogger(ConnectionPool.class);
private final AtomicInteger connectionCount = new AtomicInteger();
private final Destination destination;
@ -44,7 +44,7 @@ public class HttpConnectionPool implements Dumpable
private final BlockingDeque<Connection> idleConnections;
private final BlockingQueue<Connection> activeConnections;
public HttpConnectionPool(Destination destination, int maxConnections, Promise<Connection> connectionPromise)
public ConnectionPool(Destination destination, int maxConnections, Promise<Connection> connectionPromise)
{
this.destination = destination;
this.maxConnections = maxConnections;

View File

@ -73,4 +73,10 @@ public abstract class HttpChannel
{
disassociate();
}
@Override
public String toString()
{
return String.format("%s@%h", getClass().getSimpleName(), this);
}
}

View File

@ -27,10 +27,12 @@ import java.net.URI;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@ -44,7 +46,6 @@ import org.eclipse.jetty.client.api.AuthenticationStore;
import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Destination;
import org.eclipse.jetty.client.api.ProxyConfiguration;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
@ -57,7 +58,6 @@ import org.eclipse.jetty.io.MappedByteBufferPool;
import org.eclipse.jetty.util.Jetty;
import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.util.SocketAddressResolver;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@ -104,12 +104,13 @@ public class HttpClient extends ContainerLifeCycle
{
private static final Logger LOG = Log.getLogger(HttpClient.class);
private final ConcurrentMap<String, HttpDestination> destinations = new ConcurrentHashMap<>();
private final ConcurrentMap<Origin, HttpDestination> destinations = new ConcurrentHashMap<>();
private final ConcurrentMap<Long, HttpConversation> conversations = new ConcurrentHashMap<>();
private final List<ProtocolHandler> handlers = new ArrayList<>();
private final List<Request.Listener> requestListeners = new ArrayList<>();
private final AuthenticationStore authenticationStore = new HttpAuthenticationStore();
private final Set<ContentDecoder.Factory> decoderFactories = new ContentDecoderFactorySet();
private final ProxyConfiguration proxyConfig = new ProxyConfiguration();
private final HttpClientTransport transport;
private final SslContextFactory sslContextFactory;
private volatile CookieManager cookieManager;
@ -132,7 +133,6 @@ public class HttpClient extends ContainerLifeCycle
private volatile boolean tcpNoDelay = true;
private volatile boolean dispatchIO = true;
private volatile boolean strictEventOrdering = false;
private volatile ProxyConfiguration proxyConfig;
private volatile HttpField encodingField;
/**
@ -359,7 +359,7 @@ public class HttpClient extends ContainerLifeCycle
*/
public Request newRequest(String host, int port)
{
return newRequest(address("http", host, port));
return newRequest(new Origin("http", host, port).asString());
}
/**
@ -417,13 +417,6 @@ public class HttpClient extends ContainerLifeCycle
return newRequest;
}
public String address(String scheme, String host, int port)
{
StringBuilder result = new StringBuilder();
URIUtil.appendSchemeHostPort(result, scheme, host, port);
return result.toString();
}
/**
* Returns a {@link Destination} for the given scheme, host and port.
* Applications may use {@link Destination}s to create {@link Connection}s
@ -446,20 +439,20 @@ public class HttpClient extends ContainerLifeCycle
{
port = normalizePort(scheme, port);
String address = address(scheme, host, port);
HttpDestination destination = destinations.get(address);
Origin origin = new Origin(scheme, host, port);
HttpDestination destination = destinations.get(origin);
if (destination == null)
{
destination = transport.newHttpDestination(scheme, host, port);
destination = transport.newHttpDestination(origin);
if (isRunning())
{
HttpDestination existing = destinations.putIfAbsent(address, destination);
HttpDestination existing = destinations.putIfAbsent(origin, destination);
if (existing != null)
destination = existing;
else
LOG.debug("Created {}", destination);
if (!isRunning())
destinations.remove(address);
destinations.remove(origin);
}
}
@ -486,13 +479,16 @@ public class HttpClient extends ContainerLifeCycle
protected void newConnection(final HttpDestination destination, final Promise<Connection> promise)
{
Destination.Address address = destination.getConnectAddress();
Origin.Address address = destination.getConnectAddress();
resolver.resolve(address.getHost(), address.getPort(), new Promise<SocketAddress>()
{
@Override
public void succeeded(SocketAddress socketAddress)
{
transport.connect(destination, socketAddress, promise);
Map<String, Object> context = new HashMap<>();
context.put(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY, destination);
context.put(HttpClientTransport.HTTP_CONNECTION_PROMISE_CONTEXT_KEY, promise);
transport.connect(socketAddress, context);
}
@Override
@ -559,7 +555,7 @@ public class HttpClient extends ContainerLifeCycle
}
/**
* @return the max time a connection can take to connect to destinations
* @return the max time, in milliseconds, a connection can take to connect to destinations
*/
public long getConnectTimeout()
{
@ -567,7 +563,7 @@ public class HttpClient extends ContainerLifeCycle
}
/**
* @param connectTimeout the max time a connection can take to connect to destinations
* @param connectTimeout the max time, in milliseconds, a connection can take to connect to destinations
* @see java.net.Socket#connect(SocketAddress, int)
*/
public void setConnectTimeout(long connectTimeout)
@ -592,7 +588,7 @@ public class HttpClient extends ContainerLifeCycle
}
/**
* @return the max time a connection can be idle (that is, without traffic of bytes in either direction)
* @return the max time, in milliseconds, a connection can be idle (that is, without traffic of bytes in either direction)
*/
public long getIdleTimeout()
{
@ -600,7 +596,7 @@ public class HttpClient extends ContainerLifeCycle
}
/**
* @param idleTimeout the max time a connection can be idle (that is, without traffic of bytes in either direction)
* @param idleTimeout the max time, in milliseconds, a connection can be idle (that is, without traffic of bytes in either direction)
*/
public void setIdleTimeout(long idleTimeout)
{
@ -881,14 +877,6 @@ public class HttpClient extends ContainerLifeCycle
return proxyConfig;
}
/**
* @param proxyConfig the forward proxy configuration
*/
public void setProxyConfiguration(ProxyConfiguration proxyConfig)
{
this.proxyConfig = proxyConfig;
}
protected HttpField getAcceptEncodingField()
{
return encodingField;

View File

@ -19,9 +19,9 @@
package org.eclipse.jetty.client;
import java.net.SocketAddress;
import java.util.Map;
import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.io.ClientConnectionFactory;
/**
* {@link HttpClientTransport} represents what transport implementations should provide
@ -34,8 +34,11 @@ import org.eclipse.jetty.util.Promise;
* but the HTTP exchange may also be carried using the SPDY protocol or the FCGI protocol or, in future,
* other protocols.
*/
public interface HttpClientTransport
public interface HttpClientTransport extends ClientConnectionFactory
{
public static final String HTTP_DESTINATION_CONTEXT_KEY = "http.destination";
public static final String HTTP_CONNECTION_PROMISE_CONTEXT_KEY = "http.connection.promise";
/**
* Sets the {@link HttpClient} instance on this transport.
* <p />
@ -53,27 +56,16 @@ public interface HttpClientTransport
* {@link HttpDestination} controls the destination-connection cardinality: protocols like
* HTTP have 1-N cardinality, while multiplexed protocols like SPDY have a 1-1 cardinality.
*
* @param scheme the destination scheme
* @param host the destination host
* @param port the destination port
* @param origin the destination origin
* @return a new, transport-specific, {@link HttpDestination} object
*/
public HttpDestination newHttpDestination(String scheme, String host, int port);
public HttpDestination newHttpDestination(Origin origin);
/**
* Establishes a physical connection to the given {@code address}.
*
* @param destination the destination
* @param address the address to connect to
* @param promise the promise to notify when the connection succeeds or fails
* @param context the context information to establish the connection
*/
public void connect(HttpDestination destination, SocketAddress address, Promise<Connection> promise);
/**
* Establishes an encrypted tunnel over the given {@code connection}
*
* @param connection the connection to tunnel
* @return the tunnelled connection
*/
public Connection tunnel(Connection connection);
public void connect(SocketAddress address, Map<String, Object> context);
}

View File

@ -83,6 +83,7 @@ public abstract class HttpConnection implements Connection
HttpVersion version = request.getVersion();
HttpFields headers = request.getHeaders();
ContentProvider content = request.getContent();
ProxyConfiguration.Proxy proxy = destination.getProxy();
// Make sure the path is there
String path = request.getPath();
@ -91,7 +92,7 @@ public abstract class HttpConnection implements Connection
path = "/";
request.path(path);
}
if (destination.isProxied() && !HttpMethod.CONNECT.is(method))
if (proxy != null && !HttpMethod.CONNECT.is(method))
{
path = request.getURI().toString();
request.path(path);
@ -104,6 +105,9 @@ public abstract class HttpConnection implements Connection
headers.put(getHttpDestination().getHostField());
}
if (request.getAgent() == null)
headers.put(getHttpClient().getUserAgentField());
// Add content headers
if (content != null)
{
@ -136,9 +140,18 @@ public abstract class HttpConnection implements Connection
request.header(HttpHeader.COOKIE.asString(), cookieString.toString());
// Authorization
URI authenticationURI = destination.isProxied() ? destination.getProxyURI() : request.getURI();
Authentication.Result authnResult = getHttpClient().getAuthenticationStore().findAuthenticationResult(authenticationURI);
if (authnResult != null)
authnResult.apply(request);
URI authenticationURI = proxy != null ? proxy.getURI() : request.getURI();
if (authenticationURI != null)
{
Authentication.Result authnResult = getHttpClient().getAuthenticationStore().findAuthenticationResult(authenticationURI);
if (authnResult != null)
authnResult.apply(request);
}
}
@Override
public String toString()
{
return String.format("%s@%h", getClass().getSimpleName(), this);
}
}

View File

@ -20,51 +20,45 @@ package org.eclipse.jetty.client;
import java.io.Closeable;
import java.io.IOException;
import java.net.URI;
import java.nio.channels.AsynchronousCloseException;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.client.api.Destination;
import org.eclipse.jetty.client.api.ProxyConfiguration;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.io.ClientConnectionFactory;
import org.eclipse.jetty.io.ssl.SslClientConnectionFactory;
import org.eclipse.jetty.util.BlockingArrayQueue;
import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
import org.eclipse.jetty.util.component.Dumpable;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.ssl.SslContextFactory;
public abstract class HttpDestination implements Destination, Closeable, Dumpable
{
protected static final Logger LOG = Log.getLogger(HttpDestination.class);
private final HttpClient client;
private final String scheme;
private final String host;
private final Address address;
private final Origin origin;
private final Queue<HttpExchange> exchanges;
private final RequestNotifier requestNotifier;
private final ResponseNotifier responseNotifier;
private final Address proxyAddress;
private final ProxyConfiguration.Proxy proxy;
private final ClientConnectionFactory connectionFactory;
private final HttpField hostField;
public HttpDestination(HttpClient client, String scheme, String host, int port)
public HttpDestination(HttpClient client, Origin origin)
{
this.client = client;
this.scheme = scheme;
this.host = host;
this.address = new Address(host, port);
this.origin = origin;
this.exchanges = new BlockingArrayQueue<>(client.getMaxRequestsQueuedPerDestination());
@ -72,19 +66,40 @@ public abstract class HttpDestination implements Destination, Closeable, Dumpabl
this.responseNotifier = new ResponseNotifier(client);
ProxyConfiguration proxyConfig = client.getProxyConfiguration();
proxyAddress = proxyConfig != null && proxyConfig.matches(host, port) ?
new Address(proxyConfig.getHost(), proxyConfig.getPort()) : null;
proxy = proxyConfig.match(origin);
ClientConnectionFactory connectionFactory = client.getTransport();
if (proxy != null)
{
connectionFactory = proxy.newClientConnectionFactory(connectionFactory);
}
else
{
if (HttpScheme.HTTPS.is(getScheme()))
connectionFactory = newSslClientConnectionFactory(connectionFactory);
}
this.connectionFactory = connectionFactory;
if (!client.isDefaultPort(scheme, port))
host += ":" + port;
String host = getHost();
if (!client.isDefaultPort(getScheme(), getPort()))
host += ":" + getPort();
hostField = new HttpField(HttpHeader.HOST, host);
}
protected ClientConnectionFactory newSslClientConnectionFactory(ClientConnectionFactory connectionFactory)
{
return new SslClientConnectionFactory(client.getSslContextFactory(), client.getByteBufferPool(), client.getExecutor(), connectionFactory);
}
public HttpClient getHttpClient()
{
return client;
}
public Origin getOrigin()
{
return origin;
}
public Queue<HttpExchange> getHttpExchanges()
{
return exchanges;
@ -100,10 +115,20 @@ public abstract class HttpDestination implements Destination, Closeable, Dumpabl
return responseNotifier;
}
public ProxyConfiguration.Proxy getProxy()
{
return proxy;
}
public ClientConnectionFactory getClientConnectionFactory()
{
return connectionFactory;
}
@Override
public String getScheme()
{
return scheme;
return origin.getScheme();
}
@Override
@ -111,32 +136,18 @@ public abstract class HttpDestination implements Destination, Closeable, Dumpabl
{
// InetSocketAddress.getHostString() transforms the host string
// in case of IPv6 addresses, so we return the original host string
return host;
return origin.getAddress().getHost();
}
@Override
public int getPort()
{
return address.getPort();
return origin.getAddress().getPort();
}
public Address getConnectAddress()
public Origin.Address getConnectAddress()
{
return isProxied() ? proxyAddress : address;
}
public boolean isProxied()
{
return proxyAddress != null;
}
public URI getProxyURI()
{
ProxyConfiguration proxyConfiguration = client.getProxyConfiguration();
String uri = getScheme() + "://" + proxyConfiguration.getHost();
if (!client.isDefaultPort(getScheme(), proxyConfiguration.getPort()))
uri += ":" + proxyConfiguration.getPort();
return URI.create(uri);
return proxy == null ? origin.getAddress() : proxy.getAddress();
}
public HttpField getHostField()
@ -146,7 +157,7 @@ public abstract class HttpDestination implements Destination, Closeable, Dumpabl
protected void send(Request request, List<Response.ResponseListener> listeners)
{
if (!scheme.equals(request.getScheme()))
if (!getScheme().equals(request.getScheme()))
throw new IllegalArgumentException("Invalid request scheme " + request.getScheme() + " for destination " + this);
if (!getHost().equals(request.getHost()))
throw new IllegalArgumentException("Invalid request host " + request.getHost() + " for destination " + this);
@ -188,7 +199,7 @@ public abstract class HttpDestination implements Destination, Closeable, Dumpabl
public void newConnection(Promise<Connection> promise)
{
createConnection(new ProxyPromise(promise));
createConnection(promise);
}
protected void createConnection(Promise<Connection> promise)
@ -207,6 +218,10 @@ public abstract class HttpDestination implements Destination, Closeable, Dumpabl
LOG.debug("Closed {}", this);
}
public void close(Connection connection)
{
}
/**
* Aborts all the {@link HttpExchange}s queued in this destination.
*
@ -236,18 +251,6 @@ public abstract class HttpDestination implements Destination, Closeable, Dumpabl
getResponseNotifier().notifyComplete(listeners, new Result(request, cause, response, cause));
}
protected void tunnelSucceeded(Connection connection, Promise<Connection> promise)
{
// Wrap the connection with TLS
promise.succeeded(client.getTransport().tunnel(connection));
}
protected void tunnelFailed(Connection connection, Promise<Connection> promise, Throwable failure)
{
promise.failed(failure);
connection.close();
}
@Override
public String dump()
{
@ -262,7 +265,7 @@ public abstract class HttpDestination implements Destination, Closeable, Dumpabl
public String asString()
{
return client.address(getScheme(), getHost(), getPort());
return origin.asString();
}
@Override
@ -271,84 +274,6 @@ public abstract class HttpDestination implements Destination, Closeable, Dumpabl
return String.format("%s(%s)%s",
HttpDestination.class.getSimpleName(),
asString(),
proxyAddress == null ? "" : " via " + proxyAddress.getHost() + ":" + proxyAddress.getPort());
}
/**
* Decides whether to establish a proxy tunnel using HTTP CONNECT.
* It is implemented as a promise because it needs to establish the tunnel
* when the TCP connection is succeeded, and needs to notify another
* promise when the tunnel is established (or failed).
*/
private class ProxyPromise implements Promise<Connection>
{
private final Promise<Connection> delegate;
private ProxyPromise(Promise<Connection> delegate)
{
this.delegate = delegate;
}
@Override
public void succeeded(Connection connection)
{
if (isProxied() && HttpScheme.HTTPS.is(getScheme()))
{
if (client.getSslContextFactory() != null)
{
tunnel(connection);
}
else
{
String message = String.format("Cannot perform requests over SSL, no %s in %s",
SslContextFactory.class.getSimpleName(), HttpClient.class.getSimpleName());
delegate.failed(new IllegalStateException(message));
}
}
else
{
delegate.succeeded(connection);
}
}
@Override
public void failed(Throwable x)
{
delegate.failed(x);
}
private void tunnel(final Connection connection)
{
String target = address.getHost() + ":" + address.getPort();
Request connect = client.newRequest(proxyAddress.getHost(), proxyAddress.getPort())
.scheme(HttpScheme.HTTP.asString())
.method(HttpMethod.CONNECT)
.path(target)
.header(HttpHeader.HOST, target)
.timeout(client.getConnectTimeout(), TimeUnit.MILLISECONDS);
connection.send(connect, new Response.CompleteListener()
{
@Override
public void onComplete(Result result)
{
if (result.isFailed())
{
tunnelFailed(connection, delegate, result.getFailure());
}
else
{
Response response = result.getResponse();
if (response.getStatus() == 200)
{
tunnelSucceeded(connection, delegate);
}
else
{
tunnelFailed(connection, delegate, new HttpResponseException("Received " + response + " for " + result.getRequest(), response));
}
}
}
});
}
proxy == null ? "" : " via " + proxy);
}
}

View File

@ -0,0 +1,200 @@
//
// ========================================================================
// Copyright (c) 1995-2013 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.client;
import java.io.IOException;
import java.net.URI;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.io.ClientConnectionFactory;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.ssl.SslClientConnectionFactory;
import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.ssl.SslContextFactory;
public class HttpProxy extends ProxyConfiguration.Proxy
{
public HttpProxy(String host, int port)
{
this(new Origin.Address(host, port), false);
}
public HttpProxy(Origin.Address address, boolean secure)
{
super(address, secure);
}
@Override
public ClientConnectionFactory newClientConnectionFactory(ClientConnectionFactory connectionFactory)
{
return new HttpProxyClientConnectionFactory(connectionFactory);
}
@Override
public URI getURI()
{
String scheme = isSecure() ? HttpScheme.HTTPS.asString() : HttpScheme.HTTP.asString();
return URI.create(new Origin(scheme, getAddress()).asString());
}
public static class HttpProxyClientConnectionFactory implements ClientConnectionFactory
{
private static final Logger LOG = Log.getLogger(HttpProxyClientConnectionFactory.class);
private final ClientConnectionFactory connectionFactory;
public HttpProxyClientConnectionFactory(ClientConnectionFactory connectionFactory)
{
this.connectionFactory = connectionFactory;
}
@Override
public org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, Map<String, Object> context) throws IOException
{
@SuppressWarnings("unchecked")
Promise<Connection> promise = (Promise<Connection>)context.get(HttpClientTransport.HTTP_CONNECTION_PROMISE_CONTEXT_KEY);
final ProxyPromise proxyPromise = new ProxyPromise(endPoint, promise, context);
// Replace the promise with the proxy one
context.put(HttpClientTransport.HTTP_CONNECTION_PROMISE_CONTEXT_KEY, proxyPromise);
return connectionFactory.newConnection(endPoint, context);
}
/**
* Decides whether to establish a proxy tunnel using HTTP CONNECT.
* It is implemented as a promise because it needs to establish the
* tunnel after the TCP connection is succeeded, and needs to notify
* the nested promise when the tunnel is established (or failed).
*/
private class ProxyPromise implements Promise<Connection>
{
private final EndPoint endPoint;
private final Promise<Connection> promise;
private final Map<String, Object> context;
private ProxyPromise(EndPoint endPoint, Promise<Connection> promise, Map<String, Object> context)
{
this.endPoint = endPoint;
this.promise = promise;
this.context = context;
}
@Override
public void succeeded(Connection connection)
{
HttpDestination destination = (HttpDestination)context.get(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY);
if (HttpScheme.HTTPS.is(destination.getScheme()))
{
SslContextFactory sslContextFactory = destination.getHttpClient().getSslContextFactory();
if (sslContextFactory != null)
{
tunnel(destination, connection);
}
else
{
String message = String.format("Cannot perform requests over SSL, no %s in %s",
SslContextFactory.class.getSimpleName(), HttpClient.class.getSimpleName());
promise.failed(new IllegalStateException(message));
}
}
else
{
promise.succeeded(connection);
}
}
@Override
public void failed(Throwable x)
{
promise.failed(x);
}
private void tunnel(HttpDestination destination, final Connection connection)
{
String target = destination.getOrigin().getAddress().asString();
Origin.Address proxyAddress = destination.getConnectAddress();
HttpClient httpClient = destination.getHttpClient();
Request connect = httpClient.newRequest(proxyAddress.getHost(), proxyAddress.getPort())
.scheme(HttpScheme.HTTP.asString())
.method(HttpMethod.CONNECT)
.path(target)
.header(HttpHeader.HOST, target)
.timeout(httpClient.getConnectTimeout(), TimeUnit.MILLISECONDS);
connection.send(connect, new Response.CompleteListener()
{
@Override
public void onComplete(Result result)
{
if (result.isFailed())
{
tunnelFailed(result.getFailure());
}
else
{
Response response = result.getResponse();
if (response.getStatus() == 200)
{
tunnelSucceeded();
}
else
{
tunnelFailed(new HttpResponseException("Received " + response + " for " + result.getRequest(), response));
}
}
}
});
}
private void tunnelSucceeded()
{
try
{
// Replace the promise back with the original
context.put(HttpClientTransport.HTTP_CONNECTION_PROMISE_CONTEXT_KEY, promise);
HttpDestination destination = (HttpDestination)context.get(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY);
HttpClient client = destination.getHttpClient();
ClientConnectionFactory sslConnectionFactory = new SslClientConnectionFactory(client.getSslContextFactory(), client.getByteBufferPool(), client.getExecutor(), connectionFactory);
org.eclipse.jetty.io.Connection oldConnection = endPoint.getConnection();
org.eclipse.jetty.io.Connection newConnection = sslConnectionFactory.newConnection(endPoint, context);
Helper.replaceConnection(oldConnection, newConnection);
LOG.debug("HTTP tunnel established: {} over {}", oldConnection, newConnection);
}
catch (Throwable x)
{
tunnelFailed(x);
}
}
private void tunnelFailed(Throwable failure)
{
endPoint.close();
failed(failure);
}
}
}
}

View File

@ -36,6 +36,7 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.jetty.client.api.ContentProvider;
import org.eclipse.jetty.client.api.ContentResponse;
@ -55,10 +56,11 @@ public class HttpRequest implements Request
private static final AtomicLong ids = new AtomicLong();
private final HttpFields headers = new HttpFields();
private final Fields params = new Fields();
private final Fields params = new Fields(true);
private final Map<String, Object> attributes = new HashMap<>();
private final List<RequestListener> requestListeners = new ArrayList<>();
private final List<Response.ResponseListener> responseListeners = new ArrayList<>();
private final AtomicReference<Throwable> aborted = new AtomicReference<>();
private final HttpClient client;
private final long conversation;
private final String host;
@ -73,7 +75,6 @@ public class HttpRequest implements Request
private long timeout;
private ContentProvider content;
private boolean followRedirects;
private volatile Throwable aborted;
public HttpRequest(HttpClient client, URI uri)
{
@ -95,7 +96,6 @@ public class HttpRequest implements Request
HttpField acceptEncodingField = client.getAcceptEncodingField();
if (acceptEncodingField != null)
headers.put(acceptEncodingField);
headers.put(client.getUserAgentField());
}
@Override
@ -505,16 +505,19 @@ public class HttpRequest implements Request
@Override
public boolean abort(Throwable cause)
{
aborted = Objects.requireNonNull(cause);
// The conversation may be null if it is already completed
HttpConversation conversation = client.getConversation(getConversationID(), false);
return conversation != null && conversation.abort(cause);
if (aborted.compareAndSet(null, Objects.requireNonNull(cause)))
{
// The conversation may be null if it is already completed
HttpConversation conversation = client.getConversation(getConversationID(), false);
return conversation != null && conversation.abort(cause);
}
return false;
}
@Override
public Throwable getAbortCause()
{
return aborted;
return aborted.get();
}
private String buildQuery()
@ -523,13 +526,13 @@ public class HttpRequest implements Request
for (Iterator<Fields.Field> iterator = params.iterator(); iterator.hasNext();)
{
Fields.Field field = iterator.next();
String[] values = field.values();
for (int i = 0; i < values.length; ++i)
List<String> values = field.getValues();
for (int i = 0; i < values.size(); ++i)
{
if (i > 0)
result.append("&");
result.append(field.name()).append("=");
result.append(urlEncode(values[i]));
result.append(field.getName()).append("=");
result.append(urlEncode(values.get(i)));
}
if (iterator.hasNext())
result.append("&");
@ -589,7 +592,7 @@ public class HttpRequest implements Request
path += "?" + query;
URI result = URI.create(path);
if (!result.isAbsolute() && !result.isOpaque())
result = URI.create(client.address(getScheme(), getHost(), getPort()) + path);
result = URI.create(new Origin(getScheme(), getHost(), getPort()).asString() + path);
return result;
}

View File

@ -29,9 +29,9 @@ public abstract class MultiplexHttpDestination<C extends Connection> extends Htt
private final AtomicReference<ConnectState> connect = new AtomicReference<>(ConnectState.DISCONNECTED);
private C connection;
protected MultiplexHttpDestination(HttpClient client, String scheme, String host, int port)
protected MultiplexHttpDestination(HttpClient client, Origin origin)
{
super(client, scheme, host, port);
super(client, origin);
}
@Override
@ -94,7 +94,7 @@ public abstract class MultiplexHttpDestination<C extends Connection> extends Htt
{
HttpClient client = getHttpClient();
final HttpExchange exchange = getHttpExchanges().poll();
LOG.debug("Processing exchange {} on connection {}", exchange, connection);
LOG.debug("Processing {} on {}", exchange, connection);
if (exchange == null)
return false;
@ -126,6 +126,18 @@ public abstract class MultiplexHttpDestination<C extends Connection> extends Htt
return true;
}
@Override
public void close(Connection connection)
{
super.close(connection);
while (true)
{
ConnectState current = connect.get();
if (connect.compareAndSet(current, ConnectState.DISCONNECTED))
break;
}
}
protected abstract void send(C connection, HttpExchange exchange);
private enum ConnectState

View File

@ -0,0 +1,124 @@
//
// ========================================================================
// Copyright (c) 1995-2013 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.client;
import java.util.Objects;
import org.eclipse.jetty.util.URIUtil;
public class Origin
{
private final String scheme;
private final Address address;
public Origin(String scheme, String host, int port)
{
this(scheme, new Address(host, port));
}
public Origin(String scheme, Address address)
{
this.scheme = Objects.requireNonNull(scheme);
this.address = address;
}
public String getScheme()
{
return scheme;
}
public Address getAddress()
{
return address;
}
public String asString()
{
StringBuilder result = new StringBuilder();
URIUtil.appendSchemeHostPort(result, scheme, address.host, address.port);
return result.toString();
}
@Override
public boolean equals(Object obj)
{
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Origin that = (Origin)obj;
return scheme.equals(that.scheme) && address.equals(that.address);
}
@Override
public int hashCode()
{
int result = scheme.hashCode();
result = 31 * result + address.hashCode();
return result;
}
public static class Address
{
private final String host;
private final int port;
public Address(String host, int port)
{
this.host = Objects.requireNonNull(host);
this.port = port;
}
public String getHost()
{
return host;
}
public int getPort()
{
return port;
}
@Override
public boolean equals(Object obj)
{
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Address that = (Address)obj;
return host.equals(that.host) && port == that.port;
}
@Override
public int hashCode()
{
int result = host.hashCode();
result = 31 * result + port;
return result;
}
public String asString()
{
return String.format("%s:%d", host, port);
}
@Override
public String toString()
{
return asString();
}
}
}

View File

@ -59,6 +59,7 @@ public class ProxyAuthenticationProtocolHandler extends AuthenticationProtocolHa
protected URI getAuthenticationURI(Request request)
{
HttpDestination destination = getHttpClient().destinationFor(request.getScheme(), request.getHost(), request.getPort());
return destination.isProxied() ? destination.getProxyURI() : request.getURI();
ProxyConfiguration.Proxy proxy = destination.getProxy();
return proxy != null ? proxy.getURI() : request.getURI();
}
}

View File

@ -0,0 +1,170 @@
//
// ========================================================================
// Copyright (c) 1995-2013 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.client;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.jetty.io.ClientConnectionFactory;
/**
* The configuration of the forward proxy to use with {@link org.eclipse.jetty.client.HttpClient}.
* <p />
* Applications add subclasses of {@link Proxy} to this configuration via:
* <pre>
* ProxyConfiguration proxyConfig = httpClient.getProxyConfiguration();
* proxyConfig.getProxies().add(new HttpProxy(proxyHost, 8080));
* </pre>
*
* @see HttpClient#getProxyConfiguration()
*/
public class ProxyConfiguration
{
private final List<Proxy> proxies = new ArrayList<>();
public List<Proxy> getProxies()
{
return proxies;
}
public Proxy match(Origin origin)
{
for (Proxy proxy : getProxies())
{
if (proxy.matches(origin))
return proxy;
}
return null;
}
public static abstract class Proxy
{
private final Set<String> included = new HashSet<>();
private final Set<String> excluded = new HashSet<>();
private final Origin.Address address;
private final boolean secure;
protected Proxy(Origin.Address address, boolean secure)
{
this.address = address;
this.secure = secure;
}
/**
* @return the address of this proxy
*/
public Origin.Address getAddress()
{
return address;
}
/**
* @return whether the connection to the proxy must be secured via TLS
*/
public boolean isSecure()
{
return secure;
}
/**
* @return the list of origins that must be proxied
* @see #matches(Origin)
* @see #getExcludedAddresses()
*/
public Set<String> getIncludedAddresses()
{
return included;
}
/**
* @return the list of origins that must not be proxied.
* @see #matches(Origin)
* @see #getIncludedAddresses()
*/
public Set<String> getExcludedAddresses()
{
return excluded;
}
/**
* @return an URI representing this proxy, or null if no URI can represent this proxy
*/
public URI getURI()
{
return null;
}
/**
* Matches the given {@code origin} with the included and excluded addresses,
* returning true if the given {@code origin} is to be proxied.
*
* @param origin the origin to test for proxying
* @return true if the origin must be proxied, false otherwise
*/
public boolean matches(Origin origin)
{
boolean result = included.isEmpty();
Origin.Address address = origin.getAddress();
for (String included : this.included)
{
if (matches(address, included))
{
result = true;
break;
}
}
for (String excluded : this.excluded)
{
if (matches(address, excluded))
{
result = false;
break;
}
}
return result;
}
private boolean matches(Origin.Address address, String pattern)
{
// TODO: add support for CIDR notation like 192.168.0.0/24, see DoSFilter
int colon = pattern.indexOf(':');
if (colon < 0)
return pattern.equals(address.getHost());
String host = pattern.substring(0, colon);
String port = pattern.substring(colon + 1);
return host.equals(address.getHost()) && port.equals(String.valueOf(address.getPort()));
}
/**
* @param connectionFactory the nested {@link ClientConnectionFactory}
* @return a new {@link ClientConnectionFactory} for this {@link Proxy}
*/
public abstract ClientConnectionFactory newClientConnectionFactory(ClientConnectionFactory connectionFactory);
@Override
public String toString()
{
return address.toString();
}
}
}

View File

@ -0,0 +1,190 @@
//
// ========================================================================
// Copyright (c) 1995-2013 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.client;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.io.AbstractConnection;
import org.eclipse.jetty.io.ClientConnectionFactory;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.ssl.SslClientConnectionFactory;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
public class Socks4Proxy extends ProxyConfiguration.Proxy
{
public Socks4Proxy(String host, int port)
{
this(new Origin.Address(host, port), false);
}
public Socks4Proxy(Origin.Address address, boolean secure)
{
super(address, secure);
}
@Override
public ClientConnectionFactory newClientConnectionFactory(ClientConnectionFactory connectionFactory)
{
return new Socks4ProxyClientConnectionFactory(connectionFactory);
}
public static class Socks4ProxyClientConnectionFactory implements ClientConnectionFactory
{
private final ClientConnectionFactory connectionFactory;
public Socks4ProxyClientConnectionFactory(ClientConnectionFactory connectionFactory)
{
this.connectionFactory = connectionFactory;
}
@Override
public org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, Map<String, Object> context) throws IOException
{
HttpDestination destination = (HttpDestination)context.get(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY);
Executor executor = destination.getHttpClient().getExecutor();
return new Socks4ProxyConnection(endPoint, executor, connectionFactory, context);
}
}
private static class Socks4ProxyConnection extends AbstractConnection implements Callback
{
private static final Pattern IPv4_PATTERN = Pattern.compile("(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})");
private static final Logger LOG = Log.getLogger(Socks4ProxyConnection.class);
private final ClientConnectionFactory connectionFactory;
private final Map<String, Object> context;
public Socks4ProxyConnection(EndPoint endPoint, Executor executor, ClientConnectionFactory connectionFactory, Map<String, Object> context)
{
super(endPoint, executor);
this.connectionFactory = connectionFactory;
this.context = context;
}
@Override
public void onOpen()
{
super.onOpen();
writeSocks4Connect();
}
/**
* Writes the SOCKS "connect" bytes, differentiating between SOCKS 4 and 4A;
* the former sends an IPv4 address, the latter the full domain name.
*/
private void writeSocks4Connect()
{
HttpDestination destination = (HttpDestination)context.get(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY);
String host = destination.getHost();
short port = (short)destination.getPort();
Matcher matcher = IPv4_PATTERN.matcher(host);
if (matcher.matches())
{
// SOCKS 4
ByteBuffer buffer = ByteBuffer.allocate(9);
buffer.put((byte)4).put((byte)1).putShort(port);
for (int i = 1; i <= 4; ++i)
buffer.put((byte)Integer.parseInt(matcher.group(i)));
buffer.put((byte)0);
buffer.flip();
getEndPoint().write(this, buffer);
}
else
{
// SOCKS 4A
byte[] hostBytes = host.getBytes(StandardCharsets.UTF_8);
ByteBuffer buffer = ByteBuffer.allocate(9 + hostBytes.length + 1);
buffer.put((byte)4).put((byte)1).putShort(port);
buffer.put((byte)0).put((byte)0).put((byte)0).put((byte)1).put((byte)0);
buffer.put(hostBytes).put((byte)0);
buffer.flip();
getEndPoint().write(this, buffer);
}
}
@Override
public void succeeded()
{
LOG.debug("Written SOCKS4 connect request");
fillInterested();
}
@Override
public void failed(Throwable x)
{
close();
@SuppressWarnings("unchecked")
Promise<Connection> promise = (Promise<Connection>)context.get(HttpClientTransport.HTTP_CONNECTION_PROMISE_CONTEXT_KEY);
promise.failed(x);
}
@Override
public void onFillable()
{
try
{
ByteBuffer buffer = BufferUtil.allocate(8);
int filled = getEndPoint().fill(buffer);
LOG.debug("Read SOCKS4 connect response, {} bytes", filled);
if (filled != 8)
throw new IOException("Invalid response from SOCKS4 proxy");
int result = buffer.get(1);
if (result == 0x5A)
tunnel();
else
throw new IOException("SOCKS4 tunnel failed with code " + result);
}
catch (Throwable x)
{
failed(x);
}
}
private void tunnel()
{
try
{
HttpDestination destination = (HttpDestination)context.get(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY);
HttpClient client = destination.getHttpClient();
ClientConnectionFactory connectionFactory = this.connectionFactory;
if (HttpScheme.HTTPS.is(destination.getScheme()))
connectionFactory = new SslClientConnectionFactory(client.getSslContextFactory(), client.getByteBufferPool(), client.getExecutor(), connectionFactory);
org.eclipse.jetty.io.Connection connection = connectionFactory.newConnection(getEndPoint(), context);
ClientConnectionFactory.Helper.replaceConnection(this, connection);
LOG.debug("SOCKS4 tunnel established: {} over {}", this, connection);
}
catch (Throwable x)
{
failed(x);
}
}
}
}

View File

@ -63,43 +63,4 @@ public interface Destination
* @param promise the promise of a new, unpooled, {@link Connection}
*/
void newConnection(Promise<Connection> promise);
public static class Address
{
private final String host;
private final int port;
public Address(String host, int port)
{
this.host = host;
this.port = port;
}
public String getHost()
{
return host;
}
public int getPort()
{
return port;
}
@Override
public boolean equals(Object obj)
{
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Address that = (Address)obj;
return host.equals(that.host) && port == that.port;
}
@Override
public int hashCode()
{
int result = host.hashCode();
result = 31 * result + port;
return result;
}
}
}

View File

@ -1,81 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2013 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.client.api;
import java.util.HashSet;
import java.util.Set;
/**
* The configuration of the forward proxy to use with {@link org.eclipse.jetty.client.HttpClient}.
* <p />
* Configuration parameters include the host and port of the forward proxy, and a list of
* {@link #getExcludedOrigins() origins} that are excluded from being proxied.
*
* @see org.eclipse.jetty.client.HttpClient#setProxyConfiguration(ProxyConfiguration)
*/
public class ProxyConfiguration
{
private final Set<String> excluded = new HashSet<>();
private final String host;
private final int port;
public ProxyConfiguration(String host, int port)
{
this.host = host;
this.port = port;
}
/**
* @return the host name of the forward proxy
*/
public String getHost()
{
return host;
}
/**
* @return the port of the forward proxy
*/
public int getPort()
{
return port;
}
/**
* Matches the given {@code host} and {@code port} with the list of excluded origins,
* returning true if the origin is to be proxied, false if it is excluded from proxying.
* @param host the host to match
* @param port the port to match
* @return true if the origin made of {@code host} and {@code port} is to be proxied,
* false if it is excluded from proxying.
*/
public boolean matches(String host, int port)
{
String hostPort = host + ":" + port;
return !getExcludedOrigins().contains(hostPort);
}
/**
* @return the list of origins to exclude from proxying, in the form "host:port".
*/
public Set<String> getExcludedOrigins()
{
return excluded;
}
}

View File

@ -18,16 +18,21 @@
package org.eclipse.jetty.client.http;
import java.io.IOException;
import java.util.Map;
import org.eclipse.jetty.client.AbstractHttpClientTransport;
import org.eclipse.jetty.client.HttpDestination;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.client.Origin;
import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.Promise;
public class HttpClientTransportOverHTTP extends AbstractHttpClientTransport
{
public HttpClientTransportOverHTTP()
{
this(Runtime.getRuntime().availableProcessors());
this(Math.max(1, Runtime.getRuntime().availableProcessors() / 2));
}
public HttpClientTransportOverHTTP(int selectors)
@ -36,21 +41,20 @@ public class HttpClientTransportOverHTTP extends AbstractHttpClientTransport
}
@Override
public HttpDestination newHttpDestination(String scheme, String host, int port)
public HttpDestination newHttpDestination(Origin origin)
{
return new HttpDestinationOverHTTP(getHttpClient(), scheme, host, port);
return new HttpDestinationOverHTTP(getHttpClient(), origin);
}
@Override
protected Connection newConnection(EndPoint endPoint, HttpDestination destination)
public org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, Map<String, Object> context) throws IOException
{
return new HttpConnectionOverHTTP(endPoint, destination);
}
@Override
public org.eclipse.jetty.client.api.Connection tunnel(org.eclipse.jetty.client.api.Connection connection)
{
HttpConnectionOverHTTP httpConnection = (HttpConnectionOverHTTP)connection;
return tunnel(httpConnection.getEndPoint(), httpConnection.getHttpDestination(), connection);
HttpDestination destination = (HttpDestination)context.get(HTTP_DESTINATION_CONTEXT_KEY);
HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination);
@SuppressWarnings("unchecked")
Promise<Connection> promise = (Promise<Connection>)context.get(HTTP_CONNECTION_PROMISE_CONTEXT_KEY);
promise.succeeded(connection);
return connection;
}
}

View File

@ -96,7 +96,7 @@ public class HttpConnectionOverHTTP extends AbstractConnection implements Connec
if (exchange != null)
return exchange.getRequest().abort(new TimeoutException());
getHttpDestination().remove(this);
getHttpDestination().close(this);
return true;
}
@ -126,7 +126,7 @@ public class HttpConnectionOverHTTP extends AbstractConnection implements Connec
@Override
public void close()
{
getHttpDestination().remove(this);
getHttpDestination().close(this);
getEndPoint().shutdownOutput();
LOG.debug("{} oshut", this);
getEndPoint().close();
@ -136,9 +136,9 @@ public class HttpConnectionOverHTTP extends AbstractConnection implements Connec
@Override
public String toString()
{
return String.format("%s@%x(l:%s <-> r:%s)",
HttpConnection.class.getSimpleName(),
hashCode(),
return String.format("%s@%h(l:%s <-> r:%s)",
getClass().getSimpleName(),
this,
getEndPoint().getLocalAddress(),
getEndPoint().getRemoteAddress());
}
@ -171,5 +171,11 @@ public class HttpConnectionOverHTTP extends AbstractConnection implements Connec
{
HttpConnectionOverHTTP.this.close();
}
@Override
public String toString()
{
return HttpConnectionOverHTTP.this.toString();
}
}
}

View File

@ -21,30 +21,32 @@ package org.eclipse.jetty.client.http;
import java.io.IOException;
import java.util.Arrays;
import org.eclipse.jetty.client.ConnectionPool;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.HttpDestination;
import org.eclipse.jetty.client.HttpExchange;
import org.eclipse.jetty.client.Origin;
import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
public class HttpDestinationOverHTTP extends HttpDestination implements Promise<Connection>
public class HttpDestinationOverHTTP extends HttpDestination implements Promise<Connection>
{
private final HttpConnectionPool connectionPool;
private final ConnectionPool connectionPool;
public HttpDestinationOverHTTP(HttpClient client, String scheme, String host, int port)
public HttpDestinationOverHTTP(HttpClient client, Origin origin)
{
super(client, scheme, host, port);
this.connectionPool = newHttpConnectionPool(client);
super(client, origin);
this.connectionPool = newConnectionPool(client);
}
protected HttpConnectionPool newHttpConnectionPool(HttpClient client)
protected ConnectionPool newConnectionPool(HttpClient client)
{
return new HttpConnectionPool(this, client.getMaxConnectionsPerDestination(), this);
return new ConnectionPool(this, client.getMaxConnectionsPerDestination(), this);
}
public HttpConnectionPool getHttpConnectionPool()
public ConnectionPool getConnectionPool()
{
return connectionPool;
}
@ -153,23 +155,25 @@ public class HttpDestinationOverHTTP extends HttpDestination implements Promise
else
{
LOG.debug("{} is stopped", client);
remove(connection);
close(connection);
connection.close();
}
}
protected void remove(HttpConnectionOverHTTP connection)
@Override
public void close(Connection oldConnection)
{
connectionPool.remove(connection);
super.close(oldConnection);
connectionPool.remove(oldConnection);
// We need to execute queued requests even if this connection failed.
// We may create a connection that is not needed, but it will eventually
// idle timeout, so no worries
if (!getHttpExchanges().isEmpty())
{
connection = acquire();
if (connection != null)
process(connection, false);
HttpConnectionOverHTTP newConnection = acquire();
if (newConnection != null)
process(newConnection, false);
}
}

View File

@ -65,21 +65,25 @@ public abstract class BufferingResponseListener extends Listener.Adapter
HttpFields headers = response.getHeaders();
long length = headers.getLongField(HttpHeader.CONTENT_LENGTH.asString());
if (length > maxLength)
response.abort(new IllegalArgumentException("Buffering capacity exceeded"));
String contentType = headers.get(HttpHeader.CONTENT_TYPE);
if (contentType != null)
{
String charset = "charset=";
int index = contentType.toLowerCase(Locale.ENGLISH).indexOf(charset);
if (index > 0)
response.abort(new IllegalArgumentException("Buffering capacity exceeded"));
}
else
{
String contentType = headers.get(HttpHeader.CONTENT_TYPE);
if (contentType != null)
{
String encoding = contentType.substring(index + charset.length());
// Sometimes charsets arrive with an ending semicolon
index = encoding.indexOf(';');
String charset = "charset=";
int index = contentType.toLowerCase(Locale.ENGLISH).indexOf(charset);
if (index > 0)
encoding = encoding.substring(0, index);
this.encoding = encoding;
{
String encoding = contentType.substring(index + charset.length());
// Sometimes charsets arrive with an ending semicolon
index = encoding.indexOf(';');
if (index > 0)
encoding = encoding.substring(0, index);
this.encoding = encoding;
}
}
}
}
@ -89,12 +93,16 @@ public abstract class BufferingResponseListener extends Listener.Adapter
{
long newLength = buffer.length + content.remaining();
if (newLength > maxLength)
{
response.abort(new IllegalArgumentException("Buffering capacity exceeded"));
byte[] newBuffer = new byte[(int)newLength];
System.arraycopy(buffer, 0, newBuffer, 0, buffer.length);
content.get(newBuffer, buffer.length, content.remaining());
buffer = newBuffer;
}
else
{
byte[] newBuffer = new byte[(int)newLength];
System.arraycopy(buffer, 0, newBuffer, 0, buffer.length);
content.get(newBuffer, buffer.length, content.remaining());
buffer = newBuffer;
}
}
@Override

View File

@ -20,6 +20,7 @@ package org.eclipse.jetty.client.util;
import java.net.URI;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
@ -217,7 +218,7 @@ public class DigestAuthentication implements Authentication
if (digester == null)
return;
Charset charset = Charset.forName("ISO-8859-1");
Charset charset = StandardCharsets.ISO_8859_1;
String A1 = user + ":" + realm + ":" + password;
String hashA1 = toHexString(digester.digest(A1.getBytes(charset)));

View File

@ -112,8 +112,7 @@ public class InputStreamContentProvider implements ContentProvider
*/
private class InputStreamIterator implements Iterator<ByteBuffer>
{
private final byte[] bytes = new byte[bufferSize];
private Exception failure;
private Throwable failure;
private ByteBuffer buffer;
private Boolean hasNext;
@ -125,6 +124,7 @@ public class InputStreamContentProvider implements ContentProvider
if (hasNext != null)
return hasNext;
byte[] bytes = new byte[bufferSize];
int read = stream.read(bytes);
LOG.debug("Read {} bytes from {}", read, stream);
if (read > 0)
@ -145,7 +145,7 @@ public class InputStreamContentProvider implements ContentProvider
return true;
}
}
catch (Exception x)
catch (Throwable x)
{
LOG.debug(x);
if (failure == null)

View File

@ -37,6 +37,11 @@ public class StringContentProvider extends BytesContentProvider
public StringContentProvider(String content, String encoding)
{
super(content.getBytes(Charset.forName(encoding)));
this(content, Charset.forName(encoding));
}
public StringContentProvider(String content, Charset charset)
{
super(content.getBytes(charset));
}
}

View File

@ -116,8 +116,8 @@ public class HostnameVerificationTest
// Therefore this test may catch a SSLHandshakeException, or a ClosedChannelException.
// If it is the former, we verify that its cause is a CertificateException.
// ExecutionException wraps an EofException that wraps the SSLHandshakeException
Throwable cause = x.getCause().getCause();
// ExecutionException wraps an SSLHandshakeException
Throwable cause = x.getCause();
if (cause instanceof SSLHandshakeException)
assertThat(cause.getCause().getCause(), instanceOf(CertificateException.class));
else

View File

@ -0,0 +1,245 @@
//
// ========================================================================
// Copyright (c) 1995-2013 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.client;
import java.io.IOException;
import java.net.URI;
import java.nio.ByteBuffer;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.io.AbstractConnection;
import org.eclipse.jetty.io.ClientConnectionFactory;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.server.AbstractConnectionFactory;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.junit.After;
import org.junit.Assert;
import org.junit.Test;
public class HttpClientCustomProxyTest
{
public static final byte[] CAFE_BABE = new byte[]{(byte)0xCA, (byte)0xFE, (byte)0xBA, (byte)0xBE};
private Server server;
private ServerConnector connector;
private HttpClient client;
public void prepare(Handler handler) throws Exception
{
server = new Server();
connector = new ServerConnector(server, new CAFEBABEServerConnectionFactory(new HttpConnectionFactory()));
server.addConnector(connector);
server.setHandler(handler);
server.start();
QueuedThreadPool executor = new QueuedThreadPool();
executor.setName(executor.getName() + "-client");
client = new HttpClient();
client.setExecutor(executor);
client.start();
}
@After
public void dispose() throws Exception
{
if (client != null)
client.stop();
if (server != null)
server.stop();
}
@Test
public void testCustomProxy() throws Exception
{
final String serverHost = "server";
final int status = HttpStatus.NO_CONTENT_204;
prepare(new AbstractHandler()
{
@Override
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
baseRequest.setHandled(true);
if (!URI.create(baseRequest.getUri().toString()).isAbsolute())
response.setStatus(HttpServletResponse.SC_USE_PROXY);
else if (serverHost.equals(request.getServerName()))
response.setStatus(status);
else
response.setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE);
}
});
// Setup the custom proxy
int proxyPort = connector.getLocalPort();
int serverPort = proxyPort + 1; // Any port will do for these tests - just not the same as the proxy
client.getProxyConfiguration().getProxies().add(new CAFEBABEProxy(new Origin.Address("localhost", proxyPort), false));
ContentResponse response = client.newRequest(serverHost, serverPort)
.timeout(5, TimeUnit.SECONDS)
.send();
Assert.assertEquals(status, response.getStatus());
}
private class CAFEBABEProxy extends ProxyConfiguration.Proxy
{
private CAFEBABEProxy(Origin.Address address, boolean secure)
{
super(address, secure);
}
@Override
public ClientConnectionFactory newClientConnectionFactory(ClientConnectionFactory connectionFactory)
{
return new CAFEBABEClientConnectionFactory(connectionFactory);
}
}
private class CAFEBABEClientConnectionFactory implements ClientConnectionFactory
{
private final ClientConnectionFactory connectionFactory;
private CAFEBABEClientConnectionFactory(ClientConnectionFactory connectionFactory)
{
this.connectionFactory = connectionFactory;
}
@Override
public org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, Map<String, Object> context) throws IOException
{
HttpDestination destination = (HttpDestination)context.get(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY);
Executor executor = destination.getHttpClient().getExecutor();
return new CAFEBABEConnection(endPoint, executor, connectionFactory, context);
}
}
private class CAFEBABEConnection extends AbstractConnection
{
private final ClientConnectionFactory connectionFactory;
private final Map<String, Object> context;
public CAFEBABEConnection(EndPoint endPoint, Executor executor, ClientConnectionFactory connectionFactory, Map<String, Object> context)
{
super(endPoint, executor);
this.connectionFactory = connectionFactory;
this.context = context;
}
@Override
public void onOpen()
{
super.onOpen();
fillInterested();
getEndPoint().write(new Callback.Adapter(), ByteBuffer.wrap(CAFE_BABE));
}
@Override
public void onFillable()
{
try
{
ByteBuffer buffer = BufferUtil.allocate(4);
int filled = getEndPoint().fill(buffer);
Assert.assertEquals(4, filled);
Assert.assertArrayEquals(CAFE_BABE, buffer.array());
// We are good, upgrade the connection
ClientConnectionFactory.Helper.replaceConnection(this, connectionFactory.newConnection(getEndPoint(), context));
}
catch (Throwable x)
{
close();
@SuppressWarnings("unchecked")
Promise<Connection> promise = (Promise<Connection>)context.get(HttpClientTransport.HTTP_CONNECTION_PROMISE_CONTEXT_KEY);
promise.failed(x);
}
}
}
private class CAFEBABEServerConnectionFactory extends AbstractConnectionFactory
{
private final org.eclipse.jetty.server.ConnectionFactory connectionFactory;
private CAFEBABEServerConnectionFactory(org.eclipse.jetty.server.ConnectionFactory connectionFactory)
{
super("cafebabe");
this.connectionFactory = connectionFactory;
}
@Override
public org.eclipse.jetty.io.Connection newConnection(Connector connector, EndPoint endPoint)
{
return new CAFEBABEServerConnection(connector, endPoint, connectionFactory);
}
}
private class CAFEBABEServerConnection extends AbstractConnection
{
private final org.eclipse.jetty.server.ConnectionFactory connectionFactory;
public CAFEBABEServerConnection(Connector connector, EndPoint endPoint, org.eclipse.jetty.server.ConnectionFactory connectionFactory)
{
super(endPoint, connector.getExecutor());
this.connectionFactory = connectionFactory;
}
@Override
public void onOpen()
{
super.onOpen();
fillInterested();
}
@Override
public void onFillable()
{
try
{
ByteBuffer buffer = BufferUtil.allocate(4);
int filled = getEndPoint().fill(buffer);
Assert.assertEquals(4, filled);
Assert.assertArrayEquals(CAFE_BABE, buffer.array());
getEndPoint().write(new Callback.Adapter(), buffer);
// We are good, upgrade the connection
ClientConnectionFactory.Helper.replaceConnection(this, connectionFactory.newConnection(connector, getEndPoint()));
}
catch (Throwable x)
{
close();
}
}
}
}

View File

@ -25,7 +25,6 @@ import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Destination;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.http.HttpConnectionOverHTTP;
import org.eclipse.jetty.client.http.HttpConnectionPool;
import org.eclipse.jetty.client.http.HttpDestinationOverHTTP;
import org.eclipse.jetty.client.util.FutureResponseListener;
import org.eclipse.jetty.toolchain.test.annotation.Slow;
@ -60,7 +59,7 @@ public class HttpClientExplicitConnectionTest extends AbstractHttpClientServerTe
Assert.assertEquals(200, response.getStatus());
HttpDestinationOverHTTP httpDestination = (HttpDestinationOverHTTP)destination;
HttpConnectionPool connectionPool = httpDestination.getHttpConnectionPool();
ConnectionPool connectionPool = httpDestination.getConnectionPool();
Assert.assertTrue(connectionPool.getActiveConnections().isEmpty());
Assert.assertTrue(connectionPool.getIdleConnections().isEmpty());
}
@ -92,7 +91,7 @@ public class HttpClientExplicitConnectionTest extends AbstractHttpClientServerTe
Assert.assertFalse(httpConnection.getEndPoint().isOpen());
HttpDestinationOverHTTP httpDestination = (HttpDestinationOverHTTP)destination;
HttpConnectionPool connectionPool = httpDestination.getHttpConnectionPool();
ConnectionPool connectionPool = httpDestination.getConnectionPool();
Assert.assertTrue(connectionPool.getActiveConnections().isEmpty());
Assert.assertTrue(connectionPool.getIdleConnections().isEmpty());
}

View File

@ -37,7 +37,6 @@ import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.client.http.HttpConnectionOverHTTP;
import org.eclipse.jetty.client.http.HttpConnectionPool;
import org.eclipse.jetty.client.http.HttpDestinationOverHTTP;
import org.eclipse.jetty.client.util.BytesContentProvider;
import org.eclipse.jetty.http.HttpHeader;
@ -85,7 +84,7 @@ public class HttpClientLoadTest extends AbstractHttpClientServerTest
}
// Re-run after warmup
iterations = 50_000;
iterations = 5_000;
for (int i = 0; i < runs; ++i)
{
run(random, iterations);
@ -111,7 +110,7 @@ public class HttpClientLoadTest extends AbstractHttpClientServerTest
for (String host : Arrays.asList("localhost", "127.0.0.1"))
{
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, connector.getLocalPort());
HttpConnectionPool connectionPool = destination.getHttpConnectionPool();
ConnectionPool connectionPool = destination.getConnectionPool();
for (Connection connection : new ArrayList<>(connectionPool.getActiveConnections()))
{
HttpConnectionOverHTTP active = (HttpConnectionOverHTTP)connection;

View File

@ -27,7 +27,6 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.ProxyConfiguration;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.util.BasicAuthentication;
import org.eclipse.jetty.http.HttpHeader;
@ -68,7 +67,7 @@ public class HttpClientProxyTest extends AbstractHttpClientServerTest
int proxyPort = connector.getLocalPort();
int serverPort = proxyPort + 1; // Any port will do for these tests - just not the same as the proxy
client.setProxyConfiguration(new ProxyConfiguration("localhost", proxyPort));
client.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", proxyPort));
ContentResponse response = client.newRequest(serverHost, serverPort)
.scheme(scheme)
@ -115,7 +114,7 @@ public class HttpClientProxyTest extends AbstractHttpClientServerTest
String proxyHost = "localhost";
int proxyPort = connector.getLocalPort();
int serverPort = proxyPort + 1; // Any port will do for these tests - just not the same as the proxy
client.setProxyConfiguration(new ProxyConfiguration(proxyHost, proxyPort));
client.getProxyConfiguration().getProxies().add(new HttpProxy(proxyHost, proxyPort));
ContentResponse response1 = client.newRequest(serverHost, serverPort)
.scheme(scheme)

View File

@ -28,7 +28,9 @@ import java.nio.channels.UnresolvedAddressException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
@ -51,10 +53,10 @@ import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.client.http.HttpConnectionOverHTTP;
import org.eclipse.jetty.client.http.HttpConnectionPool;
import org.eclipse.jetty.client.http.HttpDestinationOverHTTP;
import org.eclipse.jetty.client.util.BytesContentProvider;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
@ -85,7 +87,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
Assert.assertEquals(200, response.getStatus());
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
HttpConnectionPool connectionPool = destination.getHttpConnectionPool();
ConnectionPool connectionPool = destination.getConnectionPool();
long start = System.nanoTime();
HttpConnectionOverHTTP connection = null;
@ -637,7 +639,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
public void onBegin(Request request)
{
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
destination.getHttpConnectionPool().getActiveConnections().peek().close();
destination.getConnectionPool().getActiveConnections().peek().close();
}
})
.send(new Response.Listener.Adapter()
@ -828,4 +830,37 @@ public class HttpClientTest extends AbstractHttpClientServerTest
});
Assert.assertTrue(latch.await(10, TimeUnit.SECONDS));
}
@Test
public void testCustomUserAgent() throws Exception
{
final String userAgent = "Test/1.0";
start(new AbstractHandler()
{
@Override
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
baseRequest.setHandled(true);
ArrayList<String> userAgents = Collections.list(request.getHeaders("User-Agent"));
Assert.assertEquals(1, userAgents.size());
Assert.assertEquals(userAgent, userAgents.get(0));
}
});
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
.scheme(scheme)
.agent(userAgent)
.timeout(5, TimeUnit.SECONDS)
.send();
Assert.assertEquals(200, response.getStatus());
response = client.newRequest("localhost", connector.getLocalPort())
.scheme(scheme)
.header(HttpHeader.USER_AGENT, userAgent)
.timeout(5, TimeUnit.SECONDS)
.send();
Assert.assertEquals(200, response.getStatus());
}
}

View File

@ -21,6 +21,7 @@ package org.eclipse.jetty.client;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
@ -35,9 +36,13 @@ import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
import org.eclipse.jetty.client.http.HttpDestinationOverHTTP;
import org.eclipse.jetty.client.util.BufferingResponseListener;
import org.eclipse.jetty.client.util.InputStreamContentProvider;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.ClientConnectionFactory;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.ssl.SslClientConnectionFactory;
import org.eclipse.jetty.io.ssl.SslConnection;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.toolchain.test.annotation.Slow;
@ -244,15 +249,30 @@ public class HttpClientTimeoutTest extends AbstractHttpClientServerTest
client = new HttpClient(new HttpClientTransportOverHTTP()
{
@Override
protected SslConnection newSslConnection(HttpClient httpClient, EndPoint endPoint, SSLEngine engine)
public HttpDestination newHttpDestination(Origin origin)
{
return new SslConnection(httpClient.getByteBufferPool(), httpClient.getExecutor(), endPoint, engine)
return new HttpDestinationOverHTTP(getHttpClient(), origin)
{
@Override
protected boolean onReadTimeout()
protected ClientConnectionFactory newSslClientConnectionFactory(ClientConnectionFactory connectionFactory)
{
sslIdle.set(true);
return super.onReadTimeout();
HttpClient client = getHttpClient();
return new SslClientConnectionFactory(client.getSslContextFactory(), client.getByteBufferPool(), client.getExecutor(), connectionFactory)
{
@Override
protected SslConnection newSslConnection(ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, SSLEngine engine)
{
return new SslConnection(byteBufferPool, executor, endPoint, engine)
{
@Override
protected boolean onReadTimeout()
{
sslIdle.set(true);
return super.onReadTimeout();
}
};
}
};
}
};
}

View File

@ -82,7 +82,7 @@ public class HttpClientURITest extends AbstractHttpClientServerTest
Assert.assertEquals(path, request.getPath());
Assert.assertNull(request.getQuery());
Fields params = request.getParams();
Assert.assertEquals(0, params.size());
Assert.assertEquals(0, params.getSize());
Assert.assertTrue(request.getURI().toString().endsWith(path));
ContentResponse response = request.send();
@ -118,8 +118,8 @@ public class HttpClientURITest extends AbstractHttpClientServerTest
Assert.assertEquals(query, request.getQuery());
Assert.assertTrue(request.getURI().toString().endsWith(pathQuery));
Fields params = request.getParams();
Assert.assertEquals(1, params.size());
Assert.assertEquals(value, params.get(name).value());
Assert.assertEquals(1, params.getSize());
Assert.assertEquals(value, params.get(name).getValue());
ContentResponse response = request.send();
@ -155,8 +155,8 @@ public class HttpClientURITest extends AbstractHttpClientServerTest
Assert.assertEquals(query, request.getQuery());
Assert.assertTrue(request.getURI().toString().endsWith(pathQuery));
Fields params = request.getParams();
Assert.assertEquals(1, params.size());
Assert.assertEquals(value, params.get(name).value());
Assert.assertEquals(1, params.getSize());
Assert.assertEquals(value, params.get(name).getValue());
ContentResponse response = request.send();
@ -194,9 +194,9 @@ public class HttpClientURITest extends AbstractHttpClientServerTest
Assert.assertEquals(query, request.getQuery());
Assert.assertTrue(request.getURI().toString().endsWith(pathQuery));
Fields params = request.getParams();
Assert.assertEquals(2, params.size());
Assert.assertEquals(value1, params.get(name1).value());
Assert.assertEquals(value2, params.get(name2).value());
Assert.assertEquals(2, params.getSize());
Assert.assertEquals(value1, params.get(name1).getValue());
Assert.assertEquals(value2, params.get(name2).getValue());
ContentResponse response = request.send();
@ -238,9 +238,9 @@ public class HttpClientURITest extends AbstractHttpClientServerTest
Assert.assertEquals(query, request.getQuery());
Assert.assertTrue(request.getURI().toString().endsWith(pathQuery));
Fields params = request.getParams();
Assert.assertEquals(2, params.size());
Assert.assertEquals(value1, params.get(name1).value());
Assert.assertEquals(value2, params.get(name2).value());
Assert.assertEquals(2, params.getSize());
Assert.assertEquals(value1, params.get(name1).getValue());
Assert.assertEquals(value2, params.get(name2).getValue());
ContentResponse response = request.send();
@ -273,7 +273,7 @@ public class HttpClientURITest extends AbstractHttpClientServerTest
Assert.assertEquals(query, request.getQuery());
Assert.assertTrue(request.getURI().toString().endsWith(pathQuery));
Fields params = request.getParams();
Assert.assertEquals(0, params.size());
Assert.assertEquals(0, params.getSize());
ContentResponse response = request.send();
@ -306,10 +306,36 @@ public class HttpClientURITest extends AbstractHttpClientServerTest
Assert.assertEquals(query, request.getQuery());
Assert.assertTrue(request.getURI().toString().endsWith(pathQuery));
Fields params = request.getParams();
Assert.assertEquals(0, params.size());
Assert.assertEquals(0, params.getSize());
ContentResponse response = request.send();
Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
}
@Test
public void testCaseSensitiveParameterName() throws Exception
{
final String name1 = "a";
final String name2 = "A";
start(new AbstractHandler()
{
@Override
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
baseRequest.setHandled(true);
Assert.assertEquals(name1, request.getParameter(name1));
Assert.assertEquals(name2, request.getParameter(name2));
}
});
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
.scheme(scheme)
.path("/path?" + name1 + "=" + name1)
.param(name2, name2)
.timeout(5, TimeUnit.SECONDS)
.send();
Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
}
}

View File

@ -33,7 +33,6 @@ import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.client.http.HttpConnectionPool;
import org.eclipse.jetty.client.http.HttpDestinationOverHTTP;
import org.eclipse.jetty.client.util.ByteBufferContentProvider;
import org.eclipse.jetty.http.HttpHeader;
@ -68,7 +67,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
String host = "localhost";
int port = connector.getLocalPort();
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
HttpConnectionPool connectionPool = destination.getHttpConnectionPool();
ConnectionPool connectionPool = destination.getConnectionPool();
final BlockingQueue<Connection> idleConnections = connectionPool.getIdleConnections();
Assert.assertEquals(0, idleConnections.size());
@ -129,7 +128,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
String host = "localhost";
int port = connector.getLocalPort();
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
HttpConnectionPool connectionPool = destination.getHttpConnectionPool();
ConnectionPool connectionPool = destination.getConnectionPool();
final BlockingQueue<Connection> idleConnections = connectionPool.getIdleConnections();
Assert.assertEquals(0, idleConnections.size());
@ -180,7 +179,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
String host = "localhost";
int port = connector.getLocalPort();
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
HttpConnectionPool connectionPool = destination.getHttpConnectionPool();
ConnectionPool connectionPool = destination.getConnectionPool();
final BlockingQueue<Connection> idleConnections = connectionPool.getIdleConnections();
Assert.assertEquals(0, idleConnections.size());
@ -240,7 +239,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
String host = "localhost";
int port = connector.getLocalPort();
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
HttpConnectionPool connectionPool = destination.getHttpConnectionPool();
ConnectionPool connectionPool = destination.getConnectionPool();
final BlockingQueue<Connection> idleConnections = connectionPool.getIdleConnections();
Assert.assertEquals(0, idleConnections.size());
@ -313,7 +312,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
String host = "localhost";
int port = connector.getLocalPort();
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
HttpConnectionPool connectionPool = destination.getHttpConnectionPool();
ConnectionPool connectionPool = destination.getConnectionPool();
final BlockingQueue<Connection> idleConnections = connectionPool.getIdleConnections();
Assert.assertEquals(0, idleConnections.size());
@ -366,7 +365,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
String host = "localhost";
int port = connector.getLocalPort();
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
HttpConnectionPool connectionPool = destination.getHttpConnectionPool();
ConnectionPool connectionPool = destination.getConnectionPool();
final BlockingQueue<Connection> idleConnections = connectionPool.getIdleConnections();
Assert.assertEquals(0, idleConnections.size());
@ -416,7 +415,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
String host = "localhost";
int port = connector.getLocalPort();
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
HttpConnectionPool connectionPool = destination.getHttpConnectionPool();
ConnectionPool connectionPool = destination.getConnectionPool();
final BlockingQueue<Connection> idleConnections = connectionPool.getIdleConnections();
Assert.assertEquals(0, idleConnections.size());
@ -466,7 +465,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
String host = "localhost";
int port = connector.getLocalPort();
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
HttpConnectionPool connectionPool = destination.getHttpConnectionPool();
ConnectionPool connectionPool = destination.getConnectionPool();
final BlockingQueue<Connection> idleConnections = connectionPool.getIdleConnections();
Assert.assertEquals(0, idleConnections.size());

View File

@ -0,0 +1,66 @@
//
// ========================================================================
// Copyright (c) 1995-2013 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.client;
import org.junit.Assert;
import org.junit.Test;
public class ProxyConfigurationTest
{
@Test
public void testProxyMatchesWithoutIncludesWithoutExcludes() throws Exception
{
HttpProxy proxy = new HttpProxy("host", 0);
Assert.assertTrue(proxy.matches(new Origin("http", "any", 0)));
}
@Test
public void testProxyMatchesWithOnlyExcludes() throws Exception
{
HttpProxy proxy = new HttpProxy("host", 0);
proxy.getExcludedAddresses().add("1.2.3.4:5");
Assert.assertTrue(proxy.matches(new Origin("http", "any", 0)));
Assert.assertTrue(proxy.matches(new Origin("http", "1.2.3.4", 0)));
Assert.assertFalse(proxy.matches(new Origin("http", "1.2.3.4", 5)));
}
@Test
public void testProxyMatchesWithOnlyIncludes() throws Exception
{
HttpProxy proxy = new HttpProxy("host", 0);
proxy.getIncludedAddresses().add("1.2.3.4:5");
Assert.assertFalse(proxy.matches(new Origin("http", "any", 0)));
Assert.assertFalse(proxy.matches(new Origin("http", "1.2.3.4", 0)));
Assert.assertTrue(proxy.matches(new Origin("http", "1.2.3.4", 5)));
}
@Test
public void testProxyMatchesWithIncludesAndExcludes() throws Exception
{
HttpProxy proxy = new HttpProxy("host", 0);
proxy.getIncludedAddresses().add("1.2.3.4");
proxy.getExcludedAddresses().add("1.2.3.4:5");
Assert.assertFalse(proxy.matches(new Origin("http", "any", 0)));
Assert.assertTrue(proxy.matches(new Origin("http", "1.2.3.4", 0)));
Assert.assertFalse(proxy.matches(new Origin("http", "1.2.3.4", 5)));
}
}

View File

@ -1,76 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2013 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.client.api;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import org.junit.Ignore;
import org.junit.Test;
import com.ning.http.client.AsyncHttpClient;
import com.ning.http.client.BodyDeferringAsyncHandler;
import com.ning.http.client.Cookie;
import com.ning.http.client.Realm;
import com.ning.http.client.Request;
import com.ning.http.client.Response;
@Ignore
public class NingUsage
{
@Test
public void testFileUpload() throws Exception
{
AsyncHttpClient client = new AsyncHttpClient();
Request request = client.prepareGet("http://localhost:8080/foo").setBody(new FileInputStream("")).build();
client.executeRequest(request);
}
@Test
public void testAuthentication() throws Exception
{
AsyncHttpClient client = new AsyncHttpClient();
Response response = client.prepareGet("http://localhost:8080/foo")
// Not sure what a builder buys me in this case...
.setRealm(new Realm.RealmBuilder().build()).execute().get();
}
@Test
public void testCookies() throws Exception
{
AsyncHttpClient client = new AsyncHttpClient();
// Cookie class too complex
client.prepareGet("").addCookie(new Cookie("domain", "name", "value", "path", 3600, false)).execute();
}
@Test
public void testResponseStream() throws Exception
{
AsyncHttpClient client = new AsyncHttpClient();
ByteArrayOutputStream output = new ByteArrayOutputStream();
BodyDeferringAsyncHandler handler = new BodyDeferringAsyncHandler(output);
client.prepareGet("").execute(handler);
// What I would really like is an InputStream, not an OutputStream
// so I can read the response content
Response response = handler.getResponse(); // No timeout
// Not sure how I can read the body ONLY if status == 200 ?
}
}

View File

@ -24,6 +24,7 @@ import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.client.AbstractHttpClientServerTest;
import org.eclipse.jetty.client.EmptyServerHandler;
import org.eclipse.jetty.client.Origin;
import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
@ -52,12 +53,12 @@ public class HttpDestinationOverHTTPTest extends AbstractHttpClientServerTest
@Test
public void test_FirstAcquire_WithEmptyQueue() throws Exception
{
HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, "http", "localhost", connector.getLocalPort());
HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", connector.getLocalPort()));
Connection connection = destination.acquire();
if (connection == null)
{
// There are no queued requests, so the newly created connection will be idle
connection = destination.getHttpConnectionPool().getIdleConnections().poll(5, TimeUnit.SECONDS);
connection = destination.getConnectionPool().getIdleConnections().poll(5, TimeUnit.SECONDS);
}
Assert.assertNotNull(connection);
}
@ -65,7 +66,7 @@ public class HttpDestinationOverHTTPTest extends AbstractHttpClientServerTest
@Test
public void test_SecondAcquire_AfterFirstAcquire_WithEmptyQueue_ReturnsSameConnection() throws Exception
{
HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, "http", "localhost", connector.getLocalPort());
HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", connector.getLocalPort()));
Connection connection1 = destination.acquire();
if (connection1 == null)
{
@ -74,7 +75,7 @@ public class HttpDestinationOverHTTPTest extends AbstractHttpClientServerTest
while (connection1 == null && TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - start) < 5)
{
TimeUnit.MILLISECONDS.sleep(50);
connection1 = destination.getHttpConnectionPool().getIdleConnections().peek();
connection1 = destination.getConnectionPool().getIdleConnections().peek();
}
Assert.assertNotNull(connection1);
@ -87,7 +88,7 @@ public class HttpDestinationOverHTTPTest extends AbstractHttpClientServerTest
public void test_SecondAcquire_ConcurrentWithFirstAcquire_WithEmptyQueue_CreatesTwoConnections() throws Exception
{
final CountDownLatch latch = new CountDownLatch(1);
HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, "http", "localhost", connector.getLocalPort())
HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", connector.getLocalPort()))
{
@Override
protected void process(HttpConnectionOverHTTP connection, boolean dispatch)
@ -115,23 +116,23 @@ public class HttpDestinationOverHTTPTest extends AbstractHttpClientServerTest
latch.countDown();
// There must be 2 idle connections
Connection connection = destination.getHttpConnectionPool().getIdleConnections().poll(5, TimeUnit.SECONDS);
Connection connection = destination.getConnectionPool().getIdleConnections().poll(5, TimeUnit.SECONDS);
Assert.assertNotNull(connection);
connection = destination.getHttpConnectionPool().getIdleConnections().poll(5, TimeUnit.SECONDS);
connection = destination.getConnectionPool().getIdleConnections().poll(5, TimeUnit.SECONDS);
Assert.assertNotNull(connection);
}
@Test
public void test_Acquire_Process_Release_Acquire_ReturnsSameConnection() throws Exception
{
HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, "http", "localhost", connector.getLocalPort());
HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", connector.getLocalPort()));
HttpConnectionOverHTTP connection1 = destination.acquire();
long start = System.nanoTime();
while (connection1 == null && TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - start) < 5)
{
TimeUnit.MILLISECONDS.sleep(50);
connection1 = (HttpConnectionOverHTTP)destination.getHttpConnectionPool().getIdleConnections().peek();
connection1 = (HttpConnectionOverHTTP)destination.getConnectionPool().getIdleConnections().peek();
}
Assert.assertNotNull(connection1);
@ -152,7 +153,7 @@ public class HttpDestinationOverHTTPTest extends AbstractHttpClientServerTest
long idleTimeout = 1000;
client.setIdleTimeout(idleTimeout);
HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, "http", "localhost", connector.getLocalPort());
HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", connector.getLocalPort()));
Connection connection1 = destination.acquire();
if (connection1 == null)
{
@ -161,13 +162,13 @@ public class HttpDestinationOverHTTPTest extends AbstractHttpClientServerTest
while (connection1 == null && TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - start) < 5)
{
TimeUnit.MILLISECONDS.sleep(50);
connection1 = destination.getHttpConnectionPool().getIdleConnections().peek();
connection1 = destination.getConnectionPool().getIdleConnections().peek();
}
Assert.assertNotNull(connection1);
TimeUnit.MILLISECONDS.sleep(2 * idleTimeout);
connection1 = destination.getHttpConnectionPool().getIdleConnections().poll();
connection1 = destination.getConnectionPool().getIdleConnections().poll();
Assert.assertNull(connection1);
}
}

View File

@ -88,6 +88,7 @@ public class SslBytesServerTest extends SslBytesTest
private ExecutorService threadPool;
private Server server;
private SslContextFactory sslContextFactory;
private int serverPort;
private SSLContext sslContext;
private SimpleProxy proxy;
private Runnable idleHook;
@ -215,7 +216,7 @@ public class SslBytesServerTest extends SslBytesTest
}
});
server.start();
int serverPort = connector.getLocalPort();
serverPort = connector.getLocalPort();
sslContext = sslContextFactory.getSslContext();
@ -296,6 +297,88 @@ public class SslBytesServerTest extends SslBytesTest
closeClient(client);
}
@Test
public void testHandshakeWithResumedSessionThenClose() throws Exception
{
// First socket will establish the SSL session
SSLSocket client1 = newClient();
SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow();
client1.startHandshake();
client1.close();
Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS));
int proxyPort = proxy.getPort();
proxy.stop();
proxy = new SimpleProxy(threadPool, proxyPort, "localhost", serverPort);
proxy.start();
logger.info("proxy:{} <==> server:{}", proxy.getPort(), serverPort);
final SSLSocket client2 = newClient(proxy);
Future<Object> handshake = threadPool.submit(new Callable<Object>()
{
@Override
public Object call() throws Exception
{
client2.startHandshake();
return null;
}
});
// Client Hello with SessionID
TLSRecord record = proxy.readFromClient();
Assert.assertNotNull(record);
proxy.flushToServer(record);
// Server Hello
record = proxy.readFromServer();
Assert.assertNotNull(record);
proxy.flushToClient(record);
// Change Cipher Spec
record = proxy.readFromServer();
Assert.assertNotNull(record);
proxy.flushToClient(record);
// Server Done
record = proxy.readFromServer();
Assert.assertNotNull(record);
proxy.flushToClient(record);
// Client Key Exchange
record = proxy.readFromClient();
Assert.assertNotNull(record);
// Client Done
TLSRecord doneRecord = proxy.readFromClient();
Assert.assertNotNull(doneRecord);
// Close
client2.close();
TLSRecord closeRecord = proxy.readFromClient();
Assert.assertNotNull(closeRecord);
Assert.assertEquals(TLSRecord.Type.ALERT, closeRecord.getType());
// Flush to server Client Key Exchange + Client Done + Close in one chunk
byte[] recordBytes = record.getBytes();
byte[] doneBytes = doneRecord.getBytes();
byte[] closeRecordBytes = closeRecord.getBytes();
byte[] chunk = new byte[recordBytes.length + doneBytes.length + closeRecordBytes.length];
System.arraycopy(recordBytes, 0, chunk, 0, recordBytes.length);
System.arraycopy(doneBytes, 0, chunk, recordBytes.length, doneBytes.length);
System.arraycopy(closeRecordBytes, 0, chunk, recordBytes.length + doneBytes.length, closeRecordBytes.length);
proxy.flushToServer(0, chunk);
// Close the raw socket
proxy.flushToServer(null);
// Expect the server to send a FIN as well
record = proxy.readFromServer();
Assert.assertNull(record);
// Check that we did not spin
TimeUnit.MILLISECONDS.sleep(500);
Assert.assertThat(sslFills.get(), Matchers.lessThan(20));
Assert.assertThat(sslFlushes.get(), Matchers.lessThan(20));
Assert.assertThat(httpParses.get(), Matchers.lessThan(20));
}
@Test
public void testHandshakeWithSplitBoundary() throws Exception
{
@ -1849,6 +1932,11 @@ public class SslBytesServerTest extends SslBytesTest
}
private SSLSocket newClient() throws IOException, InterruptedException
{
return newClient(proxy);
}
private SSLSocket newClient(SimpleProxy proxy) throws IOException, InterruptedException
{
SSLSocket client = (SSLSocket)sslContext.getSocketFactory().createSocket("localhost", proxy.getPort());
client.setUseClientMode(true);

View File

@ -105,6 +105,7 @@ public abstract class SslBytesTest
{
private final CountDownLatch latch = new CountDownLatch(1);
private final ExecutorService threadPool;
private final int proxyPort;
private final String serverHost;
private final int serverPort;
private volatile ServerSocket serverSocket;
@ -112,15 +113,21 @@ public abstract class SslBytesTest
private volatile Socket client;
public SimpleProxy(ExecutorService threadPool, String serverHost, int serverPort)
{
this(threadPool, 0, serverHost, serverPort);
}
public SimpleProxy(ExecutorService threadPool, int proxyPort, String serverHost, int serverPort)
{
this.threadPool = threadPool;
this.proxyPort = proxyPort;
this.serverHost = serverHost;
this.serverPort = serverPort;
}
public void start() throws Exception
{
serverSocket = new ServerSocket(0);
serverSocket = new ServerSocket(proxyPort);
Thread acceptor = new Thread(this);
acceptor.start();
server = new Socket(serverHost, serverPort);

View File

@ -6,12 +6,10 @@
webapp
[lib]
# Deploy jars
lib/jetty-deploy-${jetty.version}.jar
[files]
webapps/
[xml]
# Deploy configuration
etc/jetty-deploy.xml

View File

@ -502,7 +502,7 @@
<arguments>
<argument>jetty.home=${assembly-directory}</argument>
<argument>jetty.base=${assembly-directory}/demo-base</argument>
<argument>--add-to-start=server,deploy,jsp,ext,resources,client,annotations,jndi</argument>
<argument>--add-to-start=server,continuation,deploy,jsp,ext,resources,client,annotations,jndi</argument>
<argument>--add-to-startd-ini=http,https</argument>
</arguments>
</configuration>

View File

@ -1,3 +1,7 @@
#
# Set UID Feature
#
[depend]
server
@ -8,6 +12,7 @@ lib/setuid/jetty-setuid-java-1.0.1.jar
etc/jetty-setuid.xml
[ini-template]
## SetUID Configuration
# jetty.startServerAsPrivileged=false
# jetty.username=jetty
# jetty.groupname=jetty

View File

@ -0,0 +1,164 @@
//
// ========================================================================
// Copyright (c) 1995-2013 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.util.Calendar;
import java.util.GregorianCalendar;
import java.util.TimeZone;
import org.eclipse.jetty.util.StringUtil;
/**
* ThreadLocal Date formatters for HTTP style dates.
*
*/
public class DateGenerator
{
private static final TimeZone __GMT = TimeZone.getTimeZone("GMT");
static
{
__GMT.setID("GMT");
}
static final String[] DAYS =
{ "Sat", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
static final String[] MONTHS =
{ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "Jan"};
private static final ThreadLocal<DateGenerator> __dateGenerator =new ThreadLocal<DateGenerator>()
{
@Override
protected DateGenerator initialValue()
{
return new DateGenerator();
}
};
public final static String __01Jan1970=DateGenerator.formatDate(0);
/**
* Format HTTP date "EEE, dd MMM yyyy HH:mm:ss 'GMT'"
*/
public static String formatDate(long date)
{
return __dateGenerator.get().doFormatDate(date);
}
/**
* Format "EEE, dd-MMM-yyyy HH:mm:ss 'GMT'" for cookies
*/
public static void formatCookieDate(StringBuilder buf, long date)
{
__dateGenerator.get().doFormatCookieDate(buf,date);
}
/**
* Format "EEE, dd-MMM-yyyy HH:mm:ss 'GMT'" for cookies
*/
public static String formatCookieDate(long date)
{
StringBuilder buf = new StringBuilder(28);
formatCookieDate(buf, date);
return buf.toString();
}
private final StringBuilder buf = new StringBuilder(32);
private final GregorianCalendar gc = new GregorianCalendar(__GMT);
/**
* Format HTTP date "EEE, dd MMM yyyy HH:mm:ss 'GMT'"
*/
public String doFormatDate(long date)
{
buf.setLength(0);
gc.setTimeInMillis(date);
int day_of_week = gc.get(Calendar.DAY_OF_WEEK);
int day_of_month = gc.get(Calendar.DAY_OF_MONTH);
int month = gc.get(Calendar.MONTH);
int year = gc.get(Calendar.YEAR);
int century = year / 100;
year = year % 100;
int hours = gc.get(Calendar.HOUR_OF_DAY);
int minutes = gc.get(Calendar.MINUTE);
int seconds = gc.get(Calendar.SECOND);
buf.append(DAYS[day_of_week]);
buf.append(',');
buf.append(' ');
StringUtil.append2digits(buf, day_of_month);
buf.append(' ');
buf.append(MONTHS[month]);
buf.append(' ');
StringUtil.append2digits(buf, century);
StringUtil.append2digits(buf, year);
buf.append(' ');
StringUtil.append2digits(buf, hours);
buf.append(':');
StringUtil.append2digits(buf, minutes);
buf.append(':');
StringUtil.append2digits(buf, seconds);
buf.append(" GMT");
return buf.toString();
}
/**
* Format "EEE, dd-MMM-yy HH:mm:ss 'GMT'" for cookies
*/
public void doFormatCookieDate(StringBuilder buf, long date)
{
gc.setTimeInMillis(date);
int day_of_week = gc.get(Calendar.DAY_OF_WEEK);
int day_of_month = gc.get(Calendar.DAY_OF_MONTH);
int month = gc.get(Calendar.MONTH);
int year = gc.get(Calendar.YEAR);
year = year % 10000;
int epoch = (int) ((date / 1000) % (60 * 60 * 24));
int seconds = epoch % 60;
epoch = epoch / 60;
int minutes = epoch % 60;
int hours = epoch / 60;
buf.append(DAYS[day_of_week]);
buf.append(',');
buf.append(' ');
StringUtil.append2digits(buf, day_of_month);
buf.append('-');
buf.append(MONTHS[month]);
buf.append('-');
StringUtil.append2digits(buf, year/100);
StringUtil.append2digits(buf, year%100);
buf.append(' ');
StringUtil.append2digits(buf, hours);
buf.append(':');
StringUtil.append2digits(buf, minutes);
buf.append(':');
StringUtil.append2digits(buf, seconds);
buf.append(" GMT");
}
}

View File

@ -0,0 +1,109 @@
//
// ========================================================================
// Copyright (c) 1995-2013 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.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
/**
* ThreadLocal data parsers for HTTP style dates
*
*/
class DateParser
{
private static final TimeZone __GMT = TimeZone.getTimeZone("GMT");
static
{
__GMT.setID("GMT");
}
final static String __dateReceiveFmt[] =
{
"EEE, dd MMM yyyy HH:mm:ss zzz",
"EEE, dd-MMM-yy HH:mm:ss",
"EEE MMM dd HH:mm:ss yyyy",
"EEE, dd MMM yyyy HH:mm:ss", "EEE dd MMM yyyy HH:mm:ss zzz",
"EEE dd MMM yyyy HH:mm:ss", "EEE MMM dd yyyy HH:mm:ss zzz", "EEE MMM dd yyyy HH:mm:ss",
"EEE MMM-dd-yyyy HH:mm:ss zzz", "EEE MMM-dd-yyyy HH:mm:ss", "dd MMM yyyy HH:mm:ss zzz",
"dd MMM yyyy HH:mm:ss", "dd-MMM-yy HH:mm:ss zzz", "dd-MMM-yy HH:mm:ss", "MMM dd HH:mm:ss yyyy zzz",
"MMM dd HH:mm:ss yyyy", "EEE MMM dd HH:mm:ss yyyy zzz",
"EEE, MMM dd HH:mm:ss yyyy zzz", "EEE, MMM dd HH:mm:ss yyyy", "EEE, dd-MMM-yy HH:mm:ss zzz",
"EEE dd-MMM-yy HH:mm:ss zzz", "EEE dd-MMM-yy HH:mm:ss",
};
public static long parseDate(String date)
{
return __dateParser.get().parse(date);
}
private static final ThreadLocal<DateParser> __dateParser =new ThreadLocal<DateParser>()
{
@Override
protected DateParser initialValue()
{
return new DateParser();
}
};
final SimpleDateFormat _dateReceive[]= new SimpleDateFormat[__dateReceiveFmt.length];
private long parse(final String dateVal)
{
for (int i = 0; i < _dateReceive.length; i++)
{
if (_dateReceive[i] == null)
{
_dateReceive[i] = new SimpleDateFormat(__dateReceiveFmt[i], Locale.US);
_dateReceive[i].setTimeZone(__GMT);
}
try
{
Date date = (Date) _dateReceive[i].parseObject(dateVal);
return date.getTime();
}
catch (java.lang.Exception e)
{
// LOG.ignore(e);
}
}
if (dateVal.endsWith(" GMT"))
{
final String val = dateVal.substring(0, dateVal.length() - 4);
for (SimpleDateFormat element : _dateReceive)
{
try
{
Date date = (Date) element.parseObject(val);
return date.getTime();
}
catch (java.lang.Exception e)
{
// LOG.ignore(e);
}
}
}
return -1;
}
}

View File

@ -18,93 +18,12 @@
package org.eclipse.jetty.http;
import java.nio.ByteBuffer;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Pattern;
import org.eclipse.jetty.util.ArrayTrie;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.Trie;
/* ------------------------------------------------------------ */
/** A HTTP Field
*/
public class HttpField
{
/**
* Cache of common {@link HttpField}s including: <UL>
* <LI>Common static combinations such as:<UL>
* <li>Connection: close
* <li>Accept-Encoding: gzip
* <li>Content-Length: 0
* </ul>
* <li>Combinations of Content-Type header for common mime types by common charsets
* <li>Most common headers with null values so that a lookup will at least
* determine the header name even if the name:value combination is not cached
* </ul>
*/
public final static Trie<HttpField> CACHE = new ArrayTrie<>(2048);
public final static Trie<HttpField> CONTENT_TYPE = new ArrayTrie<>(512);
static
{
CACHE.put(new CachedHttpField(HttpHeader.CONNECTION,HttpHeaderValue.CLOSE));
CACHE.put(new CachedHttpField(HttpHeader.CONNECTION,HttpHeaderValue.KEEP_ALIVE));
CACHE.put(new CachedHttpField(HttpHeader.CONNECTION,HttpHeaderValue.UPGRADE));
CACHE.put(new CachedHttpField(HttpHeader.ACCEPT_ENCODING,"gzip"));
CACHE.put(new CachedHttpField(HttpHeader.ACCEPT_ENCODING,"gzip, deflate"));
CACHE.put(new CachedHttpField(HttpHeader.ACCEPT_ENCODING,"gzip,deflate,sdch"));
CACHE.put(new CachedHttpField(HttpHeader.ACCEPT_LANGUAGE,"en-US,en;q=0.5"));
CACHE.put(new CachedHttpField(HttpHeader.ACCEPT_LANGUAGE,"en-GB,en-US;q=0.8,en;q=0.6"));
CACHE.put(new CachedHttpField(HttpHeader.ACCEPT_CHARSET,"ISO-8859-1,utf-8;q=0.7,*;q=0.3"));
CACHE.put(new CachedHttpField(HttpHeader.ACCEPT,"*/*"));
CACHE.put(new CachedHttpField(HttpHeader.ACCEPT,"image/png,image/*;q=0.8,*/*;q=0.5"));
CACHE.put(new CachedHttpField(HttpHeader.ACCEPT,"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"));
CACHE.put(new CachedHttpField(HttpHeader.PRAGMA,"no-cache"));
CACHE.put(new CachedHttpField(HttpHeader.CACHE_CONTROL,"private, no-cache, no-cache=Set-Cookie, proxy-revalidate"));
CACHE.put(new CachedHttpField(HttpHeader.CACHE_CONTROL,"no-cache"));
CACHE.put(new CachedHttpField(HttpHeader.CONTENT_LENGTH,"0"));
CACHE.put(new CachedHttpField(HttpHeader.CONTENT_ENCODING,"gzip"));
CACHE.put(new CachedHttpField(HttpHeader.CONTENT_ENCODING,"deflate"));
CACHE.put(new CachedHttpField(HttpHeader.TRANSFER_ENCODING,"chunked"));
CACHE.put(new CachedHttpField(HttpHeader.EXPIRES,"Fri, 01 Jan 1990 00:00:00 GMT"));
// Content types
for (String type : new String[]{"text/plain","text/html","text/xml","text/json","application/x-www-form-urlencoded"})
{
HttpField field=new CachedHttpField(HttpHeader.CONTENT_TYPE,type);
CACHE.put(field);
CONTENT_TYPE.put(type,field);
for (String charset : new String[]{"UTF-8","ISO-8859-1"})
{
String type_charset=type+"; charset="+charset;
field=new CachedHttpField(HttpHeader.CONTENT_TYPE,type_charset);
CACHE.put(field);
CACHE.put(new CachedHttpField(HttpHeader.CONTENT_TYPE,type+";charset="+charset));
CONTENT_TYPE.put(type_charset,field);
CONTENT_TYPE.put(type+";charset="+charset,field);
}
}
// Add headers with null values so HttpParser can avoid looking up name again for unknown values
for (HttpHeader h:HttpHeader.values())
if (!CACHE.put(new HttpField(h,(String)null)))
throw new IllegalStateException("CACHE FULL");
// Add some more common headers
CACHE.put(new HttpField(HttpHeader.REFERER,(String)null));
CACHE.put(new HttpField(HttpHeader.IF_MODIFIED_SINCE,(String)null));
CACHE.put(new HttpField(HttpHeader.IF_NONE_MATCH,(String)null));
CACHE.put(new HttpField(HttpHeader.AUTHORIZATION,(String)null));
CACHE.put(new HttpField(HttpHeader.COOKIE,(String)null));
}
private final static Pattern __splitter = Pattern.compile("\\s*,\\s*");
private final static byte[] __colon_space = new byte[] {':',' '};
private final HttpHeader _header;
private final String _name;
private final String _value;
@ -120,7 +39,6 @@ public class HttpField
{
this(header,header.asString(),value);
}
public HttpField(HttpHeader header, HttpHeaderValue value)
{
@ -146,89 +64,7 @@ public class HttpField
{
return _value;
}
public boolean contains(String value)
{
if (_value==null)
return false;
if (value.equalsIgnoreCase(_value))
return true;
String[] split = __splitter.split(_value);
for (int i = 0; split!=null && i < split.length; i++)
{
if (value.equalsIgnoreCase(split[i]))
return true;
}
return false;
}
public int getIntValue()
{
return StringUtil.toInt(_value);
}
public long getLongValue()
{
return StringUtil.toLong(_value);
}
private static byte[] toSanitisedName(String s)
{
byte[] bytes = s.getBytes(StringUtil.__ISO_8859_1_CHARSET);
for (int i=bytes.length;i-->0;)
{
switch(bytes[i])
{
case '\r':
case '\n':
case ':' :
bytes[i]=(byte)'?';
}
}
return bytes;
}
private static byte[] toSanitisedValue(String s)
{
byte[] bytes = s.getBytes(StringUtil.__ISO_8859_1_CHARSET);
for (int i=bytes.length;i-->0;)
{
switch(bytes[i])
{
case '\r':
case '\n':
bytes[i]=(byte)'?';
}
}
return bytes;
}
public void putTo(ByteBuffer bufferInFillMode)
{
if (_header!=null)
{
bufferInFillMode.put(_header.getBytesColonSpace());
bufferInFillMode.put(toSanitisedValue(_value));
}
else
{
bufferInFillMode.put(toSanitisedName(_name));
bufferInFillMode.put(__colon_space);
bufferInFillMode.put(toSanitisedValue(_value));
}
BufferUtil.putCRLF(bufferInFillMode);
}
public void putValueTo(ByteBuffer buffer)
{
buffer.put(toSanitisedValue(_value));
}
@Override
public String toString()
{
@ -250,31 +86,4 @@ public class HttpField
}
/* ------------------------------------------------------------ */
/** A HTTP Field optimised to be reused.
*/
public static class CachedHttpField extends HttpField
{
final byte[] _bytes;
public CachedHttpField(HttpHeader header, String value)
{
super(header,value);
_bytes=new byte[header.asString().length()+2+value.length()+2];
System.arraycopy(header.getBytesColonSpace(),0,_bytes,0,header.asString().length()+2);
System.arraycopy(toSanitisedValue(value),0,_bytes,header.asString().length()+2,value.length());
_bytes[_bytes.length-2]='\r';
_bytes[_bytes.length-1]='\n';
}
CachedHttpField(HttpHeader header, HttpHeaderValue value)
{
this(header,value.asString());
}
@Override
public void putTo(ByteBuffer bufferInFillMode)
{
bufferInFillMode.put(_bytes);
}
}
}

View File

@ -18,29 +18,21 @@
package org.eclipse.jetty.http;
import java.nio.ByteBuffer;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TimeZone;
import java.util.regex.Pattern;
import org.eclipse.jetty.util.ArrayTernaryTrie;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.DateCache;
import org.eclipse.jetty.util.LazyList;
import org.eclipse.jetty.util.QuotedStringTokenizer;
import org.eclipse.jetty.util.StringUtil;
@ -48,13 +40,11 @@ import org.eclipse.jetty.util.Trie;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import static org.eclipse.jetty.util.QuotedStringTokenizer.isQuoted;
/**
* HTTP Fields. A collection of HTTP header and or Trailer fields.
*
* <p>This class is not synchronised as it is expected that modifications will only be performed by a
* <p>This class is not synchronized as it is expected that modifications will only be performed by a
* single thread.
*
* <p>The cookie handling provided by this class is guided by the Servlet specification and RFC6265.
@ -63,222 +53,9 @@ import static org.eclipse.jetty.util.QuotedStringTokenizer.isQuoted;
public class HttpFields implements Iterable<HttpField>
{
private static final Logger LOG = Log.getLogger(HttpFields.class);
public static final TimeZone __GMT = TimeZone.getTimeZone("GMT");
public static final DateCache __dateCache = new DateCache("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US);
public static final String __COOKIE_DELIM="\",;\\ \t";
static
{
__GMT.setID("GMT");
__dateCache.setTimeZone(__GMT);
}
private final static Pattern __splitter = Pattern.compile("\\s*,\\s*");
public final static String __separators = ", \t";
private static final String[] DAYS =
{ "Sat", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
private static final String[] MONTHS =
{ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "Jan"};
public static class DateGenerator
{
private final StringBuilder buf = new StringBuilder(32);
private final GregorianCalendar gc = new GregorianCalendar(__GMT);
/**
* Format HTTP date "EEE, dd MMM yyyy HH:mm:ss 'GMT'"
*/
public String formatDate(long date)
{
buf.setLength(0);
gc.setTimeInMillis(date);
int day_of_week = gc.get(Calendar.DAY_OF_WEEK);
int day_of_month = gc.get(Calendar.DAY_OF_MONTH);
int month = gc.get(Calendar.MONTH);
int year = gc.get(Calendar.YEAR);
int century = year / 100;
year = year % 100;
int hours = gc.get(Calendar.HOUR_OF_DAY);
int minutes = gc.get(Calendar.MINUTE);
int seconds = gc.get(Calendar.SECOND);
buf.append(DAYS[day_of_week]);
buf.append(',');
buf.append(' ');
StringUtil.append2digits(buf, day_of_month);
buf.append(' ');
buf.append(MONTHS[month]);
buf.append(' ');
StringUtil.append2digits(buf, century);
StringUtil.append2digits(buf, year);
buf.append(' ');
StringUtil.append2digits(buf, hours);
buf.append(':');
StringUtil.append2digits(buf, minutes);
buf.append(':');
StringUtil.append2digits(buf, seconds);
buf.append(" GMT");
return buf.toString();
}
/**
* Format "EEE, dd-MMM-yy HH:mm:ss 'GMT'" for cookies
*/
public void formatCookieDate(StringBuilder buf, long date)
{
gc.setTimeInMillis(date);
int day_of_week = gc.get(Calendar.DAY_OF_WEEK);
int day_of_month = gc.get(Calendar.DAY_OF_MONTH);
int month = gc.get(Calendar.MONTH);
int year = gc.get(Calendar.YEAR);
year = year % 10000;
int epoch = (int) ((date / 1000) % (60 * 60 * 24));
int seconds = epoch % 60;
epoch = epoch / 60;
int minutes = epoch % 60;
int hours = epoch / 60;
buf.append(DAYS[day_of_week]);
buf.append(',');
buf.append(' ');
StringUtil.append2digits(buf, day_of_month);
buf.append('-');
buf.append(MONTHS[month]);
buf.append('-');
StringUtil.append2digits(buf, year/100);
StringUtil.append2digits(buf, year%100);
buf.append(' ');
StringUtil.append2digits(buf, hours);
buf.append(':');
StringUtil.append2digits(buf, minutes);
buf.append(':');
StringUtil.append2digits(buf, seconds);
buf.append(" GMT");
}
}
private static final ThreadLocal<DateGenerator> __dateGenerator =new ThreadLocal<DateGenerator>()
{
@Override
protected DateGenerator initialValue()
{
return new DateGenerator();
}
};
/**
* Format HTTP date "EEE, dd MMM yyyy HH:mm:ss 'GMT'"
*/
public static String formatDate(long date)
{
return __dateGenerator.get().formatDate(date);
}
/**
* Format "EEE, dd-MMM-yyyy HH:mm:ss 'GMT'" for cookies
*/
public static void formatCookieDate(StringBuilder buf, long date)
{
__dateGenerator.get().formatCookieDate(buf,date);
}
/**
* Format "EEE, dd-MMM-yyyy HH:mm:ss 'GMT'" for cookies
*/
public static String formatCookieDate(long date)
{
StringBuilder buf = new StringBuilder(28);
formatCookieDate(buf, date);
return buf.toString();
}
private final static String __dateReceiveFmt[] =
{
"EEE, dd MMM yyyy HH:mm:ss zzz",
"EEE, dd-MMM-yy HH:mm:ss",
"EEE MMM dd HH:mm:ss yyyy",
"EEE, dd MMM yyyy HH:mm:ss", "EEE dd MMM yyyy HH:mm:ss zzz",
"EEE dd MMM yyyy HH:mm:ss", "EEE MMM dd yyyy HH:mm:ss zzz", "EEE MMM dd yyyy HH:mm:ss",
"EEE MMM-dd-yyyy HH:mm:ss zzz", "EEE MMM-dd-yyyy HH:mm:ss", "dd MMM yyyy HH:mm:ss zzz",
"dd MMM yyyy HH:mm:ss", "dd-MMM-yy HH:mm:ss zzz", "dd-MMM-yy HH:mm:ss", "MMM dd HH:mm:ss yyyy zzz",
"MMM dd HH:mm:ss yyyy", "EEE MMM dd HH:mm:ss yyyy zzz",
"EEE, MMM dd HH:mm:ss yyyy zzz", "EEE, MMM dd HH:mm:ss yyyy", "EEE, dd-MMM-yy HH:mm:ss zzz",
"EEE dd-MMM-yy HH:mm:ss zzz", "EEE dd-MMM-yy HH:mm:ss",
};
private static class DateParser
{
final SimpleDateFormat _dateReceive[]= new SimpleDateFormat[__dateReceiveFmt.length];
long parse(final String dateVal)
{
for (int i = 0; i < _dateReceive.length; i++)
{
if (_dateReceive[i] == null)
{
_dateReceive[i] = new SimpleDateFormat(__dateReceiveFmt[i], Locale.US);
_dateReceive[i].setTimeZone(__GMT);
}
try
{
Date date = (Date) _dateReceive[i].parseObject(dateVal);
return date.getTime();
}
catch (java.lang.Exception e)
{
// LOG.ignore(e);
}
}
if (dateVal.endsWith(" GMT"))
{
final String val = dateVal.substring(0, dateVal.length() - 4);
for (SimpleDateFormat element : _dateReceive)
{
try
{
Date date = (Date) element.parseObject(val);
return date.getTime();
}
catch (java.lang.Exception e)
{
// LOG.ignore(e);
}
}
}
return -1;
}
}
public static long parseDate(String date)
{
return __dateParser.get().parse(date);
}
private static final ThreadLocal<DateParser> __dateParser =new ThreadLocal<DateParser>()
{
@Override
protected DateParser initialValue()
{
return new DateParser();
}
};
public final static String __01Jan1970=formatDate(0);
public final static ByteBuffer __01Jan1970_BUFFER=BufferUtil.toBuffer(__01Jan1970);
public final static String __01Jan1970_COOKIE = formatCookieDate(0).trim();
private final ArrayList<HttpField> _fields = new ArrayList<>(20);
/**
@ -288,7 +65,6 @@ public class HttpFields implements Iterable<HttpField>
{
}
/**
* Get Collection of header names.
*/
@ -354,13 +130,13 @@ public class HttpFields implements Iterable<HttpField>
}
return null;
}
public boolean contains(HttpHeader header, String value)
{
for (int i=0;i<_fields.size();i++)
{
HttpField f=_fields.get(i);
if (f.getHeader()==header && f.contains(value))
if (f.getHeader()==header && contains(f,value))
return true;
}
return false;
@ -371,12 +147,31 @@ public class HttpFields implements Iterable<HttpField>
for (int i=0;i<_fields.size();i++)
{
HttpField f=_fields.get(i);
if (f.getName().equalsIgnoreCase(name) && f.contains(value))
if (f.getName().equalsIgnoreCase(name) && contains(f,value))
return true;
}
return false;
}
private boolean contains(HttpField field,String value)
{
String v = field.getValue();
if (v==null)
return false;
if (value.equalsIgnoreCase(v))
return true;
String[] split = __splitter.split(v);
for (int i = 0; split!=null && i < split.length; i++)
{
if (value.equals(split[i]))
return true;
}
return false;
}
public boolean containsKey(String name)
{
for (int i=0;i<_fields.size();i++)
@ -414,14 +209,13 @@ public class HttpFields implements Iterable<HttpField>
return field==null?null:field.getValue();
}
/**
* Get multi headers
*
* @return Enumeration of the values, or null if no such header.
* @return List the values
* @param name the case-insensitive field name
*/
public Collection<String> getValuesCollection(String name)
public List<String> getValuesList(String name)
{
final List<String> list = new ArrayList<>();
for (HttpField f : _fields)
@ -485,7 +279,6 @@ public class HttpFields implements Iterable<HttpField>
List<String> empty=Collections.emptyList();
return Collections.enumeration(empty);
}
/**
@ -683,7 +476,7 @@ public class HttpFields implements Iterable<HttpField>
public long getLongField(String name) throws NumberFormatException
{
HttpField field = getField(name);
return field==null?-1L:field.getLongValue();
return field==null?-1L:StringUtil.toLong(field.getValue());
}
/**
@ -702,7 +495,7 @@ public class HttpFields implements Iterable<HttpField>
if (val == null)
return -1;
final long date = __dateParser.get().parse(val);
final long date = DateParser.parseDate(val);
if (date==-1)
throw new IllegalArgumentException("Cannot convert date: " + val);
return date;
@ -742,7 +535,7 @@ public class HttpFields implements Iterable<HttpField>
*/
public void putDateField(HttpHeader name, long date)
{
String d=formatDate(date);
String d=DateGenerator.formatDate(date);
put(name, d);
}
@ -754,7 +547,7 @@ public class HttpFields implements Iterable<HttpField>
*/
public void putDateField(String name, long date)
{
String d=formatDate(date);
String d=DateGenerator.formatDate(date);
put(name, d);
}
@ -766,170 +559,10 @@ public class HttpFields implements Iterable<HttpField>
*/
public void addDateField(String name, long date)
{
String d=formatDate(date);
String d=DateGenerator.formatDate(date);
add(name,d);
}
/**
* Format a set cookie value
*
* @param cookie The cookie.
*/
public void addSetCookie(HttpCookie cookie)
{
addSetCookie(
cookie.getName(),
cookie.getValue(),
cookie.getDomain(),
cookie.getPath(),
cookie.getMaxAge(),
cookie.getComment(),
cookie.isSecure(),
cookie.isHttpOnly(),
cookie.getVersion());
}
/**
* Format a set cookie value
*
* @param name the name
* @param value the value
* @param domain the domain
* @param path the path
* @param maxAge the maximum age
* @param comment the comment (only present on versions > 0)
* @param isSecure true if secure cookie
* @param isHttpOnly true if for http only
* @param version version of cookie logic to use (0 == default behavior)
*/
public void addSetCookie(
final String name,
final String value,
final String domain,
final String path,
final long maxAge,
final String comment,
final boolean isSecure,
final boolean isHttpOnly,
int version)
{
// Check arguments
if (name == null || name.length() == 0)
throw new IllegalArgumentException("Bad cookie name");
// Format value and params
StringBuilder buf = new StringBuilder(128);
// Name is checked for legality by servlet spec, but can also be passed directly so check again for quoting
boolean quote_name=isQuoteNeededForCookie(name);
quoteOnlyOrAppend(buf,name,quote_name);
buf.append('=');
// Remember name= part to look for other matching set-cookie
String name_equals=buf.toString();
// Append the value
boolean quote_value=isQuoteNeededForCookie(value);
quoteOnlyOrAppend(buf,value,quote_value);
// Look for domain and path fields and check if they need to be quoted
boolean has_domain = domain!=null && domain.length()>0;
boolean quote_domain = has_domain && isQuoteNeededForCookie(domain);
boolean has_path = path!=null && path.length()>0;
boolean quote_path = has_path && isQuoteNeededForCookie(path);
// Upgrade the version if we have a comment or we need to quote value/path/domain or if they were already quoted
if (version==0 && ( comment!=null || quote_name || quote_value || quote_domain || quote_path || isQuoted(name) || isQuoted(value) || isQuoted(path) || isQuoted(domain)))
version=1;
// Append version
if (version==1)
buf.append (";Version=1");
else if (version>1)
buf.append (";Version=").append(version);
// Append path
if (has_path)
{
buf.append(";Path=");
quoteOnlyOrAppend(buf,path,quote_path);
}
// Append domain
if (has_domain)
{
buf.append(";Domain=");
quoteOnlyOrAppend(buf,domain,quote_domain);
}
// Handle max-age and/or expires
if (maxAge >= 0)
{
// Always use expires
// This is required as some browser (M$ this means you!) don't handle max-age even with v1 cookies
buf.append(";Expires=");
if (maxAge == 0)
buf.append(__01Jan1970_COOKIE);
else
formatCookieDate(buf, System.currentTimeMillis() + 1000L * maxAge);
// for v1 cookies, also send max-age
if (version>=1)
{
buf.append(";Max-Age=");
buf.append(maxAge);
}
}
// add the other fields
if (isSecure)
buf.append(";Secure");
if (isHttpOnly)
buf.append(";HttpOnly");
if (comment != null)
{
buf.append(";Comment=");
quoteOnlyOrAppend(buf,comment,isQuoteNeededForCookie(comment));
}
// remove any existing set-cookie fields of same name
Iterator<HttpField> i=_fields.iterator();
while (i.hasNext())
{
HttpField field=i.next();
if (field.getHeader()==HttpHeader.SET_COOKIE)
{
String val = field.getValue();
if (val!=null && val.startsWith(name_equals))
{
//existing cookie has same name, does it also match domain and path?
if (((!has_domain && !val.contains("Domain")) || (has_domain && val.contains(domain))) &&
((!has_path && !val.contains("Path")) || (has_path && val.contains(path))))
{
i.remove();
}
}
}
}
// add the set cookie
add(HttpHeader.SET_COOKIE.toString(), buf.toString());
// Expire responses with set-cookie headers so they do not get cached.
put(HttpHeader.EXPIRES.toString(), __01Jan1970);
}
public void putTo(ByteBuffer bufferInFillMode)
{
for (HttpField field : _fields)
{
if (field != null)
field.putTo(bufferInFillMode);
}
BufferUtil.putCRLF(bufferInFillMode);
}
@Override
public String
toString()
@ -1148,39 +781,4 @@ public class HttpFields implements Iterable<HttpField>
/* ------------------------------------------------------------ */
/** Does a cookie value need to be quoted?
* @param s value string
* @return true if quoted;
* @throws IllegalArgumentException If there a control characters in the string
*/
public static boolean isQuoteNeededForCookie(String s)
{
if (s==null || s.length()==0)
return true;
if (QuotedStringTokenizer.isQuoted(s))
return false;
for (int i=0;i<s.length();i++)
{
char c = s.charAt(i);
if (__COOKIE_DELIM.indexOf(c)>=0)
return true;
if (c<0x20 || c>=0x7f)
throw new IllegalArgumentException("Illegal character in cookie value");
}
return false;
}
private static void quoteOnlyOrAppend(StringBuilder buf, String s, boolean quote)
{
if (quote)
QuotedStringTokenizer.quoteOnly(buf,s);
else
buf.append(s);
}
}

View File

@ -37,6 +37,7 @@ public class HttpGenerator
{
private static final Logger LOG = Log.getLogger(HttpGenerator.class);
private final static byte[] __colon_space = new byte[] {':',' '};
public static final ResponseInfo CONTINUE_100_INFO = new ResponseInfo(HttpVersion.HTTP_1_1,null,-1,100,null,false);
public static final ResponseInfo PROGRESS_102_INFO = new ResponseInfo(HttpVersion.HTTP_1_1,null,-1,102,null,false);
public final static ResponseInfo RESPONSE_500_INFO =
@ -495,6 +496,9 @@ public class HttpGenerator
case HTTP_1_1:
header.put((byte)' ');
header.put(request.getHttpVersion().toBytes());
break;
default:
throw new IllegalStateException();
}
header.put(HttpTokens.CRLF);
}
@ -585,7 +589,7 @@ public class HttpGenerator
// write the field to the header
content_type=true;
field.putTo(header);
putTo(field,header);
break;
}
@ -600,7 +604,7 @@ public class HttpGenerator
case CONNECTION:
{
if (request!=null)
field.putTo(header);
putTo(field,header);
// Lookup and/or split connection value field
HttpHeaderValue[] values = new HttpHeaderValue[]{HttpHeaderValue.CACHE.get(field.getValue())};
@ -672,12 +676,12 @@ public class HttpGenerator
case SERVER:
{
send=send&~SEND_SERVER;
field.putTo(header);
putTo(field,header);
break;
}
default:
field.putTo(header);
putTo(field,header);
}
}
}
@ -776,7 +780,7 @@ public class HttpGenerator
{
String c = transfer_encoding.getValue();
if (c.endsWith(HttpHeaderValue.CHUNKED.toString()))
transfer_encoding.putTo(header);
putTo(transfer_encoding,header);
else
throw new IllegalArgumentException("BAD TE");
}
@ -1007,5 +1011,88 @@ public class HttpGenerator
{
return String.format("ResponseInfo{%s %s %s,%d,%b}",_httpVersion,_status,_reason,_contentLength,_head);
}
}
private static void putSanitisedName(String s,ByteBuffer buffer)
{
int l=s.length();
for (int i=0;i<l;i++)
{
char c=s.charAt(i);
if (c<0 || c>0xff || c=='\r' || c=='\n'|| c==':')
buffer.put((byte)'?');
else
buffer.put((byte)(0xff&c));
}
}
private static void putSanitisedValue(String s,ByteBuffer buffer)
{
int l=s.length();
for (int i=0;i<l;i++)
{
char c=s.charAt(i);
if (c<0 || c>0xff || c=='\r' || c=='\n')
buffer.put((byte)'?');
else
buffer.put((byte)(0xff&c));
}
}
public static void putTo(HttpField field, ByteBuffer bufferInFillMode)
{
if (field instanceof CachedHttpField)
{
((CachedHttpField)field).putTo(bufferInFillMode);
}
else
{
HttpHeader header=field.getHeader();
if (header!=null)
{
bufferInFillMode.put(header.getBytesColonSpace());
putSanitisedValue(field.getValue(),bufferInFillMode);
}
else
{
putSanitisedName(field.getName(),bufferInFillMode);
bufferInFillMode.put(__colon_space);
putSanitisedValue(field.getValue(),bufferInFillMode);
}
BufferUtil.putCRLF(bufferInFillMode);
}
}
public static void putTo(HttpFields fields, ByteBuffer bufferInFillMode)
{
for (HttpField field : fields)
{
if (field != null)
putTo(field,bufferInFillMode);
}
BufferUtil.putCRLF(bufferInFillMode);
}
public static class CachedHttpField extends HttpField
{
private final byte[] _bytes;
public CachedHttpField(HttpHeader header,String value)
{
super(header,value);
int cbl=header.getBytesColonSpace().length;
_bytes=new byte[cbl+value.length()+2];
System.arraycopy(header.getBytesColonSpace(),0,_bytes,0,cbl);
System.arraycopy(value.getBytes(StringUtil.__ISO_8859_1_CHARSET),0,_bytes,cbl,value.length());
_bytes[_bytes.length-2]=(byte)'\r';
_bytes[_bytes.length-1]=(byte)'\n';
}
public void putTo(ByteBuffer bufferInFillMode)
{
bufferInFillMode.put(_bytes);
}
}
}

View File

@ -22,6 +22,7 @@ import java.nio.ByteBuffer;
import org.eclipse.jetty.http.HttpTokens.EndOfContent;
import org.eclipse.jetty.util.ArrayTernaryTrie;
import org.eclipse.jetty.util.ArrayTrie;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.Trie;
@ -76,6 +77,21 @@ public class HttpParser
public final static boolean __STRICT=Boolean.getBoolean("org.eclipse.jetty.http.HttpParser.STRICT");
public final static int INITIAL_URI_LENGTH=256;
/**
* Cache of common {@link HttpField}s including: <UL>
* <LI>Common static combinations such as:<UL>
* <li>Connection: close
* <li>Accept-Encoding: gzip
* <li>Content-Length: 0
* </ul>
* <li>Combinations of Content-Type header for common mime types by common charsets
* <li>Most common headers with null values so that a lookup will at least
* determine the header name even if the name:value combination is not cached
* </ul>
*/
public final static Trie<HttpField> CACHE = new ArrayTrie<>(2048);
public final static Trie<HttpField> CONTENT_TYPE = new ArrayTrie<>(512);
// States
public enum State
{
@ -138,6 +154,59 @@ public class HttpParser
private int _length;
private final StringBuilder _string=new StringBuilder();
static
{
CACHE.put(new HttpField(HttpHeader.CONNECTION,HttpHeaderValue.CLOSE));
CACHE.put(new HttpField(HttpHeader.CONNECTION,HttpHeaderValue.KEEP_ALIVE));
CACHE.put(new HttpField(HttpHeader.CONNECTION,HttpHeaderValue.UPGRADE));
CACHE.put(new HttpField(HttpHeader.ACCEPT_ENCODING,"gzip"));
CACHE.put(new HttpField(HttpHeader.ACCEPT_ENCODING,"gzip, deflate"));
CACHE.put(new HttpField(HttpHeader.ACCEPT_ENCODING,"gzip,deflate,sdch"));
CACHE.put(new HttpField(HttpHeader.ACCEPT_LANGUAGE,"en-US,en;q=0.5"));
CACHE.put(new HttpField(HttpHeader.ACCEPT_LANGUAGE,"en-GB,en-US;q=0.8,en;q=0.6"));
CACHE.put(new HttpField(HttpHeader.ACCEPT_CHARSET,"ISO-8859-1,utf-8;q=0.7,*;q=0.3"));
CACHE.put(new HttpField(HttpHeader.ACCEPT,"*/*"));
CACHE.put(new HttpField(HttpHeader.ACCEPT,"image/png,image/*;q=0.8,*/*;q=0.5"));
CACHE.put(new HttpField(HttpHeader.ACCEPT,"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"));
CACHE.put(new HttpField(HttpHeader.PRAGMA,"no-cache"));
CACHE.put(new HttpField(HttpHeader.CACHE_CONTROL,"private, no-cache, no-cache=Set-Cookie, proxy-revalidate"));
CACHE.put(new HttpField(HttpHeader.CACHE_CONTROL,"no-cache"));
CACHE.put(new HttpField(HttpHeader.CONTENT_LENGTH,"0"));
CACHE.put(new HttpField(HttpHeader.CONTENT_ENCODING,"gzip"));
CACHE.put(new HttpField(HttpHeader.CONTENT_ENCODING,"deflate"));
CACHE.put(new HttpField(HttpHeader.TRANSFER_ENCODING,"chunked"));
CACHE.put(new HttpField(HttpHeader.EXPIRES,"Fri, 01 Jan 1990 00:00:00 GMT"));
// Content types
for (String type : new String[]{"text/plain","text/html","text/xml","text/json","application/x-www-form-urlencoded"})
{
HttpField field=new HttpField(HttpHeader.CONTENT_TYPE,type);
CACHE.put(field);
CONTENT_TYPE.put(type,field);
for (String charset : new String[]{"UTF-8","ISO-8859-1"})
{
String type_charset=type+"; charset="+charset;
field=new HttpField(HttpHeader.CONTENT_TYPE,type_charset);
CACHE.put(field);
CACHE.put(new HttpField(HttpHeader.CONTENT_TYPE,type+";charset="+charset));
CONTENT_TYPE.put(type_charset,field);
CONTENT_TYPE.put(type+";charset="+charset,field);
}
}
// Add headers with null values so HttpParser can avoid looking up name again for unknown values
for (HttpHeader h:HttpHeader.values())
if (!CACHE.put(new HttpField(h,(String)null)))
throw new IllegalStateException("CACHE FULL");
// Add some more common headers
CACHE.put(new HttpField(HttpHeader.REFERER,(String)null));
CACHE.put(new HttpField(HttpHeader.IF_MODIFIED_SINCE,(String)null));
CACHE.put(new HttpField(HttpHeader.IF_NONE_MATCH,(String)null));
CACHE.put(new HttpField(HttpHeader.AUTHORIZATION,(String)null));
CACHE.put(new HttpField(HttpHeader.COOKIE,(String)null));
}
/* ------------------------------------------------------------------------------- */
public HttpParser(RequestHandler<ByteBuffer> handler)
{
@ -801,7 +870,7 @@ public class HttpParser
if (add_to_connection_trie && !_connectionFields.isFull() && _header!=null && _valueString!=null)
{
_field=new HttpField.CachedHttpField(_header,_valueString);
_field=new HttpField(_header,_valueString);
_connectionFields.put(_field);
}
@ -944,7 +1013,7 @@ public class HttpParser
// Try a look ahead for the known header name and value.
HttpField field=_connectionFields==null?null:_connectionFields.getBest(buffer,-1,buffer.remaining());
if (field==null)
field=HttpField.CACHE.getBest(buffer,-1,buffer.remaining());
field=CACHE.getBest(buffer,-1,buffer.remaining());
if (field!=null)
{
@ -1547,5 +1616,4 @@ public class HttpParser
{
return _connectionFields;
}
}

View File

@ -136,10 +136,10 @@ public class MimeTypes
try
{
ResourceBundle mime = ResourceBundle.getBundle("org/eclipse/jetty/http/mime");
Enumeration i = mime.getKeys();
Enumeration<String> i = mime.getKeys();
while(i.hasMoreElements())
{
String ext = (String)i.nextElement();
String ext = i.nextElement();
String m = mime.getString(ext);
__dftMimeMap.put(StringUtil.asciiToLowerCase(ext),normalizeMimeType(m));
}

View File

@ -18,6 +18,7 @@
package org.eclipse.jetty.http;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@ -39,6 +40,7 @@ import org.eclipse.jetty.util.URIUtil;
* /foo/* - a prefix path specification (must end '/*').
* *.ext - a suffix path specification.
* / - the default path specification.
* "" - the / path specification
* </PRE>
* Matching is performed in the following order <NL>
* <LI>Exact match.
@ -267,20 +269,22 @@ public class PathMap<O> extends HashMap<String,O>
/** Get all entries matched by the path.
* Best match first.
* @param path Path to match
* @return LazyList of Map.Entry instances key=pathSpec
* @return List of Map.Entry instances key=pathSpec
*/
public Object getLazyMatches(String path)
public List<? extends Map.Entry<String,O>> getMatches(String path)
{
MappedEntry<O> entry;
Object entries=null;
List<MappedEntry<O>> entries=new ArrayList<>();
if (path==null)
return LazyList.getList(entries);
return entries;
if (path.length()==0)
return _defaultSingletonList;
// try exact match
entry=_exactMap.get(path);
if (entry!=null)
entries=LazyList.add(entries,entry);
entries.add(entry);
// prefix search
int l=path.length();
@ -293,14 +297,14 @@ public class PathMap<O> extends HashMap<String,O>
break;
String key = entry.getKey();
if (key.length()-2>=path.length() || path.charAt(key.length()-2)=='/')
entries=LazyList.add(entries,entry);
entries.add(entry);
i=key.length()-3;
}
// Prefix Default
if (_prefixDefault!=null)
entries=LazyList.add(entries,_prefixDefault);
entries.add(_prefixDefault);
// Extension search
i=0;
@ -309,32 +313,24 @@ public class PathMap<O> extends HashMap<String,O>
{
entry=suffix_map.get(path,i+1,l-i-1);
if (entry!=null)
entries=LazyList.add(entries,entry);
entries.add(entry);
}
// root match
if ("/".equals(path))
{
entry=_exactMap.get("");
if (entry!=null)
entries.add(entry);
}
// Default
if (_default!=null)
{
// Optimization for just the default
if (entries==null)
return _defaultSingletonList;
entries=LazyList.add(entries,_default);
}
entries.add(_default);
return entries;
}
/* --------------------------------------------------------------- */
/** Get all entries matched by the path.
* Best match first.
* @param path Path to match
* @return List of Map.Entry instances key=pathSpec
*/
public List<Map.Entry<String,O>> getMatches(String path)
{
return LazyList.getList(getLazyMatches(path));
}
/* --------------------------------------------------------------- */
/** Return whether the path matches any entries in the PathMap,
@ -381,6 +377,7 @@ public class PathMap<O> extends HashMap<String,O>
_suffixMap=new ArrayTernaryTrie<>(false);
_default=null;
_defaultSingletonList=null;
_prefixDefault=null;
super.clear();
}

View File

@ -83,7 +83,7 @@ public class HttpFieldsTest
ByteBuffer buffer=BufferUtil.allocate(1024);
BufferUtil.flipToFill(buffer);
header.putTo(buffer);
HttpGenerator.putTo(header,buffer);
BufferUtil.flipToFlush(buffer,0);
String result=BufferUtil.toString(buffer);
@ -117,7 +117,7 @@ public class HttpFieldsTest
ByteBuffer buffer = BufferUtil.allocate(1024);
BufferUtil.flipToFill(buffer);
header.putTo(buffer);
HttpGenerator.putTo(header,buffer);
BufferUtil.flipToFlush(buffer,0);
String out = BufferUtil.toString(buffer);
assertThat(out,containsString("name0: value??0"));
@ -136,7 +136,7 @@ public class HttpFieldsTest
ByteBuffer buffer = BufferUtil.allocate(1024);
BufferUtil.flipToFill(buffer);
header.putTo(buffer);
HttpGenerator.putTo(header,buffer);
BufferUtil.flipToFlush(buffer,0);
String out = BufferUtil.toString(buffer).toLowerCase();
@ -268,123 +268,6 @@ public class HttpFieldsTest
@Test
public void testSetCookie() throws Exception
{
HttpFields fields = new HttpFields();
fields.addSetCookie("null",null,null,null,-1,null,false,false,-1);
assertEquals("null=",fields.getStringField("Set-Cookie"));
fields.clear();
fields.addSetCookie("minimal","value",null,null,-1,null,false,false,-1);
assertEquals("minimal=value",fields.getStringField("Set-Cookie"));
fields.clear();
//test cookies with same name, domain and path, only 1 allowed
fields.addSetCookie("everything","wrong","domain","path",0,"to be replaced",true,true,0);
fields.addSetCookie("everything","value","domain","path",0,"comment",true,true,0);
assertEquals("everything=value;Version=1;Path=path;Domain=domain;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",fields.getStringField("Set-Cookie"));
Enumeration<String> e =fields.getValues("Set-Cookie");
assertTrue(e.hasMoreElements());
assertEquals("everything=value;Version=1;Path=path;Domain=domain;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement());
assertFalse(e.hasMoreElements());
assertEquals("Thu, 01 Jan 1970 00:00:00 GMT",fields.getStringField("Expires"));
assertFalse(e.hasMoreElements());
//test cookies with same name, different domain
fields.clear();
fields.addSetCookie("everything","other","domain1","path",0,"blah",true,true,0);
fields.addSetCookie("everything","value","domain2","path",0,"comment",true,true,0);
e =fields.getValues("Set-Cookie");
assertTrue(e.hasMoreElements());
assertEquals("everything=other;Version=1;Path=path;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=blah",e.nextElement());
assertTrue(e.hasMoreElements());
assertEquals("everything=value;Version=1;Path=path;Domain=domain2;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement());
assertFalse(e.hasMoreElements());
//test cookies with same name, same path, one with domain, one without
fields.clear();
fields.addSetCookie("everything","other","domain1","path",0,"blah",true,true,0);
fields.addSetCookie("everything","value","","path",0,"comment",true,true,0);
e =fields.getValues("Set-Cookie");
assertTrue(e.hasMoreElements());
assertEquals("everything=other;Version=1;Path=path;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=blah",e.nextElement());
assertTrue(e.hasMoreElements());
assertEquals("everything=value;Version=1;Path=path;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement());
assertFalse(e.hasMoreElements());
//test cookies with same name, different path
fields.clear();
fields.addSetCookie("everything","other","domain1","path1",0,"blah",true,true,0);
fields.addSetCookie("everything","value","domain1","path2",0,"comment",true,true,0);
e =fields.getValues("Set-Cookie");
assertTrue(e.hasMoreElements());
assertEquals("everything=other;Version=1;Path=path1;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=blah",e.nextElement());
assertTrue(e.hasMoreElements());
assertEquals("everything=value;Version=1;Path=path2;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement());
assertFalse(e.hasMoreElements());
//test cookies with same name, same domain, one with path, one without
fields.clear();
fields.addSetCookie("everything","other","domain1","path1",0,"blah",true,true,0);
fields.addSetCookie("everything","value","domain1","",0,"comment",true,true,0);
e =fields.getValues("Set-Cookie");
assertTrue(e.hasMoreElements());
assertEquals("everything=other;Version=1;Path=path1;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=blah",e.nextElement());
assertTrue(e.hasMoreElements());
assertEquals("everything=value;Version=1;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement());
assertFalse(e.hasMoreElements());
//test cookies same name only, no path, no domain
fields.clear();
fields.addSetCookie("everything","other","","",0,"blah",true,true,0);
fields.addSetCookie("everything","value","","",0,"comment",true,true,0);
e =fields.getValues("Set-Cookie");
assertTrue(e.hasMoreElements());
assertEquals("everything=value;Version=1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement());
assertFalse(e.hasMoreElements());
fields.clear();
fields.addSetCookie("ev erything","va lue","do main","pa th",1,"co mment",true,true,1);
String setCookie=fields.getStringField("Set-Cookie");
assertThat(setCookie,Matchers.startsWith("\"ev erything\"=\"va lue\";Version=1;Path=\"pa th\";Domain=\"do main\";Expires="));
assertThat(setCookie,Matchers.endsWith(" GMT;Max-Age=1;Secure;HttpOnly;Comment=\"co mment\""));
fields.clear();
fields.addSetCookie("name","value",null,null,-1,null,false,false,0);
setCookie=fields.getStringField("Set-Cookie");
assertEquals(-1,setCookie.indexOf("Version="));
fields.clear();
fields.addSetCookie("name","v a l u e",null,null,-1,null,false,false,0);
setCookie=fields.getStringField("Set-Cookie");
fields.clear();
fields.addSetCookie("json","{\"services\":[\"cwa\", \"aa\"]}",null,null,-1,null,false,false,-1);
assertEquals("json=\"{\\\"services\\\":[\\\"cwa\\\", \\\"aa\\\"]}\"",fields.getStringField("Set-Cookie"));
fields.clear();
fields.addSetCookie("name","value","domain",null,-1,null,false,false,-1);
fields.addSetCookie("name","other","domain",null,-1,null,false,false,-1);
assertEquals("name=other;Domain=domain",fields.getStringField("Set-Cookie"));
fields.addSetCookie("name","more","domain",null,-1,null,false,false,-1);
assertEquals("name=more;Domain=domain",fields.getStringField("Set-Cookie"));
fields.addSetCookie("foo","bar","domain",null,-1,null,false,false,-1);
fields.addSetCookie("foo","bob","domain",null,-1,null,false,false,-1);
assertEquals("name=more;Domain=domain",fields.getStringField("Set-Cookie"));
e=fields.getValues("Set-Cookie");
assertEquals("name=more;Domain=domain",e.nextElement());
assertEquals("foo=bob;Domain=domain",e.nextElement());
fields=new HttpFields();
fields.addSetCookie("name","value%=",null,null,-1,null,false,false,0);
setCookie=fields.getStringField("Set-Cookie");
assertEquals("name=value%=",setCookie);
}
private Set<String> enum2set(Enumeration<String> e)
{

View File

@ -372,7 +372,7 @@ public class HttpGeneratorServerTest
assertEquals(HttpGenerator.State.START, gen.getState());
ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_1, new HttpFields(), -1, 200, null, false);
info.getHttpFields().add("Last-Modified", HttpFields.__01Jan1970);
info.getHttpFields().add("Last-Modified", DateGenerator.__01Jan1970);
result = gen.generateResponse(info, null, null, null, true);
assertEquals(HttpGenerator.Result.NEED_HEADER, result);
@ -441,7 +441,7 @@ public class HttpGeneratorServerTest
assertEquals(HttpGenerator.State.START, gen.getState());
ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_1, new HttpFields(), -1, 200, null, false);
info.getHttpFields().add("Last-Modified", HttpFields.__01Jan1970);
info.getHttpFields().add("Last-Modified", DateGenerator.__01Jan1970);
result = gen.generateResponse(info, null, null, content0, false);
assertEquals(HttpGenerator.Result.NEED_HEADER, result);
assertEquals(HttpGenerator.State.START, gen.getState());
@ -503,7 +503,7 @@ public class HttpGeneratorServerTest
assertEquals(HttpGenerator.State.START, gen.getState());
ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_1, new HttpFields(), 59, 200, null, false);
info.getHttpFields().add("Last-Modified", HttpFields.__01Jan1970);
info.getHttpFields().add("Last-Modified", DateGenerator.__01Jan1970);
result = gen.generateResponse(info, null, null, content0, false);
assertEquals(HttpGenerator.Result.NEED_HEADER, result);
assertEquals(HttpGenerator.State.START, gen.getState());
@ -570,7 +570,7 @@ public class HttpGeneratorServerTest
assertEquals(HttpGenerator.State.START, gen.getState());
ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_1, new HttpFields(), 59, 200, null, false);
info.getHttpFields().add("Last-Modified", HttpFields.__01Jan1970);
info.getHttpFields().add("Last-Modified", DateGenerator.__01Jan1970);
result = gen.generateResponse(info, null, null, content0, false);
assertEquals(HttpGenerator.Result.NEED_HEADER, result);
assertEquals(HttpGenerator.State.START, gen.getState());

View File

@ -22,6 +22,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.Ignore;
import org.junit.Test;
/**
@ -30,6 +31,7 @@ import org.junit.Test;
public class PathMapTest
{
@Test
@Ignore
public void testPathMap() throws Exception
{
PathMap<String> p = new PathMap<>();
@ -65,6 +67,7 @@ public class PathMapTest
{ "/animal/path.gz", "5"},
{ "/Other/path", "8"},
{ "/\u20ACuro/path", "11"},
{ "/", "10"},
};
for (String[] test : tests)
@ -78,8 +81,8 @@ public class PathMapTest
p.getMatches("/animal/bird/path.tar.gz").toString());
assertEquals("Dir matches", "[/animal/fish/*=4, /animal/*=5, /=8]", p.getMatches("/animal/fish/").toString());
assertEquals("Dir matches", "[/animal/fish/*=4, /animal/*=5, /=8]", p.getMatches("/animal/fish").toString());
assertEquals("Dir matches", "[/=8]", p.getMatches("/").toString());
assertEquals("Dir matches", "[=10, /=8]", p.getMatches("").toString());
assertEquals("Root matches", "[=10, /=8]",p.getMatches("/").toString());
assertEquals("Dir matches", "[/=8]", p.getMatches("").toString());
assertEquals("pathMatch exact", "/Foo/bar", PathMap.pathMatch("/Foo/bar", "/Foo/bar"));
assertEquals("pathMatch prefix", "/Foo", PathMap.pathMatch("/Foo/*", "/Foo/bar"));

View File

@ -35,7 +35,7 @@ import org.eclipse.jetty.util.thread.Scheduler;
* Channel End Point.
* <p>Holds the channel and socket for an NIO endpoint.
*/
public class ChannelEndPoint extends AbstractEndPoint implements SocketBased
public class ChannelEndPoint extends AbstractEndPoint
{
private static final Logger LOG = Log.getLogger(ChannelEndPoint.class);
@ -208,7 +208,6 @@ public class ChannelEndPoint extends AbstractEndPoint implements SocketBased
return _channel;
}
@Override
public Socket getSocket()
{
return _socket;

View File

@ -0,0 +1,89 @@
//
// ========================================================================
// Copyright (c) 1995-2013 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.io;
import java.io.IOException;
import java.util.Map;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
/**
* Factory for client-side {@link Connection} instances.
*/
public interface ClientConnectionFactory
{
/**
*
* @param endPoint the {@link org.eclipse.jetty.io.EndPoint} to link the newly created connection to
* @param context the context data to create the connection
* @return a new {@link Connection}
* @throws IOException if the connection cannot be created
*/
public Connection newConnection(EndPoint endPoint, Map<String, Object> context) throws IOException;
public static class Helper
{
private static Logger LOG = Log.getLogger(Helper.class);
private Helper()
{
}
/**
* Replaces the given {@code oldConnection} with the given {@code newConnection} on the
* {@link EndPoint} associated with {@code oldConnection}, performing connection lifecycle management.
* <p />
* The {@code oldConnection} will be closed by invoking {@link org.eclipse.jetty.io.Connection#onClose()}
* and the {@code newConnection} will be opened by invoking {@link org.eclipse.jetty.io.Connection#onOpen()}.
* @param oldConnection the old connection to replace
* @param newConnection the new connection replacement
*/
public static void replaceConnection(Connection oldConnection, Connection newConnection)
{
close(oldConnection);
oldConnection.getEndPoint().setConnection(newConnection);
open(newConnection);
}
private static void open(Connection connection)
{
try
{
connection.onOpen();
}
catch (Throwable x)
{
LOG.debug(x);
}
}
private static void close(Connection connection)
{
try
{
connection.onClose();
}
catch (Throwable x)
{
LOG.debug(x);
}
}
}
}

View File

@ -133,7 +133,7 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements SelectorMa
if (_interestOps.compareAndSet(oldInterestOps, newInterestOps))
{
LOG.debug("Local interests updated {} -> {} for {}", oldInterestOps, newInterestOps, this);
_selector.submit(_updateTask);
_selector.updateKey(_updateTask);
}
else
{
@ -152,7 +152,6 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements SelectorMa
private void setKeyInterests(int oldInterestOps, int newInterestOps)
{
assert _selector.isSelectorThread();
LOG.debug("Key interests updated {} -> {}", oldInterestOps, newInterestOps);
_key.interestOps(newInterestOps);
}

View File

@ -59,11 +59,14 @@ import org.eclipse.jetty.util.thread.Scheduler;
public abstract class SelectorManager extends AbstractLifeCycle implements Dumpable
{
protected static final Logger LOG = Log.getLogger(SelectorManager.class);
public static final String SUBMIT_KEY_UPDATES="org.eclipse.jetty.io.SelectorManager.submitKeyUpdates";
/**
* The default connect timeout, in milliseconds
*/
public static final int DEFAULT_CONNECT_TIMEOUT = 15000;
private final static boolean __submitKeyUpdates=Boolean.valueOf(System.getProperty(SUBMIT_KEY_UPDATES,"FALSE"));
private final Executor executor;
private final Scheduler scheduler;
private final ManagedSelector[] _selectors;
@ -356,6 +359,25 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
LOG.debug("Stopped {}", this);
}
/**
* Submit a task to update a selector key. If the System property {@link SelectorManager#SUBMIT_KEY_UPDATES}
* is set true (default is false), the task is passed to {@link #submit(Runnable)}. Otherwise it is run immediately and the selector
* woken up if need be.
* @param update the update to a key
*/
public void updateKey(Runnable update)
{
if (__submitKeyUpdates)
submit(update);
else
{
update.run();
if (_state.compareAndSet(State.SELECT, State.WAKEUP))
wakeup();
}
}
/**
* <p>Submits a change to be executed in the selector thread.</p>
* <p>Changes may be submitted from any thread, and the selector thread woken up
@ -748,14 +770,30 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
}
catch (ClosedSelectorException | ClosedChannelException x)
{
LOG.debug(x);
failed(x);
}
}
protected void failed(Throwable failure)
{
if (failed.compareAndSet(false, true))
{
timeout.cancel();
close();
connectionFailed(channel, failure, attachment);
}
}
private void close()
{
try
{
channel.close();
}
catch (IOException x)
{
LOG.ignore(x);
}
}
}
@ -775,19 +813,7 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
if (channel.isConnectionPending())
{
LOG.debug("Channel {} timed out while connecting, closing it", channel);
try
{
// This will unregister the channel from the selector
channel.close();
}
catch (IOException x)
{
LOG.ignore(x);
}
finally
{
connect.failed(new SocketTimeoutException());
}
connect.failed(new SocketTimeoutException());
}
}
}

View File

@ -0,0 +1,72 @@
//
// ========================================================================
// Copyright (c) 1995-2013 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.io.ssl;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.Executor;
import javax.net.ssl.SSLEngine;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.ClientConnectionFactory;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.ssl.SslContextFactory;
public class SslClientConnectionFactory implements ClientConnectionFactory
{
public static final String SSL_PEER_HOST_CONTEXT_KEY = "ssl.peer.host";
public static final String SSL_PEER_PORT_CONTEXT_KEY = "ssl.peer.port";
public static final String SSL_ENGINE_CONTEXT_KEY = "ssl.engine";
private final SslContextFactory sslContextFactory;
private final ByteBufferPool byteBufferPool;
private final Executor executor;
private final ClientConnectionFactory connectionFactory;
public SslClientConnectionFactory(SslContextFactory sslContextFactory, ByteBufferPool byteBufferPool, Executor executor, ClientConnectionFactory connectionFactory)
{
this.sslContextFactory = sslContextFactory;
this.byteBufferPool = byteBufferPool;
this.executor = executor;
this.connectionFactory = connectionFactory;
}
@Override
public org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, Map<String, Object> context) throws IOException
{
String host = (String)context.get(SSL_PEER_HOST_CONTEXT_KEY);
int port = (Integer)context.get(SSL_PEER_PORT_CONTEXT_KEY);
SSLEngine engine = sslContextFactory.newSSLEngine(host, port);
engine.setUseClientMode(true);
context.put(SSL_ENGINE_CONTEXT_KEY, engine);
SslConnection sslConnection = newSslConnection(byteBufferPool, executor, endPoint, engine);
sslConnection.setRenegotiationAllowed(sslContextFactory.isRenegotiationAllowed());
endPoint.setConnection(sslConnection);
EndPoint appEndPoint = sslConnection.getDecryptedEndPoint();
appEndPoint.setConnection(connectionFactory.newConnection(appEndPoint, context));
return sslConnection;
}
protected SslConnection newSslConnection(ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, SSLEngine engine)
{
return new SslConnection(byteBufferPool, executor, endPoint, engine);
}
}

View File

@ -107,21 +107,6 @@ public class SslConnection extends AbstractConnection
this._bufferPool = byteBufferPool;
this._sslEngine = sslEngine;
this._decryptedEndPoint = newDecryptedEndPoint();
// commented out for now as it might cause native code being stuck in preClose0.
// See: https://java.net/jira/browse/GRIZZLY-547
// if (endPoint instanceof SocketBased)
// {
// try
// {
// ((SocketBased)endPoint).getSocket().setSoLinger(true, 30000);
// }
// catch (SocketException e)
// {
// throw new RuntimeIOException(e);
// }
// }
}
protected DecryptedEndPoint newDecryptedEndPoint()
@ -193,8 +178,8 @@ public class SslConnection extends AbstractConnection
// We have received a close handshake, close the end point to send FIN.
if (_decryptedEndPoint.isInputShutdown())
getEndPoint().close();
_decryptedEndPoint.close();
// wake up whoever is doing the fill or the flush so they can
// do all the filling, unwrapping, wrapping and flushing
_decryptedEndPoint.getFillInterest().fillable();
@ -312,7 +297,7 @@ public class SslConnection extends AbstractConnection
fail_filler = true;
}
}
final boolean filler_failed=fail_filler;
getExecutor().execute(new Runnable()
@ -508,150 +493,145 @@ public class SslConnection extends AbstractConnection
int net_filled = getEndPoint().fill(_encryptedInput);
if (DEBUG)
LOG.debug("{} filled {} encrypted bytes", SslConnection.this, net_filled);
if (net_filled > 0)
_underFlown = false;
// Let's try the SSL thang even if we have no net data because in that
// case we want to fall through to the handshake handling
int pos = BufferUtil.flipToFill(app_in);
SSLEngineResult unwrapResult = _sslEngine.unwrap(_encryptedInput, app_in);
BufferUtil.flipToFlush(app_in, pos);
if (DEBUG)
LOG.debug("{} unwrap {}", SslConnection.this, unwrapResult);
Status unwrapResultStatus = unwrapResult.getStatus();
HandshakeStatus unwrapHandshakeStatus = unwrapResult.getHandshakeStatus();
HandshakeStatus handshakeStatus = _sslEngine.getHandshakeStatus();
// and deal with the results
switch (unwrapResultStatus)
decryption: while (true)
{
case BUFFER_OVERFLOW:
throw new IllegalStateException();
// Let's unwrap even if we have no net data because in that
// case we want to fall through to the handshake handling
int pos = BufferUtil.flipToFill(app_in);
SSLEngineResult unwrapResult = _sslEngine.unwrap(_encryptedInput, app_in);
BufferUtil.flipToFlush(app_in, pos);
if (DEBUG)
LOG.debug("{} unwrap {}", SslConnection.this, unwrapResult);
case CLOSED:
// Dang! we have to care about the handshake state specially for close
switch (handshakeStatus)
{
case NOT_HANDSHAKING:
// We were not handshaking, so just tell the app we are closed
return -1;
HandshakeStatus handshakeStatus = _sslEngine.getHandshakeStatus();
HandshakeStatus unwrapHandshakeStatus = unwrapResult.getHandshakeStatus();
Status unwrapResultStatus = unwrapResult.getStatus();
case NEED_TASK:
// run the task
_sslEngine.getDelegatedTask().run();
continue;
_underFlown = unwrapResultStatus == Status.BUFFER_UNDERFLOW;
case NEED_WRAP:
// we need to send some handshake data (probably to send a close handshake).
// but that will not enable any extra data to fill, so we just return -1
// The wrapping can be done by any output drivers doing flushing or shutdown output.
return -1;
}
throw new IllegalStateException();
default:
if (unwrapHandshakeStatus == HandshakeStatus.FINISHED && !_handshaken)
{
_handshaken = true;
if (DEBUG)
LOG.debug("{} handshake completed client-side", SslConnection.this);
}
// Check whether renegotiation is allowed
if (_handshaken && handshakeStatus != HandshakeStatus.NOT_HANDSHAKING && !isRenegotiationAllowed())
{
if (DEBUG)
LOG.debug("{} renegotiation denied", SslConnection.this);
if (_underFlown)
{
if (net_filled < 0)
closeInbound();
return -1;
}
if (net_filled <= 0)
return net_filled;
}
if (unwrapResultStatus == Status.BUFFER_UNDERFLOW)
_underFlown = true;
// If bytes were produced, don't bother with the handshake status;
// pass the decrypted data to the application, which will perform
// another call to fill() or flush().
if (unwrapResult.bytesProduced() > 0)
switch (unwrapResultStatus)
{
case CLOSED:
{
if (app_in == buffer)
return unwrapResult.bytesProduced();
return BufferUtil.flipPutFlip(_decryptedInput, buffer);
}
// Dang! we have to care about the handshake state
switch (handshakeStatus)
{
case NOT_HANDSHAKING:
// we just didn't read anything.
if (net_filled < 0)
switch (handshakeStatus)
{
case NOT_HANDSHAKING:
{
closeInbound();
// We were not handshaking, so just tell the app we are closed
return -1;
}
return 0;
case NEED_TASK:
// run the task
_sslEngine.getDelegatedTask().run();
continue;
case NEED_WRAP:
// we need to send some handshake data
// if we are called from flush
if (buffer == __FLUSH_CALLED_FILL)
return 0; // let it do the wrapping
_fillRequiresFlushToProgress = true;
flush(__FILL_CALLED_FLUSH);
if (BufferUtil.isEmpty(_encryptedOutput))
case NEED_TASK:
{
// the flush completed so continue
_fillRequiresFlushToProgress = false;
_sslEngine.getDelegatedTask().run();
continue;
}
return 0;
case NEED_UNWRAP:
// if we just filled some net data
if (net_filled < 0)
case NEED_WRAP:
{
closeInbound();
// We need to send some handshake data (probably the close handshake).
// We return -1 so that the application can drive the close by flushing
// or shutting down the output.
return -1;
}
else if (net_filled > 0)
default:
{
// maybe we will fill some more on a retry
throw new IllegalStateException();
}
}
}
case BUFFER_UNDERFLOW:
case OK:
{
if (unwrapHandshakeStatus == HandshakeStatus.FINISHED && !_handshaken)
{
_handshaken = true;
if (DEBUG)
LOG.debug("{} {} handshake completed", SslConnection.this,
_sslEngine.getUseClientMode() ? "client-side" : "resumed session server-side");
}
// Check whether renegotiation is allowed
if (_handshaken && handshakeStatus != HandshakeStatus.NOT_HANDSHAKING && !isRenegotiationAllowed())
{
if (DEBUG)
LOG.debug("{} renegotiation denied", SslConnection.this);
closeInbound();
return -1;
}
// If bytes were produced, don't bother with the handshake status;
// pass the decrypted data to the application, which will perform
// another call to fill() or flush().
if (unwrapResult.bytesProduced() > 0)
{
if (app_in == buffer)
return unwrapResult.bytesProduced();
return BufferUtil.flipPutFlip(_decryptedInput, buffer);
}
switch (handshakeStatus)
{
case NOT_HANDSHAKING:
{
if (_underFlown)
break decryption;
continue;
}
else
case NEED_TASK:
{
if (_encryptedInput.hasRemaining())
_sslEngine.getDelegatedTask().run();
continue;
}
case NEED_WRAP:
{
// If we are called from flush()
// return to let it do the wrapping.
if (buffer == __FLUSH_CALLED_FILL)
return 0;
_fillRequiresFlushToProgress = true;
flush(__FILL_CALLED_FLUSH);
if (BufferUtil.isEmpty(_encryptedOutput))
{
// if there are more encrypted bytes,
// then we need to unwrap more, we don't
// care if net_filled is zero
// The flush wrote all the encrypted bytes so continue to fill
_fillRequiresFlushToProgress = false;
continue;
}
// we need to wait for more net data
return 0;
else
{
// The flush did not complete, return from fill()
// and let the write completion mechanism to kick in.
return 0;
}
}
case FINISHED:
throw new IllegalStateException();
case NEED_UNWRAP:
{
if (_underFlown)
break decryption;
continue;
}
default:
{
throw new IllegalStateException();
}
}
}
default:
{
throw new IllegalStateException();
}
}
}
}
}
catch (SSLException e)
{
getEndPoint().close();
throw new EofException(e);
}
catch (Exception e)
{
getEndPoint().close();
@ -778,7 +758,7 @@ public class SslConnection extends AbstractConnection
{
_handshaken = true;
if (DEBUG)
LOG.debug("{} handshake completed server-side", SslConnection.this);
LOG.debug("{} {} handshake completed", SslConnection.this, "server-side");
}
HandshakeStatus handshakeStatus = _sslEngine.getHandshakeStatus();
@ -824,7 +804,7 @@ public class SslConnection extends AbstractConnection
if (handshakeStatus == HandshakeStatus.NEED_WRAP)
continue;
}
return allConsumed&&BufferUtil.isEmpty(_encryptedOutput);
return allConsumed && BufferUtil.isEmpty(_encryptedOutput);
case FINISHED:
throw new IllegalStateException();
@ -860,17 +840,18 @@ public class SslConnection extends AbstractConnection
public void shutdownOutput()
{
boolean ishut = isInputShutdown();
boolean oshut = isOutputShutdown();
if (DEBUG)
LOG.debug("{} shutdownOutput: oshut={}, ishut={}", SslConnection.this, isOutputShutdown(), ishut);
LOG.debug("{} shutdownOutput: oshut={}, ishut={}", SslConnection.this, oshut, ishut);
if (ishut)
{
// Aggressively close, since inbound close alert has already been processed
// and the TLS specification allows to close the connection directly, which
// is what most other implementations expect: a FIN rather than a TLS close
// reply. If a TLS close reply is sent, most implementation send a RST.
// reply. If a TLS close reply is sent, most implementations send a RST.
getEndPoint().close();
}
else
else if (!oshut)
{
try
{
@ -895,6 +876,8 @@ public class SslConnection extends AbstractConnection
@Override
public void close()
{
// First send the TLS Close Alert, then the FIN
shutdownOutput();
getEndPoint().close();
}

View File

@ -26,6 +26,7 @@ import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import org.eclipse.jetty.toolchain.test.annotation.Slow;
import org.eclipse.jetty.util.Callback;
@ -67,6 +68,7 @@ public class SelectorManagerTest
client.configureBlocking(false);
client.connect(address);
final AtomicBoolean timeoutConnection = new AtomicBoolean();
final long connectTimeout = 1000;
SelectorManager selectorManager = new SelectorManager(executor, scheduler)
{
@ -81,7 +83,8 @@ public class SelectorManagerTest
{
try
{
TimeUnit.MILLISECONDS.sleep(connectTimeout * 2);
if (timeoutConnection.get())
TimeUnit.MILLISECONDS.sleep(connectTimeout * 2);
return super.finishConnect(channel);
}
catch (InterruptedException e)
@ -113,17 +116,30 @@ public class SelectorManagerTest
try
{
final CountDownLatch latch = new CountDownLatch(1);
timeoutConnection.set(true);
final CountDownLatch latch1 = new CountDownLatch(1);
selectorManager.connect(client, new Callback.Adapter()
{
@Override
public void failed(Throwable x)
{
latch.countDown();
latch1.countDown();
}
});
Assert.assertTrue(latch1.await(connectTimeout * 3, TimeUnit.MILLISECONDS));
Assert.assertTrue(latch.await(connectTimeout * 3, TimeUnit.MILLISECONDS));
// Verify that after the failure we can connect successfully
timeoutConnection.set(false);
final CountDownLatch latch2 = new CountDownLatch(1);
selectorManager.connect(client, new Callback.Adapter()
{
@Override
public void failed(Throwable x)
{
latch2.countDown();
}
});
Assert.assertTrue(latch2.await(connectTimeout, TimeUnit.MILLISECONDS));
}
finally
{

View File

@ -1,17 +1,16 @@
#
# JAAS Feature
# JAAS Module
#
[depend]
server
[lib]
# JAAS jars
lib/jetty-jaas-${jetty.version}.jar
[xml]
# JAAS configuration
etc/jetty-jaas.xml
[ini-template]
## JAAS Configuration
jaas.login.conf=etc/login.conf

View File

@ -1,6 +1,7 @@
#
# Jetty JASPI Module
#
[depend]
security

View File

@ -0,0 +1,27 @@
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
<Configure id="Server" class="org.eclipse.jetty.server.Server">
<!-- Add a remote JMX connector. The parameters of the constructor
below specify the JMX service URL, and the object name string for the
connector server bean. The parameters of the JMXServiceURL constructor
specify the protocol that clients will use to connect to the remote JMX
connector (RMI), the hostname of the server (local hostname), port number
(automatically assigned), and the URL path. Note that URL path contains
the RMI registry hostname and port number, that may need to be modified
in order to comply with the firewall requirements.
-->
<New id="ConnectorServer" class="org.eclipse.jetty.jmx.ConnectorServer">
<Arg>
<New class="javax.management.remote.JMXServiceURL">
<Arg type="java.lang.String">rmi</Arg>
<Arg type="java.lang.String" />
<Arg type="java.lang.Integer"><SystemProperty name="jetty.jmxrmiport" default="1099"/></Arg>
<Arg type="java.lang.String">/jndi/rmi://<SystemProperty name="jetty.jmxrmihost" default="localhost"/>:<SystemProperty name="jetty.jmxrmiport" default="1099"/>/jmxrmi</Arg>
</New>
</Arg>
<Arg>org.eclipse.jetty.jmx:name=rmiconnectorserver</Arg>
<Call name="start" />
</New>
</Configure>

View File

@ -39,49 +39,5 @@
<New class="org.eclipse.jetty.util.log.Log" />
</Arg>
</Call>
<!-- In order to connect to the JMX server remotely from a different
process, possibly running on a different host, Jetty JMX module
can create a remote JMX connector. It requires RMI registry to
be started prior to creating the connector server because the
JMX specification uses RMI to facilitate connections.
-->
<!-- Optionally start the RMI registry. Normally RMI registry runs on
port 1099. The argument below can be changed in order to comply
with the firewall requirements.
-->
<!--
<Call name="createRegistry" class="java.rmi.registry.LocateRegistry">
<Arg type="java.lang.Integer"><SystemProperty name="jetty.jmxrmiport" default="1099"/></Arg>
<Call name="sleep" class="java.lang.Thread">
<Arg type="java.lang.Integer">1000</Arg>
</Call>
</Call>
-->
<!-- Optionally add a remote JMX connector. The parameters of the constructor
below specify the JMX service URL, and the object name string for the
connector server bean. The parameters of the JMXServiceURL constructor
specify the protocol that clients will use to connect to the remote JMX
connector (RMI), the hostname of the server (local hostname), port number
(automatically assigned), and the URL path. Note that URL path contains
the RMI registry hostname and port number, that may need to be modified
in order to comply with the firewall requirements.
-->
<!--
<New id="ConnectorServer" class="org.eclipse.jetty.jmx.ConnectorServer">
<Arg>
<New class="javax.management.remote.JMXServiceURL">
<Arg type="java.lang.String">rmi</Arg>
<Arg type="java.lang.String" />
<Arg type="java.lang.Integer"><SystemProperty name="jetty.jmxrmiport" default="1099"/></Arg>
<Arg type="java.lang.String">/jndi/rmi://<SystemProperty name="jetty.jmxrmihost" default="localhost"/>:<SystemProperty name="jetty.jmxrmiport" default="1099"/>/jmxrmi</Arg>
</New>
</Arg>
<Arg>org.eclipse.jetty.jmx:name=rmiconnectorserver</Arg>
<Call name="start" />
</New>
-->
</Configure>

View File

@ -0,0 +1,10 @@
#
# JMX Remote Module
#
[depend]
jmx
[xml]
etc/jetty-jmx-remote.xml

View File

@ -1,16 +1,18 @@
#
# JMX Feature
# JMX Module
#
[lib]
# JMX jars (as defined in start.config)
lib/jetty-jmx-${jetty.version}.jar
[xml]
# JMX configuration
etc/jetty-jmx.xml
[ini-template]
## JMX Configuration
## Enable the "jmx-remote" module for an open port accessible by remote machines
# jetty.jmxrmihost=localhost
# jetty.jmxrmiport=1099
## Strictly speaking you shouldn't need --exec to use this in most environments.
## If this isn't working, make sure you enable --exec as well
# -Dcom.sun.management.jmxremote

View File

@ -9,5 +9,6 @@ servlet
lib/jsp/*.jar
[ini-template]
# JSP Configuration
# To use an non-jdk compiler for JSP compilation uncomment next line
#-Dorg.apache.jasper.compiler.disablejsr199=true
# -Dorg.apache.jasper.compiler.disablejsr199=true

View File

@ -163,12 +163,6 @@ public class JspcMojo extends AbstractMojo
*/
private boolean keepSources;
/**
* Default root package for all generated classes
*
* @parameter default-value="jsp"
*/
private String packageRoot;
/**
* Root directory for all html/jsp etc files
@ -209,48 +203,6 @@ public class JspcMojo extends AbstractMojo
*/
private File classesDirectory;
/**
* Whether or not to output more verbose messages during compilation.
*
* @parameter default-value="false";
*/
private boolean verbose;
/**
* If true, validates tlds when parsing.
*
* @parameter default-value="false";
*/
private boolean validateXml;
/**
* The encoding scheme to use.
*
* @parameter default-value="UTF-8"
*/
private String javaEncoding;
/**
* Whether or not to generate JSR45 compliant debug info
*
* @parameter default-value="true";
*/
private boolean suppressSmap;
/**
* Whether or not to ignore precompilation errors caused by jsp fragments.
*
* @parameter default-value="false"
*/
private boolean ignoreJspFragmentErrors;
/**
* Allows a prefix to be appended to the standard schema locations so that
* they can be loaded from elsewhere.
*
* @parameter
*/
private String schemaResourcePrefix;
/**
* Patterns of jars on the system path that contain tlds. Use | to separate each pattern.
@ -258,35 +210,32 @@ public class JspcMojo extends AbstractMojo
* @parameter default-value=".*taglibs[^/]*\.jar|.*jstl-impl[^/]*\.jar$
*/
private String tldJarNamePatterns;
/**
* Should white spaces in template text between actions or directives be trimmed? Defaults to false.
*
* The JspC instance being used to compile the jsps.
*
* @parameter
*/
private boolean trimSpaces = false;
private JspC jspc;
public void execute() throws MojoExecutionException, MojoFailureException
{
if (getLog().isDebugEnabled())
{
getLog().info("verbose=" + verbose);
getLog().info("webAppSourceDirectory=" + webAppSourceDirectory);
getLog().info("generatedClasses=" + generatedClasses);
getLog().info("webXmlFragment=" + webXmlFragment);
getLog().info("webXml="+webXml);
getLog().info("validateXml=" + validateXml);
getLog().info("packageRoot=" + packageRoot);
getLog().info("javaEncoding=" + javaEncoding);
getLog().info("insertionMarker="+ (insertionMarker == null || insertionMarker.equals("") ? END_OF_WEBAPP : insertionMarker));
getLog().info("keepSources=" + keepSources);
getLog().info("mergeFragment=" + mergeFragment);
getLog().info("suppressSmap=" + suppressSmap);
getLog().info("ignoreJspFragmentErrors=" + ignoreJspFragmentErrors);
getLog().info("schemaResourcePrefix=" + schemaResourcePrefix);
getLog().info("trimSpaces=" + trimSpaces);
getLog().info("mergeFragment=" + mergeFragment);
}
try
{
@ -338,22 +287,14 @@ public class JspcMojo extends AbstractMojo
}
Thread.currentThread().setContextClassLoader(webAppClassLoader);
JspC jspc = new JspC();
jspc.setWebXmlFragment(webXmlFragment);
jspc.setUriroot(webAppSourceDirectory);
jspc.setPackage(packageRoot);
jspc.setUriroot(webAppSourceDirectory);
jspc.setOutputDir(generatedClasses);
jspc.setValidateXml(validateXml);
jspc.setClassPath(webAppClassPath.toString());
jspc.setCompile(true);
jspc.setSmapSuppressed(suppressSmap);
jspc.setSmapDumped(!suppressSmap);
jspc.setJavaEncoding(javaEncoding);
jspc.setTrimSpaces(trimSpaces);
jspc.setSystemClassPath(sysClassPath);
// JspC#setExtensions() does not exist, so
// always set concrete list of files that will be processed.
@ -362,34 +303,8 @@ public class JspcMojo extends AbstractMojo
getLog().info("Includes="+includes);
getLog().info("Excludes="+excludes);
jspc.setJspFiles(jspFiles);
if (verbose)
{
getLog().info("Files selected to precompile: " + jspFiles);
}
try
{
jspc.setIgnoreJspFragmentErrors(ignoreJspFragmentErrors);
}
catch (NoSuchMethodError e)
{
getLog().debug("Tomcat Jasper does not support configuration option 'ignoreJspFragmentErrors': ignored");
}
try
{
if (schemaResourcePrefix != null)
jspc.setSchemaResourcePrefix(schemaResourcePrefix);
}
catch (NoSuchMethodError e)
{
getLog().debug("Tomcat Jasper does not support configuration option 'schemaResourcePrefix': ignored");
}
if (verbose)
jspc.setVerbose(99);
else
jspc.setVerbose(0);
getLog().info("Files selected to precompile: " + jspFiles);
jspc.execute();

View File

@ -68,7 +68,7 @@ import org.eclipse.jetty.util.IO;
* </p>
*
* @goal run-forked
* @requiresDependencyResolution compile+runtime
* @requiresDependencyResolution test
* @execute phase="test-compile"
* @description Runs Jetty in forked JVM on an unassembled webapp
*

View File

@ -29,6 +29,7 @@ import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.eclipse.jetty.annotations.AnnotationConfiguration;
import org.eclipse.jetty.plus.webapp.EnvConfiguration;
import org.eclipse.jetty.plus.webapp.PlusConfiguration;
import org.eclipse.jetty.servlet.FilterHolder;
@ -111,7 +112,7 @@ public class JettyWebAppContext extends WebAppContext
new FragmentConfiguration(),
_envConfig = new EnvConfiguration(),
new PlusConfiguration(),
new MavenAnnotationConfiguration(),
new AnnotationConfiguration(),
new JettyWebXmlConfiguration()
});
// Turn off copyWebInf option as it is not applicable for plugin.

View File

@ -1,93 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2013 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.maven.plugin;
import java.io.File;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.jetty.annotations.AnnotationConfiguration;
import org.eclipse.jetty.annotations.AnnotationParser;
import org.eclipse.jetty.annotations.AnnotationParser.Handler;
import org.eclipse.jetty.annotations.ClassNameResolver;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.webapp.MetaData;
import org.eclipse.jetty.webapp.WebAppContext;
public class MavenAnnotationConfiguration extends AnnotationConfiguration
{
private static final Logger LOG = Log.getLogger(MavenAnnotationConfiguration.class);
/* ------------------------------------------------------------ */
@Override
public void parseWebInfClasses(final WebAppContext context, final AnnotationParser parser) throws Exception
{
JettyWebAppContext jwac = (JettyWebAppContext)context;
if (jwac.getClassPathFiles() == null || jwac.getClassPathFiles().size() == 0)
super.parseWebInfClasses (context, parser);
else
{
LOG.debug("Scanning classes ");
//Look for directories on the classpath and process each one of those
MetaData metaData = context.getMetaData();
if (metaData == null)
throw new IllegalStateException ("No metadata");
Set<Handler> handlers = new HashSet<Handler>();
handlers.addAll(_discoverableAnnotationHandlers);
if (_classInheritanceHandler != null)
handlers.add(_classInheritanceHandler);
handlers.addAll(_containerInitializerAnnotationHandlers);
for (File f:jwac.getClassPathFiles())
{
//scan the equivalent of the WEB-INF/classes directory that has been synthesised by the plugin
if (f.isDirectory() && f.exists())
{
doParse(handlers, context, parser, Resource.newResource(f.toURI()));
}
}
//if an actual WEB-INF/classes directory also exists (eg because of overlayed wars) then scan that
//too
if (context.getWebInf() != null && context.getWebInf().exists())
{
Resource classesDir = context.getWebInf().addPath("classes/");
if (classesDir.exists())
{
doParse(handlers, context, parser, classesDir);
}
}
}
}
public void doParse (final Set<? extends Handler> handlers, final WebAppContext context, final AnnotationParser parser, Resource resource)
throws Exception
{
if (_parserTasks != null)
_parserTasks.add(new ParserTask(parser, handlers, resource, _webAppClassNameResolver));
else
parser.parse(handlers, resource, _webAppClassNameResolver);
}
}

View File

@ -238,6 +238,46 @@ public class MavenWebInfConfiguration extends WebInfConfiguration
}
/**
* Add in the classes dirs from test/classes and target/classes
* @see org.eclipse.jetty.webapp.WebInfConfiguration#findClassDirs(org.eclipse.jetty.webapp.WebAppContext)
*/
@Override
protected List<Resource> findClassDirs(WebAppContext context) throws Exception
{
List<Resource> list = new ArrayList<Resource>();
JettyWebAppContext jwac = (JettyWebAppContext)context;
if (jwac.getClassPathFiles() != null)
{
for (File f: jwac.getClassPathFiles())
{
if (f.exists() && f.isDirectory())
{
try
{
list.add(Resource.newResource(f.toURI()));
}
catch (Exception e)
{
LOG.warn("Bad url ", e);
}
}
}
}
List<Resource> classesDirs = super.findClassDirs(context);
if (classesDirs != null)
list.addAll(classesDirs);
return list;
}
protected Resource unpackOverlay (WebAppContext context, Overlay overlay)
throws IOException

View File

@ -179,8 +179,6 @@ public class AnnotationConfiguration extends org.eclipse.jetty.annotations.Annot
ClassNameResolver classNameResolver = createClassNameResolver(context);
if (_parserTasks != null)
_parserTasks.add(new BundleParserTask(parser, handlers, bundleRes, classNameResolver));
else
parser.parse(handlers, bundle, classNameResolver);
}
/**

View File

@ -9,5 +9,4 @@ deploy
lib/jetty-overlay-deployer-${jetty.version}.jar
[xml]
# Plus requires configuration
etc/jetty-overlay.xml

View File

@ -1,5 +1,5 @@
#
# Jetty Proxy module
# Jetty Plus module
#
[depend]
@ -11,5 +11,4 @@ jndi
lib/jetty-plus-${jetty.version}.jar
[xml]
# Plus requires configuration
etc/jetty-plus.xml

View File

@ -10,5 +10,4 @@ client
lib/jetty-proxy-${jetty.version}.jar
[xml]
# Proxy requires configuration
etc/jetty-proxy.xml

View File

@ -31,9 +31,9 @@ import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import javax.servlet.AsyncContext;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.UnavailableException;
import javax.servlet.http.HttpServlet;
@ -62,7 +62,7 @@ import org.eclipse.jetty.util.thread.QueuedThreadPool;
* <p/>
* To facilitate JMX monitoring, the {@link HttpClient} instance is set as context attribute,
* prefixed with the servlet's name and exposed by the mechanism provided by
* {@link ContextHandler#MANAGED_ATTRIBUTES}.
* {@link ServletContext#setAttribute(String, Object)}.
* <p/>
* The following init parameters may be used to configure the servlet:
* <ul>
@ -389,6 +389,7 @@ public class ProxyServlet extends HttpServlet
.version(HttpVersion.fromString(request.getProtocol()));
// Copy headers
boolean hasContent = false;
for (Enumeration<String> headerNames = request.getHeaderNames(); headerNames.hasMoreElements();)
{
String headerName = headerNames.nextElement();
@ -398,9 +399,13 @@ public class ProxyServlet extends HttpServlet
if (HOP_HEADERS.contains(lowerHeaderName))
continue;
if (_hostHeader!=null && lowerHeaderName.equals("host"))
if (_hostHeader != null && HttpHeader.HOST.is(headerName))
continue;
if (request.getContentLength() > 0 || request.getContentType() != null ||
HttpHeader.TRANSFER_ENCODING.is(headerName))
hasContent = true;
for (Enumeration<String> headerValues = request.getHeaders(headerName); headerValues.hasMoreElements();)
{
String headerValue = headerValues.nextElement();
@ -420,21 +425,24 @@ public class ProxyServlet extends HttpServlet
proxyRequest.header(HttpHeader.X_FORWARDED_HOST, request.getHeader(HttpHeader.HOST.asString()));
proxyRequest.header(HttpHeader.X_FORWARDED_SERVER, request.getLocalName());
proxyRequest.content(new InputStreamContentProvider(request.getInputStream())
if (hasContent)
{
@Override
public long getLength()
proxyRequest.content(new InputStreamContentProvider(request.getInputStream())
{
return request.getContentLength();
}
@Override
public long getLength()
{
return request.getContentLength();
}
@Override
protected ByteBuffer onRead(byte[] buffer, int offset, int length)
{
_log.debug("{} proxying content to upstream: {} bytes", requestId, length);
return super.onRead(buffer, offset, length);
}
});
@Override
protected ByteBuffer onRead(byte[] buffer, int offset, int length)
{
_log.debug("{} proxying content to upstream: {} bytes", requestId, length);
return super.onRead(buffer, offset, length);
}
});
}
final AsyncContext asyncContext = request.startAsync();
// We do not timeout the continuation, but the proxy request
@ -629,7 +637,12 @@ public class ProxyServlet extends HttpServlet
return null;
StringBuilder uri = new StringBuilder(_proxyTo);
uri.append(path.substring(_prefix.length()));
if (_proxyTo.endsWith("/"))
uri.setLength(uri.length() - 1);
String rest = path.substring(_prefix.length());
if (!rest.startsWith("/"))
uri.append("/");
uri.append(rest);
String query = request.getQueryString();
if (query != null)
uri.append("?").append(query);

View File

@ -28,10 +28,10 @@ import java.net.HttpCookie;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
@ -48,8 +48,8 @@ import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.HttpContentResponse;
import org.eclipse.jetty.client.HttpProxy;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.ProxyConfiguration;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.api.Result;
@ -110,7 +110,7 @@ public class ProxyServletTest
private HttpClient prepareClient() throws Exception
{
HttpClient result = new HttpClient();
result.setProxyConfiguration(new ProxyConfiguration("localhost", proxyConnector.getLocalPort()));
result.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", proxyConnector.getLocalPort()));
result.start();
return result;
}
@ -236,7 +236,7 @@ public class ProxyServletTest
prepareProxy(new ProxyServlet());
HttpClient result = new HttpClient();
result.setProxyConfiguration(new ProxyConfiguration("localhost", proxyConnector.getLocalPort()));
result.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", proxyConnector.getLocalPort()));
QueuedThreadPool threadPool = new QueuedThreadPool();
threadPool.setName("foo");
threadPool.setMaxThreads(20);
@ -246,7 +246,7 @@ public class ProxyServletTest
ContentResponse[] responses = new ContentResponse[10];
final byte[] content = new byte[1024];
Arrays.fill(content, (byte)'A');
new Random().nextBytes(content);
prepareServer(new HttpServlet()
{
@Override
@ -291,7 +291,7 @@ public class ProxyServletTest
});
byte[] content = new byte[1024];
Arrays.fill(content, (byte)'A');
new Random().nextBytes(content);
ContentResponse response = client.newRequest("localhost", serverConnector.getLocalPort())
.method(HttpMethod.POST)
.content(new BytesContentProvider(content))
@ -331,6 +331,9 @@ public class ProxyServletTest
@Test
public void testProxyWithBigRequestContentConsumed() throws Exception
{
final byte[] content = new byte[128 * 1024];
new Random().nextBytes(content);
prepareProxy(new ProxyServlet());
prepareServer(new HttpServlet()
{
@ -340,13 +343,18 @@ public class ProxyServletTest
if (req.getHeader("Via") != null)
resp.addHeader(PROXIED_HEADER, "true");
InputStream input = req.getInputStream();
int index = 0;
while (true)
if (input.read() < 0)
{
int value = input.read();
if (value < 0)
break;
Assert.assertEquals("Content mismatch at index=" + index, content[index] & 0xFF, value);
++index;
}
}
});
byte[] content = new byte[128 * 1024];
ContentResponse response = client.newRequest("localhost", serverConnector.getLocalPort())
.method(HttpMethod.POST)
.content(new BytesContentProvider(content))
@ -369,7 +377,7 @@ public class ProxyServletTest
Files.createDirectories(targetTestsDir);
final Path temp = Files.createTempFile(targetTestsDir, "test_", null);
byte[] kb = new byte[1024];
Arrays.fill(kb, (byte)'X');
new Random().nextBytes(kb);
try (OutputStream output = Files.newOutputStream(temp, CREATE))
{
for (int i = 0; i < length; ++i)
@ -631,7 +639,7 @@ public class ProxyServletTest
}
});
int port = serverConnector.getLocalPort();
client.getProxyConfiguration().getExcludedOrigins().add("127.0.0.1:" + port);
client.getProxyConfiguration().getProxies().get(0).getExcludedAddresses().add("127.0.0.1:" + port);
// Try with a proxied host
ContentResponse response = client.newRequest("localhost", port)
@ -650,6 +658,17 @@ public class ProxyServletTest
@Test
public void testTransparentProxy() throws Exception
{
testTransparentProxyWithPrefix("/proxy");
}
@Test
public void testTransparentProxyWithRootContext() throws Exception
{
testTransparentProxyWithPrefix("/");
}
private void testTransparentProxyWithPrefix(String prefix) throws Exception
{
final String target = "/test";
prepareServer(new HttpServlet()
@ -664,13 +683,12 @@ public class ProxyServletTest
});
String proxyTo = "http://localhost:" + serverConnector.getLocalPort();
String prefix = "/proxy";
ProxyServlet.Transparent proxyServlet = new ProxyServlet.Transparent(proxyTo, prefix);
prepareProxy(proxyServlet);
// Make the request to the proxy, it should transparently forward to the server
ContentResponse response = client.newRequest("localhost", proxyConnector.getLocalPort())
.path(prefix + target)
.path((prefix + target).replaceAll("//", "/"))
.timeout(5, TimeUnit.SECONDS)
.send();
Assert.assertEquals(200, response.getStatus());
@ -865,7 +883,7 @@ public class ProxyServletTest
}
});
ContentResponse response = client.newRequest("localhost", serverConnector.getLocalPort())
client.newRequest("localhost", serverConnector.getLocalPort())
.timeout(5, TimeUnit.SECONDS)
.send();

View File

@ -26,17 +26,16 @@ import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.HttpProxy;
import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Destination;
import org.eclipse.jetty.client.api.ProxyConfiguration;
import org.eclipse.jetty.client.util.FutureResponseListener;
import org.eclipse.jetty.client.util.StringContentProvider;
import org.eclipse.jetty.http.HttpHeader;
@ -143,7 +142,7 @@ public class ProxyTunnellingTest
startProxy();
HttpClient httpClient = new HttpClient(sslContextFactory);
httpClient.setProxyConfiguration(new ProxyConfiguration("localhost", proxyPort()));
httpClient.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", proxyPort()));
httpClient.start();
try
@ -172,7 +171,7 @@ public class ProxyTunnellingTest
startProxy();
HttpClient httpClient = new HttpClient(sslContextFactory);
httpClient.setProxyConfiguration(new ProxyConfiguration("localhost", proxyPort()));
httpClient.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", proxyPort()));
httpClient.start();
try
@ -215,7 +214,7 @@ public class ProxyTunnellingTest
startProxy();
final HttpClient httpClient = new HttpClient(sslContextFactory);
httpClient.setProxyConfiguration(new ProxyConfiguration("localhost", proxyPort()));
httpClient.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", proxyPort()));
httpClient.start();
try
@ -285,7 +284,7 @@ public class ProxyTunnellingTest
stopProxy();
HttpClient httpClient = new HttpClient(sslContextFactory);
httpClient.setProxyConfiguration(new ProxyConfiguration("localhost", proxyPort));
httpClient.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", proxyPort));
httpClient.start();
try
@ -317,7 +316,7 @@ public class ProxyTunnellingTest
startProxy();
HttpClient httpClient = new HttpClient(sslContextFactory);
httpClient.setProxyConfiguration(new ProxyConfiguration("localhost", proxyPort()));
httpClient.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", proxyPort()));
httpClient.start();
try
@ -354,7 +353,7 @@ public class ProxyTunnellingTest
});
HttpClient httpClient = new HttpClient(sslContextFactory);
httpClient.setProxyConfiguration(new ProxyConfiguration("localhost", proxyPort()));
httpClient.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", proxyPort()));
httpClient.start();
try
@ -394,7 +393,7 @@ public class ProxyTunnellingTest
sslContextFactory.start();
HttpClient httpClient = new HttpClient(sslContextFactory);
httpClient.setProxyConfiguration(new ProxyConfiguration(proxyHost, proxyPort));
httpClient.getProxyConfiguration().getProxies().add(new HttpProxy(proxyHost, proxyPort));
httpClient.start();
try

View File

@ -9,5 +9,4 @@ server
lib/jetty-rewrite-${jetty.version}.jar
[xml]
# Annotations needs annotations configuration
etc/jetty-rewrite.xml

View File

@ -261,7 +261,7 @@ public class JDBCLoginService extends MappedLoginService
roles.add(rs2.getString(_roleTableRoleField));
}
}
return putUser(username, Credential.getCredential(credentials),roles.toArray(new String[roles.size()]));
return putUser(username, credentials, roles.toArray(new String[roles.size()]));
}
}
}
@ -273,6 +273,13 @@ public class JDBCLoginService extends MappedLoginService
}
return null;
}
/* ------------------------------------------------------------ */
protected UserIdentity putUser (String username, String credentials, String[] roles)
{
return putUser(username, Credential.getCredential(credentials),roles);
}
/**
* Close an existing connection

View File

@ -25,6 +25,7 @@
<Item>127.0.0.2/black.html</Item>
</Array>
</Set>
<Set name="whiteListByPath">false</Set>
</New>
</Set>
</Configure>

View File

@ -20,8 +20,8 @@
<Set name="retainDays"><Property name="requestlog.retain" default="90"/></Set>
<Set name="append"><Property name="requestlog.append" default="false"/></Set>
<Set name="extended"><Property name="requestlog.extended" default="false"/></Set>
<Set name="logCookies">false</Set>
<Set name="LogTimeZone">GMT</Set>
<Set name="logCookies"><Property name="requestlog.cookies" default="false"/></Set>
<Set name="LogTimeZone"><Property name="requestlog.timezone" default="GMT"/></Set>
</New>
</Set>
</New>

View File

@ -0,0 +1,6 @@
#
# Classic Jetty Continuation Support Module
#
[lib]
lib/jetty-continuation-${jetty.version}.jar

View File

@ -9,5 +9,6 @@ server
etc/jetty-http.xml
[ini-template]
## HTTP Connector Configuration
jetty.port=8080
http.timeout=30000

View File

@ -9,5 +9,8 @@ ssl
etc/jetty-https.xml
[ini-template]
## HTTPS Configuration
# HTTP port to listen on
https.port=8443
# HTTPS idle timeout in milliseconds
https.timeout=30000

View File

@ -1,17 +1,16 @@
[ini-template]
#===========================================================
# Configure JVM arguments.
# If JVM args are include in an ini file then --exec is needed
# to start a new JVM from start.jar with the extra args.
# If you wish to avoid an extra JVM running, place JVM args
# on the normal command line and do not use --exec
#-----------------------------------------------------------
## JVM Configuration
## If JVM args are include in an ini file then --exec is needed
## to start a new JVM from start.jar with the extra args.
##
## If you wish to avoid an extra JVM running, place JVM args
## on the normal command line and do not use --exec
# --exec
# -Xmx2000m
# -Xmn512m
# -XX:+UseConcMarkSweepGC
# -XX:ParallelCMSThreads=2
# -XX:+CMSClassUnloadingEnabled
# -XX:+CMSClassUnloadingEnabled
# -XX:+UseCMSCompactAtFullCollection
# -XX:CMSInitiatingOccupancyFraction=80
# -verbose:gc

View File

@ -9,6 +9,7 @@ server
etc/jetty-lowresources.xml
[ini-template]
## Low Resources Configuration
# lowresources.period=1050
# lowresources.lowResourcesIdleTimeout=200
# lowresources.monitorThreads=true

View File

@ -12,6 +12,15 @@ etc/jetty-requestlog.xml
logs/
[ini-template]
## Request Log Configuration
# How many days to retain the logs
# requestlog.retain=90
# If an existing log with the same name is found, just append to it
# requestlog.append=true
# Use the extended log output
# requestlog.extended=true
# Log http cookie information as well
# requestlog.cookies=true
# Set the log output timezone
# requestlog.timezone=GMT

View File

@ -1,2 +1,6 @@
#
# Module to add resources directory to classpath
#
[lib]
resources

View File

@ -1,5 +1,5 @@
#
# Base server
# Base Server Module
#
[optional]
@ -12,22 +12,27 @@ resources
lib/servlet-api-3.1.jar
lib/jetty-schemas-3.1.jar
lib/jetty-http-${jetty.version}.jar
lib/jetty-continuation-${jetty.version}.jar
lib/jetty-server-${jetty.version}.jar
lib/jetty-xml-${jetty.version}.jar
lib/jetty-util-${jetty.version}.jar
lib/jetty-io-${jetty.version}.jar
[xml]
# Annotations needs annotations configuration
etc/jetty.xml
[ini-template]
## Server Threading Configuration
# minimum number of threads
threads.min=10
# maximum number of threads
threads.max=200
# thread idle timeout in milliseconds
threads.timeout=60000
# What host to listen on (leave commented to listen on all interfaces)
#jetty.host=myhost.com
# Dump the state of the Jetty server, components, and webapps after startup
jetty.dump.start=false
# Dump the state of the Jetty server, before stop
jetty.dump.stop=false

View File

@ -1,5 +1,6 @@
#
# SSL Keystore module
#
[depend]
server
@ -11,6 +12,7 @@ etc/jetty-ssl.xml
http://git.eclipse.org/c/jetty/org.eclipse.jetty.project.git/plain/jetty-server/src/main/config/etc/keystore:etc/keystore
[ini-template]
## SSL Keystore Configuration
# define the port to use for secure redirection
jetty.secure.port=8443
@ -20,6 +22,7 @@ jetty.truststore=etc/keystore
# Set the demonstration passwords.
# Note that OBF passwords are not secure, just protected from casual observation
# See http://www.eclipse.org/jetty/documentation/current/configuring-security-secure-passwords.html
jetty.keystore.password=OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4
jetty.keymanager.password=OBF:1u2u1wml1z7s1z7a1wnl1u2g
jetty.truststore.password=OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4

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