Merge branch 'master' into release-9
diff --git a/VERSION.txt b/VERSION.txt
index f0c380b..2073456 100644
--- a/VERSION.txt
+++ b/VERSION.txt
@@ -108,7 +108,6 @@
  + 410405 Avoid NPE for requestDispatcher(../)
  + 410469 UpgradeRequest is sent twice when using SSL, one fails warning about
    WritePendingException
- + 410498 ignore type of exception in GoAwayTest.testDataNotProcessedAfterGoAway
  + 410522 jetty start broken for command line options
  + 410537 Exceptions during @OnWebSocketConnect not reported to
    @OnWebSocketError
@@ -301,6 +300,75 @@
  + 402757 WebSocket client module can't be used with WebSocket server module in
    the same WAR
 
+jetty-8.1.12.v20130726 - 26 July 2013
+ + 396706 CGI support parameters
+ + 397193 MongoSessionManager refresh updates last access time
+ + 407342 ReloadedSessionMissingClassTest uses class compiled with jdk7
+ + 408529 Etags set in 304 response
+ + 408600 set correct jetty.url in all pom files
+ + 408642 setContentType from addHeader
+ + 408662 In pax-web servlet services requests even if init() has not finished
+   running
+ + 408806 getParameter returns null on Multipart request if called before
+   request.getPart()/getParts()
+ + 408909 GzipFilter setting of headers when reset and/or not compressed
+ + 409028 Jetty HttpClient does not work with proxy CONNECT method.
+ + 409133 Empty <welcome-file> causes StackOverflowError
+ + 409436 NPE on context restart using dynamic servlet registration
+ + 409449 Ensure servlets, filters and listeners added via dynamic
+   registration, annotations or descriptors are cleaned on context restarts
+ + 409556 FileInputStream not closed in DirectNIOBuffer
+ + 410405 Avoid NPE for requestDispatcher(../)
+ + 410630 MongoSessionManager conflicting session update op
+ + 410750 NoSQLSessions: implement session context data persistence across
+   server restarts
+ + 410893 async support defaults to false for spec created servlets and filters
+ + 411135 HttpClient may send proxied https requests to the proxy instead of
+   the target server.
+ + 411216 RequestLogHandler handles async completion
+ + 411458 MultiPartFilter getParameterMap doesn't preserve multivalued
+   parameters 411459  MultiPartFilter.Wrapper getParameter should use charset
+   encoding of part
+ + 411755 MultiPartInputStreamParser fails on base64 encoded content
+ + 411909 GzipFilter flushbuffer() results in erroneous finish() call
+ + 412712 HttpClient does not send the terminal chunk after partial writes.
+ + 412750 HttpClient close expired connections fix
+ + 413371 Default JSON.Converters for List and Set.
+ + 413372 JSON Enum uses name rather than toString()
+ + 413684 Trailing slash shows JSP source
+ + 413812 Make RateTracker serializable
+
+jetty-7.6.12.v20130726 - 26 July 2013
+ + 396706 CGI support parameters
+ + 397193 MongoSessionManager refresh updates last access time
+ + 407342 ReloadedSessionMissingClassTest uses class compiled with jdk7
+ + 408529 Etags set in 304 response
+ + 408600 set correct jetty.url in all pom files
+ + 408642 setContentType from addHeader
+ + 408662 In pax-web servlet services requests even if init() has not finished
+   running
+ + 408909 GzipFilter setting of headers when reset and/or not compressed
+ + 409028 Jetty HttpClient does not work with proxy CONNECT method.
+ + 409133 Empty <welcome-file> causes StackOverflowError
+ + 409556 FileInputStream not closed in DirectNIOBuffer
+ + 410630 MongoSessionManager conflicting session update op
+ + 410750 NoSQLSessions: implement session context data persistence across
+   server restarts
+ + 411135 HttpClient may send proxied https requests to the proxy instead of
+   the target server.
+ + 411216 RequestLogHandler handles async completion
+ + 411458 MultiPartFilter getParameterMap doesn't preserve multivalued
+   parameters 411459  MultiPartFilter.Wrapper getParameter should use charset
+   encoding of part
+ + 411755 MultiPartInputStreamParser fails on base64 encoded content
+ + 411909 GzipFilter flushbuffer() results in erroneous finish() call
+ + 412712 HttpClient does not send the terminal chunk after partial writes.
+ + 412750 HttpClient close expired connections fix
+ + 413371 Default JSON.Converters for List and Set.
+ + 413372 JSON Enum uses name rather than toString()
+ + 413684 Trailing slash shows JSP source
+ + 413812 Make RateTracker serializable
+
 jetty-8.1.11.v20130520 - 20 May 2013
  + 402844 STOP.PORT & STOP.KEY behaviour has changed
  + 403281 jetty.sh waits for started or failure before returning
diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FileServer.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FileServer.java
index c77ac42..15d947a 100644
--- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FileServer.java
+++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FileServer.java
@@ -23,8 +23,6 @@
 import org.eclipse.jetty.server.handler.DefaultHandler;
 import org.eclipse.jetty.server.handler.HandlerList;
 import org.eclipse.jetty.server.handler.ResourceHandler;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
 
 /* ------------------------------------------------------------ */
 /** Simple Jetty FileServer.
diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SpdyServer.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SpdyServer.java
index c2aa948..302f434 100644
--- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SpdyServer.java
+++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SpdyServer.java
@@ -55,8 +55,7 @@
         System.setProperty("jetty.home",jetty_home);
 
         // Setup Threadpool
-        QueuedThreadPool threadPool = new QueuedThreadPool();
-        threadPool.setMaxThreads(500);
+        QueuedThreadPool threadPool = new QueuedThreadPool(512);
 
         Server server = new Server(threadPool);
         server.manage(threadPool);
diff --git a/jetty-annotations/pom.xml b/jetty-annotations/pom.xml
index 122641b..0d696db 100644
--- a/jetty-annotations/pom.xml
+++ b/jetty-annotations/pom.xml
@@ -77,6 +77,11 @@
       <scope>test</scope>
     </dependency>
     <dependency>
+      <groupId>org.eclipse.jetty.toolchain</groupId>
+      <artifactId>jetty-test-helper</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
       <groupId>org.eclipse.jetty</groupId>
       <artifactId>jetty-jndi</artifactId>
       <version>${project.version}</version>
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationParser.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationParser.java
index daab409..c8048f4 100644
--- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationParser.java
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationParser.java
@@ -753,7 +753,8 @@
     public void parseDir (Resource dir, ClassNameResolver resolver)
     throws Exception
     {
-        if (!dir.isDirectory() || !dir.exists())
+        //skip dirs whose name start with . (ie hidden)
+        if (!dir.isDirectory() || !dir.exists() || dir.getName().startsWith("."))
             return;
 
         if (LOG.isDebugEnabled()) {LOG.debug("Scanning dir {}", dir);};
@@ -766,16 +767,21 @@
                 Resource res = dir.addPath(files[f]);
                 if (res.isDirectory())
                     parseDir(res, resolver);
-                String name = res.getName();
-                if (name.endsWith(".class"))
+                else
                 {
-                    if ((resolver == null)|| (!resolver.isExcluded(name) && (!isParsed(name) || resolver.shouldOverride(name))))
+                    //we've already verified the directories, so just verify the class file name
+                    String filename = res.getFile().getName();
+                    if (isValidClassFileName(filename))
                     {
-                        Resource r = Resource.newResource(res.getURL());
-                        if (LOG.isDebugEnabled()) {LOG.debug("Scanning class {}", r);};
-                        scanClass(r.getInputStream());
-                    }
+                        String name = res.getName();
+                        if ((resolver == null)|| (!resolver.isExcluded(name) && (!isParsed(name) || resolver.shouldOverride(name))))
+                        {
+                            Resource r = Resource.newResource(res.getURL());
+                            if (LOG.isDebugEnabled()) {LOG.debug("Scanning class {}", r);};
+                            scanClass(r.getInputStream());
+                        }
 
+                    }
                 }
             }
             catch (Exception ex)
@@ -812,23 +818,11 @@
             {
                 try
                 {
-                    String name = entry.getName();
-                    if (name.toLowerCase(Locale.ENGLISH).endsWith(".class"))
-                    {
-                        String shortName =  name.replace('/', '.').substring(0,name.length()-6);
-                        if ((resolver == null)
-                             ||
-                            (!resolver.isExcluded(shortName) && (!isParsed(shortName) || resolver.shouldOverride(shortName))))
-                        {
-
-                            Resource clazz = Resource.newResource("jar:"+jarUri+"!/"+name);
-                            scanClass(clazz.getInputStream());
-                        }
-                    }
+                    parseJarEntry(jarUri, entry, resolver);
                 }
                 catch (Exception e)
                 {
-                    LOG.warn("Problem processing jar entry "+entry, e);
+                    LOG.warn("Problem parsing jar entry: {}", entry.getName());
                 }
             }
 
@@ -896,6 +890,8 @@
             scanClass(r.getInputStream());
             return;
         }
+        
+        if (LOG.isDebugEnabled()) LOG.warn("Resource not scannable for classes: {}", uri);
     }
 
 
@@ -929,29 +925,8 @@
             { 
                 JarEntry entry = jar_in.getNextJarEntry();
                 while (entry!=null)
-                {                   
-                    try
-                    {
-                        String name = entry.getName();
-                        if (name.toLowerCase(Locale.ENGLISH).endsWith(".class"))
-                        {
-                            String shortName =  name.replace('/', '.').substring(0,name.length()-6);
-
-                            if ((resolver == null)
-                                 ||
-                                (!resolver.isExcluded(shortName) && (!isParsed(shortName) || resolver.shouldOverride(shortName))))
-                            {
-                                Resource clazz = Resource.newResource("jar:"+uri+"!/"+name);
-                                if (LOG.isDebugEnabled()) {LOG.debug("Scanning class from jar {}", clazz);};
-                                scanClass(clazz.getInputStream());
-                            }
-                        }
-                    }
-                    catch (Exception e)
-                    {
-                        LOG.warn("Problem processing jar entry "+entry, e);
-                    }
-                                    
+                {      
+                    parseJarEntry(uri, entry, resolver);
                     entry = jar_in.getNextJarEntry();
                 }
             }
@@ -961,8 +936,44 @@
             } 
         }   
     }
+
+    /**
+     * Parse a single entry in a jar file
+     * @param jar
+     * @param entry
+     * @param resolver
+     * @throws Exception
+     */
+    protected void parseJarEntry (URI jar, JarEntry entry, final ClassNameResolver resolver)
+    throws Exception
+    {
+        if (jar == null || entry == null)
+            return;
+
+        //skip directories
+        if (entry.isDirectory())
+            return;
+
+        String name = entry.getName();
+
+        //check file is a valid class file name
+        if (isValidClassFileName(name) && isValidClassFilePath(name))
+        {
+            String shortName =  name.replace('/', '.').substring(0,name.length()-6);
+
+            if ((resolver == null)
+                    ||
+                (!resolver.isExcluded(shortName) && (!isParsed(shortName) || resolver.shouldOverride(shortName))))
+            {
+                Resource clazz = Resource.newResource("jar:"+jar+"!/"+name);
+                if (LOG.isDebugEnabled()) {LOG.debug("Scanning class from jar {}", clazz);};
+                scanClass(clazz.getInputStream());
+            }
+        }
+    }
     
     
+
     /**
      * Use ASM on a class
      * 
@@ -975,5 +986,66 @@
         ClassReader reader = new ClassReader(is);
         reader.accept(new MyClassVisitor(), ClassReader.SKIP_CODE|ClassReader.SKIP_DEBUG|ClassReader.SKIP_FRAMES);
     }
+    
+    /**
+     * Check that the given path represents a valid class file name.
+     * The check is fairly cursory, checking that:
+     * <ul>
+     * <li> the name ends with .class</li>
+     * <li> it isn't a dot file or in a hidden directory </li>
+     * <li> the name of the class at least begins with a valid identifier for a class name </li>
+     * </ul>
+     * @param name
+     * @return
+     */
+    private boolean isValidClassFileName (String name)
+    {
+        //no name cannot be valid
+        if (name == null || name.length()==0)
+            return false;
+
+        //skip anything that is not a class file
+        if (!name.toLowerCase(Locale.ENGLISH).endsWith(".class"))
+        {
+            if (LOG.isDebugEnabled()) LOG.debug("Not a class: {}",name);
+            return false;
+        }
+
+        //skip any classfiles that are not a valid java identifier
+        int c0 = 0;      
+        int ldir = name.lastIndexOf('/', name.length()-6);
+        c0 = (ldir > -1 ? ldir+1 : c0);
+        if (!Character.isJavaIdentifierStart(name.charAt(c0)))
+        {
+            if (LOG.isDebugEnabled()) LOG.debug("Not a java identifier: {}"+name);
+            return false;
+        }
+   
+        return true;
+    }
+    
+    
+    /**
+     * Check that the given path does not contain hidden directories
+     *
+     * @param path
+     * @return
+     */
+    private boolean isValidClassFilePath (String path)
+    {
+        //no path is not valid
+        if (path == null || path.length()==0)
+            return false;
+
+
+        //skip any classfiles that are in a hidden directory
+        if (path.startsWith(".") || path.contains("/."))
+        {
+            if (LOG.isDebugEnabled()) LOG.debug("Contains hidden dirs: {}"+path);
+            return false;
+        }
+
+        return true;
+    }
 }
 
diff --git a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationParser.java b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationParser.java
index 4e95a02..5fb97a3 100644
--- a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationParser.java
+++ b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationParser.java
@@ -18,71 +18,109 @@
 
 package org.eclipse.jetty.annotations;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
 
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
 import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 import org.eclipse.jetty.annotations.AnnotationParser.DiscoverableAnnotationHandler;
 import org.eclipse.jetty.annotations.AnnotationParser.Value;
+import org.eclipse.jetty.toolchain.test.FS;
+import org.eclipse.jetty.toolchain.test.IO;
+import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+import org.eclipse.jetty.toolchain.test.TestingDir;
+import org.junit.Assert;
+import org.junit.Rule;
 import org.junit.Test;
 
 public class TestAnnotationParser
 {
+    public static class TrackingAnnotationHandler implements DiscoverableAnnotationHandler
+    {
+        private final String annotationName;
+        public final Set<String> foundClasses;
+
+        public TrackingAnnotationHandler(String annotationName)
+        {
+            this.annotationName = annotationName;
+            this.foundClasses = new HashSet<>();
+        }
+
+        @Override
+        public void handleClass(String className, int version, int access, String signature, String superName, String[] interfaces, String annotation,
+                List<Value> values)
+        {
+            foundClasses.add(className);
+        }
+
+        @Override
+        public void handleMethod(String className, String methodName, int access, String desc, String signature, String[] exceptions, String annotation,
+                List<Value> values)
+        {
+            /* ignore */
+        }
+
+        @Override
+        public void handleField(String className, String fieldName, int access, String fieldType, String signature, Object value, String annotation,
+                List<Value> values)
+        {
+            /* ignore */
+        }
+
+        @Override
+        public String getAnnotationName()
+        {
+            return this.annotationName;
+        }
+    }
+
+    @Rule
+    public TestingDir testdir = new TestingDir();
+
     @Test
     public void testSampleAnnotation() throws Exception
     {
-        String[] classNames = new String[]{"org.eclipse.jetty.annotations.ClassA"};
+        String[] classNames = new String[]
+        { "org.eclipse.jetty.annotations.ClassA" };
         AnnotationParser parser = new AnnotationParser();
 
         class SampleAnnotationHandler implements DiscoverableAnnotationHandler
         {
-            private List<String> methods = Arrays.asList("a", "b", "c", "d", "l");
+            private List<String> methods = Arrays.asList("a","b","c","d","l");
 
-           
-            
-            
             public void handleClass(String className, int version, int access, String signature, String superName, String[] interfaces, String annotation,
-                                    List<Value> values)
+                    List<Value> values)
             {
-                assertEquals ("org.eclipse.jetty.annotations.ClassA", className);
+                assertEquals("org.eclipse.jetty.annotations.ClassA",className);
             }
 
             public void handleField(String className, String fieldName, int access, String fieldType, String signature, Object value, String annotation,
-                                   List<Value> values)
+                    List<Value> values)
             {
-              assertEquals ("m", fieldName);
-              assertEquals (org.objectweb.asm.Type.OBJECT, org.objectweb.asm.Type.getType(fieldType).getSort());
-              assertEquals (1, values.size());
-              Value anv1 = values.get(0);
-              assertEquals ("value", anv1.getName());
-              assertEquals (7, anv1.getValue());
+                assertEquals("m",fieldName);
+                assertEquals(org.objectweb.asm.Type.OBJECT,org.objectweb.asm.Type.getType(fieldType).getSort());
+                assertEquals(1,values.size());
+                Value anv1 = values.get(0);
+                assertEquals("value",anv1.getName());
+                assertEquals(7,anv1.getValue());
 
             }
 
             public void handleMethod(String className, String methodName, int access, String desc, String signature, String[] exceptions, String annotation,
-                                     List<Value> values)
+                    List<Value> values)
             {
-                System.err.println("Sample annotated method : classname="+className+" methodName="+methodName+" access="+access+" desc="+desc+" signature="+signature);
-
-                org.objectweb.asm.Type retType = org.objectweb.asm.Type.getReturnType(desc);
-                System.err.println("REturn type = "+retType);
-                org.objectweb.asm.Type[] params = org.objectweb.asm.Type.getArgumentTypes(desc);
-                if (params == null)
-                    System.err.println("No params");
-                else
-                    System.err.println(params.length+" params");
-
-                if (exceptions == null)
-                    System.err.println("No exceptions");
-                else
-                    System.err.println(exceptions.length+" exceptions");
-
-                assertEquals("org.eclipse.jetty.annotations.ClassA", className);
+                assertEquals("org.eclipse.jetty.annotations.ClassA",className);
                 assertTrue(methods.contains(methodName));
-                assertEquals("org.eclipse.jetty.annotations.Sample", annotation);
+                assertEquals("org.eclipse.jetty.annotations.Sample",annotation);
             }
 
             @Override
@@ -95,7 +133,7 @@
         parser.registerHandler(new SampleAnnotationHandler());
 
         long start = System.currentTimeMillis();
-        parser.parse(classNames, new ClassNameResolver ()
+        parser.parse(classNames,new ClassNameResolver()
         {
             public boolean isExcluded(String name)
             {
@@ -110,44 +148,36 @@
         });
         long end = System.currentTimeMillis();
 
-        System.err.println("Time to parse class: "+((end-start)));
+        //System.err.println("Time to parse class: " + ((end - start)));
     }
 
     @Test
     public void testMultiAnnotation() throws Exception
     {
-        String[] classNames = new String[]{"org.eclipse.jetty.annotations.ClassB"};
+        String[] classNames = new String[]
+        { "org.eclipse.jetty.annotations.ClassB" };
         AnnotationParser parser = new AnnotationParser();
 
         class MultiAnnotationHandler implements DiscoverableAnnotationHandler
         {
             public void handleClass(String className, int version, int access, String signature, String superName, String[] interfaces, String annotation,
-                                    List<Value> values)
+                    List<Value> values)
             {
                 assertTrue("org.eclipse.jetty.annotations.ClassB".equals(className));
-
-                for (Value anv: values)
-                {
-                   System.err.println(anv.toString());
-                }
             }
 
             public void handleField(String className, String fieldName, int access, String fieldType, String signature, Object value, String annotation,
-                                    List<Value> values)
+                    List<Value> values)
             {
-                //there should not be any
+                // there should not be any
                 fail();
             }
 
             public void handleMethod(String className, String methodName, int access, String params, String signature, String[] exceptions, String annotation,
-                                     List<Value> values)
+                    List<Value> values)
             {
                 assertTrue("org.eclipse.jetty.annotations.ClassB".equals(className));
                 assertTrue("a".equals(methodName));
-                for (Value anv: values)
-                {
-                    System.err.println(anv.toString());
-                }
             }
 
             @Override
@@ -155,11 +185,71 @@
             {
                 return "org.eclipse.jetty.annotations.Multi";
             }
-            
-            
+
         }
 
         parser.registerHandler(new MultiAnnotationHandler());
-        parser.parse(classNames, null);
+        parser.parse(classNames,null);
+    }
+
+    @Test
+    public void testHiddenFilesInJar() throws Exception
+    {
+        File badClassesJar = MavenTestingUtils.getTestResourceFile("bad-classes.jar");
+        AnnotationParser parser = new AnnotationParser();
+        parser.parse(badClassesJar.toURI(),null);
+        // only the valid classes inside bad-classes.jar should be parsed. If any invalid classes are parsed and exception would be thrown here
+    }
+
+    @Test
+    public void testBasedirExclusion() throws Exception
+    {
+        // Build up basedir, which itself has a path segment that violates java package and classnaming.
+        // The basedir should have no effect on annotation scanning.
+        // Intentionally using a base director name that starts with a "."
+        // This mimics what you see in jenkins, hudson, hadoop, solr, camel, and selenium for their 
+        // installed and/or managed webapps
+        File basedir = testdir.getFile(".base/workspace/classes");
+        FS.ensureEmpty(basedir);
+
+        // Copy in class that is known to have annotations.
+        copyClass(ClassA.class,basedir);
+
+        // Setup Tracker
+        TrackingAnnotationHandler tracker = new TrackingAnnotationHandler(Sample.class.getName());
+
+        // Setup annotation scanning
+        AnnotationParser parser = new AnnotationParser();
+        parser.registerHandler(tracker);
+        
+        // Parse
+        parser.parse(basedir.toURI(),null);
+        
+        // Validate
+        Assert.assertThat("Found Class", tracker.foundClasses, contains(ClassA.class.getName()));
+    }
+
+    private void copyClass(Class<?> clazz, File basedir) throws IOException
+    {
+        String classname = clazz.getName().replace('.',File.separatorChar) + ".class";
+        URL url = this.getClass().getResource('/'+classname);
+        Assert.assertThat("URL for: " + classname,url,notNullValue());
+
+        String classpath = classname.substring(0,classname.lastIndexOf(File.separatorChar));
+        FS.ensureDirExists(new File(basedir,classpath));
+
+        InputStream in = null;
+        OutputStream out = null;
+        try
+        {
+            in = url.openStream();
+            out = new FileOutputStream(new File(basedir,classname));
+            IO.copy(in,out);
+        }
+        finally
+        {
+            IO.close(out);
+            IO.close(in);
+        }
     }
 }
diff --git a/jetty-annotations/src/test/resources/bad-classes.jar b/jetty-annotations/src/test/resources/bad-classes.jar
new file mode 100644
index 0000000..5538c18
--- /dev/null
+++ b/jetty-annotations/src/test/resources/bad-classes.jar
Binary files differ
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java
index e2d7f10..3fe4028 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java
@@ -176,11 +176,7 @@
     protected void doStart() throws Exception
     {
         if (sslContextFactory != null)
-        {
             addBean(sslContextFactory);
-            // Avoid to double dispatch when using SSL
-            setDispatchIO(false);
-        }
 
         String name = HttpClient.class.getSimpleName() + "@" + hashCode();
 
@@ -391,7 +387,7 @@
     protected Request copyRequest(Request oldRequest, URI newURI)
     {
         Request newRequest = new HttpRequest(this, oldRequest.getConversationID(), newURI);
-        newRequest.method(oldRequest.getMethod())
+        newRequest.method(oldRequest.method())
                 .version(oldRequest.getVersion())
                 .content(oldRequest.getContent());
         for (HttpField header : oldRequest.getHeaders())
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java
index 9685c20..dfd11c2 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java
@@ -53,7 +53,7 @@
     private final HttpSender sender;
     private final HttpReceiver receiver;
     private long idleTimeout;
-    private volatile boolean closed;
+    private boolean closed;
 
     public HttpConnection(HttpClient client, EndPoint endPoint, HttpDestination destination)
     {
@@ -88,15 +88,9 @@
         super.onClose();
     }
 
-    @Override
-    public void fillInterested()
+    protected boolean isClosed()
     {
-        // This is necessary when "upgrading" the connection for example after proxied
-        // CONNECT requests, because the old connection will read the CONNECT response
-        // and then set the read interest, while the new connection attached to the same
-        // EndPoint also will set the read interest, causing a ReadPendingException.
-        if (!closed)
-            super.fillInterested();
+        return closed;
     }
 
     @Override
@@ -154,7 +148,7 @@
 
     private void normalizeRequest(Request request)
     {
-        if (request.getMethod() == null)
+        if (request.method() == null)
             request.method(HttpMethod.GET);
 
         if (request.getVersion() == null)
@@ -163,7 +157,7 @@
         if (request.getIdleTimeout() <= 0)
             request.idleTimeout(client.getIdleTimeout(), TimeUnit.MILLISECONDS);
 
-        HttpMethod method = request.getMethod();
+        String method = request.method();
         HttpVersion version = request.getVersion();
         HttpFields headers = request.getHeaders();
         ContentProvider content = request.getContent();
@@ -178,7 +172,7 @@
             path = "/";
             request.path(path);
         }
-        if (destination.isProxied() && HttpMethod.CONNECT != method)
+        if (destination.isProxied() && !HttpMethod.CONNECT.is(method))
         {
             path = request.getURI().toString();
             request.path(path);
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java
index 94744d0..9b2caf8 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java
@@ -428,6 +428,19 @@
         getResponseNotifier().notifyComplete(listeners, new Result(request, cause, response, cause));
     }
 
+    protected void tunnelSucceeded(Connection connection, Promise<Connection> promise)
+    {
+        // Wrap the connection with TLS
+        Connection tunnel = client.tunnel(connection);
+        promise.succeeded(tunnel);
+    }
+
+    protected void tunnelFailed(Connection connection, Promise<Connection> promise, Throwable failure)
+    {
+        promise.failed(failure);
+        connection.close();
+    }
+
     @Override
     public String dump()
     {
@@ -516,22 +529,18 @@
                 {
                     if (result.isFailed())
                     {
-                        failed(result.getFailure());
-                        connection.close();
+                        tunnelFailed(connection, delegate, result.getFailure());
                     }
                     else
                     {
                         Response response = result.getResponse();
                         if (response.getStatus() == 200)
                         {
-                            // Wrap the connection with TLS
-                            Connection tunnel = client.tunnel(connection);
-                            delegate.succeeded(tunnel);
+                            tunnelSucceeded(connection, delegate);
                         }
                         else
                         {
-                            failed(new HttpResponseException("Received " + response + " for " + result.getRequest(), response));
-                            connection.close();
+                            tunnelFailed(connection, delegate, new HttpResponseException("Received " + response + " for " + result.getRequest(), response));
                         }
                     }
                 }
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpExchange.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpExchange.java
index 6155421..de04377 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpExchange.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpExchange.java
@@ -43,6 +43,7 @@
     private volatile HttpConnection connection;
     private volatile Throwable requestFailure;
     private volatile Throwable responseFailure;
+  
 
     public HttpExchange(HttpConversation conversation, HttpDestination destination, Request request, List<Response.ResponseListener> listeners)
     {
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpReceiver.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpReceiver.java
index 2f33731..8bb6c6d 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpReceiver.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpReceiver.java
@@ -68,21 +68,30 @@
         {
             while (true)
             {
-                int read = endPoint.fill(buffer);
-                LOG.debug("Read {} bytes from {}", read, connection);
-                if (read > 0)
+                // Connection may be closed in a parser callback
+                if (connection.isClosed())
                 {
-                    parse(buffer);
-                }
-                else if (read == 0)
-                {
-                    fillInterested();
+                    LOG.debug("{} closed", connection);
                     break;
                 }
                 else
                 {
-                    shutdown();
-                    break;
+                    int read = endPoint.fill(buffer);
+                    LOG.debug("Read {} bytes from {}", read, connection);
+                    if (read > 0)
+                    {
+                        parse(buffer);
+                    }
+                    else if (read == 0)
+                    {
+                        fillInterested();
+                        break;
+                    }
+                    else
+                    {
+                        shutdown();
+                        break;
+                    }
                 }
             }
         }
@@ -147,7 +156,8 @@
                 HttpConversation conversation = exchange.getConversation();
                 HttpResponse response = exchange.getResponse();
 
-                parser.setHeadResponse(exchange.getRequest().getMethod() == HttpMethod.HEAD);
+                String method = exchange.getRequest().method();
+                parser.setHeadResponse(HttpMethod.HEAD.is(method) || HttpMethod.CONNECT.is(method));
                 response.version(version).status(status).reason(reason);
 
                 // Probe the protocol handlers
@@ -414,6 +424,12 @@
         return updated;
     }
 
+    @Override
+    public String toString()
+    {
+        return String.format("%s@%x on %s", getClass().getSimpleName(), hashCode(), connection);
+    }
+
     private enum State
     {
         IDLE, RECEIVE, FAILURE
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java
index ca750b7..2e4cbec 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java
@@ -29,6 +29,7 @@
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
 import java.util.concurrent.ExecutionException;
@@ -65,7 +66,7 @@
     private String scheme;
     private String path;
     private String query;
-    private HttpMethod method;
+    private String method;
     private HttpVersion version;
     private long idleTimeout;
     private long timeout;
@@ -127,13 +128,26 @@
     @Override
     public HttpMethod getMethod()
     {
+        return HttpMethod.fromString(method);
+    }
+
+    @Override
+    public String method()
+    {
         return method;
     }
 
     @Override
     public Request method(HttpMethod method)
     {
-        this.method = method;
+        this.method = method.asString();
+        return this;
+    }
+
+    @Override
+    public Request method(String method)
+    {
+        this.method = Objects.requireNonNull(method).toUpperCase(Locale.ENGLISH);
         return this;
     }
 
@@ -196,6 +210,7 @@
     {
         params.add(name, value);
         this.query = buildQuery();
+        this.uri = buildURI(true);
         return this;
     }
 
@@ -541,7 +556,13 @@
             for (String nameValue : query.split("&"))
             {
                 String[] parts = nameValue.split("=");
-                param(parts[0], parts.length < 2 ? "" : urlDecode(parts[1]));
+                if (parts.length > 0)
+                {
+                    String name = parts[0];
+                    if (name.trim().length() == 0)
+                        continue;
+                    param(name, parts.length < 2 ? "" : urlDecode(parts[1]));
+                }
             }
         }
     }
@@ -574,6 +595,6 @@
     @Override
     public String toString()
     {
-        return String.format("%s[%s %s %s]@%x", HttpRequest.class.getSimpleName(), getMethod(), getPath(), getVersion(), hashCode());
+        return String.format("%s[%s %s %s]@%x", HttpRequest.class.getSimpleName(), method(), getPath(), getVersion(), hashCode());
     }
 }
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpSender.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpSender.java
index f6737cc..eff0576 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpSender.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpSender.java
@@ -176,7 +176,7 @@
                         String query = request.getQuery();
                         if (query != null)
                             path += "?" + query;
-                        requestInfo = new HttpGenerator.RequestInfo(request.getVersion(), request.getHeaders(), contentLength, request.getMethod().asString(), path);
+                        requestInfo = new HttpGenerator.RequestInfo(request.getVersion(), request.getHeaders(), contentLength, request.method(), path);
                         break;
                     }
                     case NEED_HEADER:
@@ -540,9 +540,13 @@
         boolean notCommitted = isBeforeCommit(current);
         if (result == null && notCommitted && request.getAbortCause() == null)
         {
-            result = exchange.responseComplete(failure).getReference();
-            exchange.terminateResponse();
-            LOG.debug("Failed on behalf {}", exchange);
+            completion = exchange.responseComplete(failure);
+            if (completion.isMarked())
+            {
+                result = completion.getReference();
+                exchange.terminateResponse();
+                LOG.debug("Failed on behalf {}", exchange);
+            }
         }
 
         if (result != null)
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/RedirectProtocolHandler.java b/jetty-client/src/main/java/org/eclipse/jetty/client/RedirectProtocolHandler.java
index ee177dc..7b9c42a 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/RedirectProtocolHandler.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/RedirectProtocolHandler.java
@@ -95,8 +95,9 @@
                     {
                         case 301:
                         {
-                            if (request.getMethod() == HttpMethod.GET || request.getMethod() == HttpMethod.HEAD)
-                                redirect(result, request.getMethod(), newURI);
+                            String method = request.method();
+                            if (HttpMethod.GET.is(method) || HttpMethod.HEAD.is(method))
+                                redirect(result, method, newURI);
                             else
                                 fail(result, new HttpResponseException("HTTP protocol violation: received 301 for non GET or HEAD request", response));
                             break;
@@ -105,13 +106,13 @@
                         case 303:
                         {
                             // Redirect must be done using GET
-                            redirect(result, HttpMethod.GET, newURI);
+                            redirect(result, HttpMethod.GET.asString(), newURI);
                             break;
                         }
                         case 307:
                         {
                             // Keep same method
-                            redirect(result, request.getMethod(), newURI);
+                            redirect(result, request.method(), newURI);
                             break;
                         }
                         default:
@@ -174,7 +175,7 @@
         }
     }
 
-    private void redirect(Result result, HttpMethod method, URI location)
+    private void redirect(Result result, String method, URI location)
     {
         final Request request = result.getRequest();
         HttpConversation conversation = client.getConversation(request.getConversationID(), false);
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/api/Request.java b/jetty-client/src/main/java/org/eclipse/jetty/client/api/Request.java
index d582028..225137a 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/api/Request.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/api/Request.java
@@ -76,17 +76,30 @@
     int getPort();
 
     /**
-     * @return the method of this request, such as GET or POST
+     * @return the method of this request, such as GET or POST, or null if the method is not a standard HTTP method
+     * @deprecated use {@link #method()} instead
      */
+    @Deprecated
     HttpMethod getMethod();
 
     /**
+     * @return the method of this request, such as GET or POST, as a String
+     */
+    String method();
+
+    /**
      * @param method the method of this request, such as GET or POST
      * @return this request object
      */
     Request method(HttpMethod method);
 
     /**
+     * @param method the method of this request, such as GET or POST
+     * @return this request object
+     */
+    Request method(String method);
+
+    /**
      * @return the path of this request, such as "/" or "/path" - without the query
      * @see #getQuery()
      */
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/DigestAuthentication.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/DigestAuthentication.java
index b6a54cb..55ac0ec 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/util/DigestAuthentication.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/DigestAuthentication.java
@@ -221,7 +221,7 @@
             String A1 = user + ":" + realm + ":" + password;
             String hashA1 = toHexString(digester.digest(A1.getBytes(charset)));
 
-            String A2 = request.getMethod().asString() + ":" + request.getURI();
+            String A2 = request.method() + ":" + request.getURI();
             if ("auth-int".equals(qop))
                 A2 += ":" + toHexString(digester.digest(content));
             String hashA2 = toHexString(digester.digest(A2.getBytes(charset)));
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientURITest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientURITest.java
index 0727e3a..610f891 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientURITest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientURITest.java
@@ -108,13 +108,15 @@
             }
         });
 
+        String pathQuery = path + "?" + query;
         Request request = client.newRequest("localhost", connector.getLocalPort())
                 .scheme(scheme)
                 .timeout(5, TimeUnit.SECONDS)
-                .path(path + "?" + query);
+                .path(pathQuery);
 
         Assert.assertEquals(path, request.getPath());
         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());
@@ -131,6 +133,7 @@
         String value = "1";
         final String query = name + "=" + value;
         final String path = "/path";
+        String pathQuery = path + "?" + query;
         start(new AbstractHandler()
         {
             @Override
@@ -150,6 +153,7 @@
 
         Assert.assertEquals(path, request.getPath());
         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());
@@ -168,6 +172,7 @@
         String value2 = "2";
         final String query = name1 + "=" + value1 + "&" + name2 + "=" + value2;
         final String path = "/path";
+        String pathQuery = path + "?" + query;
         start(new AbstractHandler()
         {
             @Override
@@ -187,6 +192,7 @@
 
         Assert.assertEquals(path, request.getPath());
         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());
@@ -208,6 +214,7 @@
         String encodedValue2 = URLEncoder.encode(value2, "UTF-8");
         final String query = name1 + "=" + encodedValue1 + "&" + name2 + "=" + encodedValue2;
         final String path = "/path";
+        String pathQuery = path + "?" + query;
         start(new AbstractHandler()
         {
             @Override
@@ -229,6 +236,7 @@
 
         Assert.assertEquals(path, request.getPath());
         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());
@@ -238,4 +246,70 @@
 
         Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
     }
+
+    @Test
+    public void testNoParameterNameNoParameterValue() throws Exception
+    {
+        final String path = "/path";
+        final String query = "="; // Bogus query
+        String pathQuery = path + "?" + query;
+        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(path, request.getRequestURI());
+                Assert.assertEquals(query, request.getQueryString());
+            }
+        });
+
+        Request request = client.newRequest("localhost", connector.getLocalPort())
+                .scheme(scheme)
+                .timeout(5, TimeUnit.SECONDS)
+                .path(pathQuery);
+
+        Assert.assertEquals(path, request.getPath());
+        Assert.assertEquals(query, request.getQuery());
+        Assert.assertTrue(request.getURI().toString().endsWith(pathQuery));
+        Fields params = request.getParams();
+        Assert.assertEquals(0, params.size());
+
+        ContentResponse response = request.send();
+
+        Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+    }
+
+    @Test
+    public void testNoParameterNameWithParameterValue() throws Exception
+    {
+        final String path = "/path";
+        final String query = "=1"; // Bogus query
+        String pathQuery = path + "?" + query;
+        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(path, request.getRequestURI());
+                Assert.assertEquals(query, request.getQueryString());
+            }
+        });
+
+        Request request = client.newRequest("localhost", connector.getLocalPort())
+                .scheme(scheme)
+                .timeout(5, TimeUnit.SECONDS)
+                .path(pathQuery);
+
+        Assert.assertEquals(path, request.getPath());
+        Assert.assertEquals(query, request.getQuery());
+        Assert.assertTrue(request.getURI().toString().endsWith(pathQuery));
+        Fields params = request.getParams();
+        Assert.assertEquals(0, params.size());
+
+        ContentResponse response = request.send();
+
+        Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+    }
 }
diff --git a/jetty-distribution/src/main/resources/bin/jetty.sh b/jetty-distribution/src/main/resources/bin/jetty.sh
index cbd9f24..c7ab396 100755
--- a/jetty-distribution/src/main/resources/bin/jetty.sh
+++ b/jetty-distribution/src/main/resources/bin/jetty.sh
@@ -188,13 +188,15 @@
 then
   JETTY_SH=$0
   case "$JETTY_SH" in
-    /*)   ;;
-    ./*)  ;;
-    *)    JETTY_SH=./$JETTY_SH ;;
+    /*)     JETTY_HOME=${JETTY_SH%/*/*} ;;
+    ./*/*)  JETTY_HOME=${JETTY_SH%/*/*} ;;
+    ./*)    JETTY_HOME=.. ;;
+    */*/*)  JETTY_HOME=./${JETTY_SH%/*/*} ;;
+    */*)    JETTY_HOME=. ;;
+    *)      JETTY_HOME=.. ;;
   esac
-  JETTY_HOME=${JETTY_SH%/*/*}
 
-  if [ ! -f "${JETTY_SH%/*/*}/$JETTY_INSTALL_TRACE_FILE" ]
+  if [ ! -f "$JETTY_HOME/$JETTY_INSTALL_TRACE_FILE" ]
   then 
     JETTY_HOME=
   fi
diff --git a/jetty-http-spi/pom.xml b/jetty-http-spi/pom.xml
index 4243975..263c91f 100644
--- a/jetty-http-spi/pom.xml
+++ b/jetty-http-spi/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.0.0-SNAPSHOT</version>
+    <version>9.0.5-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-http-spi</artifactId>
diff --git a/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/DelegatingThreadPool.java b/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/DelegatingThreadPool.java
new file mode 100644
index 0000000..1a1ce7b
--- /dev/null
+++ b/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/DelegatingThreadPool.java
@@ -0,0 +1,160 @@
+//
+//  ========================================================================
+//  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.spi;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jetty.util.component.ContainerLifeCycle;
+import org.eclipse.jetty.util.component.LifeCycle;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.thread.ThreadPool;
+
+public class DelegatingThreadPool extends ContainerLifeCycle implements ThreadPool
+{
+    private static final Logger LOG = Log.getLogger(DelegatingThreadPool.class);
+    
+    private Executor _executor; // memory barrier provided by start/stop semantics
+
+    public DelegatingThreadPool(Executor executor)
+    {
+        _executor=executor;
+        addBean(_executor);
+    }
+
+    /* ------------------------------------------------------------ */
+    public Executor getExecutor()
+    {
+        return _executor;
+    }
+    
+    /* ------------------------------------------------------------ */
+    public void setExecutor(Executor executor)
+    {
+        if (isRunning())
+            throw new IllegalStateException(getState());
+        updateBean(_executor,executor);
+        _executor=executor;
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public void execute(Runnable job)
+    {
+        _executor.execute(job);
+    }
+
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public boolean dispatch(Runnable job)
+    {
+        final Executor executor=_executor;
+        if (executor instanceof ThreadPool)
+            return ((ThreadPool)executor).dispatch(job);
+
+        try
+        {
+            _executor.execute(job);
+            return true;
+        }
+        catch(RejectedExecutionException e)
+        {
+            LOG.warn(e);
+            return false;
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public int getIdleThreads()
+    {
+        final Executor executor=_executor;
+        if (executor instanceof ThreadPool)
+            return ((ThreadPool)executor).getIdleThreads();
+        
+        if (executor instanceof ThreadPoolExecutor)
+        {
+            final ThreadPoolExecutor tpe = (ThreadPoolExecutor)executor;
+            return tpe.getPoolSize() - tpe.getActiveCount();
+        }
+        return -1;
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public int getThreads()
+    {
+        final Executor executor=_executor;
+        if (executor instanceof ThreadPool)
+            return ((ThreadPool)executor).getThreads();
+        
+        if (executor instanceof ThreadPoolExecutor)
+        {
+            final ThreadPoolExecutor tpe = (ThreadPoolExecutor)executor;
+            return tpe.getPoolSize();
+        }
+        return -1;
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public boolean isLowOnThreads()
+    {
+        final Executor executor=_executor;
+        if (executor instanceof ThreadPool)
+            return ((ThreadPool)executor).isLowOnThreads();
+        
+        if (executor instanceof ThreadPoolExecutor)
+        {
+            final ThreadPoolExecutor tpe = (ThreadPoolExecutor)executor;
+            // getActiveCount() locks the thread pool, so execute it last
+            return tpe.getPoolSize() == tpe.getMaximumPoolSize() &&
+                    tpe.getQueue().size() >= tpe.getPoolSize() - tpe.getActiveCount();
+        }
+        return false;
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public void join() throws InterruptedException
+    {
+        final Executor executor=_executor;
+        if (executor instanceof ThreadPool)
+            ((ThreadPool)executor).join();
+        else if (executor instanceof ExecutorService)
+            ((ExecutorService)executor).awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
+        else
+            throw new IllegalStateException();
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    protected void doStop() throws Exception
+    {
+        super.doStop();
+        if (!(_executor instanceof LifeCycle) && (_executor instanceof ExecutorService))
+            ((ExecutorService)_executor).shutdownNow();
+    }
+
+}
diff --git a/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/JettyHttpServer.java b/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/JettyHttpServer.java
index f5af972..22e6b3d 100644
--- a/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/JettyHttpServer.java
+++ b/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/JettyHttpServer.java
Binary files differ
diff --git a/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/JettyHttpServerProvider.java b/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/JettyHttpServerProvider.java
index b96d9a2..410ef2a 100644
--- a/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/JettyHttpServerProvider.java
+++ b/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/JettyHttpServerProvider.java
@@ -26,6 +26,9 @@
 import org.eclipse.jetty.server.handler.DefaultHandler;

 import org.eclipse.jetty.server.handler.HandlerCollection;

 import org.eclipse.jetty.server.handler.ContextHandlerCollection;

+import org.eclipse.jetty.util.thread.ExecutorThreadPool;

+import org.eclipse.jetty.util.thread.QueuedThreadPool;

+import org.eclipse.jetty.util.thread.ThreadPool;

 

 import com.sun.net.httpserver.HttpServer;

 import com.sun.net.httpserver.HttpsServer;

@@ -53,11 +56,12 @@
 

         if (server == null)

         {

-        	server = new Server();

-        	

-        	HandlerCollection handlerCollection = new HandlerCollection();

-        	handlerCollection.setHandlers(new Handler[] {new ContextHandlerCollection(), new DefaultHandler()});

-			server.setHandler(handlerCollection);

+            ThreadPool threadPool = new DelegatingThreadPool(new QueuedThreadPool());

+            server = new Server(threadPool);

+

+            HandlerCollection handlerCollection = new HandlerCollection();

+            handlerCollection.setHandlers(new Handler[] {new ContextHandlerCollection(), new DefaultHandler()});

+            server.setHandler(handlerCollection);

 

             shared = false;

         }

diff --git a/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/ThreadPoolExecutorAdapter.java b/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/ThreadPoolExecutorAdapter.java
deleted file mode 100644
index aaffe8e..0000000
--- a/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/ThreadPoolExecutorAdapter.java
+++ /dev/null
Binary files differ
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpField.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpField.java
index aedfd2a..6d835dc 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpField.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpField.java
@@ -68,6 +68,7 @@
         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
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java
index 98e3e59..0df2964 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java
@@ -18,6 +18,9 @@
 
 package org.eclipse.jetty.http;
 
+import static org.eclipse.jetty.util.QuotedStringTokenizer.isQuoted;
+import static org.eclipse.jetty.util.QuotedStringTokenizer.quoteOnly;
+
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.text.SimpleDateFormat;
@@ -54,23 +57,27 @@
 /**
  * HTTP Fields. A collection of HTTP header and or Trailer fields.
  *
- * <p>This class is not synchronized as it is expected that modifications will only be performed by a
+ * <p>This class is not synchronised 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.
  *
  */
 public class HttpFields implements Iterable<HttpField>
 {
     private static final Logger LOG = Log.getLogger(HttpFields.class);
-    public static final String __COOKIE_DELIM="\"\\\n\r\t\f\b%+ ;=";
     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_PATH="\"\\\t%+ :;,@?=()<>{}[]";
+    public static final String __COOKIE_DELIM=__COOKIE_DELIM_PATH+"/";
+    
     static
     {
         __GMT.setID("GMT");
         __dateCache.setTimeZone(__GMT);
     }
-
+    
     public final static String __separators = ", \t";
 
     private static final String[] DAYS =
@@ -808,69 +815,87 @@
             final boolean isHttpOnly,
             int version)
     {
-        String delim=__COOKIE_DELIM;
-
         // Check arguments
         if (name == null || name.length() == 0)
             throw new IllegalArgumentException("Bad cookie name");
 
         // Format value and params
         StringBuilder buf = new StringBuilder(128);
-        String name_value_params;
-        QuotedStringTokenizer.quoteIfNeeded(buf, name, delim);
-        buf.append('=');
-        String start=buf.toString();
-        boolean hasDomain = false;
-        boolean hasPath = false;
         
-        if (value != null && value.length() > 0)
-            QuotedStringTokenizer.quoteIfNeeded(buf, value, delim);
+        // Name is checked by servlet spec, but can also be passed directly so check again
+        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);
 
-        if (path != null && path.length() > 0)
+        // 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 && isQuoteNeededForCookiePath(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)
         {
-            hasPath = true;
             buf.append(";Path=");
-            if (path.trim().startsWith("\""))
-                buf.append(path);
-            else
-                QuotedStringTokenizer.quoteIfNeeded(buf,path,delim);
+            quoteOnlyOrAppend(buf,path,quote_path);
         }
-        if (domain != null && domain.length() > 0)
+        
+        // Append domain
+        if (has_domain)
         {
-            hasDomain = true;
             buf.append(";Domain=");
-            QuotedStringTokenizer.quoteIfNeeded(buf,domain.toLowerCase(Locale.ENGLISH),delim);
+            quoteOnlyOrAppend(buf,domain,quote_domain);
         }
 
+        // Handle max-age and/or expires
         if (maxAge >= 0)
         {
-            // Always add the expires param as some browsers still don't handle max-age
+            // 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);
-
-            buf.append(";Max-Age=");
-            buf.append(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 && comment.length() > 0)
+        if (comment != null)
         {
             buf.append(";Comment=");
-            QuotedStringTokenizer.quoteIfNeeded(buf, comment, delim);
+            quoteOnlyOrAppend(buf,comment,isQuoteNeededForCookie(comment));
         }
 
-        name_value_params = buf.toString();
-
-        // remove existing set-cookie of same name
-
+        // remove any existing set-cookie fields of same name
         Iterator<HttpField> i=_fields.iterator();
         while (i.hasNext())
         {
@@ -878,26 +903,26 @@
             if (field.getHeader()==HttpHeader.SET_COOKIE)
             {
                 String val = (field.getValue() == null ? null : field.getValue().toString());
-                if (val!=null && val.startsWith(start))
+                if (val!=null && val.startsWith(name_equals))
                 {
                     //existing cookie has same name, does it also match domain and path?
-                    if (((!hasDomain && !val.contains("Domain")) || (hasDomain && val.contains("Domain="+domain))) &&
-                        ((!hasPath && !val.contains("Path")) || (hasPath && val.contains("Path="+path))))
+                    if (((!has_domain && !val.contains("Domain")) || (has_domain && val.contains(domain))) &&
+                        ((!has_path && !val.contains("Path")) || (has_path && val.contains(path))))
                     {
                         i.remove();
                     }
                 }
-                
             }
         }
         
-        add(HttpHeader.SET_COOKIE.toString(), name_value_params);
+        // 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) throws IOException
+    public void putTo(ByteBuffer bufferInFillMode) 
     {
         for (HttpField field : _fields)
         {
@@ -1095,19 +1120,20 @@
             }
         }
 
-        List vl = LazyList.getList(list, false);
-        if (vl.size() < 2) return vl;
+        List<String> vl = LazyList.getList(list, false);
+        if (vl.size() < 2) 
+            return vl;
 
-        List ql = LazyList.getList(qual, false);
+        List<Float> ql = LazyList.getList(qual, false);
 
         // sort list with swaps
         Float last = __zero;
         for (int i = vl.size(); i-- > 0;)
         {
-            Float q = (Float) ql.get(i);
+            Float q = ql.get(i);
             if (last.compareTo(q) > 0)
             {
-                Object tmp = vl.get(i);
+                String tmp = vl.get(i);
                 vl.set(i, vl.get(i + 1));
                 vl.set(i + 1, tmp);
                 ql.set(i, ql.get(i + 1));
@@ -1123,4 +1149,66 @@
     }
 
 
+
+    /* ------------------------------------------------------------ */
+    /** 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;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Does a cookie path 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 isQuoteNeededForCookiePath(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_PATH.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);
+    }
 }
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java
index 54ecd02..af09866 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java
@@ -55,13 +55,19 @@
     private long _contentPrepared = 0;
     private boolean _noContent = false;
     private Boolean _persistent = null;
-    private boolean _sendServerVersion;
+
+    private final int _send;
+    private final static int SEND_SERVER = 0x01;
+    private final static int SEND_XPOWEREDBY = 0x02;
 
 
     /* ------------------------------------------------------------------------------- */
-    public static void setServerVersion(String version)
+    public static void setJettyVersion(String serverVersion)
     {
-        SERVER=StringUtil.getBytes("Server: Jetty("+version+")\015\012");
+        SEND[SEND_SERVER] = StringUtil.getBytes("Server: " + serverVersion + "\015\012");
+        SEND[SEND_XPOWEREDBY] = StringUtil.getBytes("X-Powered-By: " + serverVersion + "\015\012");
+        SEND[SEND_SERVER | SEND_XPOWEREDBY] = StringUtil.getBytes("Server: " + serverVersion + "\015\012X-Powered-By: " +
+                serverVersion + "\015\012");
     }
 
     /* ------------------------------------------------------------------------------- */
@@ -71,6 +77,13 @@
     /* ------------------------------------------------------------------------------- */
     public HttpGenerator()
     {
+        this(false,false);
+    }
+    
+    /* ------------------------------------------------------------------------------- */
+    public HttpGenerator(boolean sendServerVersion,boolean sendXPoweredBy)
+    {
+        _send=(sendServerVersion?SEND_SERVER:0) | (sendXPoweredBy?SEND_XPOWEREDBY:0);
     }
 
     /* ------------------------------------------------------------------------------- */
@@ -86,15 +99,17 @@
     }
 
     /* ------------------------------------------------------------ */
+    @Deprecated
     public boolean getSendServerVersion ()
     {
-        return _sendServerVersion;
+        return (_send&SEND_SERVER)!=0;
     }
 
     /* ------------------------------------------------------------ */
+    @Deprecated
     public void setSendServerVersion (boolean sendServerVersion)
     {
-        _sendServerVersion = sendServerVersion;
+        throw new UnsupportedOperationException();
     }
 
     /* ------------------------------------------------------------ */
@@ -537,11 +552,11 @@
     /* ------------------------------------------------------------ */
     private void generateHeaders(Info _info,ByteBuffer header,ByteBuffer content,boolean last)
     {
-        final RequestInfo _request=(_info instanceof RequestInfo)?(RequestInfo)_info:null;
-        final ResponseInfo _response=(_info instanceof ResponseInfo)?(ResponseInfo)_info:null;
+        final RequestInfo request=(_info instanceof RequestInfo)?(RequestInfo)_info:null;
+        final ResponseInfo response=(_info instanceof ResponseInfo)?(ResponseInfo)_info:null;
 
         // default field values
-        boolean has_server = false;
+        int send=_send;
         HttpField transfer_encoding=null;
         boolean keep_alive=false;
         boolean close=false;
@@ -584,7 +599,7 @@
 
                     case CONNECTION:
                     {
-                        if (_request!=null)
+                        if (request!=null)
                             field.putTo(header);
 
                         // Lookup and/or split connection value field
@@ -619,7 +634,7 @@
                                 case CLOSE:
                                 {
                                     close=true;
-                                    if (_response!=null)
+                                    if (response!=null)
                                     {
                                         _persistent=false;
                                         if (_endOfContent == EndOfContent.UNKNOWN_CONTENT)
@@ -633,7 +648,7 @@
                                     if (_info.getHttpVersion() == HttpVersion.HTTP_1_0)
                                     {
                                         keep_alive = true;
-                                        if (_response!=null)
+                                        if (response!=null)
                                             _persistent=true;
                                     }
                                     break;
@@ -656,11 +671,8 @@
 
                     case SERVER:
                     {
-                        if (getSendServerVersion())
-                        {
-                            has_server=true;
-                            field.putTo(header);
-                        }
+                        send=send&~SEND_SERVER;
+                        field.putTo(header);
                         break;
                     }
 
@@ -680,7 +692,7 @@
         // 4. Content-Length
         // 5. multipart/byteranges
         // 6. close
-        int status=_response!=null?_response.getStatus():-1;
+        int status=response!=null?response.getStatus():-1;
         switch (_endOfContent)
         {
             case UNKNOWN_CONTENT:
@@ -688,14 +700,14 @@
                 // written yet?
 
                 // Response known not to have a body
-                if (_contentPrepared == 0 && _response!=null && (status < 200 || status == 204 || status == 304))
+                if (_contentPrepared == 0 && response!=null && (status < 200 || status == 204 || status == 304))
                     _endOfContent=EndOfContent.NO_CONTENT;
                 else if (_info.getContentLength()>0)
                 {
                     // we have been given a content length
                     _endOfContent=EndOfContent.CONTENT_LENGTH;
                     long content_length = _info.getContentLength();
-                    if ((_response!=null || content_length>0 || content_type ) && !_noContent)
+                    if ((response!=null || content_length>0 || content_type ) && !_noContent)
                     {
                         // known length but not actually set.
                         header.put(HttpHeader.CONTENT_LENGTH.getBytesColonSpace());
@@ -710,7 +722,7 @@
                     long content_length = _contentPrepared+BufferUtil.length(content);
 
                     // Do we need to tell the headers about it
-                    if ((_response!=null || content_length>0 || content_type ) && !_noContent)
+                    if ((response!=null || content_length>0 || content_type ) && !_noContent)
                     {
                         header.put(HttpHeader.CONTENT_LENGTH.getBytesColonSpace());
                         BufferUtil.putDecLong(header, content_length);
@@ -721,7 +733,7 @@
                 {
                     // No idea, so we must assume that a body is coming
                     _endOfContent = (!isPersistent() || _info.getHttpVersion().ordinal() < HttpVersion.HTTP_1_1.ordinal() ) ? EndOfContent.EOF_CONTENT : EndOfContent.CHUNKED_CONTENT;
-                    if (_response!=null && _endOfContent==EndOfContent.EOF_CONTENT)
+                    if (response!=null && _endOfContent==EndOfContent.EOF_CONTENT)
                     {
                         _endOfContent=EndOfContent.NO_CONTENT;
                         _noContent=true;
@@ -731,7 +743,7 @@
 
             case CONTENT_LENGTH:
                 long content_length = _info.getContentLength();
-                if ((_response!=null || content_length>0 || content_type ) && !_noContent)
+                if ((response!=null || content_length>0 || content_type ) && !_noContent)
                 {
                     // known length but not actually set.
                     header.put(HttpHeader.CONTENT_LENGTH.getBytesColonSpace());
@@ -741,12 +753,12 @@
                 break;
 
             case NO_CONTENT:
-                if (_response!=null && status >= 200 && status != 204 && status != 304)
+                if (response!=null && status >= 200 && status != 204 && status != 304)
                     header.put(CONTENT_LENGTH_0);
                 break;
 
             case EOF_CONTENT:
-                _persistent = _request!=null;
+                _persistent = request!=null;
                 break;
 
             case CHUNKED_CONTENT:
@@ -780,7 +792,7 @@
         }
 
         // If this is a response, work out persistence
-        if (_response!=null)
+        if (response!=null)
         {
             if (!isPersistent() && (close || _info.getHttpVersion().ordinal() > HttpVersion.HTTP_1_0.ordinal()))
             {
@@ -814,8 +826,8 @@
             }
         }
 
-        if (!has_server && status>199 && getSendServerVersion())
-            header.put(SERVER);
+        if (status>199)
+            header.put(SEND[send]);
 
         // end the header.
         header.put(HttpTokens.CRLF);
@@ -851,7 +863,12 @@
     private static final byte[] HTTP_1_1_SPACE = StringUtil.getBytes(HttpVersion.HTTP_1_1+" ");
     private static final byte[] CRLF = StringUtil.getBytes("\015\012");
     private static final byte[] TRANSFER_ENCODING_CHUNKED = StringUtil.getBytes("Transfer-Encoding: chunked\015\012");
-    private static byte[] SERVER = StringUtil.getBytes("Server: Jetty(7.0.x)\015\012");
+    private static final byte[][] SEND = new byte[][]{
+            new byte[0],
+            StringUtil.getBytes("Server: Jetty(9.x.x)\015\012"),
+        StringUtil.getBytes("X-Powered-By: Jetty(9.x.x)\015\012"),
+        StringUtil.getBytes("Server: Jetty(9.x.x)\015\012X-Powered-By: Jetty(9.x.x)\015\012")
+    };
 
     /* ------------------------------------------------------------------------------- */
     /* ------------------------------------------------------------------------------- */
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpHeader.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpHeader.java
index 202eae1..8bba922 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpHeader.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpHeader.java
@@ -108,6 +108,8 @@
     SET_COOKIE2("Set-Cookie2"),
     MIME_VERSION("MIME-Version"),
     IDENTITY("identity"),
+    
+    X_POWERED_BY("X-Powered-By"),
 
     UNKNOWN("::UNKNOWN::");
 
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java
index 6b27173..15b2011 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java
@@ -41,7 +41,7 @@
     MOVE;
 
     /* ------------------------------------------------------------ */
-    /** 
+    /**
      * Optimised lookup to find a method name and trailing space in a byte array.
      * @param bytes Array containing ISO-8859-1 characters
      * @param position The first valid index
@@ -70,22 +70,22 @@
                     return HEAD;
                 break;
             case 'O':
-                if (bytes[position+1]=='O' && bytes[position+2]=='T' && bytes[position+3]=='I' && length>=8 && 
+                if (bytes[position+1]=='O' && bytes[position+2]=='T' && bytes[position+3]=='I' && length>=8 &&
                 bytes[position+4]=='O' && bytes[position+5]=='N' && bytes[position+6]=='S' && bytes[position+7]==' ' )
                     return OPTIONS;
                 break;
             case 'D':
-                if (bytes[position+1]=='E' && bytes[position+2]=='L' && bytes[position+3]=='E' && length>=7 && 
+                if (bytes[position+1]=='E' && bytes[position+2]=='L' && bytes[position+3]=='E' && length>=7 &&
                 bytes[position+4]=='T' && bytes[position+5]=='E' && bytes[position+6]==' ' )
                     return DELETE;
                 break;
             case 'T':
-                if (bytes[position+1]=='R' && bytes[position+2]=='A' && bytes[position+3]=='C' && length>=6 && 
+                if (bytes[position+1]=='R' && bytes[position+2]=='A' && bytes[position+3]=='C' && length>=6 &&
                 bytes[position+4]=='E' && bytes[position+5]==' ' )
                     return TRACE;
                 break;
             case 'C':
-                if (bytes[position+1]=='O' && bytes[position+2]=='N' && bytes[position+3]=='N' && length>=8 && 
+                if (bytes[position+1]=='O' && bytes[position+2]=='N' && bytes[position+3]=='N' && length>=8 &&
                 bytes[position+4]=='E' && bytes[position+5]=='C' && bytes[position+6]=='T' && bytes[position+7]==' ' )
                     return CONNECT;
                 break;
@@ -93,7 +93,7 @@
                 if (bytes[position+1]=='O' && bytes[position+2]=='V' && bytes[position+3]=='E' &&  bytes[position+4]==' ')
                     return MOVE;
                 break;
-                
+
             default:
                 break;
         }
@@ -101,7 +101,7 @@
     }
 
     /* ------------------------------------------------------------ */
-    /** 
+    /**
      * Optimised lookup to find a method name and trailing space in a byte array.
      * @param buffer buffer containing ISO-8859-1 characters
      * @return A HttpMethod if a match or null if no easy match.
@@ -110,14 +110,14 @@
     {
         if (buffer.hasArray())
             return lookAheadGet(buffer.array(),buffer.arrayOffset()+buffer.position(),buffer.arrayOffset()+buffer.limit());
-        
+
         // TODO use cache and check for space
         // return CACHE.getBest(buffer,0,buffer.remaining());
         return null;
     }
-    
+
     /* ------------------------------------------------------------ */
-    public final static Trie<HttpMethod> CACHE= new ArrayTrie<HttpMethod>();
+    public final static Trie<HttpMethod> CACHE= new ArrayTrie<>();
     static
     {
         for (HttpMethod method : HttpMethod.values())
@@ -144,15 +144,15 @@
     /* ------------------------------------------------------------ */
     public boolean is(String s)
     {
-        return toString().equalsIgnoreCase(s);    
+        return toString().equalsIgnoreCase(s);
     }
-    
+
     /* ------------------------------------------------------------ */
     public ByteBuffer asBuffer()
     {
         return _buffer.asReadOnlyBuffer();
     }
-    
+
     /* ------------------------------------------------------------ */
     public String asString()
     {
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java
index b0e7909..db46429 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java
@@ -63,11 +63,18 @@
  * per parser dynamic Trie of {@link HttpFields} from previous parsed messages
  * is used to help the parsing of subsequent messages.
  * </p>
+ * <p>
+ * If the system property "org.eclipse.jetty.http.HttpParser.STRICT" is set to true,
+ * then the parser will strictly pass on the exact strings received for methods and header
+ * fields.  Otherwise a fast case insensitive string lookup is used that may alter the
+ * case of the method and/or headers
+ * </p>
  */
 public class HttpParser
 {
     public static final Logger LOG = Log.getLogger(HttpParser.class);
-    static final int INITIAL_URI_LENGTH=256;
+    public final static boolean __STRICT=Boolean.getBoolean("org.eclipse.jetty.http.HttpParser.STRICT"); 
+    public final static int INITIAL_URI_LENGTH=256;
 
     // States
     public enum State
@@ -82,7 +89,6 @@
         REQUEST_VERSION,
         REASON,
         HEADER,
-        HEADER_NAME,
         HEADER_IN_NAME,
         HEADER_VALUE,
         HEADER_IN_VALUE,
@@ -96,10 +102,12 @@
         CLOSED
     };
 
+    private final boolean DEBUG=LOG.isDebugEnabled();
     private final HttpHandler<ByteBuffer> _handler;
     private final RequestHandler<ByteBuffer> _requestHandler;
     private final ResponseHandler<ByteBuffer> _responseHandler;
     private final int _maxHeaderBytes;
+    private final boolean _strict;
     private HttpField _field;
     private HttpHeader _header;
     private String _headerString;
@@ -131,31 +139,45 @@
     /* ------------------------------------------------------------------------------- */
     public HttpParser(RequestHandler<ByteBuffer> handler)
     {
-        this(handler,-1);
+        this(handler,-1,__STRICT);
     }
 
     /* ------------------------------------------------------------------------------- */
     public HttpParser(ResponseHandler<ByteBuffer> handler)
     {
-        this(handler,-1);
+        this(handler,-1,__STRICT);
     }
 
     /* ------------------------------------------------------------------------------- */
     public HttpParser(RequestHandler<ByteBuffer> handler,int maxHeaderBytes)
     {
-        _handler=handler;
-        _requestHandler=handler;
-        _responseHandler=null;
-        _maxHeaderBytes=maxHeaderBytes;
+        this(handler,maxHeaderBytes,__STRICT);
     }
 
     /* ------------------------------------------------------------------------------- */
     public HttpParser(ResponseHandler<ByteBuffer> handler,int maxHeaderBytes)
     {
+        this(handler,maxHeaderBytes,__STRICT);
+    }
+    
+    /* ------------------------------------------------------------------------------- */
+    public HttpParser(RequestHandler<ByteBuffer> handler,int maxHeaderBytes,boolean strict)
+    {
+        _handler=handler;
+        _requestHandler=handler;
+        _responseHandler=null;
+        _maxHeaderBytes=maxHeaderBytes;
+        _strict=strict;
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    public HttpParser(ResponseHandler<ByteBuffer> handler,int maxHeaderBytes,boolean strict)
+    {
         _handler=handler;
         _requestHandler=null;
         _responseHandler=handler;
         _maxHeaderBytes=maxHeaderBytes;
+        _strict=strict;
     }
 
     /* ------------------------------------------------------------------------------- */
@@ -239,6 +261,7 @@
         return _state == state;
     }
 
+    /* ------------------------------------------------------------------------------- */
     private static class BadMessage extends Error
     {
         private final int _code;
@@ -309,7 +332,7 @@
                 if (ch==HttpTokens.LINE_FEED)
                     return ch;
 
-                throw new BadMessage();
+                throw new BadMessage("Bad EOL");
             }
 
             // Defer lookup of LF
@@ -319,7 +342,7 @@
         
         // Only LF or TAB acceptable special characters
         if (ch!=HttpTokens.LINE_FEED && ch!=HttpTokens.TAB)
-            throw new BadMessage();
+            throw new BadMessage("Illegal character");
         
         /*
         if (ch>HttpTokens.SPACE)
@@ -374,14 +397,16 @@
         return false;
     }
 
-    private String takeString()
+    /* ------------------------------------------------------------------------------- */
+    private void setString(String s)
     {
-        String s =_string.toString();
         _string.setLength(0);
-        return s;
+        _string.append(s);
+        _length=s.length();
     }
-
-    private String takeLengthString()
+    
+    /* ------------------------------------------------------------------------------- */
+    private String takeString()
     {
         _string.setLength(_length);
         String s =_string.toString();
@@ -402,8 +427,6 @@
         {
             // process each character
             byte ch=next(buffer);
-            if (ch==-1)
-                return true;
             if (ch==0)
                 continue;
 
@@ -429,16 +452,15 @@
                 case METHOD:
                     if (ch == HttpTokens.SPACE)
                     {
+                        _length=_string.length();
                         _methodString=takeString();
                         HttpMethod method=HttpMethod.CACHE.get(_methodString);
-                        if (method!=null)
+                        if (method!=null && !_strict)
                             _methodString=method.asString();
                         setState(State.SPACE1);
                     }
-                    else if (ch < HttpTokens.SPACE && ch>=0)
-                    {
-                        throw new BadMessage(HttpStatus.BAD_REQUEST_400,"No URI");
-                    }
+                    else if (ch<HttpTokens.SPACE)
+                        throw new BadMessage(ch<0?"Illegal character":"No URI");
                     else
                         _string.append((char)ch);
                     break;
@@ -446,6 +468,7 @@
                 case RESPONSE_VERSION:
                     if (ch == HttpTokens.SPACE)
                     {
+                        _length=_string.length();
                         String version=takeString();
                         _version=HttpVersion.CACHE.get(version);
                         if (_version==null)
@@ -454,16 +477,14 @@
                         }
                         setState(State.SPACE1);
                     }
-                    else if (ch < HttpTokens.SPACE && ch>=0)
-                    {
-                        throw new BadMessage(HttpStatus.BAD_REQUEST_400,"No Status");
-                    }
+                    else if (ch < HttpTokens.SPACE)
+                        throw new BadMessage(ch<0?"Illegal character":"No Status");
                     else
                         _string.append((char)ch);
                     break;
 
                 case SPACE1:
-                    if (ch > HttpTokens.SPACE || ch<0)
+                    if (ch > HttpTokens.SPACE)
                     {
                         if (_responseHandler!=null)
                         {
@@ -507,9 +528,7 @@
                         }
                     }
                     else if (ch < HttpTokens.SPACE)
-                    {
                         throw new BadMessage(HttpStatus.BAD_REQUEST_400,_requestHandler!=null?"No URI":"No Status");
-                    }
                     break;
 
                 case STATUS:
@@ -528,7 +547,7 @@
                     }
                     else
                     {
-                        throw new IllegalStateException();
+                        throw new BadMessage();
                     }
                     break;
 
@@ -561,7 +580,7 @@
                     break;
 
                 case SPACE2:
-                    if (ch > HttpTokens.SPACE || ch<0)
+                    if (ch > HttpTokens.SPACE)
                     {
                         _string.setLength(0);
                         _string.append((char)ch);
@@ -620,13 +639,18 @@
                             return_from_parse=_handler.messageComplete()||return_from_parse;
                         }
                     }
+                    else if (ch<HttpTokens.SPACE)
+                        throw new BadMessage();
                     break;
 
                 case REQUEST_VERSION:
                     if (ch == HttpTokens.LINE_FEED)
                     {
                         if (_version==null)
+                        {
+                            _length=_string.length();
                             _version=HttpVersion.CACHE.get(takeString());
+                        }
                         if (_version==null)
                         {
                             throw new BadMessage(HttpStatus.BAD_REQUEST_400,"Unknown Version");
@@ -645,26 +669,30 @@
                         return_from_parse=_requestHandler.startRequest(_method,_methodString,_uri, _version)||return_from_parse;
                         continue;
                     }
-                    else
+                    else if (ch>HttpTokens.SPACE)
                         _string.append((char)ch);
+                    else
+                        throw new BadMessage();
 
                     break;
 
                 case REASON:
                     if (ch == HttpTokens.LINE_FEED)
                     {
-                        String reason=takeLengthString();
+                        String reason=takeString();
 
                         setState(State.HEADER);
                         return_from_parse=_responseHandler.startResponse(_version, _responseStatus, reason)||return_from_parse;
                         continue;
                     }
-                    else
+                    else if (ch>=HttpTokens.SPACE)
                     {
                         _string.append((char)ch);
                         if (ch!=' '&&ch!='\t')
                             _length=_string.length();
                     }
+                    else
+                        throw new BadMessage();
                     break;
 
                 default:
@@ -741,7 +769,8 @@
                             }
                             catch (NumberFormatException e)
                             {
-                                LOG.debug(e);
+                                if (DEBUG)
+                                    LOG.debug(e);
                                 throw new BadMessage(HttpStatus.BAD_REQUEST_400,"Bad Host header");
                             }
                             break loop;
@@ -806,8 +835,6 @@
         {
             // process each character
             byte ch=next(buffer);
-            if (ch==-1)
-                return true;
             if (ch==0)
                 continue;
             
@@ -827,14 +854,18 @@
                         case HttpTokens.TAB:
                         {
                             // header value without name - continuation?
-                            _string.setLength(0);
-                            if (_valueString!=null)
+                            if (_valueString==null)
                             {
-                                _string.append(_valueString);
-                                _string.append(' ');
+                                _string.setLength(0);
+                                _length=0;
                             }
-                            _length=_string.length();
-                            _valueString=null;
+                            else
+                            {
+                                setString(_valueString);
+                                _string.append(' ');
+                                _length++;
+                                _valueString=null;
+                            }
                             setState(State.HEADER_VALUE);
                             break;
                         }
@@ -917,6 +948,8 @@
                                         break;
                                 }
                             }
+                            else if (ch<=HttpTokens.SPACE)
+                                throw new BadMessage();
                             else
                             {
                                 if (buffer.hasRemaining())
@@ -928,14 +961,35 @@
                                         
                                     if (field!=null)
                                     {
-                                        String n=field.getName();
-                                        String v=field.getValue();
+                                        final String n;
+                                        final String v;
+
+                                        if (_strict)
+                                        {
+                                            // Have to get the fields exactly from the buffer to match case
+                                            String fn=field.getName();
+                                            String fv=field.getValue();
+                                            n=BufferUtil.toString(buffer,buffer.position()-1,fn.length(),StringUtil.__US_ASCII_CHARSET);
+                                            if (fv==null)
+                                                v=null;
+                                            else
+                                            {
+                                                v=BufferUtil.toString(buffer,buffer.position()+fn.length()+1,fv.length(),StringUtil.__ISO_8859_1_CHARSET);
+                                                field=new HttpField(field.getHeader(),n,v);
+                                            }
+                                        }
+                                        else
+                                        {
+                                            n=field.getName();
+                                            v=field.getValue(); 
+                                        }
+                                        
+                                        _header=field.getHeader();
+                                        _headerString=n;
          
                                         if (v==null)
                                         {
                                             // Header only
-                                            _header=field.getHeader();
-                                            _headerString=n;
                                             setState(State.HEADER_VALUE);
                                             _string.setLength(0);
                                             _length=0;
@@ -951,8 +1005,6 @@
                                             if (b==HttpTokens.CARRIAGE_RETURN || b==HttpTokens.LINE_FEED)
                                             {                     
                                                 _field=field;
-                                                _header=_field.getHeader();
-                                                _headerString=n;
                                                 _valueString=v;
                                                 setState(State.HEADER_IN_VALUE);
 
@@ -965,186 +1017,111 @@
                                                     buffer.position(pos);
                                                 break;
                                             }
+                                            else
+                                            {
+                                                setState(State.HEADER_IN_VALUE);
+                                                setString(v);
+                                                buffer.position(pos);
+                                                break;
+                                            }
                                         }
                                     }
                                 }
 
                                 // New header
-                                setState(State.HEADER_NAME);
+                                setState(State.HEADER_IN_NAME);
                                 _string.setLength(0);
                                 _string.append((char)ch);
                                 _length=1;
                             }
                         }
                     }
-
-                    break;
-
-                case HEADER_NAME:
-                    switch(ch)
-                    {
-                        case HttpTokens.LINE_FEED:
-                            if (_headerString==null)
-                            {
-                                _headerString=takeLengthString();
-                                _header=HttpHeader.CACHE.get(_headerString);
-                            }
-                            setState(State.HEADER);
-
-                            break;
-
-                        case HttpTokens.COLON:
-                            if (_headerString==null)
-                            {
-                                _headerString=takeLengthString();
-                                _header=HttpHeader.CACHE.get(_headerString); 
-                            }
-                            setState(State.HEADER_VALUE);
-                            break;
-                        case HttpTokens.SPACE:
-                        case HttpTokens.TAB:
-                            _string.append((char)ch);
-                            break;
-                        default:
-                        {
-                            _string.append((char)ch);
-                            _length=_string.length();
-                            setState(State.HEADER_IN_NAME);
-                        }
-                    }
-
                     break;
 
                 case HEADER_IN_NAME:
-                    switch(ch)
+                    if (ch==HttpTokens.COLON || ch==HttpTokens.LINE_FEED)
                     {
-                        case HttpTokens.LINE_FEED:
+                        if (_headerString==null)
+                        {
                             _headerString=takeString();
-                            _length=-1;
                             _header=HttpHeader.CACHE.get(_headerString);
-                            setState(State.HEADER);
-                            break;
+                        }
+                        _length=-1;
 
-                        case HttpTokens.COLON:
-                            if (_headerString==null)
-                            {
-                                _headerString=takeString();
-                                _header=HttpHeader.CACHE.get(_headerString);
-                            }
-                            _length=-1;
-                            setState(State.HEADER_VALUE);
-                            break;
-                        case HttpTokens.SPACE:
-                        case HttpTokens.TAB:
-                            if (_header!=null)
-                            {
-                                _string.setLength(0);
-                                _string.append(_header.asString());
-                                _length=_string.length();
-                                _header=null;
-                                _headerString=null;
-                            }
-                            setState(State.HEADER_NAME);
-                            _string.append((char)ch);
-                            break;
-                        default:
-                            if (_header!=null)
-                            {
-                                _string.setLength(0);
-                                _string.append(_header.asString());
-                                _length=_string.length();
-                                _header=null;
-                                _headerString=null;
-                            }
-                            _string.append((char)ch);
-                            _length++;
+                        setState(ch==HttpTokens.LINE_FEED?State.HEADER:State.HEADER_VALUE);
+                        break;
                     }
-                    break;
+                    
+                    if (ch>=HttpTokens.SPACE || ch==HttpTokens.TAB)
+                    {
+                        if (_header!=null)
+                        {
+                            setString(_header.asString());
+                            _header=null;
+                            _headerString=null;
+                        }
+
+                        _string.append((char)ch);
+                        if (ch>HttpTokens.SPACE)
+                            _length=_string.length();
+                        break;
+                    }
+                     
+                    throw new BadMessage("Illegal character");
 
                 case HEADER_VALUE:
-                    switch(ch)
+                    if (ch>HttpTokens.SPACE || ch<0)
                     {
-                        case HttpTokens.LINE_FEED:
-                            if (_length > 0)
-                            {
-                                if (_valueString!=null)
-                                {
-                                    // multi line value!
-                                    _value=null;
-                                    _valueString+=" "+takeLengthString();
-                                }
-                                else if (HttpHeaderValue.hasKnownValues(_header))
-                                {
-                                    _valueString=takeLengthString();
-                                    _value=HttpHeaderValue.CACHE.get(_valueString);
-                                }
-                                else
-                                {
-                                    _value=null;
-                                    _valueString=takeLengthString();
-                                }
-                            }
-                            setState(State.HEADER);
-                            break;
-                        case HttpTokens.SPACE:
-                        case HttpTokens.TAB:
-                            break;
-                        default:
-                        {
-                            _string.append((char)ch);
-                            _length=_string.length();
-                            setState(State.HEADER_IN_VALUE);
-                        }
+                        _string.append((char)(0xff&ch));
+                        _length=_string.length();
+                        setState(State.HEADER_IN_VALUE);
+                        break;
                     }
-                    break;
+                    
+                    if (ch==HttpTokens.SPACE || ch==HttpTokens.TAB)
+                        break;
+                    
+                    if (ch==HttpTokens.LINE_FEED)
+                    {
+                        if (_length > 0)
+                        {
+                            _value=null;
+                            _valueString=(_valueString==null)?takeString():(_valueString+" "+takeString());
+                        }
+                        setState(State.HEADER);
+                        break; 
+                    }
+
+                    throw new BadMessage("Illegal character");
 
                 case HEADER_IN_VALUE:
-                    switch(ch)
+                    if (ch>=HttpTokens.SPACE || ch<0)
                     {
-                        case HttpTokens.LINE_FEED:
-                            if (_length > 0)
-                            {
-                                if (HttpHeaderValue.hasKnownValues(_header))
-                                {
-                                    _valueString=takeString();
-                                    _value=HttpHeaderValue.CACHE.get(_valueString);
-                                }
-                                else
-                                {
-                                    _value=null;
-                                    _valueString=takeString();
-                                }
-                                _length=-1;
-                            }
-                            setState(State.HEADER);
-                            break;
-                        case HttpTokens.SPACE:
-                        case HttpTokens.TAB:
-                            if (_valueString!=null)
-                            {
-                                _string.setLength(0);
-                                _string.append(_valueString);
-                                _length=_valueString.length();
-                                _valueString=null;
-                                _field=null;
-                            }
-                            _string.append((char)ch);
-                            setState(State.HEADER_VALUE);
-                            break;
-                        default:
-                            if (_valueString!=null)
-                            {
-                                _string.setLength(0);
-                                _string.append(_valueString);
-                                _length=_valueString.length();
-                                _valueString=null;
-                                _field=null;
-                            }
-                            _string.append((char)ch);
-                            _length++;
+                        if (_valueString!=null)
+                        {
+                            setString(_valueString);
+                            _valueString=null;
+                            _field=null;
+                        }
+                        _string.append((char)(0xff&ch));
+                        if (ch>HttpTokens.SPACE || ch<0)
+                            _length=_string.length();
+                        break;
                     }
-                    break;
-
+                    
+                    if (ch==HttpTokens.LINE_FEED)
+                    {
+                        if (_length > 0)
+                        {
+                            _value=null;
+                            _valueString=takeString();
+                            _length=-1;
+                        }
+                        setState(State.HEADER);
+                        break;
+                    }
+                    throw new BadMessage("Illegal character");
+                    
                 default:
                     throw new IllegalStateException(_state.toString());
 
@@ -1368,8 +1345,9 @@
         {
             BufferUtil.clear(buffer);
 
-            LOG.warn("badMessage: "+e._code+(e._message!=null?" "+e._message:"")+" for "+_handler);
-            LOG.debug(e);
+            LOG.warn("BadMessage: "+e._code+(e._message!=null?" "+e._message:"")+" for "+_handler);
+            if (DEBUG)
+                LOG.debug(e);
             setState(State.CLOSED);
             _handler.badMessage(e._code, e._message);
             return false;
@@ -1378,8 +1356,9 @@
         {
             BufferUtil.clear(buffer);
 
-            LOG.warn("badMessage: "+e.toString()+" for "+_handler);
-            LOG.debug(e);
+            LOG.warn("Parsing Exception: "+e.toString()+" for "+_handler);
+            if (DEBUG) 
+                LOG.debug(e);
             
             if (_state.ordinal()<=State.END.ordinal())
             {
@@ -1408,7 +1387,8 @@
      */
     public boolean shutdownInput()
     {
-        LOG.debug("shutdownInput {}", this);
+        if (DEBUG)
+            LOG.debug("shutdownInput {}", this);
 
         // was this unexpected?
         switch(_state)
@@ -1437,6 +1417,8 @@
     /* ------------------------------------------------------------------------------- */
     public void close()
     {
+        if (DEBUG)
+            LOG.debug("close {}", this);
         switch(_state)
         {
             case START:
@@ -1469,6 +1451,8 @@
     /* ------------------------------------------------------------------------------- */
     public void reset()
     {
+        if (DEBUG)
+            LOG.debug("reset {}", this);
         // reset state
         setState(State.START);
         _endOfContent=EndOfContent.UNKNOWN_CONTENT;
@@ -1483,7 +1467,8 @@
     /* ------------------------------------------------------------------------------- */
     private void setState(State state)
     {
-        // LOG.debug("{} --> {}",_state,state);
+        if (DEBUG)
+            LOG.debug("{} --> {}",_state,state);
         _state=state;
     }
 
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpTester.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpTester.java
index b14221d..0170d3e 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpTester.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpTester.java
@@ -92,6 +92,19 @@
             _version=version;
         }
 
+        public void setContent(byte[] bytes)
+        {
+            try
+            {
+                _content=new ByteArrayOutputStream();
+                _content.write(bytes);
+            }
+            catch (IOException e)
+            {
+                throw new RuntimeException(e);
+            }
+        }
+
         public void setContent(String content)
         {
             try
diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldsTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldsTest.java
index 3f8a91d..d1674aa 100644
--- a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldsTest.java
+++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldsTest.java
@@ -279,10 +279,10 @@
         //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;Path=path;Domain=domain;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",fields.getStringField("Set-Cookie"));
+        assertEquals("everything=value;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;Path=path;Domain=domain;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement());
+        assertEquals("everything=value;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());
@@ -293,9 +293,9 @@
         fields.addSetCookie("everything","value","domain2","path",0,"comment",true,true,0);
         e =fields.getValues("Set-Cookie");
         assertTrue(e.hasMoreElements());
-        assertEquals("everything=other;Path=path;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=blah",e.nextElement());
+        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;Path=path;Domain=domain2;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement());
+        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
@@ -304,9 +304,9 @@
         fields.addSetCookie("everything","value","","path",0,"comment",true,true,0);
         e =fields.getValues("Set-Cookie");
         assertTrue(e.hasMoreElements());
-        assertEquals("everything=other;Path=path;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=blah",e.nextElement());
+        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;Path=path;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement());
+        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());
         
         
@@ -316,9 +316,9 @@
         fields.addSetCookie("everything","value","domain1","path2",0,"comment",true,true,0);
         e =fields.getValues("Set-Cookie");
         assertTrue(e.hasMoreElements());
-        assertEquals("everything=other;Path=path1;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=blah",e.nextElement());
+        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;Path=path2;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement());
+        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
@@ -327,9 +327,9 @@
         fields.addSetCookie("everything","value","domain1","",0,"comment",true,true,0);
         e =fields.getValues("Set-Cookie");
         assertTrue(e.hasMoreElements());
-        assertEquals("everything=other;Path=path1;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=blah",e.nextElement());
+        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;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement());
+        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
@@ -338,13 +338,13 @@
         fields.addSetCookie("everything","value","","",0,"comment",true,true,0);
         e =fields.getValues("Set-Cookie");
         assertTrue(e.hasMoreElements());
-        assertEquals("everything=value;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement());
+        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,2);
+        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\";Path=\"pa th\";Domain=\"do main\";Expires="));
+        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();
@@ -376,7 +376,7 @@
         fields=new HttpFields();
         fields.addSetCookie("name","value==",null,null,-1,null,false,false,0);
         setCookie=fields.getStringField("Set-Cookie");
-        assertEquals("name=\"value==\"",setCookie);
+        assertEquals("name=\"value==\";Version=1",setCookie);
 
     }
 
diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorServerTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorServerTest.java
index 262c0ac..026fd2c 100644
--- a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorServerTest.java
+++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorServerTest.java
@@ -34,6 +34,7 @@
 
 import org.eclipse.jetty.http.HttpGenerator.ResponseInfo;
 import org.eclipse.jetty.util.BufferUtil;
+import org.hamcrest.Matchers;
 import org.junit.Test;
 
 public class HttpGeneratorServerTest
@@ -309,6 +310,54 @@
             }
         }
     }
+    
+    @Test
+    public void testSendServerXPoweredBy() throws Exception
+    {
+        ByteBuffer header = BufferUtil.allocate(8096);
+        ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_1, new HttpFields(), -1, 200, null, false);
+        HttpFields fields = new HttpFields();
+        fields.add(HttpHeader.SERVER,"SomeServer");
+        fields.add(HttpHeader.X_POWERED_BY,"SomePower");
+        ResponseInfo infoF = new ResponseInfo(HttpVersion.HTTP_1_1, fields, -1, 200, null, false);
+        String head;
+        
+        HttpGenerator gen = new HttpGenerator(true,true);
+        gen.generateResponse(info, header, null, null, true);
+        head = BufferUtil.toString(header);
+        BufferUtil.clear(header);
+        assertThat(head, containsString("HTTP/1.1 200 OK"));
+        assertThat(head, containsString("Server: Jetty(9.x.x)"));
+        assertThat(head, containsString("X-Powered-By: Jetty(9.x.x)"));
+        gen.reset();
+        gen.generateResponse(infoF, header, null, null, true);
+        head = BufferUtil.toString(header);
+        BufferUtil.clear(header);
+        assertThat(head, containsString("HTTP/1.1 200 OK"));
+        assertThat(head, not(containsString("Server: Jetty(9.x.x)")));
+        assertThat(head, containsString("Server: SomeServer"));
+        assertThat(head, containsString("X-Powered-By: Jetty(9.x.x)"));
+        assertThat(head, containsString("X-Powered-By: SomePower"));
+        gen.reset();
+        
+        gen = new HttpGenerator(false,false);
+        gen.generateResponse(info, header, null, null, true);
+        head = BufferUtil.toString(header);
+        BufferUtil.clear(header);
+        assertThat(head, containsString("HTTP/1.1 200 OK"));
+        assertThat(head, not(containsString("Server: Jetty(9.x.x)")));
+        assertThat(head, not(containsString("X-Powered-By: Jetty(9.x.x)")));
+        gen.reset();
+        gen.generateResponse(infoF, header, null, null, true);
+        head = BufferUtil.toString(header);
+        BufferUtil.clear(header);
+        assertThat(head, containsString("HTTP/1.1 200 OK"));
+        assertThat(head, not(containsString("Server: Jetty(9.x.x)")));
+        assertThat(head, containsString("Server: SomeServer"));
+        assertThat(head, not(containsString("X-Powered-By: Jetty(9.x.x)")));
+        assertThat(head, containsString("X-Powered-By: SomePower"));
+        gen.reset(); 
+    }
 
     @Test
     public void testResponseNoContent() throws Exception
diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java
index b65e10a..654474d 100644
--- a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java
+++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java
@@ -20,6 +20,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 
 import java.nio.ByteBuffer;
@@ -29,6 +30,8 @@
 import org.eclipse.jetty.http.HttpParser.State;
 import org.eclipse.jetty.util.BufferUtil;
 import org.eclipse.jetty.util.StringUtil;
+import org.hamcrest.Matchers;
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -314,6 +317,115 @@
     }
 
     @Test
+    public void testEncodedHeader() throws Exception
+    {
+        ByteBuffer buffer=BufferUtil.allocate(4096);
+        BufferUtil.flipToFill(buffer); 
+        BufferUtil.put(BufferUtil.toBuffer("GET "),buffer);
+        buffer.put("/foo/\u0690/".getBytes(StringUtil.__UTF8_CHARSET));
+        BufferUtil.put(BufferUtil.toBuffer(" HTTP/1.0\r\n"),buffer);
+        BufferUtil.put(BufferUtil.toBuffer("Header1: "),buffer);
+        buffer.put("\u00e6 \u00e6".getBytes(StringUtil.__ISO_8859_1_CHARSET));
+        BufferUtil.put(BufferUtil.toBuffer("  \r\n\r\n"),buffer);
+        BufferUtil.flipToFlush(buffer,0);
+                    
+        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
+        HttpParser parser= new HttpParser(handler);
+        parseAll(parser,buffer);
+
+        assertEquals("GET", _methodOrVersion);
+        assertEquals("/foo/\u0690/", _uriOrStatus);
+        assertEquals("HTTP/1.0", _versionOrReason);
+        assertEquals("Header1", _hdr[0]);
+        assertEquals("\u00e6 \u00e6", _val[0]);
+        assertEquals(0, _h);
+        assertEquals(null,_bad);
+    }
+    
+    
+
+    @Test
+    public void testBadMethodEncoding() throws Exception
+    {
+        ByteBuffer buffer= BufferUtil.toBuffer(
+            "G\u00e6T / HTTP/1.0\r\nHeader0: value0\r\n\n\n");
+        
+        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
+        HttpParser parser= new HttpParser(handler);
+        parseAll(parser,buffer);
+        assertThat(_bad,Matchers.notNullValue());
+    }
+
+    @Test
+    public void testBadVersionEncoding() throws Exception
+    {
+        ByteBuffer buffer= BufferUtil.toBuffer(
+            "GET / H\u00e6P/1.0\r\nHeader0: value0\r\n\n\n");
+        
+        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
+        HttpParser parser= new HttpParser(handler);
+        parseAll(parser,buffer);
+        assertThat(_bad,Matchers.notNullValue());
+    }
+
+
+    @Test
+    public void testBadHeaderEncoding() throws Exception
+    {
+        ByteBuffer buffer= BufferUtil.toBuffer(
+            "GET / HTTP/1.0\r\nH\u00e6der0: value0\r\n\n\n");
+        
+        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
+        HttpParser parser= new HttpParser(handler);
+        parseAll(parser,buffer);
+        assertThat(_bad,Matchers.notNullValue());
+    } 
+
+    @Test
+    public void testNonStrict() throws Exception
+    {
+        ByteBuffer buffer= BufferUtil.toBuffer(
+                "get / http/1.0\015\012" +
+                "HOST: localhost\015\012" +
+                "cOnNeCtIoN: ClOsE\015\012"+
+                "\015\012");
+        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
+        HttpParser parser= new HttpParser(handler,-1,false);
+        parseAll(parser,buffer);
+
+        assertEquals("GET", _methodOrVersion);
+        assertEquals("/", _uriOrStatus);
+        assertEquals("HTTP/1.0", _versionOrReason);
+        assertEquals("Host", _hdr[0]);
+        assertEquals("localhost", _val[0]);
+        assertEquals("Connection", _hdr[1]);
+        assertEquals("close", _val[1]);
+        assertEquals(1, _h);
+    }
+    
+    @Test
+    public void testStrict() throws Exception
+    {
+        ByteBuffer buffer= BufferUtil.toBuffer(
+                "gEt / http/1.0\015\012" +
+                "HOST: localhost\015\012" +
+                "cOnNeCtIoN: ClOsE\015\012"+
+                "\015\012");
+        HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
+        HttpParser parser= new HttpParser(handler,-1,true);
+        parseAll(parser,buffer);
+
+        assertEquals("gEt", _methodOrVersion);
+        assertEquals("/", _uriOrStatus);
+        assertEquals("HTTP/1.0", _versionOrReason);
+        assertEquals("HOST", _hdr[0]);
+        assertEquals("localhost", _val[0]);
+        assertEquals("cOnNeCtIoN", _hdr[1]);
+        assertEquals("ClOsE", _val[1]);
+        assertEquals(1, _h);
+    }
+    
+    @Test
     public void testSplitHeaderParse() throws Exception
     {
         ByteBuffer buffer= BufferUtil.toBuffer(
@@ -426,7 +538,6 @@
                         + "\015\012"
                         + "0123456789\015\012");
 
-
         HttpParser.RequestHandler<ByteBuffer> handler  = new Handler();
         HttpParser parser= new HttpParser(handler);
         parser.parseNext(buffer);
@@ -1125,7 +1236,7 @@
         @Override
         public void badMessage(int status, String reason)
         {
-            _bad=reason;
+            _bad=reason==null?(""+status):reason;
         }
 
         @Override
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/NetworkTrafficSelectChannelEndPoint.java b/jetty-io/src/main/java/org/eclipse/jetty/io/NetworkTrafficSelectChannelEndPoint.java
index 5193f07..65878ce 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/NetworkTrafficSelectChannelEndPoint.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/NetworkTrafficSelectChannelEndPoint.java
@@ -57,7 +57,7 @@
             if (b.hasRemaining())
             {
                 int position = b.position();
-                flushed|=super.flush(b);
+                flushed&=super.flush(b);
                 int l=b.position()-position;
                 notifyOutgoing(b, position, l);
                 if (!flushed)
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java
index 7342c91..b972ce9 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java
@@ -19,7 +19,6 @@
 package org.eclipse.jetty.io.ssl;
 
 import java.io.IOException;
-import java.net.SocketException;
 import java.nio.ByteBuffer;
 import java.nio.channels.ClosedChannelException;
 import java.util.Arrays;
@@ -39,7 +38,6 @@
 import org.eclipse.jetty.io.FillInterest;
 import org.eclipse.jetty.io.RuntimeIOException;
 import org.eclipse.jetty.io.SelectChannelEndPoint;
-import org.eclipse.jetty.io.SocketBased;
 import org.eclipse.jetty.io.WriteFlusher;
 import org.eclipse.jetty.util.BufferUtil;
 import org.eclipse.jetty.util.Callback;
@@ -110,17 +108,20 @@
         this._sslEngine = sslEngine;
         this._decryptedEndPoint = newDecryptedEndPoint();
 
-        if (endPoint instanceof SocketBased)
-        {
-            try
-            {
-                ((SocketBased)endPoint).getSocket().setSoLinger(true, 30000);
-            }
-            catch (SocketException e)
-            {
-                throw new RuntimeIOException(e);
-            }
-        }
+        // 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()
diff --git a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/PropertyFileLoginModule.java b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/PropertyFileLoginModule.java
index 45561d4..028e4f2 100644
--- a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/PropertyFileLoginModule.java
+++ b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/PropertyFileLoginModule.java
@@ -20,10 +20,10 @@
 
 import java.security.Principal;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 
 import javax.security.auth.Subject;
 import javax.security.auth.callback.CallbackHandler;
@@ -45,7 +45,7 @@
 
     private static final Logger LOG = Log.getLogger(PropertyFileLoginModule.class);
 
-    private static Map<String, PropertyUserStore> _propertyUserStores = new HashMap<String, PropertyUserStore>();
+    private static ConcurrentHashMap<String, PropertyUserStore> _propertyUserStores = new ConcurrentHashMap<String, PropertyUserStore>();
 
     private int _refreshInterval = 0;
     private String _filename = DEFAULT_FILENAME;
@@ -68,33 +68,37 @@
 
     private void setupPropertyUserStore(Map<String, ?> options)
     {
+        parseConfig(options);
+
         if (_propertyUserStores.get(_filename) == null)
         {
-            parseConfig(options);
+            PropertyUserStore propertyUserStore = new PropertyUserStore();
+            propertyUserStore.setConfig(_filename);
+            propertyUserStore.setRefreshInterval(_refreshInterval);
 
-            PropertyUserStore _propertyUserStore = new PropertyUserStore();
-            _propertyUserStore.setConfig(_filename);
-            _propertyUserStore.setRefreshInterval(_refreshInterval);
-            LOG.debug("setupPropertyUserStore: Starting new PropertyUserStore. PropertiesFile: " + _filename + " refreshInterval: " + _refreshInterval);
-
-            try
+            PropertyUserStore prev = _propertyUserStores.putIfAbsent(_filename, propertyUserStore);
+            if (prev == null)
             {
-                _propertyUserStore.start();
-            }
-            catch (Exception e)
-            {
-                LOG.warn("Exception while starting propertyUserStore: ",e);
-            }
+                LOG.debug("setupPropertyUserStore: Starting new PropertyUserStore. PropertiesFile: " + _filename + " refreshInterval: " + _refreshInterval);
 
-            _propertyUserStores.put(_filename,_propertyUserStore);
+                try
+                {
+                    propertyUserStore.start();
+                }
+                catch (Exception e)
+                {
+                    LOG.warn("Exception while starting propertyUserStore: ",e);
+                }
+            }
         }
     }
 
     private void parseConfig(Map<String, ?> options)
     {
-        _filename = (String)options.get("file") != null?(String)options.get("file"):DEFAULT_FILENAME;
-        String refreshIntervalString = (String)options.get("refreshInterval");
-        _refreshInterval = refreshIntervalString == null?_refreshInterval:Integer.parseInt(refreshIntervalString);
+        String tmp = (String)options.get("file");
+        _filename = (tmp == null? DEFAULT_FILENAME : tmp);
+        tmp = (String)options.get("refreshInterval");
+        _refreshInterval = (tmp == null?_refreshInterval:Integer.parseInt(tmp));
     }
 
     /**
diff --git a/jetty-jndi/src/main/java/org/eclipse/jetty/jndi/ContextFactory.java b/jetty-jndi/src/main/java/org/eclipse/jetty/jndi/ContextFactory.java
index b3c6907..c56b694 100644
--- a/jetty-jndi/src/main/java/org/eclipse/jetty/jndi/ContextFactory.java
+++ b/jetty-jndi/src/main/java/org/eclipse/jetty/jndi/ContextFactory.java
@@ -75,7 +75,14 @@
      * Threadlocal for injecting a context to use
      * instead of looking up the map.
      */
-    private static final ThreadLocal __threadContext = new ThreadLocal();
+    private static final ThreadLocal<Context> __threadContext = new ThreadLocal<Context>();
+    
+    /**
+     * Threadlocal for setting a classloader which must be used 
+     * when finding the comp context.
+     */
+    private static final ThreadLocal<ClassLoader> __threadClassLoader = new ThreadLocal<ClassLoader>();
+    
 
 
     /**
@@ -107,10 +114,25 @@
             return ctx;
         }
 
+        //See if there is a classloader to use for finding the comp context
+        //Don't use its parent hierarchy if set.
+        ClassLoader loader = (ClassLoader)__threadClassLoader.get();
+        if (loader != null)
+        {
+            if (__log.isDebugEnabled() && loader != null) __log.debug("Using threadlocal classloader");
+            ctx = getContextForClassLoader(loader);
+            if (ctx == null)
+            {
+                ctx = newNamingContext(obj, loader, env, name, nameCtx);
+                __contextMap.put (loader, ctx);
+                if(__log.isDebugEnabled())__log.debug("Made context "+name.get(0)+" for classloader: "+loader);
+            }
+            return ctx;
+        }
        
-        ClassLoader tccl = Thread.currentThread().getContextClassLoader();
-        ClassLoader loader = tccl;
         //If the thread context classloader is set, then try its hierarchy to find a matching context
+        ClassLoader tccl = Thread.currentThread().getContextClassLoader();
+        loader = tccl;      
         if (loader != null)
         {
             if (__log.isDebugEnabled() && loader != null) __log.debug("Trying thread context classloader");
@@ -194,25 +216,34 @@
 
     /**
      * Associate the given Context with the current thread.
-     * resetComponentContext method should be called to reset the context.
+     * disassociate method should be called to reset the context.
      * @param ctx the context to associate to the current thread.
      * @return the previous context associated on the thread (can be null)
      */
-    public static Context setComponentContext(final Context ctx)
+    public static Context associateContext(final Context ctx)
     {
         Context previous = (Context)__threadContext.get();
         __threadContext.set(ctx);
         return previous;
     }
 
-    /**
-     * Set back the context with the given value.
-     * Don't return the previous context, use setComponentContext() method for this.
-     * @param ctx the context to associate to the current thread.
-     */
-    public static void resetComponentContext(final Context ctx)
+    public static void disassociateContext(final Context ctx)
     {
-        __threadContext.set(ctx);
+        __threadContext.remove();
+    }
+    
+    
+    public static ClassLoader associateClassLoader(final ClassLoader loader)
+    {
+        ClassLoader prev = (ClassLoader)__threadClassLoader.get();
+        __threadClassLoader.set(loader);
+        return prev;
+    }
+    
+    
+    public static void disassociateClassLoader ()
+    {
+        __threadClassLoader.remove();
     }
 
     public static void dump(Appendable out, String indent) throws IOException
diff --git a/jetty-maven-plugin/pom.xml b/jetty-maven-plugin/pom.xml
index 55a68c0..8b5f991 100644
--- a/jetty-maven-plugin/pom.xml
+++ b/jetty-maven-plugin/pom.xml
@@ -120,6 +120,11 @@
       <artifactId>jetty-jsp</artifactId>
       <version>${project.version}</version>
     </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-continuation</artifactId>
+      <version>${project.version}</version>
+    </dependency>
   </dependencies>
   <reporting>
   <plugins>
diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/AbstractJettyMojo.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/AbstractJettyMojo.java
index 4e976f4..50643d5 100644
--- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/AbstractJettyMojo.java
+++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/AbstractJettyMojo.java
@@ -203,6 +203,12 @@
      */
     protected String stopKey;
 
+    /**
+     * Use the dump() facility of jetty to print out the server configuration to logging
+     * 
+     * @parameter expression"${dumponStart}" default-value="false"
+     */
+    protected boolean dumpOnStart;
     
     /**
      * <p>
@@ -272,6 +278,7 @@
      */
     protected List pluginArtifacts;
     
+    
 
     /**
      * A ServerConnector to use.
@@ -558,6 +565,11 @@
             getLog().info("Started Jetty Server");
            
             
+            if ( dumpOnStart )
+            {
+                getLog().info(this.server.dump());
+            }
+            
             // start the scanner thread (if necessary) on the main webapp
             configureScanner ();
             startScanner();
diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyStopMojo.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyStopMojo.java
index 9985446..2270d7a 100644
--- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyStopMojo.java
+++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyStopMojo.java
@@ -18,6 +18,8 @@
 
 package org.eclipse.jetty.maven.plugin;
 
+import java.io.InputStreamReader;
+import java.io.LineNumberReader;
 import java.io.OutputStream;
 import java.net.ConnectException;
 import java.net.InetAddress;
@@ -54,6 +56,13 @@
      * @required
      */
     protected String stopKey;
+    
+    /**
+     * Max time in seconds that the plugin will wait for confirmation that jetty has stopped.
+     * @parameter
+     */
+    protected int stopWait;
+    
 
     public void execute() throws MojoExecutionException, MojoFailureException 
     {
@@ -66,10 +75,29 @@
         {        
             Socket s=new Socket(InetAddress.getByName("127.0.0.1"),stopPort);
             s.setSoLinger(false, 0);
-            
+
             OutputStream out=s.getOutputStream();
             out.write((stopKey+"\r\nstop\r\n").getBytes());
             out.flush();
+
+            if (stopWait > 0)
+            {      
+                s.setSoTimeout(stopWait * 1000);
+                s.getInputStream();
+
+                System.err.printf("Waiting %d seconds for jetty to stop%n",stopWait);
+                LineNumberReader lin = new LineNumberReader(new InputStreamReader(s.getInputStream()));
+                String response;
+                boolean stopped = false;
+                while (!stopped && ((response = lin.readLine()) != null))
+                {
+                    if ("Stopped".equals(response))
+                    {
+                        stopped = true;
+                        System.err.println("Server reports itself as Stopped");
+                    }                
+                }
+            }
             s.close();
         }
         catch (ConnectException e)
diff --git a/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionIdManager.java b/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionIdManager.java
index 1c0bae8..e25c80f 100644
--- a/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionIdManager.java
+++ b/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionIdManager.java
@@ -450,6 +450,7 @@
     /**
      * is the session id known to mongo, and is it valid
      */
+    @Override
     public boolean idInUse(String sessionId)
     {        
         /*
@@ -473,6 +474,7 @@
     }
 
     /* ------------------------------------------------------------ */
+    @Override
     public void addSession(HttpSession session)
     {
         if (session == null)
@@ -494,6 +496,7 @@
     }
 
     /* ------------------------------------------------------------ */
+    @Override
     public void removeSession(HttpSession session)
     {
         if (session == null)
@@ -508,6 +511,7 @@
     }
 
     /* ------------------------------------------------------------ */
+    @Override
     public void invalidateAll(String sessionId)
     {
         synchronized (_sessionsIds)
@@ -520,7 +524,7 @@
             Handler[] contexts = _server.getChildHandlersByClass(ContextHandler.class);
             for (int i=0; contexts!=null && i<contexts.length; i++)
             {
-                SessionHandler sessionHandler = (SessionHandler)((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
+                SessionHandler sessionHandler = ((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
                 if (sessionHandler != null) 
                 {
                     SessionManager manager = sessionHandler.getSessionManager();
@@ -532,26 +536,7 @@
                 }
             }
         }      
-    }
-
-    /* ------------------------------------------------------------ */
-    // TODO not sure if this is correct
-    public String getClusterId(String nodeId)
-    {
-        int dot=nodeId.lastIndexOf('.');
-        return (dot>0)?nodeId.substring(0,dot):nodeId;
-    }
-
-    /* ------------------------------------------------------------ */
-    // TODO not sure if this is correct
-    public String getNodeId(String clusterId, HttpServletRequest request)
-    {
-        if (_workerName!=null)
-            return clusterId+'.'+_workerName;
-
-        return clusterId;
-    }
-    
+    } 
     
     /* ------------------------------------------------------------ */
     @Override
@@ -569,7 +554,7 @@
             Handler[] contexts = _server.getChildHandlersByClass(ContextHandler.class);
             for (int i=0; contexts!=null && i<contexts.length; i++)
             {
-                SessionHandler sessionHandler = (SessionHandler)((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
+                SessionHandler sessionHandler = ((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
                 if (sessionHandler != null) 
                 {
                     SessionManager manager = sessionHandler.getSessionManager();
diff --git a/jetty-plus/src/main/java/org/eclipse/jetty/plus/servlet/ServletHandler.java b/jetty-plus/src/main/java/org/eclipse/jetty/plus/servlet/ServletHandler.java
deleted file mode 100644
index ab2e127..0000000
--- a/jetty-plus/src/main/java/org/eclipse/jetty/plus/servlet/ServletHandler.java
+++ /dev/null
@@ -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.plus.servlet;
-
-import org.eclipse.jetty.plus.annotation.InjectionCollection;
-import org.eclipse.jetty.plus.annotation.LifeCycleCallbackCollection;
-
-/**
- * ServletHandler
- *
- *
- */
-public class ServletHandler extends org.eclipse.jetty.servlet.ServletHandler
-{
-
-    private InjectionCollection _injections = null;
-    private LifeCycleCallbackCollection _callbacks = null;
-    
-
-
-    /**
-     * @return the callbacks
-     */
-    public LifeCycleCallbackCollection getCallbacks()
-    {
-        return _callbacks;
-    }
-
-
-
-    /**
-     * @param callbacks the callbacks to set
-     */
-    public void setCallbacks(LifeCycleCallbackCollection callbacks)
-    {
-        this._callbacks = callbacks;
-    }
-
-
-
-    /**
-     * @return the injections
-     */
-    public InjectionCollection getInjections()
-    {
-        return _injections;
-    }
-
-
-
-    /**
-     * @param injections the injections to set
-     */
-    public void setInjections(InjectionCollection injections)
-    {
-        this._injections = injections;
-    }
-    
-
-}
diff --git a/jetty-plus/src/main/java/org/eclipse/jetty/plus/servlet/package-info.java b/jetty-plus/src/main/java/org/eclipse/jetty/plus/servlet/package-info.java
deleted file mode 100644
index f83c607..0000000
--- a/jetty-plus/src/main/java/org/eclipse/jetty/plus/servlet/package-info.java
+++ /dev/null
@@ -1,23 +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.
-//  ========================================================================
-//
-
-/**
- * Jetty Plus : Servlet Handler for Limited Additional JEE support
- */
-package org.eclipse.jetty.plus.servlet;
-
diff --git a/jetty-plus/src/main/java/org/eclipse/jetty/plus/webapp/EnvConfiguration.java b/jetty-plus/src/main/java/org/eclipse/jetty/plus/webapp/EnvConfiguration.java
index 660231d..26227f1 100644
--- a/jetty-plus/src/main/java/org/eclipse/jetty/plus/webapp/EnvConfiguration.java
+++ b/jetty-plus/src/main/java/org/eclipse/jetty/plus/webapp/EnvConfiguration.java
@@ -31,6 +31,7 @@
 import javax.naming.NameNotFoundException;
 import javax.naming.NamingException;
 
+import org.eclipse.jetty.jndi.ContextFactory;
 import org.eclipse.jetty.jndi.NamingContext;
 import org.eclipse.jetty.jndi.NamingUtil;
 import org.eclipse.jetty.jndi.local.localContextRoot;
@@ -147,6 +148,7 @@
         //get rid of any bindings for comp/env for webapp
         ClassLoader oldLoader = Thread.currentThread().getContextClassLoader();
         Thread.currentThread().setContextClassLoader(context.getClassLoader());
+        ContextFactory.associateClassLoader(context.getClassLoader());
         try
         {
             Context ic = new InitialContext();
@@ -170,6 +172,7 @@
         }
         finally
         {
+            ContextFactory.disassociateClassLoader();
             Thread.currentThread().setContextClassLoader(oldLoader);
         }
     }
@@ -251,6 +254,7 @@
     {
         ClassLoader old_loader = Thread.currentThread().getContextClassLoader();
         Thread.currentThread().setContextClassLoader(wac.getClassLoader());
+        ContextFactory.associateClassLoader(wac.getClassLoader());
         try
         {
             Context context = new InitialContext();
@@ -259,8 +263,9 @@
         }
         finally
         {
-           Thread.currentThread().setContextClassLoader(old_loader);
-       }
+            ContextFactory.disassociateClassLoader();
+            Thread.currentThread().setContextClassLoader(old_loader);
+        }
     }
 
     private static class Bound
diff --git a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/BalancerServletTest.java b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/BalancerServletTest.java
index 6c3cc5c..ee3c4c6 100644
--- a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/BalancerServletTest.java
+++ b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/BalancerServletTest.java
@@ -35,6 +35,7 @@
 import org.eclipse.jetty.server.NetworkConnector;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.session.AbstractSessionIdManager;
 import org.eclipse.jetty.server.session.HashSessionIdManager;
 import org.eclipse.jetty.servlet.ServletContextHandler;
 import org.eclipse.jetty.servlet.ServletHolder;
@@ -99,7 +100,7 @@
 
         if (nodeName != null)
         {
-            HashSessionIdManager sessionIdManager = new HashSessionIdManager();
+            AbstractSessionIdManager sessionIdManager = new HashSessionIdManager();
             sessionIdManager.setWorkerName(nodeName);
             server.setSessionIdManager(sessionIdManager);
         }
diff --git a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyTunnellingTest.java b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyTunnellingTest.java
index 9db2204..e4de3f3 100644
--- a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyTunnellingTest.java
+++ b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyTunnellingTest.java
@@ -20,11 +20,13 @@
 
 import java.io.IOException;
 import java.net.ConnectException;
+import java.net.Socket;
 import java.net.URLEncoder;
 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;
@@ -55,6 +57,8 @@
 import org.hamcrest.Matchers;
 import org.junit.After;
 import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 
@@ -116,14 +120,20 @@
 
     protected void stopServer() throws Exception
     {
-        server.stop();
-        server.join();
+        if (server != null)
+        {
+            server.stop();
+            server.join();
+        }
     }
 
     protected void stopProxy() throws Exception
     {
-        proxy.stop();
-        proxy.join();
+        if (proxy != null)
+        {
+            proxy.stop();
+            proxy.join();
+        }
     }
 
     @Test
@@ -364,6 +374,43 @@
         }
     }
 
+    @Test
+    @Ignore // to delicate to rely on external proxy.
+    public void testExternalProxy() throws Exception
+    {
+        // Free proxy server obtained from http://hidemyass.com/proxy-list/
+        String proxyHost = "81.208.25.53";
+        int proxyPort = 3128;
+        try
+        {
+            new Socket(proxyHost, proxyPort).close();
+        }
+        catch (IOException x)
+        {
+            Assume.assumeNoException(x);
+        }
+
+        SslContextFactory sslContextFactory = new SslContextFactory();
+        sslContextFactory.start();
+
+        HttpClient httpClient = new HttpClient(sslContextFactory);
+        httpClient.setProxyConfiguration(new ProxyConfiguration(proxyHost, proxyPort));
+        httpClient.start();
+
+        try
+        {
+            ContentResponse response = httpClient.newRequest("https://www.google.com")
+                    // Use a longer timeout, sometimes the proxy takes a while to answer
+                    .timeout(20, TimeUnit.SECONDS)
+                    .send();
+            assertEquals(HttpStatus.OK_200, response.getStatus());
+        }
+        finally
+        {
+            httpClient.stop();
+        }
+    }
+
     private static class ServerHandler extends AbstractHandler
     {
         public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException
diff --git a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RewritePatternRule.java b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RewritePatternRule.java
index 48047b8..d6bd65d 100644
--- a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RewritePatternRule.java
+++ b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RewritePatternRule.java
@@ -19,20 +19,21 @@
 package org.eclipse.jetty.rewrite.handler;
 
 import java.io.IOException;
-
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.eclipse.jetty.http.HttpURI;
 import org.eclipse.jetty.http.PathMap;
 import org.eclipse.jetty.server.Request;
 import org.eclipse.jetty.util.URIUtil;
 
 /**
- * Rewrite the URI by replacing the matched {@link PathMap} path with a fixed string. 
+ * Rewrite the URI by replacing the matched {@link PathMap} path with a fixed string.
  */
 public class RewritePatternRule extends PatternRule implements Rule.ApplyURI
 {
     private String _replacement;
+    private String _query;
 
     /* ------------------------------------------------------------ */
     public RewritePatternRule()
@@ -44,32 +45,63 @@
     /* ------------------------------------------------------------ */
     /**
      * Whenever a match is found, it replaces with this value.
-     * 
-     * @param value the replacement string.
+     *
+     * @param replacement the replacement string.
      */
-    public void setReplacement(String value)
+    public void setReplacement(String replacement)
     {
-        _replacement = value;
+        String[] split = replacement.split("\\?", 2);
+        _replacement = split[0];
+        _query = split.length == 2 ? split[1] : null;
     }
 
     /* ------------------------------------------------------------ */
     /*
      * (non-Javadoc)
-     * @see org.eclipse.jetty.server.handler.rules.RuleBase#apply(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
+     *
+     * @see org.eclipse.jetty.server.handler.rules.RuleBase#apply(javax.servlet.http.HttpServletRequest,
+     * javax.servlet.http.HttpServletResponse)
      */
     @Override
     public String apply(String target, HttpServletRequest request, HttpServletResponse response) throws IOException
     {
-        target = URIUtil.addPaths(_replacement, PathMap.pathInfo(_pattern,target));   
+        target = URIUtil.addPaths(_replacement, PathMap.pathInfo(_pattern, target));
         return target;
     }
 
     /* ------------------------------------------------------------ */
+
+    /**
+     * This method will add _query to the requests's queryString and also combine it with existing queryStrings in
+     * the request. However it won't take care for duplicate. E.g. if request.getQueryString contains a parameter
+     * "param1 = true" and _query will contain "param1=false" the result will be param1=true&param1=false.
+     * To cover this use case some more complex pattern matching is necessary. We can implement this if there's use
+     * cases.
+     *
+     * @param request
+     * @param oldTarget
+     * @param newTarget
+     * @throws IOException
+     */
     @Override
-    public void applyURI(Request request, String oldTarget, String newTarget) throws IOException 
+    public void applyURI(Request request, String oldTarget, String newTarget) throws IOException
     {
-        String uri = URIUtil.addPaths(_replacement, PathMap.pathInfo(_pattern,request.getRequestURI()));
-        request.setRequestURI(uri);
+        if (_query == null)
+        {
+            request.setRequestURI(newTarget);
+        }
+        else
+        {
+            String queryString = request.getQueryString();
+            if (queryString != null)
+                queryString = queryString + "&" + _query;
+            else
+                queryString = _query;
+            HttpURI uri = new HttpURI(newTarget + "?" + queryString);
+            request.setUri(uri);
+            request.setRequestURI(newTarget);
+            request.setQueryString(queryString);
+        }
     }
 
     /* ------------------------------------------------------------ */
diff --git a/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/RewritePatternRuleTest.java b/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/RewritePatternRuleTest.java
index 51ed119..20d52fb 100644
--- a/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/RewritePatternRuleTest.java
+++ b/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/RewritePatternRuleTest.java
@@ -18,23 +18,25 @@
 
 package org.eclipse.jetty.rewrite.handler;
 
-import static org.junit.Assert.assertEquals;
-
 import java.io.IOException;
 
+import org.eclipse.jetty.http.HttpURI;
 import org.junit.Before;
 import org.junit.Test;
 
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
 public class RewritePatternRuleTest extends AbstractRuleTestCase
 {
-    private String[][] _tests=
-    {
-            {"/foo/bar","/","/replace"},
-            {"/foo/bar","/*","/replace/foo/bar"},
-            {"/foo/bar","/foo/*","/replace/bar"},
-            {"/foo/bar","/foo/bar","/replace"},
-            {"/foo/bar.txt","*.txt","/replace"},
-    };
+    private String[][] _tests =
+            {
+                    {"/foo/bar", "/", "/replace"},
+                    {"/foo/bar", "/*", "/replace/foo/bar"},
+                    {"/foo/bar", "/foo/*", "/replace/bar"},
+                    {"/foo/bar", "/foo/bar", "/replace"},
+                    {"/foo/bar.txt", "*.txt", "/replace"},
+            };
     private RewritePatternRule _rule;
 
     @Before
@@ -46,13 +48,82 @@
     }
 
     @Test
-    public void testRequestUriEnabled() throws IOException
+    public void testMatchAndApplyAndApplyURI() throws IOException
     {
         for (String[] test : _tests)
         {
             _rule.setPattern(test[1]);
             String result = _rule.matchAndApply(test[0], _request, _response);
-            assertEquals(test[1], test[2], result);
+            assertThat(test[1], test[2], is(result));
+
+            _rule.applyURI(_request, null, result);
+            assertThat(_request.getRequestURI(), is(test[2]));
         }
     }
+
+    @Test
+    public void testReplacementWithQueryString() throws IOException
+    {
+        String replacement = "/replace?given=param";
+        String[] split = replacement.split("\\?", 2);
+        String path = split[0];
+        String queryString = split[1];
+
+        RewritePatternRule rewritePatternRule = new RewritePatternRule();
+        rewritePatternRule.setPattern("/old/context");
+        rewritePatternRule.setReplacement(replacement);
+
+        String result = rewritePatternRule.matchAndApply("/old/context", _request, _response);
+        assertThat(result, is(path));
+
+        rewritePatternRule.applyURI(_request, null, result);
+        assertThat("queryString matches expected", _request.getQueryString(), is(queryString));
+        assertThat("request URI matches expected", _request.getRequestURI(), is(path));
+
+    }
+
+    @Test
+    public void testRequestWithQueryString() throws IOException
+    {
+        String replacement = "/replace";
+        String queryString = "request=parameter";
+        _request.setUri(new HttpURI("/old/context"));
+        _request.setQueryString(queryString);
+
+        RewritePatternRule rewritePatternRule = new RewritePatternRule();
+        rewritePatternRule.setPattern("/old/context");
+        rewritePatternRule.setReplacement(replacement);
+
+        String result = rewritePatternRule.matchAndApply("/old/context", _request, _response);
+        assertThat("result matches expected", result, is(replacement));
+
+        rewritePatternRule.applyURI(_request, null, result);
+        assertThat("queryString matches expected", _request.getQueryString(), is(queryString));
+        assertThat("request URI matches expected", _request.getRequestURI(), is(replacement));
+    }
+
+    @Test
+    public void testRequestAndReplacementWithQueryString() throws IOException
+    {
+        String requestQueryString = "request=parameter";
+        String replacement = "/replace?given=param";
+        String[] split = replacement.split("\\?", 2);
+        String path = split[0];
+        String queryString = split[1];
+        _request.setUri(new HttpURI("/old/context"));
+        _request.setQueryString(requestQueryString);
+
+        RewritePatternRule rewritePatternRule = new RewritePatternRule();
+        rewritePatternRule.setPattern("/old/context");
+        rewritePatternRule.setReplacement(replacement);
+
+        String result = rewritePatternRule.matchAndApply("/old/context", _request, _response);
+        assertThat(result, is(path));
+
+        rewritePatternRule.applyURI(_request, null, result);
+        assertThat("queryString matches expected", _request.getQueryString(),
+                is(requestQueryString + "&" + queryString));
+        assertThat("request URI matches expected", _request.getRequestURI(), is(path));
+    }
+
 }
diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/DefaultUserIdentity.java b/jetty-security/src/main/java/org/eclipse/jetty/security/DefaultUserIdentity.java
index 28b0cb3..dd12b1d 100644
--- a/jetty-security/src/main/java/org/eclipse/jetty/security/DefaultUserIdentity.java
+++ b/jetty-security/src/main/java/org/eclipse/jetty/security/DefaultUserIdentity.java
@@ -54,13 +54,19 @@
     }
 
     public boolean isUserInRole(String role, Scope scope)
-    {
+    {  
         if (scope!=null && scope.getRoleRefMap()!=null)
-            role=scope.getRoleRefMap().get(role);
-
+        {
+            String mappedRole = scope.getRoleRefMap().get(role);
+            if (mappedRole != null)
+                role = mappedRole;
+        }
+        
         for (String r :_roles)
+        {
             if (r.equals(role))
                 return true;
+        }
         return false;
     }
 
diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/MappedLoginService.java b/jetty-security/src/main/java/org/eclipse/jetty/security/MappedLoginService.java
index 41648d6..37c72c3 100644
--- a/jetty-security/src/main/java/org/eclipse/jetty/security/MappedLoginService.java
+++ b/jetty-security/src/main/java/org/eclipse/jetty/security/MappedLoginService.java
@@ -212,6 +212,9 @@
      */
     public UserIdentity login(String username, Object credentials)
     {
+        if (username == null)
+            return null;
+        
         UserIdentity user = _users.get(username);
 
         if (user==null)
diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/DeferredAuthentication.java b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/DeferredAuthentication.java
index d47a65d..b1a2c60 100644
--- a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/DeferredAuthentication.java
+++ b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/DeferredAuthentication.java
@@ -116,6 +116,9 @@
     @Override
     public Authentication login(String username, Object password, ServletRequest request)
     {
+        if (username == null)
+            return null;
+        
         UserIdentity identity = _authenticator.login(username, password, request);
         if (identity != null)
         {
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractNCSARequestLog.java b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractNCSARequestLog.java
index 5223e97..d00f67c 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractNCSARequestLog.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractNCSARequestLog.java
@@ -25,6 +25,7 @@
 
 import org.eclipse.jetty.http.HttpHeader;
 import org.eclipse.jetty.http.PathMap;
+import org.eclipse.jetty.server.handler.StatisticsHandler;
 import org.eclipse.jetty.util.DateCache;
 import org.eclipse.jetty.util.annotation.ManagedAttribute;
 import org.eclipse.jetty.util.component.AbstractLifeCycle;
@@ -59,7 +60,6 @@
     private boolean _logLatency = false;
     private boolean _logCookies = false;
     private boolean _logServer = false;
-    private boolean _logDispatch = false;
     private boolean _preferProxiedForAddress;
     private transient DateCache _logDateCache;
     private String _logDateFormat = "dd/MMM/yyyy:HH:mm:ss Z";
@@ -193,17 +193,10 @@
                 }
             }
 
-            if (_logDispatch || _logLatency)
+            if (_logLatency)
             {
                 long now = System.currentTimeMillis();
 
-                if (_logDispatch)
-                {
-                    long d = request.getDispatchTime();
-                    buf.append(' ');
-                    buf.append(now - (d==0 ? request.getTimeStamp():d));
-                }
-
                 if (_logLatency)
                 {
                     buf.append(' ');
@@ -340,24 +333,18 @@
     }
 
     /**
-     * Controls logging of the request dispatch time
-     *
-     * @param value true - request dispatch time will be logged
-     *              false - request dispatch time will not be logged
+     * @deprecated use {@link StatisticsHandler}
      */
     public void setLogDispatch(boolean value)
     {
-        _logDispatch = value;
     }
 
     /**
-     * Retrieve request dispatch time logging flag
-     *
-     * @return value of the flag
+     * @deprecated use {@link StatisticsHandler}
      */
     public boolean isLogDispatch()
     {
-        return _logDispatch;
+        return false;
     }
 
     /**
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContextState.java b/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContextState.java
index 1688ed6..876e7ae 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContextState.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContextState.java
@@ -38,7 +38,7 @@
         _state=state;
     }
     
-    private HttpChannelState state()
+    HttpChannelState state()
     {
         HttpChannelState state=_state;
         if (state==null)
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java
index e9f61ac..7d54230 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java
@@ -23,6 +23,7 @@
 import java.nio.ByteBuffer;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
+
 import javax.servlet.DispatcherType;
 import javax.servlet.RequestDispatcher;
 
@@ -330,16 +331,11 @@
             }
             finally
             {
-                next=Next.RECYCLE;
+                _request.setHandled(true);
+                _transport.completed();
             }
         }
 
-        if (next==Next.RECYCLE)
-        {
-            _request.setHandled(true);
-            _transport.completed();
-        }
-
         LOG.debug("{} handle exit, result {}", this, next);
 
         return next!=Next.WAIT;
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelState.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelState.java
index d070aee..127bafa 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelState.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelState.java
@@ -71,13 +71,12 @@
         COMPLETING,    // Request is completable
         COMPLETED      // Request is complete
     }
-    
+
     public enum Next
     {
         CONTINUE,       // Continue handling the channel        
         WAIT,           // Wait for further events 
-        COMPLETE,       // Complete the channel
-        RECYCLE,        // Channel is completed
+        COMPLETE        // Complete the channel
     }
 
     private final HttpChannel<?> _channel;
@@ -190,12 +189,12 @@
 
                 case COMPLETING:
                     return Next.COMPLETE;
-                    
+
                 case ASYNCWAIT:
                     return Next.WAIT;
-                    
+
                 case COMPLETED:
-                    return Next.RECYCLE;
+                    return Next.WAIT;
 
                 case REDISPATCH:
                     _state=State.REDISPATCHED;
@@ -325,7 +324,7 @@
                     _event.setDispatchTarget(context,path);
                     _dispatched=true;
                     break;
-                    
+
                 default:
                     throw new IllegalStateException(this.getStatusString());
             }
@@ -393,7 +392,7 @@
                     break;
             }
         }
-        
+
         scheduleDispatch();
     }
 
@@ -528,7 +527,7 @@
             return _expired;
         }
     }
-    
+
     public boolean isInitial()
     {
         synchronized(this)
@@ -563,13 +562,23 @@
         }
     }
 
+    boolean isCompleted()
+    {
+        synchronized (this)
+        {
+            return _state == State.COMPLETED;
+        }
+    }
+
     public boolean isAsyncStarted()
     {
         synchronized (this)
         {
             switch(_state)
             {
-                case ASYNCSTARTED:
+                case ASYNCSTARTED:  // Suspend called, but not yet returned to container
+                case REDISPATCHING: // resumed while dispatched
+                case COMPLETECALLED:   // complete called
                 case ASYNCWAIT:
                     return true;
 
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java
index e6ed10b..2feb032 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java
@@ -22,6 +22,7 @@
 import java.util.concurrent.CopyOnWriteArrayList;
 
 import org.eclipse.jetty.http.HttpScheme;
+import org.eclipse.jetty.util.Jetty;
 import org.eclipse.jetty.util.annotation.ManagedAttribute;
 import org.eclipse.jetty.util.annotation.ManagedObject;
 
@@ -40,6 +41,8 @@
 @ManagedObject("HTTP Configuration")
 public class HttpConfiguration
 {
+    public static final String SERVER_VERSION = "Jetty(" + Jetty.VERSION + ")";
+
     private List<Customizer> _customizers=new CopyOnWriteArrayList<>();
     private int _outputBufferSize=32*1024;
     private int _requestHeaderSize=8*1024;
@@ -48,8 +51,8 @@
     private int _securePort;
     private String _secureScheme = HttpScheme.HTTPS.asString();
     private boolean _sendServerVersion = true; //send Server: header
+    private boolean _sendXPoweredBy = false; //send X-Powered-By: header
     private boolean _sendDateHeader = false; //send Date: header
-    
 
     public interface Customizer
     {
@@ -150,11 +153,22 @@
         _sendServerVersion = sendServerVersion;
     }
 
-    @ManagedAttribute("if true, include the server version in HTTP headers")
+    @ManagedAttribute("if true, send the Server header in responses")
     public boolean getSendServerVersion()
     {
         return _sendServerVersion;
     }
+    
+    public void setSendXPoweredBy (boolean sendXPoweredBy)
+    {
+        _sendXPoweredBy=sendXPoweredBy;
+    }
+
+    @ManagedAttribute("if true, send the X-Powered-By header in responses")
+    public boolean getSendXPoweredBy()
+    {
+        return _sendXPoweredBy;
+    }
 
     public void setSendDateHeader(boolean sendDateHeader)
     {
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java
index 616dc0d..d56fdd3 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java
@@ -91,8 +91,7 @@
         _config = config;
         _connector = connector;
         _bufferPool = _connector.getByteBufferPool();
-        _generator = new HttpGenerator();
-        _generator.setSendServerVersion(_config.getSendServerVersion());
+        _generator = new HttpGenerator(_config.getSendServerVersion(),_config.getSendXPoweredBy());
         _channel = new HttpChannelOverHttp(connector, config, endPoint, this, new Input());
         _parser = newHttpParser();
 
@@ -133,14 +132,20 @@
             _parser.reset();
             // close to seek EOF
             _parser.close();
+            if (getEndPoint().isOpen())
+                fillInterested();
         }
         // else if we are persistent
         else if (_generator.isPersistent())
             // reset to seek next request
             _parser.reset();
         else
+        {
             // else seek EOF
             _parser.close();
+            if (getEndPoint().isOpen())
+                fillInterested();
+        }
 
         _generator.reset();
         _channel.reset();
@@ -235,7 +240,7 @@
                     int filled = getEndPoint().fill(_requestBuffer);
                     if (filled==0) // Do a retry on fill 0 (optimisation for SSL connections)
                         filled = getEndPoint().fill(_requestBuffer);
-
+                    
                     LOG.debug("{} filled {}", this, filled);
 
                     // If we failed to fill
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java
index 79f442f..147eca4 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java
@@ -373,7 +373,8 @@
 
     /* ------------------------------------------------------------ */
     /** Asynchronous send of content.
-     * @param in The content to send
+     * @param in The content to send as a stream.  The stream will be closed 
+     * after reading all content.
      * @param callback The callback to use to notify success or failure
      */
     public void sendContent(InputStream in, Callback callback)
@@ -383,7 +384,8 @@
 
     /* ------------------------------------------------------------ */
     /** Asynchronous send of content.
-     * @param in The content to send
+     * @param in The content to send as a channel.  The channel will be closed 
+     * after reading all content.
      * @param callback The callback to use to notify success or failure
      */
     public void sendContent(ReadableByteChannel in, Callback callback)
@@ -472,28 +474,38 @@
         @Override
         protected boolean process() throws Exception
         {
-            int len=_in.read(_buffer.array(),0,_buffer.capacity());
-            if (len==-1)
-            {
-                closed();
-                _channel.getByteBufferPool().release(_buffer);
-                return true;
-            }
             boolean eof=false;
-
-            // if we read less than a buffer, are we at EOF?
-            if (len<_buffer.capacity())
+            int len=_in.read(_buffer.array(),0,_buffer.capacity());
+            
+            if (len<0)
             {
+                eof=true;
+                len=0;
+                _in.close();
+            }
+            else if (len<_buffer.capacity())
+            {
+                // read ahead for EOF to try for single commit
                 int len2=_in.read(_buffer.array(),len,_buffer.capacity()-len);
                 if (len2<0)
                     eof=true;
                 else
                     len+=len2;
             }
-
+            
+            // write what we have
             _buffer.position(0);
             _buffer.limit(len);
             _channel.write(_buffer,eof,this);
+            
+            // Handle EOF
+            if (eof)
+            {
+                closed();
+                _channel.getByteBufferPool().release(_buffer);
+                return true;
+            }
+
             return false;
         }
 
@@ -502,6 +514,14 @@
         {
             super.failed(x);
             _channel.getByteBufferPool().release(_buffer);
+            try
+            {
+                _in.close();
+            }
+            catch (IOException e)
+            {
+                LOG.ignore(e);
+            }
         }
         
     }
@@ -531,30 +551,38 @@
         protected boolean process() throws Exception
         {
             _buffer.clear();
-            int len=_in.read(_buffer);
-            if (len==-1)
-            {
-                closed();
-                _channel.getByteBufferPool().release(_buffer);
-                return true;
-            }
-
             boolean eof=false;
-
-            // if we read less than a buffer, are we at EOF?
-            if (len<_buffer.capacity())
+            int len=_in.read(_buffer);
+            
+            if (len<0)
             {
+                eof=true;
+                len=0;
+                _in.close();
+            }
+            else if (len<_buffer.capacity())
+            {
+                // read ahead for EOF to try for single commit
                 int len2=_in.read(_buffer);
                 if (len2<0)
                     eof=true;
                 else
                     len+=len2;
             }
-
+            
+            // write what we have
             _buffer.flip();
             _channel.write(_buffer,eof,this);
+            
+            // Handle EOF
+            if (eof)
+            {
+                closed();
+                _channel.getByteBufferPool().release(_buffer);
+                return true;
+            }
+
             return false;
-           
         }
 
         @Override
@@ -562,6 +590,14 @@
         {
             super.failed(x);
             _channel.getByteBufferPool().release(_buffer);
+            try
+            {
+                _in.close();
+            }
+            catch (IOException e)
+            {
+                LOG.ignore(e);
+            }
         }
     }
 }
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java
index 267a199..c6db08c 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java
@@ -28,6 +28,7 @@
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.nio.charset.Charset;
+import java.nio.charset.UnsupportedCharsetException;
 import java.security.Principal;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -38,7 +39,6 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
-
 import javax.servlet.AsyncContext;
 import javax.servlet.AsyncListener;
 import javax.servlet.DispatcherType;
@@ -63,7 +63,6 @@
 import org.eclipse.jetty.http.HttpFields;
 import org.eclipse.jetty.http.HttpHeader;
 import org.eclipse.jetty.http.HttpMethod;
-import org.eclipse.jetty.http.HttpScheme;
 import org.eclipse.jetty.http.HttpStatus;
 import org.eclipse.jetty.http.HttpURI;
 import org.eclipse.jetty.http.HttpVersion;
@@ -203,7 +202,6 @@
     private HttpSession _session;
     private SessionManager _sessionManager;
     private long _timeStamp;
-    private long _dispatchTime;
     private HttpURI _uri;
     private MultiPartInputStreamParser _multiPartInputStream; //if the request is a multi-part mime
     private AsyncContextState _async;
@@ -1395,16 +1393,6 @@
         return null;
     }
 
-    /* ------------------------------------------------------------ */
-    /**
-     * Get timestamp of the request dispatch
-     *
-     * @return timestamp
-     */
-    public long getDispatchTime()
-    {
-        return _dispatchTime;
-    }
 
     /* ------------------------------------------------------------ */
     public boolean isHandled()
@@ -1714,7 +1702,16 @@
 
         // check encoding is supported
         if (!StringUtil.isUTF8(encoding))
-            Charset.forName(encoding);
+        {
+            try
+            {
+                Charset.forName(encoding);
+            }
+            catch (UnsupportedCharsetException e)
+            {
+                throw new UnsupportedEncodingException(e.getMessage());
+            }
+        }
     }
 
     /* ------------------------------------------------------------ */
@@ -1997,18 +1994,6 @@
     }
 
     /* ------------------------------------------------------------ */
-    /**
-     * Set timetstamp of request dispatch
-     *
-     * @param value
-     *            timestamp
-     */
-    public void setDispatchTime(long value)
-    {
-        _dispatchTime = value;
-    }
-
-    /* ------------------------------------------------------------ */
     @Override
     public AsyncContext startAsync() throws IllegalStateException
     {
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java
index cc377f0..31a67de 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java
@@ -281,8 +281,8 @@
 
         ShutdownMonitor.getInstance().start(); // initialize
 
-        LOG.info("jetty-"+getVersion());
-        HttpGenerator.setServerVersion(getVersion());
+        LOG.info("jetty-" + getVersion());
+        HttpGenerator.setJettyVersion(HttpConfiguration.SERVER_VERSION);
         MultiException mex=new MultiException();
 
         try
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ServerConnector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ServerConnector.java
index eadb2f4..8e815d9 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/ServerConnector.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ServerConnector.java
@@ -98,6 +98,23 @@
     {
         this(server,null,null,null,0,0,new HttpConnectionFactory());
     }
+    
+    /* ------------------------------------------------------------ */
+    /** HTTP Server Connection.
+     * <p>Construct a ServerConnector with a private instance of {@link HttpConnectionFactory} as the only factory.</p>
+     * @param server The {@link Server} this connector will accept connection for. 
+     * @param acceptors 
+     *          the number of acceptor threads to use, or 0 for a default value. Acceptors accept new TCP/IP connections.
+     * @param selectors
+     *          the number of selector threads, or 0 for a default value. Selectors notice and schedule established connection that can make IO progress.
+     */
+    public ServerConnector(
+        @Name("server") Server server,
+        @Name("acceptors") int acceptors,
+        @Name("selectors") int selectors)
+    {
+        this(server,null,null,null,acceptors,selectors,new HttpConnectionFactory());
+    }
 
     /* ------------------------------------------------------------ */
     /** Generic Server Connection with default configuration.
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ShutdownMonitor.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ShutdownMonitor.java
index e789d38..ded5bdc 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/ShutdownMonitor.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ShutdownMonitor.java
@@ -102,6 +102,13 @@
                         // Graceful Shutdown
                         debug("Issuing graceful shutdown..");
                         ShutdownThread.getInstance().run();
+                        
+                        //Stop accepting any more
+                        close(serverSocket);
+                        serverSocket = null;
+                        
+                        //Shutdown input from client
+                        shutdownInput(socket);
 
                         // Reply to client
                         debug("Informing client that we are stopped.");
@@ -109,11 +116,10 @@
                         out.flush();
 
                         // Shutdown Monitor
-                        debug("Shutting down monitor");
+                        socket.shutdownOutput();
                         close(socket);
-                        socket = null;
-                        close(serverSocket);
-                        serverSocket = null;
+                        socket = null;                        
+                        debug("Shutting down monitor");
 
                         if (exitVm)
                         {
@@ -248,7 +254,7 @@
         }
         catch (IOException ignore)
         {
-            /* ignore */
+            debug(ignore);
         }
     }
 
@@ -265,10 +271,27 @@
         }
         catch (IOException ignore)
         {
-            /* ignore */
+            debug(ignore);
         }
     }
 
+    
+    private void shutdownInput(Socket socket)
+    {
+        if (socket == null)
+            return;
+        
+        try
+        {
+            socket.shutdownInput();
+        }   
+        catch (IOException ignore)
+        {
+            debug(ignore);
+        }
+    }
+    
+    
     private void debug(String format, Object... args)
     {
         if (DEBUG)
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java
index f1f2d6f..94c2be1 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java
@@ -101,8 +101,8 @@
 @ManagedObject("URI Context")
 public class ContextHandler extends ScopedHandler implements Attributes, Graceful
 {
-    public static int SERVLET_MAJOR_VERSION=3;
-    public static int SERVLET_MINOR_VERSION=0;
+    public final static int SERVLET_MAJOR_VERSION=3;
+    public final static int SERVLET_MINOR_VERSION=0;
 
     final private static String __unimplmented="Unimplemented - use org.eclipse.jetty.servlet.ServletContextHandler";
 
@@ -884,8 +884,10 @@
             String vhost = normalizeHostname(baseRequest.getServerName());
 
             boolean match = false;
+            boolean connectorName = false;
+            boolean connectorMatch = false;
 
-            loop: for (String contextVhost:_vhosts)
+            for (String contextVhost:_vhosts)
             {
                 if (contextVhost == null || contextVhost.length()==0)
                     continue;
@@ -895,21 +897,21 @@
                     case '*':
                         if (contextVhost.startsWith("*."))
                             // wildcard only at the beginning, and only for one additional subdomain level
-                            match = contextVhost.regionMatches(true,2,vhost,vhost.indexOf(".") + 1,contextVhost.length() - 2);
+                            match = match || contextVhost.regionMatches(true,2,vhost,vhost.indexOf(".") + 1,contextVhost.length() - 2);
                         break;
                     case '@':
+                        connectorName=true;
                         String name=baseRequest.getHttpChannel().getConnector().getName();
-                        match=name!=null && contextVhost.length()==name.length()+1 && contextVhost.endsWith(name);
+                        boolean m=name!=null && contextVhost.length()==name.length()+1 && contextVhost.endsWith(name);
+                        match = match || m;
+                        connectorMatch = connectorMatch || m;
                         break;
                     default:
-                        match = contextVhost.equalsIgnoreCase(vhost);
+                        match = match || contextVhost.equalsIgnoreCase(vhost);
                 }
 
-                if (match)
-                    break loop;
-
             }
-            if (!match)
+            if (!match || connectorName && !connectorMatch)
                 return false;
         }
 
@@ -1287,8 +1289,26 @@
      */
     public void setContextPath(String contextPath)
     {
-        if (contextPath != null && contextPath.length() > 1 && contextPath.endsWith("/"))
-            throw new IllegalArgumentException("ends with /");
+        if (contextPath == null)
+            throw new IllegalArgumentException("null contextPath");
+
+        if (contextPath.endsWith("/*"))
+        {
+            LOG.warn(this+" contextPath ends with /*");
+            contextPath=contextPath.substring(0,contextPath.length()-2);
+        }
+        else if (contextPath.endsWith("/"))
+        {
+            LOG.warn(this+" contextPath ends with /");
+            contextPath=contextPath.substring(0,contextPath.length()-1);
+        }
+        
+        if (contextPath.length()==0)
+        {
+            LOG.warn("Empty contextPath");
+            contextPath="/";
+        }
+        
         _contextPath = contextPath;
 
         if (getServer() != null && (getServer().isStarting() || getServer().isStarted()))
@@ -2576,7 +2596,7 @@
             if (dot<0)
                 return false;
             String suffix=path.substring(dot);
-            return resource.getAlias().toString().endsWith(suffix);
+            return resource.toString().endsWith(suffix);
         }
     }
 
@@ -2592,10 +2612,10 @@
         public boolean check(String path, Resource resource)
         {
             int slash = path.lastIndexOf('/');
-            if (slash<0)
+            if (slash<0 || slash==path.length()-1)
                 return false;
             String suffix=path.substring(slash);
-            return resource.getAlias().toString().endsWith(suffix);
+            return resource.toString().endsWith(suffix);
         }
     }
     /* ------------------------------------------------------------ */
@@ -2609,7 +2629,7 @@
         public boolean check(String path, Resource resource)
         {
             int slash = path.lastIndexOf('/');
-            if (slash<0)
+            if (slash<0 || resource.exists())
                 return false;
             String suffix=path.substring(slash);
             return resource.getAlias().toString().endsWith(suffix);
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandlerCollection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandlerCollection.java
index b43ce52..6bed059 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandlerCollection.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandlerCollection.java
@@ -19,19 +19,19 @@
 package org.eclipse.jetty.server.handler;
 
 import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.Arrays;
 
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import org.eclipse.jetty.http.PathMap;
 import org.eclipse.jetty.server.Handler;
 import org.eclipse.jetty.server.HandlerContainer;
 import org.eclipse.jetty.server.HttpChannelState;
 import org.eclipse.jetty.server.Request;
-import org.eclipse.jetty.util.LazyList;
+import org.eclipse.jetty.util.ArrayTernaryTrie;
+import org.eclipse.jetty.util.ArrayUtil;
+import org.eclipse.jetty.util.Trie;
 import org.eclipse.jetty.util.annotation.ManagedObject;
 import org.eclipse.jetty.util.annotation.ManagedOperation;
 import org.eclipse.jetty.util.log.Log;
@@ -53,7 +53,7 @@
 {
     private static final Logger LOG = Log.getLogger(ContextHandlerCollection.class);
 
-    private volatile PathMap<Object> _contextMap;
+    private volatile Trie<ContextHandler[]> _contexts;
     private Class<? extends ContextHandler> _contextClass = ContextHandler.class;
 
     /* ------------------------------------------------------------ */
@@ -70,90 +70,71 @@
     @ManagedOperation("update the mapping of context path to context")
     public void mapContexts()
     {
-        PathMap<Object> contextMap = new PathMap<Object>();
-        Handler[] branches = getHandlers();
-
-
-        for (int b=0;branches!=null && b<branches.length;b++)
+        int capacity=512;
+        
+        // Loop until we have a big enough trie to hold all the context paths
+        Trie<ContextHandler[]> trie;
+        loop: while(true)
         {
-            Handler[] handlers=null;
+            trie=new ArrayTernaryTrie<>(false,capacity);
 
-            if (branches[b] instanceof ContextHandler)
+            Handler[] branches = getHandlers();
+
+            // loop over each group of contexts
+            for (int b=0;branches!=null && b<branches.length;b++)
             {
-                handlers = new Handler[]{ branches[b] };
-            }
-            else if (branches[b] instanceof HandlerContainer)
-            {
-                handlers = ((HandlerContainer)branches[b]).getChildHandlersByClass(ContextHandler.class);
-            }
-            else
-                continue;
+                Handler[] handlers=null;
 
-            for (int i=0;i<handlers.length;i++)
-            {
-                ContextHandler handler=(ContextHandler)handlers[i];
-
-                String contextPath=handler.getContextPath();
-
-                if (contextPath==null || contextPath.indexOf(',')>=0 || contextPath.startsWith("*"))
-                    throw new IllegalArgumentException ("Illegal context spec:"+contextPath);
-
-                if(!contextPath.startsWith("/"))
-                    contextPath='/'+contextPath;
-
-                if (contextPath.length()>1)
+                if (branches[b] instanceof ContextHandler)
                 {
-                    if (contextPath.endsWith("/"))
-                        contextPath+="*";
-                    else if (!contextPath.endsWith("/*"))
-                        contextPath+="/*";
+                    handlers = new Handler[]{ branches[b] };
                 }
-
-                Object contexts=contextMap.get(contextPath);
-                String[] vhosts=handler.getVirtualHosts();
-
-
-                if (vhosts!=null && vhosts.length>0)
+                else if (branches[b] instanceof HandlerContainer)
                 {
-                    Map<String, Object> hosts;
-
-                    if (contexts instanceof Map)
-                        hosts=(Map<String, Object>)contexts;
-                    else
-                    {
-                        hosts=new HashMap<String, Object>();
-                        hosts.put("*",contexts);
-                        contextMap.put(contextPath, hosts);
-                    }
-
-                    for (int j=0;j<vhosts.length;j++)
-                    {
-                        String vhost=vhosts[j];
-                        contexts=hosts.get(vhost);
-                        contexts=LazyList.add(contexts,branches[b]);
-                        hosts.put(vhost,contexts);
-                    }
-                }
-                else if (contexts instanceof Map)
-                {
-                    Map<String, Object> hosts=(Map<String, Object>)contexts;
-                    contexts=hosts.get("*");
-                    contexts= LazyList.add(contexts, branches[b]);
-                    hosts.put("*",contexts);
+                    handlers = ((HandlerContainer)branches[b]).getChildHandlersByClass(ContextHandler.class);
                 }
                 else
+                    continue;
+
+                // for each context handler in a group
+                for (int i=0;handlers!=null && i<handlers.length;i++)
                 {
-                    contexts= LazyList.add(contexts, branches[b]);
-                    contextMap.put(contextPath, contexts);
+                    ContextHandler handler=(ContextHandler)handlers[i];
+                    String contextPath=handler.getContextPath().substring(1);
+                    ContextHandler[] contexts=trie.get(contextPath);
+                    
+                    if (!trie.put(contextPath,ArrayUtil.addToArray(contexts,handler,ContextHandler.class)))
+                    {
+                        capacity+=512;
+                        continue loop;
+                    }
                 }
             }
+            
+            break;
         }
-        _contextMap=contextMap;
+        
+        // Sort the contexts so those with virtual hosts are considered before those without
+        for (String ctx : trie.keySet())
+        {
+            ContextHandler[] contexts=trie.get(ctx);
+            ContextHandler[] sorted=new ContextHandler[contexts.length];
+            int i=0;
+            for (ContextHandler handler:contexts)
+                if (handler.getVirtualHosts()!=null && handler.getVirtualHosts().length>0)
+                    sorted[i++]=handler;
+            for (ContextHandler handler:contexts)
+                if (handler.getVirtualHosts()==null || handler.getVirtualHosts().length==0)
+                    sorted[i++]=handler;
+            trie.put(ctx,sorted);
+        }
 
+        //for (String ctx : trie.keySet())
+        //    System.err.printf("'%s'->%s%n",ctx,Arrays.asList(trie.get(ctx)));
+        _contexts=trie;
     }
 
 
-
     /* ------------------------------------------------------------ */
     /*
      * @see org.eclipse.jetty.server.server.handler.HandlerCollection#setHandlers(org.eclipse.jetty.server.server.Handler[])
@@ -161,7 +142,7 @@
     @Override
     public void setHandlers(Handler[] handlers)
     {
-        _contextMap=null;
+        _contexts=null;
         super.setHandlers(handlers);
         if (isStarted())
             mapContexts();
@@ -199,67 +180,31 @@
 	}
 
 	// data structure which maps a request to a context; first-best match wins
-	// { context path =>
-	//     { virtual host => context }
+	// { context path => [ context ] }
 	// }
-	PathMap<Object> map = _contextMap;
-	if (map!=null && target!=null && target.startsWith("/"))
+	if (target.startsWith("/"))
 	{
-	    // first, get all contexts matched by context path
-	    Object contexts = map.getLazyMatches(target);
+	    int limit = target.length()-1;
 
-            for (int i=0; i<LazyList.size(contexts); i++)
-            {
-                // then, match against the virtualhost of each context
-                Map.Entry entry = (Map.Entry)LazyList.get(contexts, i);
-                Object list = entry.getValue();
+	    while (limit>=0)
+	    {
+	        // Get best match
+	        ContextHandler[] contexts = _contexts.getBest(target,1,limit);
+	        if (contexts==null)
+	            break;
 
-                if (list instanceof Map)
-                {
-                    Map hosts = (Map)list;
-                    String host = normalizeHostname(request.getServerName());
-
-                    // explicitly-defined virtual hosts, most specific
-                    list=hosts.get(host);
-                    for (int j=0; j<LazyList.size(list); j++)
-                    {
-                        Handler handler = (Handler)LazyList.get(list,j);
-                        handler.handle(target,baseRequest, request, response);
-                        if (baseRequest.isHandled())
-                            return;
-                    }
-
-                    // wildcard for one level of names
-                    list=hosts.get("*."+host.substring(host.indexOf(".")+1));
-                    for (int j=0; j<LazyList.size(list); j++)
-                    {
-                        Handler handler = (Handler)LazyList.get(list,j);
-                        handler.handle(target,baseRequest, request, response);
-                        if (baseRequest.isHandled())
-                            return;
-                    }
-
-                    // no virtualhosts defined for the context, least specific
-                    // will handle any request that does not match to a specific virtual host above
-                    list=hosts.get("*");
-                    for (int j=0; j<LazyList.size(list); j++)
-                    {
-                        Handler handler = (Handler)LazyList.get(list,j);
-                        handler.handle(target,baseRequest, request, response);
-                        if (baseRequest.isHandled())
-                            return;
-                    }
-                }
-                else
-                {
-                    for (int j=0; j<LazyList.size(list); j++)
-                    {
-                        Handler handler = (Handler)LazyList.get(list,j);
-                        handler.handle(target,baseRequest, request, response);
-                        if (baseRequest.isHandled())
-                            return;
-                    }
-                }
+	        int l=contexts[0].getContextPath().length();
+	        if (l==1 || target.length()==l || target.charAt(l)=='/')
+	        {
+	            for (ContextHandler handler : contexts)
+	            {
+	                handler.handle(target,baseRequest, request, response);
+	                if (baseRequest.isHandled())
+	                    return;
+	            }
+	        }
+	        
+	        limit=l-2;
 	    }
 	}
 	else
@@ -320,16 +265,5 @@
         _contextClass = contextClass;
     }
 
-    /* ------------------------------------------------------------ */
-    private String normalizeHostname( String host )
-    {
-        if ( host == null )
-            return null;
-
-        if ( host.endsWith( "." ) )
-            return host.substring( 0, host.length() -1);
-
-        return host;
-    }
 
 }
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/RequestLogHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/RequestLogHandler.java
index 899a8bc..bad2be3 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/RequestLogHandler.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/RequestLogHandler.java
@@ -20,15 +20,18 @@
 
 import java.io.IOException;
 
+import javax.servlet.AsyncEvent;
+import javax.servlet.AsyncListener;
 import javax.servlet.DispatcherType;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import org.eclipse.jetty.server.HttpChannelState;
+import org.eclipse.jetty.server.AsyncContextState;
 import org.eclipse.jetty.server.Request;
 import org.eclipse.jetty.server.RequestLog;
 import org.eclipse.jetty.server.Response;
+import org.eclipse.jetty.util.component.AbstractLifeCycle;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 
@@ -44,8 +47,37 @@
 public class RequestLogHandler extends HandlerWrapper
 {
     private static final Logger LOG = Log.getLogger(RequestLogHandler.class);
-
     private RequestLog _requestLog;
+    private final AsyncListener _listener = new AsyncListener()
+    {
+        
+        @Override
+        public void onTimeout(AsyncEvent event) throws IOException
+        {
+            
+        }
+        
+        @Override
+        public void onStartAsync(AsyncEvent event) throws IOException
+        {
+            event.getAsyncContext().addListener(this);
+        }
+        
+        @Override
+        public void onError(AsyncEvent event) throws IOException
+        {
+            
+        }
+        
+        @Override
+        public void onComplete(AsyncEvent event) throws IOException
+        {
+            AsyncContextState context = (AsyncContextState)event.getAsyncContext();
+            Request request=context.getHttpChannelState().getBaseRequest();
+            Response response=request.getResponse();
+            _requestLog.log(request,response);
+        }
+    };
 
     /* ------------------------------------------------------------ */
     /*
@@ -55,21 +87,21 @@
     public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
             throws IOException, ServletException
     {
-        HttpChannelState continuation = baseRequest.getHttpChannelState();
-        if (!continuation.isInitial())
-        {
-            baseRequest.setDispatchTime(System.currentTimeMillis());
-        }
-
         try
         {
             super.handle(target, baseRequest, request, response);
         }
         finally
         {
-            if (_requestLog != null && DispatcherType.REQUEST.equals(baseRequest.getDispatcherType()))
+            if (_requestLog != null && baseRequest.getDispatcherType().equals(DispatcherType.REQUEST))
             {
-                _requestLog.log(baseRequest, (Response)response);
+                if (baseRequest.getHttpChannelState().isAsync())
+                {
+                    if (baseRequest.getHttpChannelState().isInitial())
+                        baseRequest.getAsyncContext().addListener(_listener);
+                }
+                else
+                    _requestLog.log(baseRequest, (Response)response);
             }
         }
     }
@@ -86,5 +118,36 @@
     {
         return _requestLog;
     }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    protected void doStart() throws Exception
+    {
+        if (_requestLog==null)
+        {
+            LOG.warn("!RequestLog");
+            _requestLog=new NullRequestLog();
+        }
+        super.doStart();
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    protected void doStop() throws Exception
+    {
+        super.doStop();
+        if (_requestLog instanceof NullRequestLog)
+            _requestLog=null;
+    }
 
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    private static class NullRequestLog extends AbstractLifeCycle implements RequestLog
+    {
+        @Override
+        public void log(Request request, Response response)
+        {            
+        }
+    }
 }
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionIdManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionIdManager.java
index e3df34f..3ddc0bd 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionIdManager.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionIdManager.java
@@ -37,6 +37,7 @@
     protected Random _random;
     protected boolean _weakRandom;
     protected String _workerName;
+    protected String _workerAttr;
     protected long _reseed=100000L;
 
     /* ------------------------------------------------------------ */
@@ -58,6 +59,7 @@
      *
      * @return String or null
      */
+    @Override
     public String getWorkerName()
     {
         return _workerName;
@@ -67,11 +69,16 @@
     /**
      * Set the workname. If set, the workername is dot appended to the session
      * ID and can be used to assist session affinity in a load balancer.
+     * A worker name starting with $ is used as a request attribute name to
+     * lookup the worker name that can be dynamically set by a request
+     * customiser.
      *
      * @param workerName
      */
     public void setWorkerName(String workerName)
     {
+        if (isRunning())
+            throw new IllegalStateException(getState());
         if (workerName.contains("."))
             throw new IllegalArgumentException("Name cannot contain '.'");
         _workerName=workerName;
@@ -114,27 +121,28 @@
      *
      * @see org.eclipse.jetty.server.SessionIdManager#newSessionId(javax.servlet.http.HttpServletRequest, long)
      */
+    @Override
     public String newSessionId(HttpServletRequest request, long created)
     {
         synchronized (this)
         {
-            if (request!=null)
-            {
-                // A requested session ID can only be used if it is in use already.
-                String requested_id=request.getRequestedSessionId();
-                if (requested_id!=null)
-                {
-                    String cluster_id=getClusterId(requested_id);
-                    if (idInUse(cluster_id))
-                        return cluster_id;
-                }
+            if (request==null)
+                return newSessionId(created);
 
-                // Else reuse any new session ID already defined for this request.
-                String new_id=(String)request.getAttribute(__NEW_SESSION_ID);
-                if (new_id!=null&&idInUse(new_id))
-                    return new_id;
+            // A requested session ID can only be used if it is in use already.
+            String requested_id=request.getRequestedSessionId();
+            if (requested_id!=null)
+            {
+                String cluster_id=getClusterId(requested_id);
+                if (idInUse(cluster_id))
+                    return cluster_id;
             }
 
+            // Else reuse any new session ID already defined for this request.
+            String new_id=(String)request.getAttribute(__NEW_SESSION_ID);
+            if (new_id!=null&&idInUse(new_id))
+                return new_id;
+
             // pick a new unique ID!
             String id = newSessionId(request.hashCode());
 
@@ -190,15 +198,16 @@
 
 
     /* ------------------------------------------------------------ */
+    @Override
     public abstract void renewSessionId(String oldClusterId, String oldNodeId, HttpServletRequest request);
 
-
     
     /* ------------------------------------------------------------ */
     @Override
     protected void doStart() throws Exception
     {
        initRandom();
+       _workerAttr=(_workerName!=null && _workerName.startsWith("$"))?_workerName.substring(1):null;
     }
 
     /* ------------------------------------------------------------ */
@@ -232,5 +241,39 @@
             _random.setSeed(_random.nextLong()^System.currentTimeMillis()^hashCode()^Runtime.getRuntime().freeMemory());
     }
 
+    /** Get the session ID with any worker ID.
+     *
+     * @param clusterId
+     * @param request
+     * @return sessionId plus any worker ID.
+     */
+    @Override
+    public String getNodeId(String clusterId, HttpServletRequest request)
+    {
+        if (_workerName!=null)
+        {
+            if (_workerAttr==null)
+                return clusterId+'.'+_workerName;
+
+            String worker=(String)request.getAttribute(_workerAttr);
+            if (worker!=null)
+                return clusterId+'.'+worker;
+        }
+    
+        return clusterId;
+    }
+
+    /** Get the session ID without any worker ID.
+     *
+     * @param nodeId the node id
+     * @return sessionId without any worker ID.
+     */
+    @Override
+    public String getClusterId(String nodeId)
+    {
+        int dot=nodeId.lastIndexOf('.');
+        return (dot>0)?nodeId.substring(0,dot):nodeId;
+    }
+
 
 }
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionManager.java
index a81453e..4801d16 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionManager.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionManager.java
@@ -459,16 +459,16 @@
     {
         if (isUsingCookies())
         {
-            String sessionPath = (_sessionPath==null) ? contextPath : _sessionPath;
+            String sessionPath = (_cookieConfig.getPath()==null) ? contextPath : _cookieConfig.getPath();
             sessionPath = (sessionPath==null||sessionPath.length()==0) ? "/" : sessionPath;
             String id = getNodeId(session);
             HttpCookie cookie = null;
             if (_sessionComment == null)
             {
                 cookie = new HttpCookie(
-                                        _sessionCookie,
+                                        _cookieConfig.getName(),
                                         id,
-                                        _sessionDomain,
+                                        _cookieConfig.getDomain(),
                                         sessionPath,
                                         _cookieConfig.getMaxAge(),
                                         _cookieConfig.isHttpOnly(),
@@ -477,9 +477,9 @@
             else
             {
                 cookie = new HttpCookie(
-                                        _sessionCookie,
+                                        _cookieConfig.getName(),
                                         id,
-                                        _sessionDomain,
+                                        _cookieConfig.getDomain(),
                                         sessionPath,
                                         _cookieConfig.getMaxAge(),
                                         _cookieConfig.isHttpOnly(),
@@ -796,9 +796,11 @@
 
             if (invalidate && _sessionListeners!=null)
             {
-                HttpSessionEvent event=new HttpSessionEvent(session);
-                for (HttpSessionListener listener : _sessionListeners)
-                    listener.sessionDestroyed(event);
+                HttpSessionEvent event=new HttpSessionEvent(session);      
+                for (int i = _sessionListeners.size()-1; i>=0; i--)
+                {
+                    _sessionListeners.get(i).sessionDestroyed(event);
+                }
             }
         }
     }
@@ -899,43 +901,57 @@
 
             @Override
             public void setComment(String comment)
-            {
+            {  
+                if (_context != null && _context.getContextHandler().isAvailable())
+                    throw new IllegalStateException("CookieConfig cannot be set after ServletContext is started");
                 _sessionComment = comment;
             }
 
             @Override
             public void setDomain(String domain)
             {
+                if (_context != null && _context.getContextHandler().isAvailable())
+                    throw new IllegalStateException("CookieConfig cannot be set after ServletContext is started");
                 _sessionDomain=domain;
             }
 
             @Override
             public void setHttpOnly(boolean httpOnly)
-            {
+            {   
+                if (_context != null && _context.getContextHandler().isAvailable())
+                    throw new IllegalStateException("CookieConfig cannot be set after ServletContext is started");
                 _httpOnly=httpOnly;
             }
 
             @Override
             public void setMaxAge(int maxAge)
-            {
+            {               
+                if (_context != null && _context.getContextHandler().isAvailable())
+                    throw new IllegalStateException("CookieConfig cannot be set after ServletContext is started");
                 _maxCookieAge=maxAge;
             }
 
             @Override
             public void setName(String name)
-            {
+            {  
+                    if (_context != null && _context.getContextHandler().isAvailable())
+                        throw new IllegalStateException("CookieConfig cannot be set after ServletContext is started");
                 _sessionCookie=name;
             }
 
             @Override
             public void setPath(String path)
             {
+                if (_context != null && _context.getContextHandler().isAvailable())
+                    throw new IllegalStateException("CookieConfig cannot be set after ServletContext is started"); 
                 _sessionPath=path;
             }
 
             @Override
             public void setSecure(boolean secure)
             {
+                if (_context != null && _context.getContextHandler().isAvailable())
+                    throw new IllegalStateException("CookieConfig cannot be set after ServletContext is started");
                 _secureCookies=secure;
             }
 
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionIdManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionIdManager.java
index 218fe3d..0ebd29b 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionIdManager.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionIdManager.java
@@ -81,38 +81,7 @@
         }
         return sessions;
     }
-    /* ------------------------------------------------------------ */
-    /** Get the session ID with any worker ID.
-     *
-     * @param clusterId
-     * @param request
-     * @return sessionId plus any worker ID.
-     */
-    public String getNodeId(String clusterId,HttpServletRequest request)
-    {
-        // used in Ajp13Parser
-        String worker=request==null?null:(String)request.getAttribute("org.eclipse.jetty.ajp.JVMRoute");
-        if (worker!=null)
-            return clusterId+'.'+worker;
-
-        if (_workerName!=null)
-            return clusterId+'.'+_workerName;
-
-        return clusterId;
-    }
-
-    /* ------------------------------------------------------------ */
-    /** Get the session ID without any worker ID.
-     *
-     * @param nodeId the node id
-     * @return sessionId without any worker ID.
-     */
-    public String getClusterId(String nodeId)
-    {
-        int dot=nodeId.lastIndexOf('.');
-        return (dot>0)?nodeId.substring(0,dot):nodeId;
-    }
-
+    
     /* ------------------------------------------------------------ */
     @Override
     protected void doStart() throws Exception
@@ -132,6 +101,7 @@
     /**
      * @see SessionIdManager#idInUse(String)
      */
+    @Override
     public boolean idInUse(String id)
     {
         synchronized (this)
@@ -144,6 +114,7 @@
     /**
      * @see SessionIdManager#addSession(HttpSession)
      */
+    @Override
     public void addSession(HttpSession session)
     {
         String id = getClusterId(session.getId());
@@ -165,6 +136,7 @@
     /**
      * @see SessionIdManager#removeSession(HttpSession)
      */
+    @Override
     public void removeSession(HttpSession session)
     {
         String id = getClusterId(session.getId());
@@ -199,6 +171,7 @@
     /**
      * @see SessionIdManager#invalidateAll(String)
      */
+    @Override
     public void invalidateAll(String id)
     {
         Collection<WeakReference<HttpSession>> sessions;
@@ -221,6 +194,7 @@
     
     
     /* ------------------------------------------------------------ */
+    @Override
     public void renewSessionId (String oldClusterId, String oldNodeId, HttpServletRequest request)
     {
         //generate a new id
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionIdManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionIdManager.java
index 15deaef..6590e24 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionIdManager.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionIdManager.java
@@ -29,11 +29,7 @@
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Statement;
-import java.util.ArrayList;
-import java.util.Collection;
 import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
 import java.util.Locale;
 import java.util.Random;
 import java.util.Set;
@@ -380,6 +376,7 @@
     }
 
 
+    @Override
     public void addSession(HttpSession session)
     {
         if (session == null)
@@ -422,6 +419,7 @@
 
 
 
+    @Override
     public void removeSession(HttpSession session)
     {
         if (session == null)
@@ -456,32 +454,7 @@
     }
 
 
-    /**
-     * Get the session id without any node identifier suffix.
-     *
-     * @see org.eclipse.jetty.server.SessionIdManager#getClusterId(java.lang.String)
-     */
-    public String getClusterId(String nodeId)
-    {
-        int dot=nodeId.lastIndexOf('.');
-        return (dot>0)?nodeId.substring(0,dot):nodeId;
-    }
-
-
-    /**
-     * Get the session id, including this node's id as a suffix.
-     *
-     * @see org.eclipse.jetty.server.SessionIdManager#getNodeId(java.lang.String, javax.servlet.http.HttpServletRequest)
-     */
-    public String getNodeId(String clusterId, HttpServletRequest request)
-    {
-        if (_workerName!=null)
-            return clusterId+'.'+_workerName;
-
-        return clusterId;
-    }
-
-
+    @Override
     public boolean idInUse(String id)
     {
         if (id == null)
@@ -515,6 +488,7 @@
      *
      * @see org.eclipse.jetty.server.SessionIdManager#invalidateAll(java.lang.String)
      */
+    @Override
     public void invalidateAll(String id)
     {
         //take the id out of the list of known sessionids for this node
@@ -527,7 +501,7 @@
             Handler[] contexts = _server.getChildHandlersByClass(ContextHandler.class);
             for (int i=0; contexts!=null && i<contexts.length; i++)
             {
-                SessionHandler sessionHandler = (SessionHandler)((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
+                SessionHandler sessionHandler = ((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
                 if (sessionHandler != null)
                 {
                     SessionManager manager = sessionHandler.getSessionManager();
@@ -542,6 +516,7 @@
     }
 
 
+    @Override
     public void renewSessionId (String oldClusterId, String oldNodeId, HttpServletRequest request)
     {
         //generate a new id
@@ -556,7 +531,7 @@
             Handler[] contexts = _server.getChildHandlersByClass(ContextHandler.class);
             for (int i=0; contexts!=null && i<contexts.length; i++)
             {
-                SessionHandler sessionHandler = (SessionHandler)((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
+                SessionHandler sessionHandler = ((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
                 if (sessionHandler != null) 
                 {
                     SessionManager manager = sessionHandler.getSessionManager();
@@ -971,7 +946,7 @@
         Handler[] contexts = _server.getChildHandlersByClass(ContextHandler.class);
         for (int i=0; contexts!=null && i<contexts.length; i++)
         {
-            SessionHandler sessionHandler = (SessionHandler)((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
+            SessionHandler sessionHandler = ((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
             if (sessionHandler != null)
             {
                 SessionManager manager = sessionHandler.getSessionManager();
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpOutputTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpOutputTest.java
new file mode 100644
index 0000000..c64387b
--- /dev/null
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpOutputTest.java
@@ -0,0 +1,226 @@
+//
+//  ========================================================================
+//  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.server;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.Assert.assertThat;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.ReadableByteChannel;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.util.resource.Resource;
+import org.hamcrest.Matchers;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class HttpOutputTest
+{
+    private Server _server;
+    private LocalConnector _connector;
+    private ContentHandler _handler;
+
+    @Before
+    public void init() throws Exception
+    {
+        _server = new Server();
+
+        HttpConnectionFactory http = new HttpConnectionFactory();
+        http.getHttpConfiguration().setRequestHeaderSize(1024);
+        http.getHttpConfiguration().setResponseHeaderSize(1024);
+        http.getHttpConfiguration().setOutputBufferSize(4096);
+        
+        _connector = new LocalConnector(_server,http,null);
+        _server.addConnector(_connector);
+        _handler=new ContentHandler();
+        _server.setHandler(_handler);
+        _server.start();
+    }
+
+    @After
+    public void destroy() throws Exception
+    {
+        _server.stop();
+        _server.join();
+    }
+
+    @Test
+    public void testSimple() throws Exception
+    {
+        String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
+        assertThat(response,containsString("HTTP/1.1 200 OK"));
+    }
+    
+    @Test
+    public void testSendInputStreamSimple() throws Exception
+    {
+        Resource simple = Resource.newClassPathResource("simple/simple.txt");
+        _handler._contentInputStream=simple.getInputStream();
+        String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
+        assertThat(response,containsString("HTTP/1.1 200 OK"));
+        assertThat(response,containsString("Content-Length: 11"));
+    }
+    
+    @Test
+    public void testSendInputStreamBig() throws Exception
+    {
+        Resource big = Resource.newClassPathResource("simple/big.txt");
+        _handler._contentInputStream=big.getInputStream();
+        String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
+        assertThat(response,containsString("HTTP/1.1 200 OK"));
+        assertThat(response,Matchers.not(containsString("Content-Length")));
+    }
+    
+    @Test
+    public void testSendInputStreamBigChunked() throws Exception
+    {
+        Resource big = Resource.newClassPathResource("simple/big.txt");
+        _handler._contentInputStream= new FilterInputStream(big.getInputStream())
+        {
+            @Override
+            public int read(byte[] b, int off, int len) throws IOException
+            {
+                int filled= super.read(b,off,len>2000?2000:len);
+                return filled;
+            }
+        };
+        String response=_connector.getResponses(
+            "GET / HTTP/1.1\nHost: localhost:80\n\n"+
+            "GET / HTTP/1.1\nHost: localhost:80\nConnection: close\n\n"
+        );
+        response=response.substring(0,response.lastIndexOf("HTTP/1.1 200 OK"));
+        
+        assertThat(response,containsString("HTTP/1.1 200 OK"));
+        assertThat(response,containsString("Transfer-Encoding: chunked"));
+        assertThat(response,containsString("\r\n0\r\n"));
+    }
+    
+
+    @Test
+    public void testSendChannelSimple() throws Exception
+    {
+        Resource simple = Resource.newClassPathResource("simple/simple.txt");
+        _handler._contentChannel=simple.getReadableByteChannel();
+        String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
+        assertThat(response,containsString("HTTP/1.1 200 OK"));
+        assertThat(response,containsString("Content-Length: 11"));
+    }
+    
+    @Test
+    public void testSendChannelBig() throws Exception
+    {
+        Resource big = Resource.newClassPathResource("simple/big.txt");
+        _handler._contentChannel=big.getReadableByteChannel();
+        String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
+        assertThat(response,containsString("HTTP/1.1 200 OK"));
+        assertThat(response,Matchers.not(containsString("Content-Length")));
+    }
+    
+    @Test
+    public void testSendChannelBigChunked() throws Exception
+    {
+        Resource big = Resource.newClassPathResource("simple/big.txt");
+        final ReadableByteChannel channel = big.getReadableByteChannel();
+        _handler._contentChannel=new ReadableByteChannel()
+        {
+            
+            @Override
+            public boolean isOpen()
+            {
+                return channel.isOpen();
+            }
+            
+            @Override
+            public void close() throws IOException
+            {
+                channel.close();
+            }
+            
+            @Override
+            public int read(ByteBuffer dst) throws IOException
+            {
+                int filled=0;
+                if (dst.position()==0 && dst.limit()>2000)
+                {
+                    int limit=dst.limit();
+                    dst.limit(2000);
+                    filled=channel.read(dst);
+                    dst.limit(limit);
+                }
+                else 
+                    filled=channel.read(dst);
+                return filled;
+            }
+        };
+        
+        String response=_connector.getResponses(
+            "GET / HTTP/1.1\nHost: localhost:80\n\n"+
+            "GET / HTTP/1.1\nHost: localhost:80\nConnection: close\n\n"
+        );
+        response=response.substring(0,response.lastIndexOf("HTTP/1.1 200 OK"));
+        
+        assertThat(response,containsString("HTTP/1.1 200 OK"));
+        assertThat(response,containsString("Transfer-Encoding: chunked"));
+        assertThat(response,containsString("\r\n0\r\n"));
+    }
+    
+    static class ContentHandler extends AbstractHandler
+    {
+        InputStream _contentInputStream;
+        ReadableByteChannel _contentChannel;
+        
+        @Override
+        public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+        {
+            baseRequest.setHandled(true);
+            response.setContentType("text/plain");
+            
+            HttpOutput out = (HttpOutput) response.getOutputStream();
+            
+            if (_contentInputStream!=null)
+            {
+                out.sendContent(_contentInputStream);
+                _contentInputStream=null;
+                return;
+            }
+            
+            if (_contentChannel!=null)
+            {
+                out.sendContent(_contentChannel);
+                _contentChannel=null;
+                return;
+            }
+            
+        }
+        
+    }
+}
+
+
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java
index 5a5f594..d98e654 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java
@@ -48,6 +48,7 @@
 import org.eclipse.jetty.io.EndPoint;
 import org.eclipse.jetty.io.EofException;
 import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.util.BufferUtil;
 import org.eclipse.jetty.util.IO;
 import org.eclipse.jetty.util.StringUtil;
 import org.eclipse.jetty.util.log.Log;
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpURITest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpURITest.java
index a770f95..05958cc 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpURITest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpURITest.java
@@ -33,6 +33,7 @@
 import org.eclipse.jetty.util.MultiMap;
 import org.eclipse.jetty.util.StringUtil;
 import org.eclipse.jetty.util.TypeUtil;
+import org.eclipse.jetty.util.Utf8Appendable;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -322,29 +323,15 @@
         {
         }
 
-        try
-        {
-            HttpURI huri=new HttpURI(uri);
-            MultiMap<String> params = new MultiMap<>();
-            huri.decodeQueryTo(params);
-            System.err.println(params);
-            Assert.assertTrue(false);
-        }
-        catch (IllegalArgumentException e)
-        {
-        }
+        HttpURI huri=new HttpURI(uri);
+        MultiMap<String> params = new MultiMap<>();
+        huri.decodeQueryTo(params);
+        assertEquals("data"+Utf8Appendable.REPLACEMENT+"here"+Utf8Appendable.REPLACEMENT,params.getValue("invalid",0));
 
-        try
-        {
-            HttpURI huri=new HttpURI(uri);
-            MultiMap<String> params = new MultiMap<>();
-            huri.decodeQueryTo(params,"UTF-8");
-            System.err.println(params);
-            Assert.assertTrue(false);
-        }
-        catch (IllegalArgumentException e)
-        {
-        }
+        huri=new HttpURI(uri);
+        params = new MultiMap<>();
+        huri.decodeQueryTo(params,"UTF-8");
+        assertEquals("data"+Utf8Appendable.REPLACEMENT+"here"+Utf8Appendable.REPLACEMENT,params.getValue("invalid",0));
 
     }
 
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java
index 3249d96..e2d94bf 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java
@@ -18,26 +18,17 @@
 
 package org.eclipse.jetty.server;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNotSame;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileReader;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.Reader;
+import java.io.UnsupportedEncodingException;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Enumeration;
 import java.util.Map;
 import java.util.concurrent.TimeUnit;
-
 import javax.servlet.MultipartConfigElement;
 import javax.servlet.ServletException;
 import javax.servlet.ServletRequestEvent;
@@ -52,15 +43,23 @@
 import org.eclipse.jetty.util.IO;
 import org.eclipse.jetty.util.MultiPartInputStreamParser;
 import org.eclipse.jetty.util.StringUtil;
+import org.eclipse.jetty.util.Utf8Appendable;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 import org.eclipse.jetty.util.log.StdErrLog;
 import org.hamcrest.Matchers;
 import org.junit.After;
-import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
 public class RequestTest
 {
     private static final Logger LOG = Log.getLogger(RequestTest.class);
@@ -100,24 +99,11 @@
             @Override
             public boolean check(HttpServletRequest request,HttpServletResponse response)
             {
-                Map map = null;
-                try
-                {
-                    //do the parse
-                    request.getParameterMap();
-                    Assert.fail("Expected parsing failure");
-                    return false;
-                }
-                catch (Exception e)
-                {
-                    //catch the error and check the param map is not null
-                    map = request.getParameterMap();
-                    assertFalse(map == null);
-                    assertTrue(map.isEmpty());
-
-                    Enumeration names = request.getParameterNames();
-                    assertFalse(names.hasMoreElements());
-                }
+                Map<String,String[]> map = null;
+                //do the parse
+                map = request.getParameterMap();
+                assertEquals("aaa"+Utf8Appendable.REPLACEMENT+"bbb",map.get("param")[0]);
+                assertEquals("value",map.get("other")[0]);
 
                 return true;
             }
@@ -125,7 +111,7 @@
 
         //Send a request with query string with illegal hex code to cause
         //an exception parsing the params
-        String request="GET /?param=%ZZaaa HTTP/1.1\r\n"+
+        String request="GET /?param=aaa%ZZbbb&other=value HTTP/1.1\r\n"+
         "Host: whatever\r\n"+
         "Content-Type: text/html;charset=utf8\n"+
         "Connection: close\n"+
@@ -237,6 +223,7 @@
         "Host: whatever\r\n"+
         "Content-Type: multipart/form-data; boundary=\"AaB03x\"\r\n"+
         "Content-Length: "+multipart.getBytes().length+"\r\n"+
+        "Connection: close\r\n"+
         "\r\n"+
         multipart;
 
@@ -363,12 +350,13 @@
         };
 
         results.clear();
-        _connector.getResponses(
+        String response=_connector.getResponses(
                 "GET / HTTP/1.1\n"+
                 "Host: myhost\n"+
                 "Connection: close\n"+
                 "\n");
         int i=0;
+        assertThat(response,Matchers.containsString("200 OK"));
         assertEquals("http://myhost/",results.get(i++));
         assertEquals("0.0.0.0",results.get(i++));
         assertEquals("myhost",results.get(i++));
@@ -376,12 +364,13 @@
         
         
         results.clear();
-        _connector.getResponses(
+        response=_connector.getResponses(
                 "GET / HTTP/1.1\n"+
                 "Host: myhost:8888\n"+
                 "Connection: close\n"+
                 "\n");
         i=0;
+        assertThat(response,Matchers.containsString("200 OK"));
         assertEquals("http://myhost:8888/",results.get(i++));
         assertEquals("0.0.0.0",results.get(i++));
         assertEquals("myhost",results.get(i++));
@@ -389,13 +378,14 @@
         
         
         results.clear();
-        _connector.getResponses(
+        response=_connector.getResponses(
                 "GET / HTTP/1.1\n"+
                 "Host: 1.2.3.4\n"+
                 "Connection: close\n"+
                 "\n");
         i=0;
-        
+
+        assertThat(response,Matchers.containsString("200 OK"));
         assertEquals("http://1.2.3.4/",results.get(i++));
         assertEquals("0.0.0.0",results.get(i++));
         assertEquals("1.2.3.4",results.get(i++));
@@ -403,12 +393,13 @@
         
         
         results.clear();
-        _connector.getResponses(
+        response=_connector.getResponses(
                 "GET / HTTP/1.1\n"+
                 "Host: 1.2.3.4:8888\n"+
                 "Connection: close\n"+
                 "\n");
         i=0;
+        assertThat(response,Matchers.containsString("200 OK"));
         assertEquals("http://1.2.3.4:8888/",results.get(i++));
         assertEquals("0.0.0.0",results.get(i++));
         assertEquals("1.2.3.4",results.get(i++));
@@ -416,12 +407,13 @@
         
         
         results.clear();
-        _connector.getResponses(
+        response=_connector.getResponses(
                 "GET / HTTP/1.1\n"+
                 "Host: [::1]\n"+
                 "Connection: close\n"+
                 "\n");
         i=0;
+        assertThat(response,Matchers.containsString("200 OK"));
         assertEquals("http://[::1]/",results.get(i++));
         assertEquals("0.0.0.0",results.get(i++));
         assertEquals("::1",results.get(i++));
@@ -429,12 +421,13 @@
         
         
         results.clear();
-        _connector.getResponses(
+        response=_connector.getResponses(
                 "GET / HTTP/1.1\n"+
                 "Host: [::1]:8888\n"+
                 "Connection: close\n"+
                 "\n");
         i=0;
+        assertThat(response,Matchers.containsString("200 OK"));
         assertEquals("http://[::1]:8888/",results.get(i++));
         assertEquals("0.0.0.0",results.get(i++));
         assertEquals("::1",results.get(i++));
@@ -442,7 +435,7 @@
         
         
         results.clear();
-        _connector.getResponses(
+        response=_connector.getResponses(
                 "GET / HTTP/1.1\n"+
                 "Host: [::1]\n"+
                 "x-forwarded-for: remote\n"+
@@ -450,6 +443,7 @@
                 "Connection: close\n"+
                 "\n");
         i=0;
+        assertThat(response,Matchers.containsString("200 OK"));
         assertEquals("https://[::1]/",results.get(i++));
         assertEquals("remote",results.get(i++));
         assertEquals("::1",results.get(i++));
@@ -457,7 +451,7 @@
         
         
         results.clear();
-        _connector.getResponses(
+        response=_connector.getResponses(
                 "GET / HTTP/1.1\n"+
                 "Host: [::1]:8888\n"+
                 "Connection: close\n"+
@@ -465,18 +459,11 @@
                 "x-forwarded-proto: https\n"+
                 "\n");
         i=0;
-        
+        assertThat(response,Matchers.containsString("200 OK"));
         assertEquals("https://[::1]:8888/",results.get(i++));
         assertEquals("remote",results.get(i++));
         assertEquals("::1",results.get(i++));
         assertEquals("8888",results.get(i++));
-
-        
-
-
-        
-        
-        
     }
 
     @Test
@@ -1046,6 +1033,12 @@
         }
     }
 
+    @Test(expected = UnsupportedEncodingException.class)
+    public void testNotSupportedCharacterEncoding() throws UnsupportedEncodingException
+    {
+        Request request = new Request(null, null);
+        request.setCharacterEncoding("doesNotExist");
+    }
 
     interface RequestTester
     {
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java
index 84967af..245fb5c 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java
@@ -601,7 +601,7 @@
 
         String set = response.getHttpFields().getStringField("Set-Cookie");
 
-        assertEquals("name=value;Path=/path;Domain=domain;Secure;HttpOnly;Comment=comment", set);
+        assertEquals("name=value;Version=1;Path=/path;Domain=domain;Secure;HttpOnly;Comment=comment", set);
     }
 
 
@@ -630,7 +630,7 @@
         assertNotNull(set);
         ArrayList<String> list = Collections.list(set);
         assertEquals(2, list.size());
-        assertTrue(list.contains("name=value;Path=/path;Domain=domain;Secure;HttpOnly;Comment=comment"));
+        assertTrue(list.contains("name=value;Version=1;Path=/path;Domain=domain;Secure;HttpOnly;Comment=comment"));
         assertTrue(list.contains("name2=value2;Path=/path;Domain=domain"));
 
         //get rid of the cookies
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ContextHandlerCollectionTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ContextHandlerCollectionTest.java
index 9dab6b4..a287dcd 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ContextHandlerCollectionTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ContextHandlerCollectionTest.java
@@ -23,6 +23,8 @@
 import static org.junit.Assert.assertTrue;
 
 import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
 
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
@@ -32,60 +34,121 @@
 import org.eclipse.jetty.server.LocalConnector;
 import org.eclipse.jetty.server.Request;
 import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.util.ArrayUtil;
+import org.hamcrest.Matchers;
+import org.junit.Assert;
 import org.junit.Test;
 
 public class ContextHandlerCollectionTest
 {
     @Test
-    public void testVirtualHostNormalization() throws Exception
+    public void testVirtualHosts() throws Exception
     {
         Server server = new Server();
-        LocalConnector connector = new LocalConnector(server);
+        LocalConnector connector0 = new LocalConnector(server);
+        LocalConnector connector1 = new LocalConnector(server);
+        connector1.setName("connector1");
+        
         server.setConnectors(new Connector[]
-        { connector });
+        { connector0,connector1});
 
-        ContextHandler contextA = new ContextHandler("/");
+        ContextHandler contextA = new ContextHandler("/ctx");
         contextA.setVirtualHosts(new String[]
-        { "www.example.com" });
-        IsHandledHandler handlerA = new IsHandledHandler();
+        { "www.example.com", "alias.example.com" });
+        IsHandledHandler handlerA = new IsHandledHandler("A");
         contextA.setHandler(handlerA);
+        contextA.setAllowNullPathInfo(true);
 
-        ContextHandler contextB = new ContextHandler("/");
-        IsHandledHandler handlerB = new IsHandledHandler();
+        ContextHandler contextB = new ContextHandler("/ctx");
+        IsHandledHandler handlerB = new IsHandledHandler("B");
         contextB.setHandler(handlerB);
         contextB.setVirtualHosts(new String[]
-        { "www.example2.com." });
+        { "*.other.com" , "@connector1"});
 
-        ContextHandler contextC = new ContextHandler("/");
-        IsHandledHandler handlerC = new IsHandledHandler();
+        ContextHandler contextC = new ContextHandler("/ctx");
+        IsHandledHandler handlerC = new IsHandledHandler("C");
         contextC.setHandler(handlerC);
 
-        ContextHandlerCollection c = new ContextHandlerCollection();
+        ContextHandler contextD = new ContextHandler("/");
+        IsHandledHandler handlerD = new IsHandledHandler("D");
+        contextD.setHandler(handlerD);
+        
+        ContextHandler contextE = new ContextHandler("/ctx/foo");
+        IsHandledHandler handlerE = new IsHandledHandler("E");
+        contextE.setHandler(handlerE);
+        
+        ContextHandler contextF = new ContextHandler("/ctxlong");
+        IsHandledHandler handlerF = new IsHandledHandler("F");
+        contextF.setHandler(handlerF);
 
+        ContextHandlerCollection c = new ContextHandlerCollection();
         c.addHandler(contextA);
         c.addHandler(contextB);
         c.addHandler(contextC);
-
+        
+        HandlerList list = new HandlerList();
+        list.addHandler(contextD);
+        list.addHandler(contextE);
+        list.addHandler(contextF);
+        c.addHandler(list);
+        
         server.setHandler(c);
 
         try
         {
             server.start();
-            connector.getResponses("GET / HTTP/1.0\n" + "Host: www.example.com.\n\n");
+            
+            Object[][] tests = new Object[][] {
+                {connector0,"www.example.com.", "/ctx",    handlerA},
+                {connector0,"www.example.com.", "/ctx/",    handlerA},
+                {connector0,"www.example.com.", "/ctx/info",    handlerA},
+                {connector0,"www.example.com",  "/ctx/info",    handlerA},
+                {connector0,"alias.example.com",  "/ctx/info",    handlerA},
+                {connector1,"www.example.com.", "/ctx/info",    handlerA},
+                {connector1,"www.example.com",  "/ctx/info",    handlerA},
+                {connector1,"alias.example.com",  "/ctx/info",    handlerA},
 
-            assertTrue(handlerA.isHandled());
-            assertFalse(handlerB.isHandled());
-            assertFalse(handlerC.isHandled());
+                {connector1,"www.other.com",  "/ctx",    null},
+                {connector1,"www.other.com",  "/ctx/",    handlerB},
+                {connector1,"www.other.com",  "/ctx/info",    handlerB},
+                {connector0,"www.other.com",  "/ctx/info",    handlerC},
+                
+                {connector0,"www.example.com",  "/ctxinfo",    handlerD},
+                {connector1,"unknown.com",  "/unknown",    handlerD},
+                
+                {connector0,"alias.example.com",  "/ctx/foo/info",    handlerE},
+                {connector0,"alias.example.com",  "/ctxlong/info",    handlerF},
+            };
+            
+            for (int i=0;i<tests.length;i++)
+            {
+                Object[] test=tests[i];
+                LocalConnector connector = (LocalConnector)test[0];
+                String host=(String)test[1];
+                String uri=(String)test[2];
+                IsHandledHandler handler = (IsHandledHandler)test[3];
 
-            handlerA.reset();
-            handlerB.reset();
-            handlerC.reset();
+                handlerA.reset();
+                handlerB.reset();
+                handlerC.reset();
+                handlerD.reset();
+                handlerE.reset();
+                handlerF.reset();
 
-            connector.getResponses("GET / HTTP/1.0\n" + "Host: www.example2.com\n\n");
-
-            assertFalse(handlerA.isHandled());
-            assertTrue(handlerB.isHandled());
-            assertFalse(handlerC.isHandled());
+                // System.err.printf("test   %d %s@%s --> %s | %s%n",i,uri,host,connector.getName(),handler);
+                String response = connector.getResponses("GET "+uri+" HTTP/1.0\nHost: "+host+"\n\n");
+                
+                if (handler==null)
+                {
+                    Assert.assertThat(response,Matchers.containsString(" 302 "));
+                }
+                else if (!handler.isHandled())
+                {
+                    System.err.printf("FAILED %d %s@%s --> %s | %s%n",i,uri,host,connector.getName(),handler);
+                    System.err.println(response);
+                    Assert.fail();
+                }
+            }
 
         }
         finally
@@ -103,7 +166,7 @@
 
         ContextHandler context = new ContextHandler("/");
 
-        IsHandledHandler handler = new IsHandledHandler();
+        IsHandledHandler handler = new IsHandledHandler("H");
         context.setHandler(handler);
 
         ContextHandlerCollection c = new ContextHandlerCollection();
@@ -149,7 +212,10 @@
 
         for(String host : requestHosts)
         {
-            connector.getResponses("GET / HTTP/1.0\n" + "Host: "+host+"\nConnection:close\n\n");
+            // System.err.printf("host=%s in %s%n",host,contextHosts==null?Collections.emptyList():Arrays.asList(contextHosts));
+            
+            String response=connector.getResponses("GET / HTTP/1.0\n" + "Host: "+host+"\nConnection:close\n\n");
+            // System.err.println(response);
             if(succeed)
                 assertTrue("'"+host+"' should have been handled.",handler.isHandled());
             else
@@ -166,17 +232,17 @@
         Server server = new Server();
 
         ContextHandler contextA = new ContextHandler("/a");
-        IsHandledHandler handlerA = new IsHandledHandler();
+        IsHandledHandler handlerA = new IsHandledHandler("A");
         contextA.setHandler(handlerA);
 
         ContextHandler contextB = new ContextHandler("/b");
-        IsHandledHandler handlerB = new IsHandledHandler();
+        IsHandledHandler handlerB = new IsHandledHandler("B");
         HandlerWrapper wrapperB = new HandlerWrapper();
         wrapperB.setHandler(handlerB);
         contextB.setHandler(wrapperB);
 
         ContextHandler contextC = new ContextHandler("/c");
-        IsHandledHandler handlerC = new IsHandledHandler();
+        IsHandledHandler handlerC = new IsHandledHandler("C");
         contextC.setHandler(handlerC);
 
         ContextHandlerCollection collection = new ContextHandlerCollection();
@@ -202,22 +268,36 @@
     private static final class IsHandledHandler extends AbstractHandler
     {
         private boolean handled;
+        private final String name;
+
+        public IsHandledHandler(String string)
+        {
+            name=string;
+        }
 
         public boolean isHandled()
         {
             return handled;
         }
 
+        @Override
         public void handle(String s, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
         {
             baseRequest.setHandled(true);
             this.handled = true;
+            response.getWriter().print(name);
         }
 
         public void reset()
         {
             handled = false;
         }
+        
+        @Override
+        public String toString()
+        {
+            return name;
+        }
     }
 
 
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/session/HashSessionManagerTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/session/HashSessionManagerTest.java
index 11ad5a9..960fa62 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/session/HashSessionManagerTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/session/HashSessionManagerTest.java
@@ -102,7 +102,7 @@
         Assert.assertTrue(testDir.exists());
         Assert.assertTrue(testDir.canWrite());
         
-        HashSessionIdManager idManager = new HashSessionIdManager();
+        AbstractSessionIdManager idManager = new HashSessionIdManager();
         idManager.setWorkerName("foo");
         manager.setSessionIdManager(idManager);
         
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/session/SessionCookieTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/session/SessionCookieTest.java
index f5b58e2..0c52028 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/session/SessionCookieTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/session/SessionCookieTest.java
@@ -58,6 +58,7 @@
         /**
          * @see org.eclipse.jetty.server.SessionIdManager#idInUse(java.lang.String)
          */
+        @Override
         public boolean idInUse(String id)
         {
             return false;
@@ -66,6 +67,7 @@
         /**
          * @see org.eclipse.jetty.server.SessionIdManager#addSession(javax.servlet.http.HttpSession)
          */
+        @Override
         public void addSession(HttpSession session)
         {
 
@@ -74,6 +76,7 @@
         /**
          * @see org.eclipse.jetty.server.SessionIdManager#removeSession(javax.servlet.http.HttpSession)
          */
+        @Override
         public void removeSession(HttpSession session)
         {
 
@@ -82,28 +85,12 @@
         /**
          * @see org.eclipse.jetty.server.SessionIdManager#invalidateAll(java.lang.String)
          */
+        @Override
         public void invalidateAll(String id)
         {
 
         }
 
-        /**
-         * @see org.eclipse.jetty.server.SessionIdManager#getClusterId(java.lang.String)
-         */
-        public String getClusterId(String nodeId)
-        {
-            int dot=nodeId.lastIndexOf('.');
-            return (dot>0)?nodeId.substring(0,dot):nodeId;
-        }
-
-        /**
-         * @see org.eclipse.jetty.server.SessionIdManager#getNodeId(java.lang.String, javax.servlet.http.HttpServletRequest)
-         */
-        public String getNodeId(String clusterId, HttpServletRequest request)
-        {
-            return clusterId+'.'+_workerName;
-        }
-
         @Override
         public void renewSessionId(String oldClusterId, String oldNodeId, HttpServletRequest request)
         {
@@ -119,6 +106,7 @@
         /**
          * @see org.eclipse.jetty.server.session.AbstractSessionManager#addSession(org.eclipse.jetty.server.session.AbstractSession)
          */
+        @Override
         protected void addSession(AbstractSession session)
         {
 
@@ -127,6 +115,7 @@
         /**
          * @see org.eclipse.jetty.server.session.AbstractSessionManager#getSession(java.lang.String)
          */
+        @Override
         public AbstractSession getSession(String idInCluster)
         {
             return null;
@@ -135,6 +124,7 @@
         /**
          * @see org.eclipse.jetty.server.session.AbstractSessionManager#invalidateSessions()
          */
+        @Override
         protected void invalidateSessions() throws Exception
         {
 
@@ -143,6 +133,7 @@
         /**
          * @see org.eclipse.jetty.server.session.AbstractSessionManager#newSession(javax.servlet.http.HttpServletRequest)
          */
+        @Override
         protected AbstractSession newSession(HttpServletRequest request)
         {
             return null;
@@ -151,6 +142,7 @@
         /**
          * @see org.eclipse.jetty.server.session.AbstractSessionManager#removeSession(java.lang.String)
          */
+        @Override
         protected boolean removeSession(String idInCluster)
         {
             return false;
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncServletTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncServletTest.java
index 42da56a..8d2e453 100644
--- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncServletTest.java
+++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncServletTest.java
@@ -21,6 +21,8 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.Socket;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Timer;
 import java.util.TimerTask;
 import javax.servlet.AsyncContext;
@@ -33,9 +35,14 @@
 import javax.servlet.http.HttpServletResponse;
 
 import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.RequestLog;
+import org.eclipse.jetty.server.Response;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.handler.RequestLogHandler;
 import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.component.AbstractLifeCycle;
 import org.hamcrest.Matchers;
 import org.junit.After;
 import org.junit.Assert;
@@ -53,15 +60,28 @@
     protected Server _server = new Server();
     protected ServletHandler _servletHandler;
     protected ServerConnector _connector;
+    protected List<String> _log;
+    protected int _expectedLogs;
+    protected String _expectedCode;
 
     @Before
     public void setUp() throws Exception
     {
         _connector = new ServerConnector(_server);
         _server.setConnectors(new Connector[]{ _connector });
+        
+        _log=new ArrayList<>();
+        RequestLog log=new Log();
+        RequestLogHandler logHandler = new RequestLogHandler();
+        logHandler.setRequestLog(log);
+        _server.setHandler(logHandler);
+        _expectedLogs=1;
+        _expectedCode="200 ";
+
         ServletContextHandler context = new ServletContextHandler(ServletContextHandler.NO_SESSIONS);
         context.setContextPath("/ctx");
-        _server.setHandler(context);
+        logHandler.setHandler(context);
+        
         _servletHandler=context.getServletHandler();
         ServletHolder holder=new ServletHolder(_servlet);
         holder.setAsyncSupported(true);
@@ -73,6 +93,8 @@
     @After
     public void tearDown() throws Exception
     {
+        assertEquals(_expectedLogs,_log.size());
+        Assert.assertThat(_log.get(0),Matchers.containsString(_expectedCode));
         _server.stop();
     }
 
@@ -105,6 +127,7 @@
     @Test
     public void testSuspend() throws Exception
     {
+        _expectedCode="500 ";
         String response=process("suspend=200",null);
         assertEquals("HTTP/1.1 500 Async Timeout",response.substring(0,26));
         assertContains(
@@ -258,6 +281,7 @@
     @Test
     public void testSuspendWaitResumeSuspend() throws Exception
     {
+        _expectedCode="500 ";
         String response=process("suspend=1000&resume=10&suspend2=10",null);
         assertEquals("HTTP/1.1 500 Async Timeout",response.substring(0,26));
         assertContains(
@@ -316,6 +340,7 @@
     @Test
     public void testSuspendTimeoutSuspend() throws Exception
     {
+        _expectedCode="500 ";
         String response=process("suspend=10&suspend2=10",null);
         assertContains(
             "history: REQUEST\r\n"+
@@ -335,6 +360,7 @@
     @Test
     public void testAsyncRead() throws Exception
     {
+        _expectedLogs=2;
         String header="GET /ctx/path/info?suspend=2000&resume=1500 HTTP/1.1\r\n"+
             "Host: localhost\r\n"+
             "Content-Length: 10\r\n"+
@@ -691,4 +717,13 @@
             ((HttpServletResponse)event.getSuppliedResponse()).addHeader("history","onComplete");
         }
     };
+
+    class Log extends AbstractLifeCycle implements RequestLog
+    {
+        @Override
+        public void log(Request request, Response response)
+        {            
+            _log.add(response.getStatus()+" "+response.getContentCount()+" "+request.getRequestURI());
+        }
+    }
 }
diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/DoSFilter.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/DoSFilter.java
index 392d4e5..047f599 100644
--- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/DoSFilter.java
+++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/DoSFilter.java
@@ -19,6 +19,7 @@
 package org.eclipse.jetty.servlets;
 
 import java.io.IOException;
+import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
@@ -1066,8 +1067,10 @@
      * A RateTracker is associated with a connection, and stores request rate
      * data.
      */
-    class RateTracker extends Timeout.Task implements HttpSessionBindingListener, HttpSessionActivationListener
+    class RateTracker extends Timeout.Task implements HttpSessionBindingListener, HttpSessionActivationListener, Serializable
     {
+        private static final long serialVersionUID = 3534663738034577872L;
+
         transient protected final String _id;
         transient protected final int _type;
         transient protected final long[] _timestamps;
diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/MultiPartFilter.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/MultiPartFilter.java
index 8fcb8b2..253d6d5 100644
--- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/MultiPartFilter.java
+++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/MultiPartFilter.java
@@ -45,10 +45,12 @@
 import javax.servlet.http.Part;
 
 import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.http.MimeTypes;
 import org.eclipse.jetty.util.LazyList;
 import org.eclipse.jetty.util.MultiMap;
 import org.eclipse.jetty.util.MultiPartInputStreamParser;
 import org.eclipse.jetty.util.StringUtil;
+import org.eclipse.jetty.util.TypeUtil;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 
@@ -259,11 +261,11 @@
             {
                 try
                 {
-                    return new String((byte[])o,_encoding);
+                    return getParameterBytesAsString(name, (byte[])o);
                 }
                 catch(Exception e)
                 {
-                    e.printStackTrace();
+                    LOG.warn(e);
                 }
             }
             else if (o!=null)
@@ -282,9 +284,7 @@
             
             for ( Object key : _params.keySet() )
             {
-                String[] a = LazyList.toStringArray(getParameter((String)key));
-                cmap.put((String)key,a);
-
+                cmap.put((String)key,getParameterValues((String)key));
             }
 
             return Collections.unmodifiableMap(cmap);
@@ -318,7 +318,7 @@
                 {
                     try
                     {
-                        v[i]=new String((byte[])o,_encoding);
+                        v[i]=getParameterBytesAsString(name, (byte[])o);
                     }
                     catch(Exception e)
                     {
@@ -341,5 +341,23 @@
         {
             _encoding=enc;
         }
+        
+        
+        /* ------------------------------------------------------------------------------- */
+        private String getParameterBytesAsString (String name, byte[] bytes) 
+        throws UnsupportedEncodingException
+        {
+            //check if there is a specific encoding for the parameter
+            Object ct = _params.getValue(name+CONTENT_TYPE_SUFFIX,0);
+            //use default if not
+            String contentType = _encoding;
+            if (ct != null)
+            {
+                String tmp = MimeTypes.getCharsetFromContentType((String)ct);
+                contentType = (tmp == null?_encoding:tmp);
+            }
+            
+            return new String(bytes,contentType);
+        }
     }
 }
diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/CompressedResponseWrapper.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/CompressedResponseWrapper.java
index 6930536..59324b0 100644
--- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/CompressedResponseWrapper.java
+++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/CompressedResponseWrapper.java
@@ -243,7 +243,7 @@
         if (_writer!=null)
             _writer.flush();
         if (_compressedStream!=null)
-            _compressedStream.finish();
+            _compressedStream.flush();
         else
             getResponse().flushBuffer();
     }
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterContentLengthTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterContentLengthTest.java
index cc89b1c..101eaaf 100644
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterContentLengthTest.java
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterContentLengthTest.java
@@ -32,6 +32,7 @@
 import org.eclipse.jetty.servlets.gzip.TestServletLengthStreamTypeWrite;
 import org.eclipse.jetty.servlets.gzip.TestServletLengthTypeStreamWrite;
 import org.eclipse.jetty.servlets.gzip.TestServletStreamLengthTypeWrite;
+import org.eclipse.jetty.servlets.gzip.TestServletStreamLengthTypeWriteWithFlush;
 import org.eclipse.jetty.servlets.gzip.TestServletStreamTypeLengthWrite;
 import org.eclipse.jetty.servlets.gzip.TestServletTypeLengthStreamWrite;
 import org.eclipse.jetty.servlets.gzip.TestServletTypeStreamLengthWrite;
@@ -73,12 +74,14 @@
         { TestServletLengthStreamTypeWrite.class, GzipFilter.GZIP },
         { TestServletLengthTypeStreamWrite.class, GzipFilter.GZIP },
         { TestServletStreamLengthTypeWrite.class, GzipFilter.GZIP },
+        { TestServletStreamLengthTypeWriteWithFlush.class, GzipFilter.GZIP },
         { TestServletStreamTypeLengthWrite.class, GzipFilter.GZIP },
         { TestServletTypeLengthStreamWrite.class, GzipFilter.GZIP },
         { TestServletTypeStreamLengthWrite.class, GzipFilter.GZIP },
         { TestServletLengthStreamTypeWrite.class, GzipFilter.DEFLATE },
         { TestServletLengthTypeStreamWrite.class, GzipFilter.DEFLATE },
         { TestServletStreamLengthTypeWrite.class, GzipFilter.DEFLATE },
+        { TestServletStreamLengthTypeWriteWithFlush.class, GzipFilter.DEFLATE },
         { TestServletStreamTypeLengthWrite.class, GzipFilter.DEFLATE },
         { TestServletTypeLengthStreamWrite.class, GzipFilter.DEFLATE },
         { TestServletTypeStreamLengthWrite.class, GzipFilter.DEFLATE }
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/MultipartFilterTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/MultipartFilterTest.java
index de08023..9098ea3 100644
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/MultipartFilterTest.java
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/MultipartFilterTest.java
@@ -26,11 +26,13 @@
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 
+import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.EnumSet;
+import java.util.Map;
 
 import javax.servlet.DispatcherType;
 import javax.servlet.ServletException;
@@ -38,10 +40,12 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+
 import org.eclipse.jetty.http.HttpTester;
 import org.eclipse.jetty.servlet.FilterHolder;
 import org.eclipse.jetty.servlet.ServletTester;
 import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.StringUtil;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -105,6 +109,7 @@
     public void tearDown() throws Exception
     {
         tester.stop();
+        tester=null;
     }
 
     @Test
@@ -698,8 +703,6 @@
         assertTrue(response.getContent().contains("aaaa,bbbbb"));
     }
     
-
-    @Test
     public void testContentTypeWithCharSet() throws Exception
     {
         // generated and parsed test
@@ -730,6 +733,39 @@
     }
     
     
+    @Test
+    public void testBufferOverflowNoCRLF () throws Exception
+    {
+        String boundary="XyXyXy";
+        // generated and parsed test
+        HttpTester.Request request = HttpTester.newRequest();
+        HttpTester.Response response;
+        tester.addServlet(BoundaryServlet.class,"/testb");
+        tester.setAttribute("fileName", "abc");
+        tester.setAttribute("desc", "123");
+        tester.setAttribute("title", "ttt");
+        request.setMethod("POST");
+        request.setVersion("HTTP/1.0");
+        request.setHeader("Host","tester");
+        request.setURI("/context/testb");
+        request.setHeader("Content-Type","multipart/form-data; boundary="+boundary);
+
+        String content = "--XyXyXy";
+
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        baos.write(content.getBytes());
+
+        for (int i=0; i< 8500; i++) //create content that will overrun default buffer size of BufferedInputStream
+        {
+            baos.write('a');
+        }
+        request.setContent(baos.toString());
+
+        response = HttpTester.parseResponse(tester.getResponses(request.generate()));
+        assertTrue(response.getContent().contains("Buffer size exceeded"));
+        assertEquals(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, response.getStatus());
+    }
+
     /*
      * see the testParameterMap test
      *
@@ -786,6 +822,59 @@
         assertTrue(response.getContent().indexOf("brown cow")>=0);
     }
 
+    public static class TestServletCharSet extends HttpServlet
+    {
+
+        @Override
+        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
+        {
+            //test that the multipart content bytes were converted correctly from their charset to unicode
+            String content = (String)req.getParameter("ttt");
+            assertNotNull(content);
+            assertEquals("ttt\u01FCzzz",content);       
+            assertEquals("application/octet-stream; charset=UTF-8",req.getParameter("ttt"+MultiPartFilter.CONTENT_TYPE_SUFFIX));
+                  
+            
+            //test that the parameter map retrieves values as String[]
+            Map map = req.getParameterMap();
+            Object o = map.get("ttt");
+            assertTrue(o.getClass().isArray());
+            super.doPost(req, resp);
+        } 
+    }
+    
+    
+    @Test
+    public void testWithCharSet()
+    throws Exception
+    {
+        // generated and parsed test
+        HttpTester.Request request = HttpTester.newRequest();
+        HttpTester.Response response;
+        tester.addServlet(TestServletCharSet.class,"/test3");
+        
+        // test GET
+        request.setMethod("POST");
+        request.setVersion("HTTP/1.0");
+        request.setHeader("Host","tester");
+        request.setURI("/context/test3");
+        
+        String boundary="XyXyXy";
+        request.setHeader("Content-Type","multipart/form-data; boundary="+boundary);
+        
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        
+        baos.write(("--" + boundary + "\r\n"+
+                "Content-Disposition: form-data; name=\"ttt\"\r\n"+
+                "Content-Type: application/octet-stream; charset=UTF-8\r\n\r\n").getBytes());
+        baos.write("ttt\u01FCzzz".getBytes(StringUtil.__UTF8));
+        baos.write(("\r\n--" + boundary + "--\r\n\r\n").getBytes());
+  
+        
+        request.setContent(baos.toByteArray());   
+        response = HttpTester.parseResponse(tester.getResponses(request.generate()));
+    }
+
     public static class DumpServlet extends HttpServlet
     {
         private static final long serialVersionUID = 201012011130L;
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/GzipTester.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/GzipTester.java
index 3cf181a..1cf6f10 100644
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/GzipTester.java
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/GzipTester.java
@@ -18,13 +18,8 @@
 
 package org.eclipse.jetty.servlets.gzip;
 
-import static org.hamcrest.Matchers.containsString;
-import static org.hamcrest.Matchers.equalTo;
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.not;
-import static org.hamcrest.Matchers.notNullValue;
-import static org.hamcrest.Matchers.nullValue;
-import static org.junit.Assert.assertThat;
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
@@ -113,7 +108,20 @@
         
         // Assert the response headers
         // Assert.assertThat("Response.status",response.getStatus(),is(HttpServletResponse.SC_OK));
-        Assert.assertThat("Response.header[Content-Length]",response.get("Content-Length"),notNullValue());
+        
+        // Response headers should have either a Transfer-Encoding indicating chunked OR a Content-Length
+        String contentLength = response.get("Content-Length");
+        String transferEncoding = response.get("Transfer-Encoding");
+        
+        /* TODO need to check for the 3rd option of EOF content.  To do this properly you might need to look at both HTTP/1.1 and HTTP/1.0 requests
+        boolean chunked = (transferEncoding != null) && (transferEncoding.indexOf("chunk") >= 0);
+        if(!chunked) {
+            Assert.assertThat("Response.header[Content-Length]",contentLength,notNullValue());
+        } else {
+            Assert.assertThat("Response.header[Transfer-Encoding]",transferEncoding,notNullValue());
+        }
+        */
+        
         int qindex = compressionType.indexOf(";");
         if (qindex < 0)
             Assert.assertThat("Response.header[Content-Encoding]",response.get("Content-Encoding"),containsString(compressionType));
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/TestServletStreamLengthTypeWriteWithFlush.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/TestServletStreamLengthTypeWriteWithFlush.java
new file mode 100644
index 0000000..ff8841c
--- /dev/null
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/TestServletStreamLengthTypeWriteWithFlush.java
@@ -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.servlets.gzip;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.servlets.GzipFilter;
+
+/**
+ * A sample servlet to serve static content, using a order of construction that has caused problems for
+ * {@link GzipFilter} in the past.
+ * 
+ * Using a real-world pattern of:
+ * 
+ * <pre>
+ *  1) get stream
+ *  2) set content length
+ *  3) set content type
+ *  4) write and flush
+ * </pre>
+ * 
+ * @see <a href="Eclipse Bug 354014">http://bugs.eclipse.org/354014</a>
+ */
+@SuppressWarnings("serial")
+public class TestServletStreamLengthTypeWriteWithFlush extends TestDirContentServlet
+{
+    @Override
+    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+    {
+        String fileName = request.getServletPath();
+        byte[] dataBytes = loadContentFileBytes(fileName);
+
+        ServletOutputStream out = response.getOutputStream();
+
+        // set content-length of uncompressed content (GzipFilter should handle this)
+        response.setContentLength(dataBytes.length);
+        
+        if (fileName.endsWith("txt"))
+            response.setContentType("text/plain");
+        else if (fileName.endsWith("mp3"))
+            response.setContentType("audio/mpeg");
+        response.setHeader("ETag","W/etag-"+fileName);
+
+        for ( int i = 0 ; i < dataBytes.length ; i++)
+        {
+            out.write(dataBytes[i]);
+            // flush using response object (not the stream itself)
+            response.flushBuffer();
+        }
+    }
+}
\ No newline at end of file
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/StandardSession.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/StandardSession.java
index 067f7f7..e50f98e 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/StandardSession.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/StandardSession.java
@@ -218,11 +218,22 @@
             if (stream != null)
             {
                 stream.process(frame);
+                removeFrameBytesFromQueue(stream);
                 removeStream(stream);
             }
         }
     }
 
+    private void removeFrameBytesFromQueue(Stream stream)
+    {
+        synchronized (queue)
+        {
+            for (FrameBytes frameBytes : queue)
+                if (frameBytes.getStream() == stream)
+                    queue.remove(frameBytes);
+        }
+    }
+
     @Override
     public void settings(SettingsInfo settingsInfo) throws ExecutionException, InterruptedException, TimeoutException
     {
@@ -492,7 +503,8 @@
 
     private void onSyn(final SynStreamFrame frame)
     {
-        IStream stream = createStream(frame, null, false, new Promise.Adapter<Stream>(){
+        IStream stream = createStream(frame, null, false, new Promise.Adapter<Stream>()
+        {
             @Override
             public void failed(Throwable x)
             {
@@ -1054,7 +1066,7 @@
         }
     }
 
-    private void append(FrameBytes frameBytes)
+    void append(FrameBytes frameBytes)
     {
         Throwable failure;
         synchronized (queue)
@@ -1215,7 +1227,7 @@
         public abstract void fail(Throwable throwable);
     }
 
-    private abstract class AbstractFrameBytes implements FrameBytes, Runnable
+    abstract class AbstractFrameBytes implements FrameBytes, Runnable
     {
         private final IStream stream;
         private final Callback callback;
diff --git a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/generator/DataFrameGeneratorTest.java b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/generator/DataFrameGeneratorTest.java
new file mode 100644
index 0000000..42e3d5d
--- /dev/null
+++ b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/generator/DataFrameGeneratorTest.java
@@ -0,0 +1,110 @@
+//
+//  ========================================================================
+//  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.spdy.generator;
+
+import java.nio.ByteBuffer;
+import java.util.Random;
+
+import org.eclipse.jetty.io.ArrayByteBufferPool;
+import org.eclipse.jetty.spdy.api.ByteBufferDataInfo;
+import org.eclipse.jetty.spdy.api.DataInfo;
+import org.eclipse.jetty.spdy.frames.DataFrame;
+import org.eclipse.jetty.util.BufferUtil;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+@RunWith(JUnit4.class)
+public class DataFrameGeneratorTest
+{
+    private int increment = 1024;
+    private int streamId = 1;
+    private ArrayByteBufferPool bufferPool;
+    private DataFrameGenerator dataFrameGenerator;
+    private ByteBuffer headerBuffer = ByteBuffer.allocate(DataFrame.HEADER_LENGTH);
+
+    @Before
+    public void setUp()
+    {
+        bufferPool = new ArrayByteBufferPool(64, increment, 8192);
+        dataFrameGenerator = new DataFrameGenerator(bufferPool);
+        headerBuffer.putInt(0, streamId & 0x7F_FF_FF_FF);
+
+    }
+
+    @Test
+    public void testGenerateSmallFrame()
+    {
+        int bufferSize = 256;
+        generateFrame(bufferSize);
+    }
+
+    @Test
+    public void testGenerateFrameWithBufferThatEqualsBucketSize()
+    {
+        int bufferSize = increment;
+        generateFrame(bufferSize);
+    }
+
+    @Test
+    public void testGenerateFrameWithBufferThatEqualsBucketSizeMinusHeaderLength()
+    {
+        int bufferSize = increment - DataFrame.HEADER_LENGTH;
+        generateFrame(bufferSize);
+    }
+
+    private void generateFrame(int bufferSize)
+    {
+        ByteBuffer byteBuffer = createByteBuffer(bufferSize);
+        ByteBufferDataInfo dataInfo = new ByteBufferDataInfo(byteBuffer, true);
+        fillHeaderBuffer(bufferSize);
+        ByteBuffer dataFrameBuffer = dataFrameGenerator.generate(streamId, bufferSize, dataInfo);
+
+        assertThat("The content size in dataFrameBuffer matches the buffersize + header length",
+                dataFrameBuffer.limit(),
+                is(bufferSize + DataFrame.HEADER_LENGTH));
+
+        byte[] headerBytes = new byte[DataFrame.HEADER_LENGTH];
+        dataFrameBuffer.get(headerBytes, 0, DataFrame.HEADER_LENGTH);
+        
+        assertThat("Header bytes are prepended", headerBytes, is(headerBuffer.array()));
+    }
+
+    private ByteBuffer createByteBuffer(int bufferSize)
+    {
+        byte[] bytes = new byte[bufferSize];
+        new Random().nextBytes(bytes);
+        ByteBuffer byteBuffer = bufferPool.acquire(bufferSize, false);
+        BufferUtil.flipToFill(byteBuffer);
+        byteBuffer.put(bytes);
+        BufferUtil.flipToFlush(byteBuffer, 0);
+        return byteBuffer;
+    }
+
+    private void fillHeaderBuffer(int bufferSize)
+    {
+        headerBuffer.putInt(4, bufferSize & 0x00_FF_FF_FF);
+        headerBuffer.put(4, DataInfo.FLAG_CLOSE);
+    }
+
+}
diff --git a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/http/HTTPSPDYServerConnectionFactory.java b/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/http/HTTPSPDYServerConnectionFactory.java
index ca71cb3..45d867a 100644
--- a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/http/HTTPSPDYServerConnectionFactory.java
+++ b/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/http/HTTPSPDYServerConnectionFactory.java
@@ -105,7 +105,8 @@
             if (!(headers.get("accept-encoding") != null && headers.get("accept-encoding").value().contains
                     ("gzip")))
                 headers.add("accept-encoding", "gzip");
-            HttpTransportOverSPDY transport = new HttpTransportOverSPDY(connector, httpConfiguration, endPoint, pushStrategy, stream, headers);
+            HttpTransportOverSPDY transport = new HttpTransportOverSPDY(connector, httpConfiguration, endPoint,
+                    pushStrategy, stream, headers);
             HttpInputOverSPDY input = new HttpInputOverSPDY();
             HttpChannelOverSPDY channel = new HttpChannelOverSPDY(connector, httpConfiguration, endPoint, transport, input, stream);
             stream.setAttribute(CHANNEL_ATTRIBUTE, channel);
diff --git a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/http/HttpTransportOverSPDY.java b/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/http/HttpTransportOverSPDY.java
index 4705525..0c352e8 100644
--- a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/http/HttpTransportOverSPDY.java
+++ b/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/http/HttpTransportOverSPDY.java
@@ -28,6 +28,8 @@
 import org.eclipse.jetty.http.HttpField;
 import org.eclipse.jetty.http.HttpFields;
 import org.eclipse.jetty.http.HttpGenerator;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpMethod;
 import org.eclipse.jetty.http.HttpStatus;
 import org.eclipse.jetty.http.HttpVersion;
 import org.eclipse.jetty.io.EndPoint;
@@ -41,6 +43,7 @@
 import org.eclipse.jetty.spdy.api.PushInfo;
 import org.eclipse.jetty.spdy.api.ReplyInfo;
 import org.eclipse.jetty.spdy.api.SPDY;
+import org.eclipse.jetty.spdy.api.Session;
 import org.eclipse.jetty.spdy.api.Stream;
 import org.eclipse.jetty.spdy.api.StreamStatus;
 import org.eclipse.jetty.util.BlockingCallback;
@@ -61,6 +64,7 @@
     private final EndPoint endPoint;
     private final PushStrategy pushStrategy;
     private final Stream stream;
+    private final short version;
     private final Fields requestHeaders;
     private final BlockingCallback streamBlocker = new BlockingCallback();
     private final AtomicBoolean committed = new AtomicBoolean();
@@ -73,6 +77,8 @@
         this.pushStrategy = pushStrategy == null ? new PushStrategy.None() : pushStrategy;
         this.stream = stream;
         this.requestHeaders = requestHeaders;
+        Session session = stream.getSession();
+        this.version = session.getVersion();
     }
 
     protected Stream getStream()
@@ -94,7 +100,7 @@
     }
 
     @Override
-    public void send(HttpGenerator.ResponseInfo info, ByteBuffer content, boolean lastContent, Callback callback)
+    public void send(HttpGenerator.ResponseInfo info, ByteBuffer content, boolean lastContent, final Callback callback)
     {
         if (LOG.isDebugEnabled())
             LOG.debug("Sending {} {} {} {} last={}", this, stream, info, BufferUtil.toDetailString(content), lastContent);
@@ -115,7 +121,9 @@
         // info!=null content!=null lastContent==false          reply, commit with content
         // info!=null content!=null lastContent==true           reply, commit with content and complete
 
-        boolean hasContent = BufferUtil.hasContent(content);
+        boolean isHeadRequest = HttpMethod.HEAD.name().equalsIgnoreCase(requestHeaders.get(HTTPSPDYHeader.METHOD.name(version)).value());
+        boolean hasContent = BufferUtil.hasContent(content) && !isHeadRequest;
+        boolean close = !hasContent && lastContent;
 
         if (info != null)
         {
@@ -127,68 +135,77 @@
                 LOG.warn("Committed response twice.", exception);
                 return;
             }
-            short version = stream.getSession().getVersion();
-            Fields headers = new Fields();
-
-            HttpVersion httpVersion = HttpVersion.HTTP_1_1;
-            headers.put(HTTPSPDYHeader.VERSION.name(version), httpVersion.asString());
-
-            int status = info.getStatus();
-            StringBuilder httpStatus = new StringBuilder().append(status);
-            String reason = info.getReason();
-            if (reason == null)
-                reason = HttpStatus.getMessage(status);
-            if (reason != null)
-                httpStatus.append(" ").append(reason);
-            headers.put(HTTPSPDYHeader.STATUS.name(version), httpStatus.toString());
-            LOG.debug("HTTP < {} {}", httpVersion, httpStatus);
-
-            // TODO merge the two Field classes into one
-            HttpFields fields = info.getHttpFields();
-            if (fields != null)
+            sendReply(info, !hasContent ? callback : new Callback.Adapter()
             {
-                for (int i = 0; i < fields.size(); ++i)
+                @Override
+                public void failed(Throwable x)
                 {
-                    HttpField field = fields.getField(i);
-                    String name = field.getName();
-                    String value = field.getValue();
-                    headers.add(name, value);
-                    LOG.debug("HTTP < {}: {}", name, value);
+                    callback.failed(x);
                 }
-            }
-
-            boolean close = !hasContent && lastContent;
-            ReplyInfo reply = new ReplyInfo(headers, close);
-            reply(stream, reply);
+            }, close);
         }
 
         // Do we have some content to send as well
         if (hasContent)
         {
-            // Is the stream still open?
-            if (stream.isClosed() || stream.isReset())
-                // tell the callback about the EOF
-                callback.failed(new EofException("stream closed"));
-            else
-                // send the data and let it call the callback
-                stream.data(new ByteBufferDataInfo(endPoint.getIdleTimeout(), TimeUnit.MILLISECONDS, content, lastContent
-                ), callback);
+            // send the data and let it call the callback
+            LOG.debug("Send content: {} on stream: {} lastContent={}", BufferUtil.toDetailString(content), stream,
+                    lastContent);
+            stream.data(new ByteBufferDataInfo(endPoint.getIdleTimeout(), TimeUnit.MILLISECONDS, content, lastContent
+            ), callback);
         }
         // else do we need to close
-        else if (lastContent)
+        else if (lastContent && info == null)
         {
-            // Are we closed ?
-            if (stream.isClosed() || stream.isReset())
-                // already closed by reply, so just tell callback we are complete
-                callback.succeeded();
-            else
-                // send empty data to close and let the send call the callback
-                stream.data(new ByteBufferDataInfo(endPoint.getIdleTimeout(), TimeUnit.MILLISECONDS,
-                        BufferUtil.EMPTY_BUFFER, lastContent), callback);
+            // send empty data to close and let the send call the callback
+            LOG.debug("No content and lastContent=true. Sending empty ByteBuffer to close stream: {}", stream);
+            stream.data(new ByteBufferDataInfo(endPoint.getIdleTimeout(), TimeUnit.MILLISECONDS,
+                    BufferUtil.EMPTY_BUFFER, lastContent), callback);
         }
-        else
-            // No data and no close so tell callback we are completed
-            callback.succeeded();
+        else if (!lastContent && !hasContent && info == null)
+            throw new IllegalStateException("not lastContent, no content and no responseInfo!");
+
+    }
+
+    private void sendReply(HttpGenerator.ResponseInfo info, Callback callback, boolean close)
+    {
+        Fields headers = new Fields();
+
+        HttpVersion httpVersion = HttpVersion.HTTP_1_1;
+        headers.put(HTTPSPDYHeader.VERSION.name(version), httpVersion.asString());
+
+        int status = info.getStatus();
+        StringBuilder httpStatus = new StringBuilder().append(status);
+        String reason = info.getReason();
+        if (reason == null)
+            reason = HttpStatus.getMessage(status);
+        if (reason != null)
+            httpStatus.append(" ").append(reason);
+        headers.put(HTTPSPDYHeader.STATUS.name(version), httpStatus.toString());
+        LOG.debug("HTTP < {} {}", httpVersion, httpStatus);
+
+        // TODO merge the two Field classes into one
+        HttpFields fields = info.getHttpFields();
+        if (fields != null)
+        {
+            for (int i = 0; i < fields.size(); ++i)
+            {
+                HttpField field = fields.getField(i);
+                String name = field.getName();
+                String value = field.getValue();
+                headers.add(name, value);
+                LOG.debug("HTTP < {}: {}", name, value);
+            }
+        }
+
+        if (configuration.getSendServerVersion())
+            headers.add(HttpHeader.SERVER.asString(), HttpConfiguration.SERVER_VERSION);
+        if (configuration.getSendXPoweredBy())
+            headers.add(HttpHeader.X_POWERED_BY.asString(), HttpConfiguration.SERVER_VERSION);
+
+        ReplyInfo reply = new ReplyInfo(headers, close);
+        LOG.debug("Sending reply: {} on stream: {}", reply, stream);
+        reply(stream, reply, callback);
     }
 
     @Override
@@ -208,18 +225,17 @@
     @Override
     public void completed()
     {
-        LOG.debug("Completed");
+        LOG.debug("Completed {}", this);
     }
 
-    private void reply(Stream stream, ReplyInfo replyInfo)
+    private void reply(Stream stream, ReplyInfo replyInfo, Callback callback)
     {
         if (!stream.isUnidirectional())
-            stream.reply(replyInfo, new Callback.Adapter());
+            stream.reply(replyInfo, callback);
         else
-            stream.headers(new HeadersInfo(replyInfo.getHeaders(), replyInfo.isClose()), new Callback.Adapter());
+            stream.headers(new HeadersInfo(replyInfo.getHeaders(), replyInfo.isClose()), callback);
 
         Fields responseHeaders = replyInfo.getHeaders();
-        short version = stream.getSession().getVersion();
         if (responseHeaders.get(HTTPSPDYHeader.STATUS.name(version)).value().startsWith("200") && !stream.isClosed())
         {
             Set<String> pushResources = pushStrategy.apply(stream, requestHeaders, responseHeaders);
@@ -234,13 +250,15 @@
     private static class PushHttpTransportOverSPDY extends HttpTransportOverSPDY
     {
         private final PushResourceCoordinator coordinator;
+        private final short version;
 
         private PushHttpTransportOverSPDY(Connector connector, HttpConfiguration configuration, EndPoint endPoint,
                                           PushStrategy pushStrategy, Stream stream, Fields requestHeaders,
-                                          PushResourceCoordinator coordinator)
+                                          PushResourceCoordinator coordinator, short version)
         {
             super(connector, configuration, endPoint, pushStrategy, stream, requestHeaders);
             this.coordinator = coordinator;
+            this.version = version;
         }
 
         @Override
@@ -248,7 +266,7 @@
         {
             Stream stream = getStream();
             LOG.debug("Resource pushed for {} on {}",
-                    getRequestHeaders().get(HTTPSPDYHeader.URI.name(stream.getSession().getVersion())), stream);
+                    getRequestHeaders().get(HTTPSPDYHeader.URI.name(version)), stream);
             coordinator.complete();
         }
     }
@@ -257,7 +275,7 @@
     {
         private final Queue<PushResource> queue = new ConcurrentArrayQueue<>();
         private final Set<String> resources;
-        private boolean active;
+        private AtomicBoolean active = new AtomicBoolean(false);
 
         private PushResourceCoordinator(Set<String> resources)
         {
@@ -266,6 +284,7 @@
 
         private void coordinate()
         {
+            LOG.debug("Pushing resources: {}", resources);
             // Must send all push frames to the client at once before we
             // return from this method and send the main resource data
             for (String pushResource : resources)
@@ -274,31 +293,40 @@
 
         private void sendNextResourceData()
         {
-            PushResource resource;
-            synchronized (this)
+            LOG.debug("{} sendNextResourceData active: {}", hashCode(), active.get());
+            if (active.compareAndSet(false, true))
             {
-                if (active)
+                PushResource resource = queue.poll();
+                if (resource != null)
+                {
+                    LOG.debug("Opening new push channel for: {}", resource);
+                    HttpChannelOverSPDY pushChannel = newHttpChannelOverSPDY(resource.getPushStream(), resource.getPushRequestHeaders());
+                    pushChannel.requestStart(resource.getPushRequestHeaders(), true);
                     return;
-                resource = queue.poll();
-                if (resource == null)
-                    return;
-                active = true;
+                }
+
+                if (active.compareAndSet(true, false))
+                {
+                    if (queue.peek() != null)
+                        sendNextResourceData();
+                }
+                else
+                {
+                    throw new IllegalStateException("active must not be false here! Concurrency bug!");
+                }
             }
-            HttpChannelOverSPDY pushChannel = newHttpChannelOverSPDY(resource.getPushStream(), resource.getPushRequestHeaders());
-            pushChannel.requestStart(resource.getPushRequestHeaders(), true);
         }
 
         private HttpChannelOverSPDY newHttpChannelOverSPDY(Stream pushStream, Fields pushRequestHeaders)
         {
             HttpTransport transport = new PushHttpTransportOverSPDY(connector, configuration, endPoint, pushStrategy,
-                    pushStream, pushRequestHeaders, this);
+                    pushStream, pushRequestHeaders, this, version);
             HttpInputOverSPDY input = new HttpInputOverSPDY();
             return new HttpChannelOverSPDY(connector, configuration, endPoint, transport, input, pushStream);
         }
 
         private void pushResource(String pushResource)
         {
-            final short version = stream.getSession().getVersion();
             Fields.Field scheme = requestHeaders.get(HTTPSPDYHeader.SCHEME.name(version));
             Fields.Field host = requestHeaders.get(HTTPSPDYHeader.HOST.name(version));
             Fields.Field uri = requestHeaders.get(HTTPSPDYHeader.URI.name(version));
@@ -319,14 +347,21 @@
                 public void failed(Throwable x)
                 {
                     LOG.debug("Creating push stream failed.", x);
+                    sendNextResourceData();
                 }
             });
         }
 
+        private void complete()
+        {
+            if (!active.compareAndSet(true, false))
+                throw new IllegalStateException();
+            sendNextResourceData();
+        }
+
         private Fields createRequestHeaders(Fields.Field scheme, Fields.Field host, Fields.Field uri, String pushResourcePath)
         {
             final Fields newRequestHeaders = new Fields(requestHeaders, false);
-            short version = stream.getSession().getVersion();
             newRequestHeaders.put(HTTPSPDYHeader.METHOD.name(version), "GET");
             newRequestHeaders.put(HTTPSPDYHeader.VERSION.name(version), "HTTP/1.1");
             newRequestHeaders.put(scheme);
@@ -341,7 +376,6 @@
         private Fields createPushHeaders(Fields.Field scheme, Fields.Field host, String pushResourcePath)
         {
             final Fields pushHeaders = new Fields();
-            short version = stream.getSession().getVersion();
             if (version == SPDY.V2)
                 pushHeaders.put(HTTPSPDYHeader.URI.name(version), scheme.value() + "://" + host.value() + pushResourcePath);
             else
@@ -352,15 +386,6 @@
             }
             return pushHeaders;
         }
-
-        private void complete()
-        {
-            synchronized (this)
-            {
-                active = false;
-            }
-            sendNextResourceData();
-        }
     }
 
     private static class PushResource
@@ -383,5 +408,14 @@
         {
             return pushRequestHeaders;
         }
+
+        @Override
+        public String toString()
+        {
+            return "PushResource{" +
+                    "pushStream=" + pushStream +
+                    ", pushRequestHeaders=" + pushRequestHeaders +
+                    '}';
+        }
     }
 }
diff --git a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/proxy/HTTPProxyEngine.java b/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/proxy/HTTPProxyEngine.java
index b425a58..b46d11c 100644
--- a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/proxy/HTTPProxyEngine.java
+++ b/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/proxy/HTTPProxyEngine.java
@@ -141,6 +141,7 @@
             {
                 LOG.debug("onHeaders called with response: {}. Sending replyInfo to client.", response);
                 Fields responseHeaders = createResponseHeaders(clientStream, response);
+                removeHopHeaders(responseHeaders);
                 ReplyInfo replyInfo = new ReplyInfo(responseHeaders, false);
                 clientStream.reply(replyInfo, new Callback.Adapter()
                 {
diff --git a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/proxy/ProxyEngine.java b/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/proxy/ProxyEngine.java
index c5a5cff..0b980b5 100644
--- a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/proxy/ProxyEngine.java
+++ b/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/proxy/ProxyEngine.java
@@ -87,6 +87,8 @@
 
     protected void removeHopHeaders(Fields headers)
     {
+        // Header names are case-insensitive (RFC2616) and oej.util.Fields.add converts the names to lowercase. So we
+        // need to compare with the lowercase values only
         for (String hopHeader : HOP_HEADERS)
             headers.remove(hopHeader);
     }
diff --git a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/AbstractHTTPSPDYTest.java b/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/AbstractHTTPSPDYTest.java
index fec3936..de96a4c 100644
--- a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/AbstractHTTPSPDYTest.java
+++ b/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/AbstractHTTPSPDYTest.java
@@ -99,7 +99,10 @@
     protected HTTPSPDYServerConnector newHTTPSPDYServerConnector(short version)
     {
         // For these tests, we need the connector to speak HTTP over SPDY even in non-SSL
-        HTTPSPDYServerConnector connector = new HTTPSPDYServerConnector(server,version,new HttpConfiguration(), new PushStrategy.None());
+        HttpConfiguration httpConfiguration = new HttpConfiguration();
+        httpConfiguration.setSendServerVersion(true);
+        httpConfiguration.setSendXPoweredBy(true);
+        HTTPSPDYServerConnector connector = new HTTPSPDYServerConnector(server,version, httpConfiguration, new PushStrategy.None());
         return connector;
     }
 
diff --git a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/HttpTransportOverSPDYTest.java b/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/HttpTransportOverSPDYTest.java
index e8d76c7..9689a2d 100644
--- a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/HttpTransportOverSPDYTest.java
+++ b/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/HttpTransportOverSPDYTest.java
@@ -18,13 +18,6 @@
 
 package org.eclipse.jetty.spdy.server.http;
 
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.util.Random;
@@ -52,6 +45,13 @@
 import org.mockito.Mock;
 import org.mockito.runners.MockitoJUnitRunner;
 
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
 @RunWith(MockitoJUnitRunner.class)
 public class HttpTransportOverSPDYTest
 {
@@ -72,19 +72,22 @@
     @Mock
     HttpGenerator.ResponseInfo responseInfo;
 
-    private Random random = new Random();
+    Random random = new Random();
+    short version = SPDY.V3;
 
     HttpTransportOverSPDY httpTransportOverSPDY;
 
     @Before
     public void setUp() throws Exception
     {
-        httpTransportOverSPDY = new HttpTransportOverSPDY(connector, httpConfiguration, endPoint, pushStrategy,
-                stream, new Fields());
+        Fields requestHeaders = new Fields();
+        requestHeaders.add(HTTPSPDYHeader.METHOD.name(version), "GET");
         when(responseInfo.getStatus()).thenReturn(HttpStatus.OK_200);
         when(stream.getSession()).thenReturn(session);
         when(session.getVersion()).thenReturn(SPDY.V3);
         when(stream.isClosed()).thenReturn(false);
+        httpTransportOverSPDY = new HttpTransportOverSPDY(connector, httpConfiguration, endPoint, pushStrategy,
+                stream, requestHeaders);
     }
 
     @Test
@@ -95,7 +98,10 @@
 
         httpTransportOverSPDY.send(null, content, lastContent, callback);
         ArgumentCaptor<ByteBufferDataInfo> dataInfoCaptor = ArgumentCaptor.forClass(ByteBufferDataInfo.class);
-        verify(stream, times(1)).data(dataInfoCaptor.capture(), any(Callback.class));
+        ArgumentCaptor<Callback> callbackCaptor = ArgumentCaptor.forClass(Callback.class);
+        verify(stream, times(1)).data(dataInfoCaptor.capture(), callbackCaptor.capture());
+        callbackCaptor.getValue().succeeded();
+        verify(callback, times(1)).succeeded();
         assertThat("lastContent is true", dataInfoCaptor.getValue().isClose(), is(true));
         assertThat("ByteBuffer is empty", dataInfoCaptor.getValue().length(), is(0));
     }
@@ -109,7 +115,10 @@
 
         httpTransportOverSPDY.send(null, content, lastContent, callback);
         ArgumentCaptor<ByteBufferDataInfo> dataInfoCaptor = ArgumentCaptor.forClass(ByteBufferDataInfo.class);
-        verify(stream, times(1)).data(dataInfoCaptor.capture(), any(Callback.class));
+        ArgumentCaptor<Callback> callbackCaptor = ArgumentCaptor.forClass(Callback.class);
+        verify(stream, times(1)).data(dataInfoCaptor.capture(), callbackCaptor.capture());
+        callbackCaptor.getValue().succeeded();
+        verify(callback, times(1)).succeeded();
         assertThat("lastContent is true", dataInfoCaptor.getValue().isClose(), is(true));
         assertThat("ByteBuffer length is 4096", dataInfoCaptor.getValue().length(), is(4096));
     }
@@ -119,48 +128,62 @@
     {
         ByteBuffer content = BufferUtil.EMPTY_BUFFER;
         boolean lastContent = true;
-        
 
         httpTransportOverSPDY.send(null, content, lastContent, callback);
         ArgumentCaptor<ByteBufferDataInfo> dataInfoCaptor = ArgumentCaptor.forClass(ByteBufferDataInfo.class);
-        verify(stream, times(1)).data(dataInfoCaptor.capture(), any(Callback.class));
+        ArgumentCaptor<Callback> callbackCaptor = ArgumentCaptor.forClass(Callback.class);
+        verify(stream, times(1)).data(dataInfoCaptor.capture(), callbackCaptor.capture());
+        callbackCaptor.getValue().succeeded();
+        verify(callback, times(1)).succeeded();
         assertThat("lastContent is true", dataInfoCaptor.getValue().isClose(), is(true));
         assertThat("ByteBuffer is empty", dataInfoCaptor.getValue().length(), is(0));
     }
 
     @Test
-    public void testSendWithResponseInfoNullAndContentNullAndLastContentFalse() throws Exception
-    {
-        ByteBuffer content = null;
-        boolean lastContent = false;
-        
-
-        httpTransportOverSPDY.send(null, content, lastContent, callback);
-        verify(stream, times(0)).data(any(ByteBufferDataInfo.class), any(Callback.class));
-    }
-
-    @Test
     public void testSendWithResponseInfoNullAndContentAndLastContentFalse() throws Exception
     {
         ByteBuffer content = createRandomByteBuffer();
         boolean lastContent = false;
-        
 
         httpTransportOverSPDY.send(null, content, lastContent, callback);
         ArgumentCaptor<ByteBufferDataInfo> dataInfoCaptor = ArgumentCaptor.forClass(ByteBufferDataInfo.class);
-        verify(stream, times(1)).data(dataInfoCaptor.capture(), any(Callback.class));
+        ArgumentCaptor<Callback> callbackCaptor = ArgumentCaptor.forClass(Callback.class);
+        verify(stream, times(1)).data(dataInfoCaptor.capture(), callbackCaptor.capture());
+        callbackCaptor.getValue().succeeded();
+        verify(callback, times(1)).succeeded();
         assertThat("lastContent is false", dataInfoCaptor.getValue().isClose(), is(false));
         assertThat("ByteBuffer is empty", dataInfoCaptor.getValue().length(), is(4096));
     }
 
-    @Test
+    @Test(expected = IllegalStateException.class)
+    public void testSendWithResponseInfoNullAndContentNullAndLastContentFalse() throws Exception
+    {
+        ByteBuffer content = null;
+        boolean lastContent = false;
+        httpTransportOverSPDY.send(null, content, lastContent, callback);
+    }
+
+    @Test(expected = IllegalStateException.class)
     public void testSendWithResponseInfoNullAndEmptyContentAndLastContentFalse() throws Exception
     {
         ByteBuffer content = BufferUtil.EMPTY_BUFFER;
         boolean lastContent = false;
-        
-
         httpTransportOverSPDY.send(null, content, lastContent, callback);
+    }
+
+    @Test
+    public void testSendWithResponseInfoAndContentNullAndLastContentFalse() throws Exception
+    {
+        ByteBuffer content = null;
+        boolean lastContent = false;
+
+        httpTransportOverSPDY.send(responseInfo, content, lastContent, callback);
+        ArgumentCaptor<ReplyInfo> replyInfoCaptor = ArgumentCaptor.forClass(ReplyInfo.class);
+        ArgumentCaptor<Callback> callbackCaptor = ArgumentCaptor.forClass(Callback.class);
+        verify(stream, times(1)).reply(replyInfoCaptor.capture(), callbackCaptor.capture());
+        callbackCaptor.getValue().succeeded();
+        assertThat("ReplyInfo close is true", replyInfoCaptor.getValue().isClose(), is(false));
+
         verify(stream, times(0)).data(any(ByteBufferDataInfo.class), any(Callback.class));
         verify(callback, times(1)).succeeded();
     }
@@ -170,13 +193,15 @@
     {
         ByteBuffer content = null;
         boolean lastContent = true;
-        
+
         // when stream.isClosed() is called a 2nd time, the reply has closed the stream already
         when(stream.isClosed()).thenReturn(false).thenReturn(true);
 
         httpTransportOverSPDY.send(responseInfo, content, lastContent, callback);
         ArgumentCaptor<ReplyInfo> replyInfoCaptor = ArgumentCaptor.forClass(ReplyInfo.class);
-        verify(stream, times(1)).reply(replyInfoCaptor.capture(), any(Callback.class));
+        ArgumentCaptor<Callback> callbackCaptor = ArgumentCaptor.forClass(Callback.class);
+        verify(stream, times(1)).reply(replyInfoCaptor.capture(), callbackCaptor.capture());
+        callbackCaptor.getValue().succeeded();
         assertThat("ReplyInfo close is true", replyInfoCaptor.getValue().isClose(), is(true));
 
         verify(callback, times(1)).succeeded();
@@ -186,54 +211,42 @@
     public void testSendWithResponseInfoAndContentAndLastContentTrue() throws Exception
     {
         ByteBuffer content = createRandomByteBuffer();
-
         boolean lastContent = true;
-        
 
         httpTransportOverSPDY.send(responseInfo, content, lastContent, callback);
-
         ArgumentCaptor<ReplyInfo> replyInfoCaptor = ArgumentCaptor.forClass(ReplyInfo.class);
-        verify(stream, times(1)).reply(replyInfoCaptor.capture(), any(Callback.class));
+        ArgumentCaptor<Callback> callbackCaptor = ArgumentCaptor.forClass(Callback.class);
+        verify(stream, times(1)).reply(replyInfoCaptor.capture(), callbackCaptor.capture());
+        callbackCaptor.getValue().succeeded();
         assertThat("ReplyInfo close is false", replyInfoCaptor.getValue().isClose(), is(false));
-
         ArgumentCaptor<ByteBufferDataInfo> dataInfoCaptor = ArgumentCaptor.forClass(ByteBufferDataInfo.class);
-        verify(stream, times(1)).data(dataInfoCaptor.capture(), any(Callback.class));
+        verify(stream, times(1)).data(dataInfoCaptor.capture(), callbackCaptor.capture());
+        callbackCaptor.getValue().succeeded();
         assertThat("lastContent is true", dataInfoCaptor.getValue().isClose(), is(true));
         assertThat("ByteBuffer length is 4096", dataInfoCaptor.getValue().length(), is(4096));
-    }
-
-    @Test
-    public void testSendWithResponseInfoAndContentNullAndLastContentFalse() throws Exception
-    {
-        ByteBuffer content = null;
-        boolean lastContent = false;
-        
-
-        httpTransportOverSPDY.send(responseInfo, content, lastContent, callback);
-        ArgumentCaptor<ReplyInfo> replyInfoCaptor = ArgumentCaptor.forClass(ReplyInfo.class);
-        verify(stream, times(1)).reply(replyInfoCaptor.capture(), any(Callback.class));
-        assertThat("ReplyInfo close is true", replyInfoCaptor.getValue().isClose(), is(false));
-
-        verify(stream, times(0)).data(any(ByteBufferDataInfo.class), any(Callback.class));
+        verify(callback, times(1)).succeeded();
     }
 
     @Test
     public void testSendWithResponseInfoAndContentAndLastContentFalse() throws Exception
     {
         ByteBuffer content = createRandomByteBuffer();
-
         boolean lastContent = false;
-        
 
         httpTransportOverSPDY.send(responseInfo, content, lastContent, callback);
         ArgumentCaptor<ReplyInfo> replyInfoCaptor = ArgumentCaptor.forClass(ReplyInfo.class);
-        verify(stream, times(1)).reply(replyInfoCaptor.capture(), any(Callback.class));
+        ArgumentCaptor<Callback> callbackCaptor = ArgumentCaptor.forClass(Callback.class);
+        verify(stream, times(1)).reply(replyInfoCaptor.capture(), callbackCaptor.capture());
+        callbackCaptor.getValue().succeeded();
         assertThat("ReplyInfo close is false", replyInfoCaptor.getValue().isClose(), is(false));
 
         ArgumentCaptor<ByteBufferDataInfo> dataInfoCaptor = ArgumentCaptor.forClass(ByteBufferDataInfo.class);
-        verify(stream, times(1)).data(dataInfoCaptor.capture(), any(Callback.class));
+        verify(stream, times(1)).data(dataInfoCaptor.capture(), callbackCaptor.capture());
+        callbackCaptor.getValue().succeeded();
         assertThat("lastContent is false", dataInfoCaptor.getValue().isClose(), is(false));
         assertThat("ByteBuffer length is 4096", dataInfoCaptor.getValue().length(), is(4096));
+
+        verify(callback, times(1)).succeeded();
     }
 
     @Test
diff --git a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/ReferrerPushStrategyTest.java b/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/ReferrerPushStrategyTest.java
index 5b501fe..d120166 100644
--- a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/ReferrerPushStrategyTest.java
+++ b/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/ReferrerPushStrategyTest.java
@@ -60,7 +60,6 @@
 import org.eclipse.jetty.util.log.StdErrLog;
 import org.junit.Assert;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 
 import static org.hamcrest.CoreMatchers.is;
@@ -338,7 +337,6 @@
     }
 
     @Test
-    @Ignore
     public void testPushResourceAreSentNonInterleaved() throws Exception
     {
         final CountDownLatch allExpectedPushesReceivedLatch = new CountDownLatch(4);
diff --git a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/ServerHTTPSPDYTest.java b/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/ServerHTTPSPDYTest.java
index 1c09046..9851995 100644
--- a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/ServerHTTPSPDYTest.java
+++ b/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/ServerHTTPSPDYTest.java
@@ -29,7 +29,6 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
-
 import javax.servlet.ServletException;
 import javax.servlet.ServletOutputStream;
 import javax.servlet.http.Cookie;
@@ -38,6 +37,7 @@
 
 import org.eclipse.jetty.continuation.Continuation;
 import org.eclipse.jetty.continuation.ContinuationSupport;
+import org.eclipse.jetty.http.HttpHeader;
 import org.eclipse.jetty.server.HttpChannel;
 import org.eclipse.jetty.server.Request;
 import org.eclipse.jetty.server.handler.AbstractHandler;
@@ -57,10 +57,12 @@
 
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
 {
@@ -100,7 +102,9 @@
             {
                 assertTrue(replyInfo.isClose());
                 Fields replyHeaders = replyInfo.getHeaders();
-                assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version)).value().contains("200"));
+                assertThat(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version)).value().contains("200"), is(true));
+                assertThat(replyHeaders.get(HttpHeader.SERVER.asString()), is(notNullValue()));
+                assertThat(replyHeaders.get(HttpHeader.X_POWERED_BY.asString()), is(notNullValue()));
                 replyLatch.countDown();
             }
         });
@@ -185,9 +189,9 @@
                 assertThat("response code is 200 OK", replyHeaders.get(HTTPSPDYHeader.STATUS.name(version)).value()
                         .contains("200"), is(true));
                 assertThat(replyInfo.getHeaders().get("Set-Cookie").values()[0], is(cookie1 + "=\"" + cookie1Value +
-                        "\""));
+                        "\";Version=1"));
                 assertThat(replyInfo.getHeaders().get("Set-Cookie").values()[1], is(cookie2 + "=\"" + cookie2Value +
-                        "\""));
+                        "\";Version=1"));
                 replyLatch.countDown();
             }
         });
@@ -210,6 +214,7 @@
                 assertEquals("HEAD", httpRequest.getMethod());
                 assertEquals(path, target);
                 assertEquals(path, httpRequest.getRequestURI());
+                httpResponse.getWriter().write("body that shouldn't be sent on a HEAD request");
                 handlerLatch.countDown();
             }
         }), null);
@@ -226,12 +231,56 @@
                 assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version)).value().contains("200"));
                 replyLatch.countDown();
             }
+
+            @Override
+            public void onData(Stream stream, DataInfo dataInfo)
+            {
+                fail("HEAD request shouldn't send any data");
+            }
         });
         assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
         assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
     }
 
     @Test
+    public void testPOSTWithDelayedContentBody() throws Exception
+    {
+        final String path = "/foo";
+        final CountDownLatch handlerLatch = new CountDownLatch(1);
+        Session session = startClient(version, startHTTPServer(version, new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
+                    throws IOException, ServletException
+            {
+                // don't read the request body, reply immediately
+                request.setHandled(true);
+                handlerLatch.countDown();
+            }
+        }), null);
+
+        Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "POST", path);
+        headers.put("content-type", "application/x-www-form-urlencoded");
+        final CountDownLatch replyLatch = new CountDownLatch(1);
+        Stream stream = session.syn(new SynInfo(5, TimeUnit.SECONDS, headers, false, (byte)0),
+                new StreamFrameListener.Adapter()
+                {
+                    @Override
+                    public void onReply(Stream stream, ReplyInfo replyInfo)
+                    {
+                        assertTrue(replyInfo.isClose());
+                        Fields replyHeaders = replyInfo.getHeaders();
+                        assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version)).value().contains("200"));
+                        replyLatch.countDown();
+                    }
+                });
+        stream.data(new StringDataInfo("a", false));
+        assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
+        assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
+        stream.data(new StringDataInfo("b", true));
+    }
+
+    @Test
     public void testPOSTWithParameters() throws Exception
     {
         final String path = "/foo";
diff --git a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/proxy/ProxySPDYToHTTPTest.java b/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/proxy/ProxySPDYToHTTPTest.java
index 658c386..556c148 100644
--- a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/proxy/ProxySPDYToHTTPTest.java
+++ b/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/proxy/ProxySPDYToHTTPTest.java
@@ -219,6 +219,8 @@
             public void onReply(Stream stream, ReplyInfo replyInfo)
             {
                 Fields headers = replyInfo.getHeaders();
+                assertThat("Trailer header has been filtered by proxy", headers.get("trailer"),
+                        is(nullValue()));
                 assertThat("custom header exists in response", headers.get(header), is(notNullValue()));
                 replyLatch.countDown();
             }
@@ -545,6 +547,8 @@
             while ((read = bufferedReader.read()) != -1)
                 response.getOutputStream().write(read);
 
+            // add some hop header to be removed on the proxy
+            response.addHeader("Trailer", "bla");
             if (responseHeader != null)
                 response.addHeader(responseHeader, "bar");
             if (responseData != null)
diff --git a/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/MaxConcurrentStreamTest.java b/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/MaxConcurrentStreamTest.java
index 0b41c90..a3fe141 100644
--- a/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/MaxConcurrentStreamTest.java
+++ b/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/MaxConcurrentStreamTest.java
@@ -36,13 +36,14 @@
 import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
 import org.eclipse.jetty.util.BufferUtil;
 import org.eclipse.jetty.util.Fields;
-import org.junit.Ignore;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 
 import static org.hamcrest.Matchers.is;
 import static org.junit.Assert.assertThat;
 
-@Ignore("Fails the build too often. Runs fine always when run alone.")
+@RunWith(JUnit4.class)
 public class MaxConcurrentStreamTest extends AbstractTest
 {
     @Test
@@ -116,7 +117,5 @@
 
         stream.data(new ByteBufferDataInfo(BufferUtil.EMPTY_BUFFER, true));
         assertThat("Data has been received on first stream.", dataReceivedLatch.await(5, TimeUnit.SECONDS), is(true));
-
-        session.syn(synInfo, null);
     }
 }
diff --git a/jetty-util-ajax/src/main/java/org/eclipse/jetty/util/ajax/JSON.java b/jetty-util-ajax/src/main/java/org/eclipse/jetty/util/ajax/JSON.java
index d405677..894a17f 100644
--- a/jetty-util-ajax/src/main/java/org/eclipse/jetty/util/ajax/JSON.java
+++ b/jetty-util-ajax/src/main/java/org/eclipse/jetty/util/ajax/JSON.java
@@ -39,8 +39,7 @@
 
 /**
  * JSON Parser and Generator.
- *
- * <p>
+ * <p />
  * This class provides some static methods to convert POJOs to and from JSON
  * notation. The mapping from JSON to java is:
  *
@@ -52,9 +51,7 @@
  *   null   ==> null
  *   bool   ==> Boolean
  * </pre>
- *
- * </p>
- * <p>
+
  * The java to JSON mapping is:
  *
  * <pre>
@@ -68,30 +65,27 @@
  *   Object --> string (dubious!)
  * </pre>
  *
- * </p>
- * <p>
  * The interface {@link JSON.Convertible} may be implemented by classes that
  * wish to externalize and initialize specific fields to and from JSON objects.
  * Only directed acyclic graphs of objects are supported.
- * </p>
- * <p>
+ * <p />
  * The interface {@link JSON.Generator} may be implemented by classes that know
  * how to render themselves as JSON and the {@link #toString(Object)} method
  * will use {@link JSON.Generator#addJSON(Appendable)} to generate the JSON.
  * The class {@link JSON.Literal} may be used to hold pre-generated JSON object.
- * <p>
+ * <p />
  * The interface {@link JSON.Convertor} may be implemented to provide static
- * convertors for objects that may be registered with
- * {@link #registerConvertor(Class, org.eclipse.jetty.util.ajax.JSON.Convertor)}
- * . These convertors are looked up by class, interface and super class by
+ * converters for objects that may be registered with
+ * {@link #registerConvertor(Class, Convertor)}.
+ * These converters are looked up by class, interface and super class by
  * {@link #getConvertor(Class)}.
- * </p>
- * <p>If a JSON object has a "class" field, then a java class for that name is
- * looked up and the method {@link convertTo(Class,Map)} is used to find a
- * Convertor for that class.   If a JSON object has a "x-class" field then a 
- * direct lookup for a Convertor for that named x-class is done, so that none
- * java classes may be converted.
- * </p>
+ * <p />
+ * If a JSON object has a "class" field, then a java class for that name is
+ * loaded and the method {@link #convertTo(Class,Map)} is used to find a
+ * {@link JSON.Convertor} for that class.
+ * <p />
+ * If a JSON object has a "x-class" field then a direct lookup for a
+ * {@link JSON.Convertor} for that class name is done (without loading the class).
  */
 public class JSON
 {
@@ -105,7 +99,6 @@
     {
     }
 
-    /* ------------------------------------------------------------ */
     /**
      * @return the initial stringBuffer size to use when creating JSON strings
      *         (default 1024)
@@ -115,7 +108,6 @@
         return _stringBufferSize;
     }
 
-    /* ------------------------------------------------------------ */
     /**
      * @param stringBufferSize
      *            the initial stringBuffer size to use when creating JSON
@@ -126,7 +118,6 @@
         _stringBufferSize = stringBufferSize;
     }
 
-    /* ------------------------------------------------------------ */
     /**
      * Register a {@link Convertor} for a class or interface.
      *
@@ -140,19 +131,16 @@
         DEFAULT.addConvertor(forClass,convertor);
     }
 
-    /* ------------------------------------------------------------ */
     public static JSON getDefault()
     {
         return DEFAULT;
     }
 
-    /* ------------------------------------------------------------ */
     @Deprecated
     public static void setDefault(JSON json)
     {
     }
 
-    /* ------------------------------------------------------------ */
     public static String toString(Object object)
     {
         StringBuilder buffer = new StringBuilder(DEFAULT.getStringBufferSize());
@@ -160,7 +148,6 @@
         return buffer.toString();
     }
 
-    /* ------------------------------------------------------------ */
     public static String toString(Map object)
     {
         StringBuilder buffer = new StringBuilder(DEFAULT.getStringBufferSize());
@@ -168,7 +155,6 @@
         return buffer.toString();
     }
 
-    /* ------------------------------------------------------------ */
     public static String toString(Object[] array)
     {
         StringBuilder buffer = new StringBuilder(DEFAULT.getStringBufferSize());
@@ -176,7 +162,6 @@
         return buffer.toString();
     }
 
-    /* ------------------------------------------------------------ */
     /**
      * @param s
      *            String containing JSON object or array.
@@ -187,7 +172,6 @@
         return DEFAULT.parse(new StringSource(s),false);
     }
 
-    /* ------------------------------------------------------------ */
     /**
      * @param s
      *            String containing JSON object or array.
@@ -200,7 +184,6 @@
         return DEFAULT.parse(new StringSource(s),stripOuterComment);
     }
 
-    /* ------------------------------------------------------------ */
     /**
      * @param in
      *            Reader containing JSON object or array.
@@ -211,7 +194,6 @@
         return DEFAULT.parse(new ReaderSource(in),false);
     }
 
-    /* ------------------------------------------------------------ */
     /**
      * @param in
      *            Reader containing JSON object or array.
@@ -224,7 +206,6 @@
         return DEFAULT.parse(new ReaderSource(in),stripOuterComment);
     }
 
-    /* ------------------------------------------------------------ */
     /**
      * @deprecated use {@link #parse(Reader)}
      * @param in
@@ -237,7 +218,6 @@
         return DEFAULT.parse(new StringSource(IO.toString(in)),false);
     }
 
-    /* ------------------------------------------------------------ */
     /**
      * @deprecated use {@link #parse(Reader, boolean)}
      * @param in
@@ -252,7 +232,6 @@
         return DEFAULT.parse(new StringSource(IO.toString(in)),stripOuterComment);
     }
 
-    /* ------------------------------------------------------------ */
     /**
      * Convert Object to JSON
      *
@@ -267,7 +246,6 @@
         return buffer.toString();
     }
 
-    /* ------------------------------------------------------------ */
     /**
      * Convert JSON to Object
      *
@@ -287,7 +265,6 @@
         append((Appendable)buffer,object);
     }
 
-    /* ------------------------------------------------------------ */
     /**
      * Append object as JSON to string buffer.
      *
@@ -301,32 +278,58 @@
         try
         {
             if (object == null)
+            {
                 buffer.append("null");
-            else if (object instanceof Convertible)
-                appendJSON(buffer,(Convertible)object);
-            else if (object instanceof Generator)
-                appendJSON(buffer,(Generator)object);
+            }
+            // Most likely first
             else if (object instanceof Map)
+            {
                 appendMap(buffer,(Map)object);
-            else if (object instanceof Collection)
-                appendArray(buffer,(Collection)object);
-            else if (object.getClass().isArray())
-                appendArray(buffer,object);
-            else if (object instanceof Number)
-                appendNumber(buffer,(Number)object);
-            else if (object instanceof Boolean)
-                appendBoolean(buffer,(Boolean)object);
-            else if (object instanceof Character)
-                appendString(buffer,object.toString());
+            }
             else if (object instanceof String)
+            {
                 appendString(buffer,(String)object);
+            }
+            else if (object instanceof Number)
+            {
+                appendNumber(buffer,(Number)object);
+            }
+            else if (object instanceof Boolean)
+            {
+                appendBoolean(buffer,(Boolean)object);
+            }
+            else if (object.getClass().isArray())
+            {
+                appendArray(buffer,object);
+            }
+            else if (object instanceof Character)
+            {
+                appendString(buffer,object.toString());
+            }
+            else if (object instanceof Convertible)
+            {
+                appendJSON(buffer,(Convertible)object);
+            }
+            else if (object instanceof Generator)
+            {
+                appendJSON(buffer,(Generator)object);
+            }
             else
             {
+                // Check Convertor before Collection to support JSONCollectionConvertor
                 Convertor convertor = getConvertor(object.getClass());
                 if (convertor != null)
+                {
                     appendJSON(buffer,convertor,object);
+                }
+                else if (object instanceof Collection)
+                {
+                    appendArray(buffer,(Collection)object);
+                }
                 else
+                {
                     appendString(buffer,object.toString());
+                }
             }
         }
         catch (IOException e)
@@ -335,14 +338,12 @@
         }
     }
 
-    /* ------------------------------------------------------------ */
     @Deprecated
     public void appendNull(StringBuffer buffer)
     {
         appendNull((Appendable)buffer);
     }
 
-    /* ------------------------------------------------------------ */
     public void appendNull(Appendable buffer)
     {
         try
@@ -355,14 +356,12 @@
         }
     }
 
-    /* ------------------------------------------------------------ */
     @Deprecated
     public void appendJSON(final StringBuffer buffer, final Convertor convertor, final Object object)
     {
         appendJSON((Appendable)buffer,convertor,object);
     }
 
-    /* ------------------------------------------------------------ */
     public void appendJSON(final Appendable buffer, final Convertor convertor, final Object object)
     {
         appendJSON(buffer,new Convertible()
@@ -378,14 +377,12 @@
         });
     }
 
-    /* ------------------------------------------------------------ */
     @Deprecated
     public void appendJSON(final StringBuffer buffer, Convertible converter)
     {
         appendJSON((Appendable)buffer,converter);
     }
 
-    /* ------------------------------------------------------------ */
     public void appendJSON(final Appendable buffer, Convertible converter)
     {
         ConvertableOutput out=new ConvertableOutput(buffer);
@@ -393,27 +390,23 @@
         out.complete();
     }
 
-    /* ------------------------------------------------------------ */
     @Deprecated
     public void appendJSON(StringBuffer buffer, Generator generator)
     {
         generator.addJSON(buffer);
     }
 
-    /* ------------------------------------------------------------ */
     public void appendJSON(Appendable buffer, Generator generator)
     {
         generator.addJSON(buffer);
     }
 
-    /* ------------------------------------------------------------ */
     @Deprecated
     public void appendMap(StringBuffer buffer, Map<?,?> map)
     {
         appendMap((Appendable)buffer,map);
     }
 
-    /* ------------------------------------------------------------ */
     public void appendMap(Appendable buffer, Map<?,?> map)
     {
         try
@@ -444,14 +437,12 @@
         }
     }
 
-    /* ------------------------------------------------------------ */
     @Deprecated
     public void appendArray(StringBuffer buffer, Collection collection)
     {
-    	appendArray((Appendable)buffer,collection);
+        appendArray((Appendable)buffer,collection);
     }
 
-    /* ------------------------------------------------------------ */
     public void appendArray(Appendable buffer, Collection collection)
     {
         try
@@ -482,14 +473,12 @@
         }
     }
 
-    /* ------------------------------------------------------------ */
     @Deprecated
     public void appendArray(StringBuffer buffer, Object array)
     {
-	appendArray((Appendable)buffer,array);
+    appendArray((Appendable)buffer,array);
     }
 
-    /* ------------------------------------------------------------ */
     public void appendArray(Appendable buffer, Object array)
     {
         try
@@ -518,14 +507,12 @@
         }
     }
 
-    /* ------------------------------------------------------------ */
     @Deprecated
     public void appendBoolean(StringBuffer buffer, Boolean b)
     {
         appendBoolean((Appendable)buffer,b);
     }
 
-    /* ------------------------------------------------------------ */
     public void appendBoolean(Appendable buffer, Boolean b)
     {
         try
@@ -535,7 +522,7 @@
                 appendNull(buffer);
                 return;
             }
-            buffer.append(b.booleanValue()?"true":"false");
+            buffer.append(b?"true":"false");
         }
         catch (IOException e)
         {
@@ -543,14 +530,12 @@
         }
     }
 
-    /* ------------------------------------------------------------ */
     @Deprecated
     public void appendNumber(StringBuffer buffer, Number number)
     {
-	appendNumber((Appendable)buffer,number);
+        appendNumber((Appendable)buffer,number);
     }
 
-    /* ------------------------------------------------------------ */
     public void appendNumber(Appendable buffer, Number number)
     {
         try
@@ -568,14 +553,12 @@
         }
     }
 
-    /* ------------------------------------------------------------ */
     @Deprecated
     public void appendString(StringBuffer buffer, String string)
     {
-    	appendString((Appendable)buffer,string);
+        appendString((Appendable)buffer,string);
     }
 
-    /* ------------------------------------------------------------ */
     public void appendString(Appendable buffer, String string)
     {
         if (string == null)
@@ -589,37 +572,31 @@
 
     // Parsing utilities
 
-    /* ------------------------------------------------------------ */
     protected String toString(char[] buffer, int offset, int length)
     {
         return new String(buffer,offset,length);
     }
 
-    /* ------------------------------------------------------------ */
     protected Map<String, Object> newMap()
     {
         return new HashMap<String, Object>();
     }
 
-    /* ------------------------------------------------------------ */
     protected Object[] newArray(int size)
     {
         return new Object[size];
     }
 
-    /* ------------------------------------------------------------ */
     protected JSON contextForArray()
     {
         return this;
     }
 
-    /* ------------------------------------------------------------ */
     protected JSON contextFor(String field)
     {
         return this;
     }
 
-    /* ------------------------------------------------------------ */
     protected Object convertTo(Class type, Map map)
     {
         if (type != null && Convertible.class.isAssignableFrom(type))
@@ -644,7 +621,6 @@
         return map;
     }
 
-    /* ------------------------------------------------------------ */
     /**
      * Register a {@link Convertor} for a class or interface.
      *
@@ -658,7 +634,6 @@
         _convertors.put(forClass.getName(),convertor);
     }
 
-    /* ------------------------------------------------------------ */
     /**
      * Lookup a convertor for a class.
      * <p>
@@ -677,7 +652,7 @@
         if (convertor == null && this != DEFAULT)
             convertor = DEFAULT.getConvertor(cls);
 
-        while (convertor == null && cls != null && cls != Object.class)
+        while (convertor == null && cls != Object.class)
         {
             Class[] ifs = cls.getInterfaces();
             int i = 0;
@@ -692,7 +667,6 @@
         return convertor;
     }
 
-    /* ------------------------------------------------------------ */
     /**
      * Register a {@link JSON.Convertor} for a named class or interface.
      *
@@ -706,7 +680,6 @@
         _convertors.put(name,convertor);
     }
 
-    /* ------------------------------------------------------------ */
     /**
      * Lookup a convertor for a named class.
      *
@@ -716,14 +689,12 @@
      */
     public Convertor getConvertorFor(String name)
     {
-        String clsName = name;
-        Convertor convertor = _convertors.get(clsName);
+        Convertor convertor = _convertors.get(name);
         if (convertor == null && this != DEFAULT)
-            convertor = DEFAULT.getConvertorFor(clsName);
+            convertor = DEFAULT.getConvertorFor(name);
         return convertor;
     }
 
-    /* ------------------------------------------------------------ */
     public Object parse(Source source, boolean stripOuterComment)
     {
         int comment_state = 0; // 0=no comment, 1="/", 2="/*", 3="/* *" -1="//"
@@ -811,7 +782,6 @@
         return o;
     }
 
-    /* ------------------------------------------------------------ */
     public Object parse(Source source)
     {
         int comment_state = 0; // 0=no comment, 1="/", 2="/*", 3="/* *" -1="//"
@@ -911,13 +881,11 @@
         return null;
     }
 
-    /* ------------------------------------------------------------ */
     protected Object handleUnknown(Source source, char c)
     {
         throw new IllegalStateException("unknown char '" + c + "'(" + (int)c + ") in " + source);
     }
 
-    /* ------------------------------------------------------------ */
     protected Object parseObject(Source source)
     {
         if (source.next() != '{')
@@ -952,10 +920,10 @@
         String xclassname = (String)map.get("x-class");
         if (xclassname != null)
         {
-        	Convertor c = getConvertorFor(xclassname);
-        	if (c != null)
-        		return c.fromJSON(map);
-        	LOG.warn("no Convertor for xclassname '%s'", xclassname);
+            Convertor c = getConvertorFor(xclassname);
+            if (c != null)
+                return c.fromJSON(map);
+            LOG.warn("No Convertor for x-class '{}'", xclassname);
         }
 
         String classname = (String)map.get("class");
@@ -968,14 +936,13 @@
             }
             catch (ClassNotFoundException e)
             {
-                LOG.warn("no Class for classname '%s'", classname);
+                LOG.warn("No Class for '{}'", classname);
             }
         }
-        
+
         return map;
     }
 
-    /* ------------------------------------------------------------ */
     protected Object parseArray(Source source)
     {
         if (source.next() != '[')
@@ -1042,7 +1009,6 @@
         throw new IllegalStateException("unexpected end of array");
     }
 
-    /* ------------------------------------------------------------ */
     protected String parseString(Source source)
     {
         if (source.next() != '"')
@@ -1110,7 +1076,6 @@
                 else if (c == '\\')
                 {
                     escape = true;
-                    continue;
                 }
                 else if (c == '\"')
                 {
@@ -1118,7 +1083,9 @@
                     return toString(scratch,0,i);
                 }
                 else
+                {
                     scratch[i++] = c;
+                }
             }
 
             // Missing end quote, but return string anyway ?
@@ -1175,17 +1142,19 @@
             else if (c == '\\')
             {
                 escape = true;
-                continue;
             }
             else if (c == '\"')
+            {
                 break;
+            }
             else
+            {
                 builder.append(c);
+            }
         }
         return builder.toString();
     }
 
-    /* ------------------------------------------------------------ */
     public Number parseNumber(Source source)
     {
         boolean minus = false;
@@ -1270,7 +1239,6 @@
 
     }
 
-    /* ------------------------------------------------------------ */
     protected void seekTo(char seek, Source source)
     {
         while (source.hasNext())
@@ -1287,7 +1255,6 @@
         throw new IllegalStateException("Expected '" + seek + "'");
     }
 
-    /* ------------------------------------------------------------ */
     protected char seekTo(String seek, Source source)
     {
         while (source.hasNext())
@@ -1306,7 +1273,6 @@
         throw new IllegalStateException("Expected one of '" + seek + "'");
     }
 
-    /* ------------------------------------------------------------ */
     protected static void complete(String seek, Source source)
     {
         int i = 0;
@@ -1398,7 +1364,7 @@
                 _buffer.append(c);
                 QuotedStringTokenizer.quote(_buffer,name);
                 _buffer.append(':');
-                appendNumber(_buffer,new Double(value));
+                appendNumber(_buffer, value);
                 c = ',';
             }
             catch (IOException e)
@@ -1444,7 +1410,6 @@
         }
     }
 
-    /* ------------------------------------------------------------ */
     public interface Source
     {
         boolean hasNext();
@@ -1456,7 +1421,6 @@
         char[] scratchBuffer();
     }
 
-    /* ------------------------------------------------------------ */
     public static class StringSource implements Source
     {
         private final String string;
@@ -1500,7 +1464,6 @@
         }
     }
 
-    /* ------------------------------------------------------------ */
     public static class ReaderSource implements Source
     {
         private Reader _reader;
@@ -1567,7 +1530,6 @@
 
     }
 
-    /* ------------------------------------------------------------ */
     /**
      * JSON Output class for use by {@link Convertible}.
      */
@@ -1587,7 +1549,6 @@
     }
 
     /* ------------------------------------------------------------ */
-    /* ------------------------------------------------------------ */
     /**
      * JSON Convertible object. Object can implement this interface in a similar
      * way to the {@link Externalizable} interface is used to allow classes to
@@ -1607,7 +1568,6 @@
         public void fromJSON(Map object);
     }
 
-    /* ------------------------------------------------------------ */
     /**
      * Static JSON Convertor.
      * <p>
@@ -1626,7 +1586,6 @@
         public Object fromJSON(Map object);
     }
 
-    /* ------------------------------------------------------------ */
     /**
      * JSON Generator. A class that can add it's JSON representation directly to
      * a StringBuffer. This is useful for object instances that are frequently
@@ -1637,7 +1596,6 @@
         public void addJSON(Appendable buffer);
     }
 
-    /* ------------------------------------------------------------ */
     /**
      * A Literal JSON generator A utility instance of {@link JSON.Generator}
      * that holds a pre-generated string on JSON text.
@@ -1646,7 +1604,6 @@
     {
         private String _json;
 
-        /* ------------------------------------------------------------ */
         /**
          * Construct a literal JSON instance for use by
          * {@link JSON#toString(Object)}. If {@link Log#isDebugEnabled()} is
diff --git a/jetty-util-ajax/src/main/java/org/eclipse/jetty/util/ajax/JSONCollectionConvertor.java b/jetty-util-ajax/src/main/java/org/eclipse/jetty/util/ajax/JSONCollectionConvertor.java
new file mode 100644
index 0000000..9629e3c
--- /dev/null
+++ b/jetty-util-ajax/src/main/java/org/eclipse/jetty/util/ajax/JSONCollectionConvertor.java
@@ -0,0 +1,50 @@
+//
+//  ========================================================================
+//  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.util.ajax;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+
+import org.eclipse.jetty.util.Loader;
+
+public class JSONCollectionConvertor implements JSON.Convertor
+{
+    public void toJSON(Object obj, JSON.Output out)
+    {
+        out.addClass(obj.getClass());
+        out.add("list", ((Collection)obj).toArray());
+    }
+
+    public Object fromJSON(Map object)
+    {
+        try
+        {
+            Collection result = (Collection)Loader.loadClass(getClass(), (String)object.get("class")).newInstance();
+            Collections.addAll(result, (Object[])object.get("list"));
+            return result;
+        }
+        catch (Exception x)
+        {
+            if (x instanceof RuntimeException)
+                throw (RuntimeException)x;
+            throw new RuntimeException(x);
+        }
+    }
+}
diff --git a/jetty-util-ajax/src/main/java/org/eclipse/jetty/util/ajax/JSONDateConvertor.java b/jetty-util-ajax/src/main/java/org/eclipse/jetty/util/ajax/JSONDateConvertor.java
index a244b1f..e50206b 100644
--- a/jetty-util-ajax/src/main/java/org/eclipse/jetty/util/ajax/JSONDateConvertor.java
+++ b/jetty-util-ajax/src/main/java/org/eclipse/jetty/util/ajax/JSONDateConvertor.java
@@ -40,9 +40,10 @@
 public class JSONDateConvertor implements JSON.Convertor
 {
     private static final Logger LOG = Log.getLogger(JSONDateConvertor.class);
-    private boolean _fromJSON;
-    DateCache _dateCache;
-    SimpleDateFormat _format;
+
+    private final boolean _fromJSON;
+    private final DateCache _dateCache;
+    private final SimpleDateFormat _format;
 
     public JSONDateConvertor()
     {
@@ -53,7 +54,7 @@
     {
         this(DateCache.DEFAULT_FORMAT,TimeZone.getTimeZone("GMT"),fromJSON);
     }
-    
+
     public JSONDateConvertor(String format,TimeZone zone,boolean fromJSON)
     {
         _dateCache=new DateCache(format);
@@ -62,7 +63,7 @@
         _format=new SimpleDateFormat(format);
         _format.setTimeZone(zone);
     }
-    
+
     public JSONDateConvertor(String format, TimeZone zone, boolean fromJSON, Locale locale)
     {
         _dateCache = new DateCache(format, locale);
@@ -71,7 +72,7 @@
         _format = new SimpleDateFormat(format, new DateFormatSymbols(locale));
         _format.setTimeZone(zone);
     }
-    
+
     public Object fromJSON(Map map)
     {
         if (!_fromJSON)
@@ -85,7 +86,7 @@
         }
         catch(Exception e)
         {
-            LOG.warn(e);  
+            LOG.warn(e);
         }
         return null;
     }
diff --git a/jetty-util-ajax/src/main/java/org/eclipse/jetty/util/ajax/JSONEnumConvertor.java b/jetty-util-ajax/src/main/java/org/eclipse/jetty/util/ajax/JSONEnumConvertor.java
index 3d90eaa..54c10c6 100644
--- a/jetty-util-ajax/src/main/java/org/eclipse/jetty/util/ajax/JSONEnumConvertor.java
+++ b/jetty-util-ajax/src/main/java/org/eclipse/jetty/util/ajax/JSONEnumConvertor.java
@@ -32,7 +32,7 @@
  * If fromJSON is true in the constructor, the JSON generated will
  * be of the form {class="com.acme.TrafficLight",value="Green"}
  * If fromJSON is false, then only the string value of the enum is generated.
- * 
+ *
  *
  */
 public class JSONEnumConvertor implements JSON.Convertor
@@ -43,8 +43,8 @@
     {
         try
         {
-            Class e = Loader.loadClass(getClass(),"java.lang.Enum");
-            _valueOf=e.getMethod("valueOf",new Class[]{Class.class,String.class});
+            Class<?> e = Loader.loadClass(getClass(),"java.lang.Enum");
+            _valueOf=e.getMethod("valueOf",Class.class,String.class);
         }
         catch(Exception e)
         {
@@ -56,12 +56,12 @@
     {
         this(false);
     }
-    
+
     public JSONEnumConvertor(boolean fromJSON)
     {
         _fromJSON=fromJSON;
     }
-    
+
     public Object fromJSON(Map map)
     {
         if (!_fromJSON)
@@ -69,11 +69,11 @@
         try
         {
             Class c=Loader.loadClass(getClass(),(String)map.get("class"));
-            return _valueOf.invoke(null,new Object[]{c,map.get("value")});
+            return _valueOf.invoke(null,c,map.get("value"));
         }
         catch(Exception e)
         {
-            LOG.warn(e);  
+            LOG.warn(e);
         }
         return null;
     }
@@ -83,12 +83,11 @@
         if (_fromJSON)
         {
             out.addClass(obj.getClass());
-            out.add("value",obj.toString());
+            out.add("value",((Enum)obj).name());
         }
         else
         {
-            out.add(obj.toString());
+            out.add(((Enum)obj).name());
         }
     }
-
 }
diff --git a/jetty-util-ajax/src/test/java/org/eclipse/jetty/util/ajax/JSONCollectionConvertorTest.java b/jetty-util-ajax/src/test/java/org/eclipse/jetty/util/ajax/JSONCollectionConvertorTest.java
new file mode 100644
index 0000000..6534483
--- /dev/null
+++ b/jetty-util-ajax/src/test/java/org/eclipse/jetty/util/ajax/JSONCollectionConvertorTest.java
@@ -0,0 +1,112 @@
+//
+//  ========================================================================
+//  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.util.ajax;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class JSONCollectionConvertorTest
+{
+    @Test
+    public void testArrayList() throws Exception
+    {
+        List<String> list = new ArrayList<String>();
+        Collections.addAll(list, "one", "two");
+        testList(list);
+    }
+
+    @Test
+    public void testLinkedList() throws Exception
+    {
+        List<String> list = new LinkedList<String>();
+        Collections.addAll(list, "one", "two");
+        testList(list);
+    }
+
+    @Test
+    public void testCopyOnWriteArrayList() throws Exception
+    {
+        List<String> list = new CopyOnWriteArrayList<String>();
+        Collections.addAll(list, "one", "two");
+        testList(list);
+    }
+
+    private void testList(List<String> list1) throws Exception
+    {
+        JSON json = new JSON();
+        json.addConvertor(List.class, new JSONCollectionConvertor());
+
+        Map<String, Object> object1 = new HashMap<String, Object>();
+        String field = "field";
+        object1.put(field, list1);
+
+        String string = json.toJSON(object1);
+        Assert.assertTrue(string.contains(list1.getClass().getName()));
+
+        @SuppressWarnings("unchecked")
+        Map<String, Object> object2 = (Map<String, Object>)json.parse(new JSON.StringSource(string));
+        @SuppressWarnings("unchecked")
+        List<String> list2 = (List<String>)object2.get(field);
+
+        Assert.assertSame(list1.getClass(), list2.getClass());
+        Assert.assertEquals(list1, list2);
+    }
+
+    @Test
+    public void testHashSet() throws Exception
+    {
+        Set<String> set = new HashSet<String>();
+        Collections.addAll(set, "one", "two", "three");
+        testSet(set);
+    }
+
+    @Test
+    public void testTreeSet() throws Exception
+    {
+        Set<String> set = new TreeSet<String>();
+        Collections.addAll(set, "one", "two", "three");
+        testSet(set);
+    }
+
+    private void testSet(Set<String> set1)
+    {
+        JSON json = new JSON();
+        json.addConvertor(Set.class, new JSONCollectionConvertor());
+
+        String string = json.toJSON(set1);
+        Assert.assertTrue(string.contains(set1.getClass().getName()));
+
+        @SuppressWarnings("unchecked")
+        Set<String> set2 = (Set<String>)json.parse(new JSON.StringSource(string));
+
+        Assert.assertSame(set1.getClass(), set2.getClass());
+        Assert.assertEquals(set1, set2);
+    }
+}
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayTernaryTrie.java b/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayTernaryTrie.java
index 646f183..05d8380 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayTernaryTrie.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayTernaryTrie.java
@@ -118,12 +118,10 @@
     @Override
     public boolean put(String s, V v)
     {
-        int last=EQ;
-        int t=_tree[last];
-        int k;
+        int t=0;
         int limit = s.length();
-        int node=0;
-        for(k=0; k < limit; k++)
+        int last=0;
+        for(int k=0; k < limit; k++)
         {
             char c=s.charAt(k);
             if(isCaseInsensitive() && c<128)
@@ -131,49 +129,66 @@
             
             while (true)
             {
-                if (t==0)
+                int row=ROW_SIZE*t;
+                
+                // Do we need to create the new row?
+                if (t==_rows)
                 {
-                    node=t=++_rows;
+                    _rows++;
                     if (_rows>=_key.length)
                     {
                         _rows--;
                         return false;
                     }
-                    int row=ROW_SIZE*t;
                     _tree[row]=c;
-                    _tree[last]=(char)t;
-                    last=row+EQ;
                 }
 
-                int row=ROW_SIZE*t;
                 char n=_tree[row];
                 int diff=n-c;
                 if (diff==0)
-                {
-                    node=t;
                     t=_tree[last=(row+EQ)];
-                    break;
-                }
-                if (diff<0)
+                else if (diff<0)
                     t=_tree[last=(row+LO)];
                 else
                     t=_tree[last=(row+HI)];
+                
+                // do we need a new row?
+                if (t==0)
+                {
+                    t=_rows;
+                    _tree[last]=(char)t;
+                }
+                
+                if (diff==0)
+                    break;
             }
         }
-        _key[node]=v==null?null:s;
-        _value[node] = v;
-        
+
+        // Do we need to create the new row?
+        if (t==_rows)
+        {
+            _rows++;
+            if (_rows>=_key.length)
+            {
+                _rows--;
+                return false;
+            }
+        }
+
+        // Put the key and value
+        _key[t]=v==null?null:s;
+        _value[t] = v;
+                
         return true;
     }
+    
 
     /* ------------------------------------------------------------ */
     @Override
-    public V get(String s,int offset, int length)
+    public V get(String s,int offset, int len)
     {
-        int t = _tree[EQ];
-        int len = length;
-        int i=0;
-        while(i<len)
+        int t = 0;
+        for(int i=0; i < len;)
         {
             char c=s.charAt(offset+i++);
             if(isCaseInsensitive() && c<128)
@@ -187,8 +202,6 @@
                 
                 if (diff==0)
                 {
-                    if (i==len)
-                        return (V)_value[t];
                     t=_tree[row+EQ];
                     if (t==0)
                         return null;
@@ -201,19 +214,17 @@
             }
         }
         
-        return null;
+        return (V)_value[t];
     }
 
     
     @Override
-    public V get(ByteBuffer b, int offset, int length)
+    public V get(ByteBuffer b, int offset, int len)
     {
-        int t = _tree[EQ];
-        int len = length;
-        int i=0;
+        int t = 0;
         offset+=b.position();
         
-        while(i<len)
+        for(int i=0; i < len;)
         {
             byte c=(byte)(b.get(offset+i++)&0x7f);
             if(isCaseInsensitive())
@@ -227,8 +238,6 @@
                 
                 if (diff==0)
                 {
-                    if (i==len)
-                        return (V)_value[t];
                     t=_tree[row+EQ];
                     if (t==0)
                         return null;
@@ -240,35 +249,35 @@
                     return null;
             }
         }
-        
-        return null;
+
+        return (V)_value[t];
     }
 
     /* ------------------------------------------------------------ */
     @Override
     public V getBest(String s)
     {
-        return getBest(_tree[EQ],s,0,s.length());
+        return getBest(0,s,0,s.length());
     }
     
     /* ------------------------------------------------------------ */
     @Override
     public V getBest(String s, int offset, int length)
     {
-        return getBest(_tree[EQ],s,offset,length);
+        return getBest(0,s,offset,length);
     }
 
     /* ------------------------------------------------------------ */
     private V getBest(int t,String s,int offset,int len)
     {
-        int node=0;
-        for(int i=0; t!=0 && i<len; i++)
+        int node=t;
+        loop: for(int i=0; i<len; i++)
         {
             char c=s.charAt(offset+i);
             if(isCaseInsensitive() && c<128)
                 c=StringUtil.lowercases[c];
 
-            while (t!=0)
+            while (true)
             {
                 int row = ROW_SIZE*t;
                 char n=_tree[row];
@@ -276,25 +285,27 @@
                 
                 if (diff==0)
                 {
-                    node=t;
                     t=_tree[row+EQ];
+                    if (t==0)
+                        break loop;
                     
                     // if this node is a match, recurse to remember 
-                    if (_key[node]!=null)
+                    if (_key[t]!=null)
                     {
+                        node=t;
                         V best=getBest(t,s,offset+i+1,len-i-1);
                         if (best!=null)
                             return best;
-                        return (V)_value[node];
                     }
-                    
                     break;
                 }
 
                 t=_tree[row+hilo(diff)];
+                if (t==0)
+                    break loop;
             }
         }
-        return null;
+        return (V)_value[node];
     }
 
 
@@ -303,21 +314,21 @@
     public V getBest(ByteBuffer b, int offset, int len)
     {
         if (b.hasArray())
-            return getBest(_tree[EQ],b.array(),b.arrayOffset()+b.position()+offset,len);
-        return getBest(_tree[EQ],b,offset,len);
+            return getBest(0,b.array(),b.arrayOffset()+b.position()+offset,len);
+        return getBest(0,b,offset,len);
     }
 
     /* ------------------------------------------------------------ */
     private V getBest(int t,byte[] b, int offset, int len)
     {
-        int node=0;
-        for(int i=0; t!=0 && i<len; i++)
+        int node=t;
+        loop: for(int i=0; i<len; i++)
         {
             byte c=(byte)(b[offset+i]&0x7f);
             if(isCaseInsensitive())
                 c=(byte)StringUtil.lowercases[c];
 
-            while (t!=0)
+            while (true)
             {
                 int row = ROW_SIZE*t;
                 char n=_tree[row];
@@ -325,40 +336,42 @@
                 
                 if (diff==0)
                 {
-                    node=t;
                     t=_tree[row+EQ];
+                    if (t==0)
+                        break loop;
                     
                     // if this node is a match, recurse to remember 
-                    if (_key[node]!=null)
+                    if (_key[t]!=null)
                     {
+                        node=t;
                         V best=getBest(t,b,offset+i+1,len-i-1);
                         if (best!=null)
                             return best;
-                        return (V)_value[node];
                     }
-                    
                     break;
                 }
 
                 t=_tree[row+hilo(diff)];
+                if (t==0)
+                    break loop;
             }
         }
-        return null;
+        return (V)_value[node];
     }
 
     /* ------------------------------------------------------------ */
     private V getBest(int t,ByteBuffer b, int offset, int len)
     {
-        int node=0;
+        int node=t;
         int o= offset+b.position();
         
-        for(int i=0; t!=0 && i<len; i++)
+        loop: for(int i=0; i<len; i++)
         {
             byte c=(byte)(b.get(o+i)&0x7f);
             if(isCaseInsensitive())
                 c=(byte)StringUtil.lowercases[c];
 
-            while (t!=0)
+            while (true)
             {
                 int row = ROW_SIZE*t;
                 char n=_tree[row];
@@ -366,32 +379,34 @@
                 
                 if (diff==0)
                 {
-                    node=t;
                     t=_tree[row+EQ];
+                    if (t==0)
+                        break loop;
                     
                     // if this node is a match, recurse to remember 
-                    if (_key[node]!=null)
+                    if (_key[t]!=null)
                     {
+                        node=t;
                         V best=getBest(t,b,offset+i+1,len-i-1);
                         if (best!=null)
                             return best;
-                        return (V)_value[node];
                     }
-                    
                     break;
                 }
 
                 t=_tree[row+hilo(diff)];
+                if (t==0)
+                    break loop;
             }
         }
-        return null;
+        return (V)_value[node];
     }
 
     @Override
     public String toString()
     {
         StringBuilder buf = new StringBuilder();
-        for (int r=1;r<=_rows;r++)
+        for (int r=0;r<=_rows;r++)
         {
             if (_key[r]!=null && _value[r]!=null)
             {
@@ -416,7 +431,7 @@
     {
         Set<String> keys = new HashSet<>();
 
-        for (int r=1;r<=_rows;r++)
+        for (int r=0;r<=_rows;r++)
         {
             if (_key[r]!=null && _value[r]!=null)
                 keys.add(_key[r]);
@@ -439,10 +454,10 @@
     
     public void dump()
     {
-        for (int r=0;r<=_rows;r++)
+        for (int r=0;r<_rows;r++)
         {
             char c=_tree[r*ROW_SIZE+0];
-            System.err.printf("%4d [%s,%d,%d,%d] %s:%s%n",
+            System.err.printf("%4d [%s,%d,%d,%d] '%s':%s%n",
                 r,
                 (c<' '||c>127)?(""+(int)c):"'"+c+"'",
                 (int)_tree[r*ROW_SIZE+LO],
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayTrie.java b/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayTrie.java
index df1571f..99a3443 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayTrie.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayTrie.java
@@ -249,18 +249,20 @@
             if (index>=0)
             {
                 int idx=t*ROW_SIZE+index;
-                t=_rowIndex[idx];
-                if (t==0)
-                    return null;
+                int nt=_rowIndex[idx];
+                if (nt==0)
+                    break;
+                t=nt;
             }
             else
             {
                 char[] big = _bigIndex==null?null:_bigIndex[t];
                 if (big==null)
                     return null;
-                t=big[c];
-                if (t==0)
-                    return null;
+                int nt=big[c];
+                if (nt==0)
+                    break;
+                t=nt;
             }
             
             // Is the next Trie is a match
@@ -286,18 +288,20 @@
             if (index>=0)
             {
                 int idx=t*ROW_SIZE+index;
-                t=_rowIndex[idx];
-                if (t==0)
-                    return null;
+                int nt=_rowIndex[idx];
+                if (nt==0)
+                    break;
+                t=nt;
             }
             else
             {
                 char[] big = _bigIndex==null?null:_bigIndex[t];
                 if (big==null)
                     return null;
-                t=big[c];
-                if (t==0)
-                    return null;
+                int nt=big[c];
+                if (nt==0)
+                    break;
+                t=nt;
             }
             
             // Is the next Trie is a match
@@ -307,7 +311,7 @@
                 V best=getBest(t,b,offset+i+1,len-i-1);
                 if (best!=null)
                     return best;
-                return (V)_value[t];
+                break;
             }
         }
         return (V)_value[t];
@@ -323,18 +327,20 @@
             if (index>=0)
             {
                 int idx=t*ROW_SIZE+index;
-                t=_rowIndex[idx];
-                if (t==0)
-                    return null;
+                int nt=_rowIndex[idx];
+                if (nt==0)
+                    break;
+                t=nt;
             }
             else
             {
                 char[] big = _bigIndex==null?null:_bigIndex[t];
                 if (big==null)
                     return null;
-                t=big[c];
-                if (t==0)
-                    return null;
+                int nt=big[c];
+                if (nt==0)
+                    break;
+                t=nt;
             }
             
             // Is the next Trie is a match
@@ -344,7 +350,7 @@
                 V best=getBest(t,b,offset+i+1,len-i-1);
                 if (best!=null)
                     return best;
-                return (V)_value[t];
+                break;
             }
         }
         return (V)_value[t];
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/B64Code.java b/jetty-util/src/main/java/org/eclipse/jetty/util/B64Code.java
index 5af71bc..936984c 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/B64Code.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/B64Code.java
@@ -336,11 +336,33 @@
         if (encoded==null)
             return null;
 
+        ByteArrayOutputStream bout = new ByteArrayOutputStream(4*encoded.length()/3);        
+        decode(encoded, bout);
+        return bout.toByteArray();
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Base 64 decode as described in RFC 2045.
+     * <p>Unlike {@link #decode(char[])}, extra whitespace is ignored.
+     * @param encoded String to decode.
+     * @param output stream for decoded bytes
+     * @return byte array containing the decoded form of the input.
+     * @throws IllegalArgumentException if the input is not a valid
+     *         B64 encoding.
+     */
+    static public void decode (String encoded, ByteArrayOutputStream bout)
+    {
+        if (encoded==null)
+            return;
+        
+        if (bout == null)
+            throw new IllegalArgumentException("No outputstream for decoded bytes");
+        
         int ci=0;
         byte nibbles[] = new byte[4];
         int s=0;
-        ByteArrayOutputStream bout = new ByteArrayOutputStream(4*encoded.length()/3);
-
+  
         while (ci<encoded.length())
         {
             char c=encoded.charAt(ci++);
@@ -375,8 +397,9 @@
 
         }
 
-        return bout.toByteArray();
+        return;
     }
+    
 
     public static void encode(int value,Appendable buf) throws IOException
     {
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartInputStreamParser.java b/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartInputStreamParser.java
index 1429d08..f47a197 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartInputStreamParser.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartInputStreamParser.java
@@ -20,15 +20,14 @@
 
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
-import java.io.BufferedReader;
 import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.FilterInputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.InputStreamReader;
 import java.io.OutputStream;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -479,7 +478,16 @@
         byte[] byteBoundary=(boundary+"--").getBytes(StringUtil.__ISO_8859_1);
 
         // Get first boundary
-        String line=((ReadLineInputStream)_in).readLine();
+        String line = null;
+        try
+        {
+            line=((ReadLineInputStream)_in).readLine();  
+        }
+        catch (IOException e)
+        {
+            LOG.warn("Badly formatted multipart request");
+            throw e;
+        }
         
         if (line == null)
             throw new IOException("Missing content for multipart request");
@@ -502,11 +510,13 @@
 
         // Read each part
         boolean lastPart=false;
-        String contentDisposition=null;
-        String contentType=null;
-        String contentTransferEncoding=null;
+
         outer:while(!lastPart)
         {
+            String contentDisposition=null;
+            String contentType=null;
+            String contentTransferEncoding=null;
+            
             MultiMap headers = new MultiMap();
             while(true)
             {
@@ -577,13 +587,21 @@
                 continue;
             }
 
+            //Have a new Part
+            MultiPart part = new MultiPart(name, filename);
+            part.setHeaders(headers);
+            part.setContentType(contentType);
+            _parts.add(name, part);
+            part.open();
+            
+            InputStream partInput = null;
             if ("base64".equalsIgnoreCase(contentTransferEncoding))
             {
-                _in = new Base64InputStream(_in);
+                partInput = new Base64InputStream((ReadLineInputStream)_in);
             }
             else if ("quoted-printable".equalsIgnoreCase(contentTransferEncoding))
             {
-                _in = new FilterInputStream(_in)
+                partInput = new FilterInputStream(_in)
                 {
                     @Override
                     public int read() throws IOException
@@ -604,17 +622,10 @@
                     }
                 };
             }
+            else
+                partInput = _in;
 
-
-
-            //Have a new Part
-            MultiPart part = new MultiPart(name, filename);
-            part.setHeaders(headers);
-            part.setContentType(contentType);
-            _parts.add(name, part);
-
-            part.open();
-
+            
             try
             {
                 int state=-2;
@@ -626,33 +637,38 @@
                 while(true)
                 {
                     int b=0;
-                    while((c=(state!=-2)?state:_in.read())!=-1)
+                    while((c=(state!=-2)?state:partInput.read())!=-1)
                     {
                         total ++;
                         if (_config.getMaxRequestSize() > 0 && total > _config.getMaxRequestSize())
                             throw new IllegalStateException("Request exceeds maxRequestSize ("+_config.getMaxRequestSize()+")");
 
                         state=-2;
+                        
                         // look for CR and/or LF
                         if(c==13||c==10)
                         {
                             if(c==13)
                             {
-                                _in.mark(1);
-                                int tmp=_in.read();
+                                partInput.mark(1);
+                                int tmp=partInput.read();
                                 if (tmp!=10)
-                                    _in.reset();
+                                    partInput.reset();
                                 else
                                     state=tmp;
                             }
                             break;
                         }
-                        // look for boundary
+                        
+                        // Look for boundary
                         if(b>=0&&b<byteBoundary.length&&c==byteBoundary[b])
+                        {
                             b++;
+                        }
                         else
                         {
-                            // this is not a boundary
+                            // Got a character not part of the boundary, so we don't have the boundary marker.
+                            // Write out as many chars as we matched, then the char we're looking at.
                             if(cr)
                                 part.write(13);
 
@@ -667,7 +683,8 @@
                             part.write(c);
                         }
                     }
-                    // check partial boundary
+                    
+                    // Check for incomplete boundary match, writing out the chars we matched along the way
                     if((b>0&&b<byteBoundary.length-2)||(b==byteBoundary.length-1))
                     {
                         if(cr)
@@ -680,15 +697,18 @@
                         part.write(byteBoundary,0,b);
                         b=-1;
                     }
-                    // boundary match
+                    
+                    // Boundary match. If we've run out of input or we matched the entire final boundary marker, then this is the last part.
                     if(b>0||c==-1)
                     {
+                       
                         if(b==byteBoundary.length)
                             lastPart=true;
                         if(state==10)
                             state=-2;
                         break;
                     }
+                    
                     // handle CR LF
                     if(cr)
                         part.write(13);
@@ -764,14 +784,15 @@
 
     private static class Base64InputStream extends InputStream
     {
-        BufferedReader _in;
+        ReadLineInputStream _in;
         String _line;
         byte[] _buffer;
         int _pos;
 
-        public Base64InputStream (InputStream in)
+    
+        public Base64InputStream(ReadLineInputStream rlis)
         {
-            _in = new BufferedReader(new InputStreamReader(in));
+            _in = rlis;
         }
 
         @Override
@@ -779,18 +800,29 @@
         {
             if (_buffer==null || _pos>= _buffer.length)
             {
-                _line = _in.readLine();
+                //Any CR and LF will be consumed by the readLine() call.
+                //We need to put them back into the bytes returned from this
+                //method because the parsing of the multipart content uses them
+                //as markers to determine when we've reached the end of a part.
+                _line = _in.readLine(); 
                 if (_line==null)
-                    return -1;
+                    return -1;  //nothing left
                 if (_line.startsWith("--"))
-                    _buffer=(_line+"\r\n").getBytes();
+                    _buffer=(_line+"\r\n").getBytes(); //boundary marking end of part
                 else if (_line.length()==0)
-                    _buffer="\r\n".getBytes();
+                    _buffer="\r\n".getBytes(); //blank line
                 else
-                    _buffer=B64Code.decode(_line);
+                {
+                    ByteArrayOutputStream baos = new ByteArrayOutputStream((4*_line.length()/3)+2);
+                    B64Code.decode(_line, baos);
+                    baos.write(13);
+                    baos.write(10);
+                    _buffer = baos.toByteArray();
+                }
 
                 _pos=0;
             }
+            
             return _buffer[_pos++];
         }
     }
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/QuotedStringTokenizer.java b/jetty-util/src/main/java/org/eclipse/jetty/util/QuotedStringTokenizer.java
index 561c674..0931f32 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/QuotedStringTokenizer.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/QuotedStringTokenizer.java
@@ -334,6 +334,32 @@
 
     /* ------------------------------------------------------------ */
     /** Quote a string into an Appendable.
+     * Only quotes and backslash are escaped. 
+     * @param buffer The Appendable
+     * @param input The String to quote.
+     */
+    public static void quoteOnly(Appendable buffer, String input)
+    {
+        try
+        {
+            buffer.append('"');
+            for (int i = 0; i < input.length(); ++i)
+            {
+                char c = input.charAt(i);
+                if (c == '"' || c == '\\')
+                    buffer.append('\\');
+                buffer.append(c);
+            }
+            buffer.append('"');
+        }
+        catch (IOException x)
+        {
+            throw new RuntimeException(x);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Quote a string into an Appendable.
      * The characters ", \, \n, \r, \t, \f and \b are escaped
      * @param buffer The Appendable
      * @param input The String to quote.
@@ -377,38 +403,6 @@
         }
     }
 
-    /* ------------------------------------------------------------ */
-    /** Quote a string into a StringBuffer only if needed.
-     * Quotes are forced if any delim characters are present.
-     *
-     * @param buf The StringBuffer
-     * @param s The String to quote.
-     * @param delim String of characters that must be quoted.
-     * @return true if quoted;
-     */
-    public static boolean quoteIfNeeded(Appendable buf, String s,String delim)
-    {
-        for (int i=0;i<s.length();i++)
-        {
-            char c = s.charAt(i);
-            if (delim.indexOf(c)>=0)
-            {
-            	quote(buf,s);
-            	return true;
-            }
-        }
-
-        try
-        {
-            buf.append(s);
-            return false;
-        }
-        catch(IOException e)
-        {
-            throw new RuntimeException(e);
-        }
-    }
-    
     
     /* ------------------------------------------------------------ */
     public static String unquoteOnly(String s)
@@ -566,6 +560,12 @@
     }
 
     /* ------------------------------------------------------------ */
+    public static boolean isQuoted(String s)
+    {
+        return s!=null && s.length()>0 && s.charAt(0)=='"' && s.charAt(s.length()-1)=='"';
+    }
+    
+    /* ------------------------------------------------------------ */
     /**
      * @return handle double quotes if true
      */
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/ReadLineInputStream.java b/jetty-util/src/main/java/org/eclipse/jetty/util/ReadLineInputStream.java
index bb4c9fb..1aeec5f 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/ReadLineInputStream.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/ReadLineInputStream.java
@@ -49,6 +49,10 @@
         while (true)
         {
             int b=super.read();
+            
+            if (markpos < 0)
+                throw new IOException("Buffer size exceeded: no line terminator");
+            
             if (b==-1)
             {
                 int m=markpos;
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/StringUtil.java b/jetty-util/src/main/java/org/eclipse/jetty/util/StringUtil.java
index 29db211..58ede8b 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/StringUtil.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/StringUtil.java
@@ -53,12 +53,14 @@
     public final static Charset __UTF8_CHARSET;
     public final static Charset __ISO_8859_1_CHARSET;
     public final static Charset __UTF16_CHARSET;
+    public final static Charset __US_ASCII_CHARSET;
     
     static
     {
         __UTF8_CHARSET=Charset.forName(__UTF8);
         __ISO_8859_1_CHARSET=Charset.forName(__ISO_8859_1);
         __UTF16_CHARSET=Charset.forName(__UTF16);
+        __US_ASCII_CHARSET=Charset.forName("US-ASCII");
         
         CHARSETS.put("UTF-8",__UTF8);
         CHARSETS.put("UTF8",__UTF8);
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/TreeTrie.java b/jetty-util/src/main/java/org/eclipse/jetty/util/TreeTrie.java
index 484a642..b82b6f4 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/TreeTrie.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/TreeTrie.java
@@ -70,9 +70,8 @@
     public boolean put(String s, V v)
     {
         TreeTrie<V> t = this;
-        int k;
         int limit = s.length();
-        for(k=0; k < limit; k++)
+        for(int k=0; k < limit; k++)
         {
             char c=s.charAt(k);
             
@@ -102,7 +101,6 @@
             }
         }
         t._key=v==null?null:s;
-        V old=t._value;
         t._value = v;
         return true;
     }
@@ -182,7 +180,7 @@
             if (index>=0)
             {
                 if (t._nextIndex[index] == null) 
-                    return null;
+                    break;
                 t = t._nextIndex[index];
             }
             else
@@ -196,7 +194,7 @@
                     n=null;
                 }
                 if (n==null)
-                    return null;
+                    break;
                 t=n;
             }
             
@@ -207,7 +205,7 @@
                 V best=t.getBest(b,offset+i+1,len-i-1);
                 if (best!=null)
                     return best;
-                return t._value;
+                break;
             }
         }
         return t._value;
@@ -240,7 +238,7 @@
             if (index>=0)
             {
                 if (t._nextIndex[index] == null) 
-                    return null;
+                    break;
                 t = t._nextIndex[index];
             }
             else
@@ -254,7 +252,7 @@
                     n=null;
                 }
                 if (n==null)
-                    return null;
+                    break;
                 t=n;
             }
             
@@ -265,7 +263,7 @@
                 V best=t.getBest(b,offset+i+1,len-i-1);
                 if (best!=null)
                     return best;
-                return t._value;
+                break;
             }
         }
         return t._value;
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/TypeUtil.java b/jetty-util/src/main/java/org/eclipse/jetty/util/TypeUtil.java
index 68de404..d63fe37 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/TypeUtil.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/TypeUtil.java
@@ -353,7 +353,7 @@
     {
         byte b = (byte)((c & 0x1f) + ((c >> 6) * 0x19) - 0x10);
         if (b<0 || b>15)
-            throw new IllegalArgumentException("!hex "+c);
+            throw new NumberFormatException("!hex "+c);
         return b;
     }
     
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/UrlEncoded.java b/jetty-util/src/main/java/org/eclipse/jetty/util/UrlEncoded.java
index fed74d5..dfe0203 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/UrlEncoded.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/UrlEncoded.java
@@ -323,7 +323,13 @@
                                 {
                                     i++;
                                     if (i+4<end)
-                                        buffer.getStringBuilder().append(Character.toChars((convertHexDigit(raw[++i])<<12) +(convertHexDigit(raw[++i])<<8) + (convertHexDigit(raw[++i])<<4) +convertHexDigit(raw[++i])));
+                                    {
+                                        byte top=raw[++i];
+                                        byte hi=raw[++i];
+                                        byte lo=raw[++i];
+                                        byte bot=raw[++i];
+                                        buffer.getStringBuilder().append(Character.toChars((convertHexDigit(top)<<12) +(convertHexDigit(hi)<<8) + (convertHexDigit(lo)<<4) +convertHexDigit(bot)));
+                                    }
                                     else
                                     {
                                         buffer.getStringBuilder().append(Utf8Appendable.REPLACEMENT);
@@ -331,7 +337,11 @@
                                     }
                                 }
                                 else
-                                    buffer.append((byte)((convertHexDigit(raw[++i])<<4) + convertHexDigit(raw[++i])));
+                                {
+                                    byte hi=raw[++i];
+                                    byte lo=raw[++i];
+                                    buffer.append((byte)((convertHexDigit(hi)<<4) + convertHexDigit(lo)));
+                                }
                             }
                             else
                             {
@@ -350,6 +360,12 @@
                     LOG.warn(e.toString());
                     LOG.debug(e);
                 }
+                catch(NumberFormatException e)
+                {
+                    buffer.append(Utf8Appendable.REPLACEMENT_UTF8,0,3);
+                    LOG.warn(e.toString());
+                    LOG.debug(e);
+                }
             }
             
             if (key != null)
@@ -552,6 +568,12 @@
                     LOG.warn(e.toString());
                     LOG.debug(e);
                 }
+                catch(NumberFormatException e)
+                {
+                    buffer.append(Utf8Appendable.REPLACEMENT_UTF8,0,3);
+                    LOG.warn(e.toString());
+                    LOG.debug(e);
+                }
                 if (maxLength>=0 && (++totalLength > maxLength))
                     throw new IllegalStateException("Form too large");
             }
@@ -798,9 +820,10 @@
                             LOG.warn(e.toString());
                             LOG.debug(e);
                         }
-                        catch(NumberFormatException nfe)
+                        catch(NumberFormatException e)
                         {
-                            LOG.debug(nfe);
+                            LOG.warn(e.toString());
+                            LOG.debug(e);
                             buffer.getStringBuffer().append(Utf8Appendable.REPLACEMENT);  
                         }
                     }
@@ -870,32 +893,33 @@
                                 {
                                     if ('u'==encoded.charAt(offset+i+1))
                                     {
-                                            if (i+6<length)
-                                            {
-                                        int o=offset+i+2;
-                                        i+=6;
-                                        String unicode = new String(Character.toChars(TypeUtil.parseInt(encoded,o,4,16)));
-                                        byte[] reencoded = unicode.getBytes(charset);
-                                        System.arraycopy(reencoded,0,ba,n,reencoded.length);
-                                        n+=reencoded.length;
-                                    }
-                                    else
-                                    {
-                                                ba[n++] = (byte)'?';
-                                                i=length;
-                                            }
+                                        if (i+6<length)
+                                        {
+                                            int o=offset+i+2;
+                                            i+=6;
+                                            String unicode = new String(Character.toChars(TypeUtil.parseInt(encoded,o,4,16)));
+                                            byte[] reencoded = unicode.getBytes(charset);
+                                            System.arraycopy(reencoded,0,ba,n,reencoded.length);
+                                            n+=reencoded.length;
                                         }
                                         else
                                         {
+                                            ba[n++] = (byte)'?';
+                                            i=length;
+                                        }
+                                    }
+                                    else
+                                    {
                                         int o=offset+i+1;
                                         i+=3;
                                         ba[n]=(byte)TypeUtil.parseInt(encoded,o,2,16);
                                         n++;
                                     }
                                 }
-                                catch(NumberFormatException nfe)
+                                catch(Exception e)
                                 {   
-                                    LOG.ignore(nfe);
+                                    LOG.warn(e.toString());
+                                    LOG.debug(e);
                                     ba[n++] = (byte)'?';
                                 }
                             }
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/Utf8Appendable.java b/jetty-util/src/main/java/org/eclipse/jetty/util/Utf8Appendable.java
index bce0746..616fa76 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/Utf8Appendable.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/Utf8Appendable.java
@@ -52,6 +52,7 @@
 {
     protected static final Logger LOG = Log.getLogger(Utf8Appendable.class);
     public static final char REPLACEMENT = '\ufffd';
+    public static final byte[] REPLACEMENT_UTF8 = new byte[] {(byte)0xEF,(byte)0xBF,(byte)0xBD };
     private static final int UTF8_ACCEPT = 0;
     private static final int UTF8_REJECT = 12;
 
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/log/StdErrLog.java b/jetty-util/src/main/java/org/eclipse/jetty/util/log/StdErrLog.java
index 2bbabfb..1439b8a 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/log/StdErrLog.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/log/StdErrLog.java
@@ -82,6 +82,11 @@
  *   long form, fully qualified class names.  when false, use abbreviated package names<br/>
  *   Default: false
  *   </dd>
+ *   <dt>org.eclipse.jetty.util.log.stderr.ESCAPE=(true|false)</dt>
+ *   <dd>Global Configuration, when true output logging events to STDERR are always
+ *   escaped so that control characters are replaced with '?";  '\r' with '<' and '\n' replaced '|'<br/>
+ *   Default: true
+ *   </dd>
  * </dl>
  */
 @ManagedObject("Jetty StdErr Logging Implementation")
@@ -94,6 +99,7 @@
     private final static boolean __source = Boolean.parseBoolean(Log.__props.getProperty("org.eclipse.jetty.util.log.SOURCE",
             Log.__props.getProperty("org.eclipse.jetty.util.log.stderr.SOURCE","false")));
     private final static boolean __long = Boolean.parseBoolean(Log.__props.getProperty("org.eclipse.jetty.util.log.stderr.LONG","false"));
+    private final static boolean __escape = Boolean.parseBoolean(Log.__props.getProperty("org.eclipse.jetty.util.log.stderr.ESCAPE","true"));
 
     static
     {
@@ -642,29 +648,34 @@
 
     private void escape(StringBuilder builder, String string)
     {
-        for (int i = 0; i < string.length(); ++i)
+        if (__escape)
         {
-            char c = string.charAt(i);
-            if (Character.isISOControl(c))
+            for (int i = 0; i < string.length(); ++i)
             {
-                if (c == '\n')
+                char c = string.charAt(i);
+                if (Character.isISOControl(c))
                 {
-                    builder.append('|');
-                }
-                else if (c == '\r')
-                {
-                    builder.append('<');
+                    if (c == '\n')
+                    {
+                        builder.append('|');
+                    }
+                    else if (c == '\r')
+                    {
+                        builder.append('<');
+                    }
+                    else
+                    {
+                        builder.append('?');
+                    }
                 }
                 else
                 {
-                    builder.append('?');
+                    builder.append(c);
                 }
             }
-            else
-            {
-                builder.append(c);
-            }
         }
+        else
+            builder.append(string);
     }
 
     private void format(StringBuilder buffer, Throwable thrown)
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/Resource.java b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/Resource.java
index 6831566..3523105 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/Resource.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/Resource.java
@@ -114,6 +114,7 @@
             }
             catch(Exception e)
             {
+                LOG.warn(e.toString());
                 LOG.debug(Log.EXCEPTION,e);
                 return new BadResource(url,e.toString());
             }
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/QueuedThreadPool.java b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/QueuedThreadPool.java
index 0febc24..7c8b5e6 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/QueuedThreadPool.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/QueuedThreadPool.java
@@ -80,7 +80,7 @@
 
     public QueuedThreadPool(@Name("maxThreads") int maxThreads,  @Name("minThreads") int minThreads, @Name("idleTimeout")int idleTimeout)
     {
-        this(maxThreads, minThreads, 60000,null);
+        this(maxThreads, minThreads, idleTimeout, null);
     }
 
     public QueuedThreadPool(@Name("maxThreads") int maxThreads, @Name("minThreads") int minThreads, @Name("idleTimeout") int idleTimeout, @Name("queue") BlockingQueue<Runnable> queue)
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/MultiPartInputStreamTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/MultiPartInputStreamTest.java
index a706e73..a352d12 100644
--- a/jetty-util/src/test/java/org/eclipse/jetty/util/MultiPartInputStreamTest.java
+++ b/jetty-util/src/test/java/org/eclipse/jetty/util/MultiPartInputStreamTest.java
@@ -18,17 +18,8 @@
 
 package org.eclipse.jetty.util;
 
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.not;
-import static org.hamcrest.Matchers.notNullValue;
-import static org.hamcrest.Matchers.nullValue;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
@@ -530,6 +521,34 @@
     }
     
     @Test
+    public void testBufferOverflowNoCRLF () throws Exception
+    {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        baos.write("--AaB03x".getBytes());
+        for (int i=0; i< 8500; i++) //create content that will overrun default buffer size of BufferedInputStream
+        {
+            baos.write('a');
+        }
+        
+        MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
+        MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(baos.toByteArray()), 
+                                                             _contentType,
+                                                             config,
+                                                             _tmpDir);
+        mpis.setDeleteOnExit(true);
+        try
+        {
+            mpis.getParts();
+            fail ("Multipart buffer overrun");
+        }
+        catch (IOException e)
+        {
+            assertTrue(e.getMessage().startsWith("Buffer size exceeded"));
+        }
+
+    }
+    
+    
     public void testCharsetEncoding () throws Exception
     {
         String contentType = "multipart/form-data; boundary=TheBoundary; charset=ISO-8859-1";
@@ -724,6 +743,98 @@
         assertEquals(5, p.getSize());
     }
 
+    @Test
+    public void testBase64EncodedContent () throws Exception
+    {
+        String contentWithEncodedPart =
+                "--AaB03x\r\n"+
+                        "Content-disposition: form-data; name=\"other\"\r\n"+
+                        "Content-Type: text/plain\r\n"+
+                        "\r\n"+
+                        "other" + "\r\n"+
+                        "--AaB03x\r\n"+
+                        "Content-disposition: form-data; name=\"stuff\"; filename=\"stuff.txt\"\r\n"+
+                        "Content-Transfer-Encoding: base64\r\n"+
+                        "Content-Type: application/octet-stream\r\n"+
+                        "\r\n"+
+                        B64Code.encode("hello jetty") + "\r\n"+                  
+                        "--AaB03x\r\n"+
+                        "Content-disposition: form-data; name=\"final\"\r\n"+
+                        "Content-Type: text/plain\r\n"+
+                        "\r\n"+
+                        "the end" + "\r\n"+
+                        "--AaB03x--\r\n";
+
+        MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
+        MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(contentWithEncodedPart.getBytes()),
+                                                                         _contentType,
+                                                                         config,
+                                                                         _tmpDir);
+        mpis.setDeleteOnExit(true);
+        Collection<Part> parts = mpis.getParts();
+        assertEquals(3, parts.size());
+
+        Part p1 = mpis.getPart("other");
+        assertNotNull(p1);
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        IO.copy(p1.getInputStream(), baos);
+        assertEquals("other", baos.toString("US-ASCII"));
+
+        Part p2 = mpis.getPart("stuff");
+        assertNotNull(p2);
+        baos = new ByteArrayOutputStream();
+        IO.copy(p2.getInputStream(), baos);
+        assertEquals("hello jetty", baos.toString("US-ASCII"));
+        
+        Part p3 = mpis.getPart("final");
+        assertNotNull(p3);
+        baos = new ByteArrayOutputStream();
+        IO.copy(p3.getInputStream(), baos);
+        assertEquals("the end", baos.toString("US-ASCII"));
+    }
+    
+    @Test
+    public void testQuotedPrintableEncoding () throws Exception
+    {
+        String contentWithEncodedPart = 
+                "--AaB03x\r\n"+
+                        "Content-disposition: form-data; name=\"other\"\r\n"+
+                        "Content-Type: text/plain\r\n"+
+                        "\r\n"+
+                        "other" + "\r\n"+
+                        "--AaB03x\r\n"+
+                        "Content-disposition: form-data; name=\"stuff\"; filename=\"stuff.txt\"\r\n"+
+                        "Content-Transfer-Encoding: quoted-printable\r\n"+
+                        "Content-Type: text/plain\r\n"+
+                        "\r\n"+
+                        "truth=3Dbeauty" + "\r\n"+
+                        "--AaB03x--\r\n";  
+        MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
+        MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(contentWithEncodedPart.getBytes()),
+                                                                         _contentType,
+                                                                         config,
+                                                                         _tmpDir);
+        mpis.setDeleteOnExit(true);
+        Collection<Part> parts = mpis.getParts();
+        assertEquals(2, parts.size());
+
+        Part p1 = mpis.getPart("other");
+        assertNotNull(p1);
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        IO.copy(p1.getInputStream(), baos);
+        assertEquals("other", baos.toString("US-ASCII"));
+
+        Part p2 = mpis.getPart("stuff");
+        assertNotNull(p2);
+        baos = new ByteArrayOutputStream();
+        IO.copy(p2.getInputStream(), baos);
+        assertEquals("truth=beauty", baos.toString("US-ASCII"));
+    }
+
+
+
+
+    
     private String createMultipartRequestString(String filename)
     {
         int length = filename.length();
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/QuotedStringTokenizerTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/QuotedStringTokenizerTest.java
index a4290e4..692f61b 100644
--- a/jetty-util/src/test/java/org/eclipse/jetty/util/QuotedStringTokenizerTest.java
+++ b/jetty-util/src/test/java/org/eclipse/jetty/util/QuotedStringTokenizerTest.java
@@ -112,14 +112,6 @@
         QuotedStringTokenizer.quote(buf,"abcefg\"");
         assertEquals("\"abcefg\\\"\"",buf.toString());
 
-        buf.setLength(0);
-        QuotedStringTokenizer.quoteIfNeeded(buf,"abc \n efg","\"\\\n\r\t\f\b%+ ;=");
-        assertEquals("\"abc \\n efg\"",buf.toString());
-
-        buf.setLength(0);
-        QuotedStringTokenizer.quoteIfNeeded(buf,"abcefg","\"\\\n\r\t\f\b%+ ;=");
-        assertEquals("abcefg",buf.toString());
-
     }
 
     /*
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/TrieTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/TrieTest.java
index 8fe5f6e..d1ff2fb 100644
--- a/jetty-util/src/test/java/org/eclipse/jetty/util/TrieTest.java
+++ b/jetty-util/src/test/java/org/eclipse/jetty/util/TrieTest.java
@@ -23,6 +23,7 @@
 import java.util.Arrays;
 import java.util.Collection;
 
+import org.hamcrest.Matchers;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
@@ -62,6 +63,21 @@
         trie.put("foo-bar",6);
         trie.put("foo+bar",7);
         trie.put("HELL4",8);
+        trie.put("",9);
+    }
+
+    @Test
+    public void testKeySet() throws Exception
+    {
+        Assert.assertTrue(trie.keySet().contains("hello"));
+        Assert.assertTrue(trie.keySet().contains("He"));
+        Assert.assertTrue(trie.keySet().contains("HELL"));
+        Assert.assertTrue(trie.keySet().contains("wibble"));
+        Assert.assertTrue(trie.keySet().contains("Wobble"));
+        Assert.assertTrue(trie.keySet().contains("foo-bar"));
+        Assert.assertTrue(trie.keySet().contains("foo+bar"));
+        Assert.assertTrue(trie.keySet().contains("HELL4"));
+        Assert.assertTrue(trie.keySet().contains(""));        
     }
     
     @Test
@@ -82,6 +98,8 @@
         Assert.assertEquals(5,trie.get("wobble").intValue());
         Assert.assertEquals(6,trie.get("Foo-bar").intValue());
         Assert.assertEquals(7,trie.get("FOO+bar").intValue());
+        Assert.assertEquals(8,trie.get("HELL4").intValue());
+        Assert.assertEquals(9,trie.get("").intValue());
         
         Assert.assertEquals(null,trie.get("helloworld"));
         Assert.assertEquals(null,trie.get("Help"));
@@ -151,6 +169,7 @@
         Assert.assertEquals(3,trie.getBest(StringUtil.getUtf8Bytes("xHELLxxxxx"),1,8).intValue()); 
         Assert.assertEquals(6,trie.getBest(StringUtil.getUtf8Bytes("xfoo-BARxx"),1,8).intValue()); 
         Assert.assertEquals(8,trie.getBest(StringUtil.getUtf8Bytes("xHELL4xxxx"),1,8).intValue());  
+        Assert.assertEquals(9,trie.getBest(StringUtil.getUtf8Bytes("xZZZZZxxxx"),1,8).intValue());  
     }
 
     @Test
@@ -167,6 +186,7 @@
         Assert.assertEquals(3,trie.getBest(BufferUtil.toBuffer("xHELLxxxxx"),1,8).intValue()); 
         Assert.assertEquals(6,trie.getBest(BufferUtil.toBuffer("xfoo-BARxx"),1,8).intValue()); 
         Assert.assertEquals(8,trie.getBest(BufferUtil.toBuffer("xHELL4xxxx"),1,8).intValue());  
+        Assert.assertEquals(9,trie.getBest(BufferUtil.toBuffer("xZZZZZxxxx"),1,8).intValue());  
         
         ByteBuffer buffer = (ByteBuffer)BufferUtil.toBuffer("xhelloxxxxxxx").position(2);
         Assert.assertEquals(1,trie.getBest(buffer,-1,10).intValue());
@@ -186,6 +206,7 @@
         Assert.assertEquals(3,trie.getBest(BufferUtil.toDirectBuffer("xHELLxxxxx"),1,8).intValue()); 
         Assert.assertEquals(6,trie.getBest(BufferUtil.toDirectBuffer("xfoo-BARxx"),1,8).intValue()); 
         Assert.assertEquals(8,trie.getBest(BufferUtil.toDirectBuffer("xHELL4xxxx"),1,8).intValue());  
+        Assert.assertEquals(9,trie.getBest(BufferUtil.toDirectBuffer("xZZZZZxxxx"),1,8).intValue());  
         
         ByteBuffer buffer = (ByteBuffer)BufferUtil.toDirectBuffer("xhelloxxxxxxx").position(2);
         Assert.assertEquals(1,trie.getBest(buffer,-1,10).intValue());
diff --git a/pom.xml b/pom.xml
index aebaa96..a8b9d44 100644
--- a/pom.xml
+++ b/pom.xml
@@ -169,7 +169,7 @@
             </configuration>
           </execution>
           <execution>
-            <id>ban-junit.jar</id>
+            <id>ban-junit-dep.jar</id>
             <goals>
               <goal>enforce</goal>
             </goals>
@@ -177,10 +177,10 @@
               <rules>
                 <bannedDependencies>
                   <excludes>
-                    <exclude>junit:junit:*:jar</exclude>
+                    <exclude>junit:junit-dep:*:jar</exclude>
                   </excludes>
                   <searchTransitive>true</searchTransitive>
-                  <message>We use junit-dep.jar, not junit.jar (as the standard junit.jar aggregates too many 3rd party libs inside of it)</message>
+                  <message>We use junit.jar, not junit-dep.jar (as of junit 4.11, hamcrest is no longer embedded)</message>
                 </bannedDependencies>
               </rules>
             </configuration>
@@ -451,10 +451,10 @@
     <module>jetty-distribution</module>
     <module>jetty-runner</module>
     <module>jetty-monitor</module>
+    <module>jetty-http-spi</module>
 
     <!-- modules that need fixed and added back, or simply dropped and not maintained
     <module>jetty-rhttp</module>
-    <module>jetty-http-spi</module>
     -->
     <module>jetty-overlay-deployer</module>
   </modules>
@@ -543,7 +543,7 @@
       <dependency>
         <groupId>org.eclipse.jetty.toolchain</groupId>
         <artifactId>jetty-test-helper</artifactId>
-        <version>2.3</version>
+        <version>2.5</version>
       </dependency>
       <dependency>
         <groupId>org.slf4j</groupId>
@@ -565,7 +565,7 @@
       <dependency>
         <groupId>junit</groupId>
         <artifactId>junit</artifactId>
-        <version>4.8.1</version>
+        <version>4.11</version>
       </dependency>
         -->
       <dependency>
@@ -576,17 +576,17 @@
       <dependency>
         <groupId>org.hamcrest</groupId>
         <artifactId>hamcrest-core</artifactId>
-        <version>1.2.1</version>
+        <version>1.3</version>
       </dependency>
       <dependency>
         <groupId>org.hamcrest</groupId>
         <artifactId>hamcrest-library</artifactId>
-        <version>1.2.1</version>
+        <version>1.3</version>
       </dependency>
       <dependency>
         <groupId>org.mockito</groupId>
         <artifactId>mockito-core</artifactId>
-        <version>1.8.5</version>
+        <version>1.9.5</version>
         <exclusions>
           <exclusion>
             <groupId>junit</groupId>
diff --git a/tests/test-continuation/src/test/java/org/eclipse/jetty/continuation/ContinuationTest.java b/tests/test-continuation/src/test/java/org/eclipse/jetty/continuation/ContinuationTest.java
index d0df8d5..2ca10a0 100644
--- a/tests/test-continuation/src/test/java/org/eclipse/jetty/continuation/ContinuationTest.java
+++ b/tests/test-continuation/src/test/java/org/eclipse/jetty/continuation/ContinuationTest.java
@@ -20,15 +20,24 @@
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.Assert;
 
 import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.RequestLog;
+import org.eclipse.jetty.server.Response;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.handler.RequestLogHandler;
 import org.eclipse.jetty.servlet.FilterHolder;
 import org.eclipse.jetty.servlet.ServletContextHandler;
 import org.eclipse.jetty.servlet.ServletHandler;
 import org.eclipse.jetty.servlet.ServletHolder;
 import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.component.AbstractLifeCycle;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -41,19 +50,27 @@
     protected ServletHandler _servletHandler;
     protected ServerConnector _connector;
     FilterHolder _filter;
+    protected List<String> _log = new ArrayList<String>();
 
     @Before
     public void setUp() throws Exception
     {
         _connector = new ServerConnector(_server);
         _server.setConnectors(new Connector[]{ _connector });
+
+        _log.clear();
+        RequestLogHandler requestLogHandler = new RequestLogHandler();
+        requestLogHandler.setRequestLog(new Log());
+        _server.setHandler(requestLogHandler);
+        
         ServletContextHandler servletContext = new ServletContextHandler(ServletContextHandler.NO_SECURITY|ServletContextHandler.NO_SESSIONS);
-        _server.setHandler(servletContext);
+        requestLogHandler.setHandler(servletContext);
+        
         _servletHandler=servletContext.getServletHandler();
         ServletHolder holder=new ServletHolder(_servlet);
         holder.setAsyncSupported(true);
         _servletHandler.addServletWithMapping(holder,"/");
-
+        
         _server.start();
         _port=_connector.getLocalPort();
     }
@@ -61,6 +78,9 @@
     @After
     public void tearDown() throws Exception
     {
+        Assert.assertEquals(1,_log.size());
+        Assert.assertTrue(_log.get(0).startsWith("200 "));
+        Assert.assertTrue(_log.get(0).endsWith(" /"));
         _server.stop();
     }
     
@@ -172,4 +192,12 @@
         return IO.toString(in);
     }
     
+    class Log extends AbstractLifeCycle implements RequestLog
+    {
+        public void log(Request request, Response response)
+        {
+            _log.add(response.getStatus()+" "+response.getContentCount()+" "+request.getRequestURI());
+        }
+        
+    }
 }
diff --git a/tests/test-integration/pom.xml b/tests/test-integration/pom.xml
index fb5784a..d3232a3 100644
--- a/tests/test-integration/pom.xml
+++ b/tests/test-integration/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.eclipse.jetty.tests</groupId>
     <artifactId>tests-parent</artifactId>
-    <version>9.0.0-SNAPSHOT</version>
+    <version>9.0.5-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>test-integration</artifactId>
@@ -111,8 +111,6 @@
       <version>${project.version}</version>
     </dependency>
     <dependency>
-    </dependency>
-    <dependency>
       <groupId>org.eclipse.jetty.toolchain</groupId>
       <artifactId>jetty-test-helper</artifactId>
       <scope>test</scope>
diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/DefaultHandlerTest.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/DefaultHandlerTest.java
index 96d0556..f6baafa 100644
--- a/tests/test-integration/src/test/java/org/eclipse/jetty/test/DefaultHandlerTest.java
+++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/DefaultHandlerTest.java
@@ -18,6 +18,8 @@
 
 package org.eclipse.jetty.test;
 
+import static org.junit.Assert.*;
+
 import java.io.ByteArrayInputStream;
 import java.io.InputStream;
 import java.net.InetAddress;
@@ -27,9 +29,9 @@
 import java.util.List;
 
 import org.eclipse.jetty.http.HttpScheme;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http.HttpTester;
 import org.eclipse.jetty.test.support.TestableJettyServer;
-import org.eclipse.jetty.test.support.rawhttp.HttpRequestTester;
-import org.eclipse.jetty.test.support.rawhttp.HttpResponseTester;
 import org.eclipse.jetty.test.support.rawhttp.HttpSocketImpl;
 import org.eclipse.jetty.test.support.rawhttp.HttpTesting;
 import org.eclipse.jetty.util.IO;
@@ -55,6 +57,7 @@
         server = new TestableJettyServer();
         server.setScheme(HttpScheme.HTTP.asString());
         server.addConfiguration("DefaultHandler.xml");
+        server.addConfiguration("NIOHttp.xml");
 
         server.load();
         server.start();
@@ -107,14 +110,15 @@
         // Collect response
         String rawResponse = IO.toString(sock.getInputStream());
         DEBUG("--raw-response--\n" + rawResponse);
-        HttpResponseTester response = new HttpResponseTester();
-        response.parse(rawResponse);
+        
+        HttpTester.Response response = HttpTester.parseResponse(rawResponse);
 
-        response.assertStatusOK();
+        assertEquals(HttpStatus.OK_200, response.getStatus());
 
-        response.assertBody("ABCDEFGHIJKLMNOPQRSTUVWXYZ\n");
+        assertTrue(response.getContent().contains("ABCDEFGHIJKLMNOPQRSTUVWXYZ\n"));
     }
 
+    /*
     @Test
     public void testMultiGET_Raw() throws Exception
     {
@@ -131,38 +135,43 @@
         rawRequests.append("\r\n");
 
         HttpTesting http = new HttpTesting(new HttpSocketImpl(),serverPort);
+      
 
-        List<HttpResponseTester> responses = http.requests(rawRequests);
+        List<HttpTester.Response> responses = http.requests(rawRequests);
 
-        HttpResponseTester response = responses.get(0);
-        response.assertStatusOK();
-        response.assertBody("ABCDEFGHIJKLMNOPQRSTUVWXYZ\n");
+        HttpTester.Response response = responses.get(0);
+        assertEquals(HttpStatus.OK_200, response.getStatus());
+        assertTrue(response.getContent().contains("ABCDEFGHIJKLMNOPQRSTUVWXYZ\n"));
 
         response = responses.get(1);
-        response.assertStatusOK();
-        response.assertBody("Host=Default\nResource=R1\n");
+        assertEquals(HttpStatus.OK_200, response.getStatus()); 
+        assertTrue(response.getContent().contains("Host=Default\nResource=R1\n"));
 
         response = responses.get(2);
-        response.assertStatusOK();
-        response.assertBody("Host=Default\nResource=R1\n");
+        assertEquals(HttpStatus.OK_200, response.getStatus()); 
+        assertTrue(response.getContent().contains("Host=Default\nResource=R1\n"));
     }
+    */
+    
+    
+    
 
     @Test
     public void testGET_HttpTesting() throws Exception
     {
-        HttpRequestTester request = new HttpRequestTester();
+        HttpTester.Request request = HttpTester.newRequest();
         request.setMethod("GET");
         request.setURI("/tests/alpha.txt");
-        request.addHeader("Host","localhost");
-        request.addHeader("Connection","close");
+        request.put("Host","localhost");
+        request.put("Connection","close");
         // request.setContent(null);
 
         HttpTesting testing = new HttpTesting(new HttpSocketImpl(),serverPort);
-        HttpResponseTester response = testing.request(request);
+        HttpTester.Response response = testing.request(request);
 
-        response.assertStatusOK();
-        response.assertContentType("text/plain");
-        response.assertBody("ABCDEFGHIJKLMNOPQRSTUVWXYZ\n");
+        assertEquals(HttpStatus.OK_200, response.getStatus());
+        assertEquals("text/plain", response.get("Content-Type"));
+        assertTrue(response.getContent().contains("ABCDEFGHIJKLMNOPQRSTUVWXYZ\n"));
     }
 
     private void DEBUG(String msg)
diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/DigestPostTest.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/DigestPostTest.java
index 2432ae9..4911cd2 100644
--- a/tests/test-integration/src/test/java/org/eclipse/jetty/test/DigestPostTest.java
+++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/DigestPostTest.java
@@ -21,6 +21,7 @@
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.net.Socket;
+import java.net.URI;
 import java.security.MessageDigest;
 import java.util.Collections;
 import java.util.concurrent.TimeUnit;
@@ -230,14 +231,15 @@
         try
         {
             AuthenticationStore authStore = client.getAuthenticationStore();
-            authStore.addAuthentication(new DigestAuthentication(srvUrl, "test", "testuser", "password"));
+            authStore.addAuthentication(new DigestAuthentication(new URI(srvUrl), "test", "testuser", "password"));
             client.start();
 
             Request request = client.newRequest(srvUrl);
             request.method(HttpMethod.POST);
             request.content(new BytesContentProvider(__message.getBytes("UTF8")));
             _received=null;
-            ContentResponse response = request.send().get(5, TimeUnit.SECONDS);
+            request = request.timeout(5, TimeUnit.SECONDS);
+            ContentResponse response = request.send();
             Assert.assertEquals(__message,_received);
             Assert.assertEquals(200,response.getStatus());
         }
@@ -255,7 +257,7 @@
         try
         {
             AuthenticationStore authStore = client.getAuthenticationStore();
-            authStore.addAuthentication(new DigestAuthentication(srvUrl, "test", "testuser", "password"));   
+            authStore.addAuthentication(new DigestAuthentication(new URI(srvUrl), "test", "testuser", "password"));   
             client.start();
 
             String sent = IO.toString(new FileInputStream("src/test/resources/message.txt"));
@@ -264,7 +266,8 @@
             request.method(HttpMethod.POST);
             request.content(new StringContentProvider(sent));
             _received=null;
-            ContentResponse response = request.send().get(5, TimeUnit.SECONDS);
+            request = request.timeout(5, TimeUnit.SECONDS);
+            ContentResponse response = request.send();
            
             Assert.assertEquals(200,response.getStatus());
             Assert.assertEquals(sent,_received);
diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/jsp/JspAndDefaultWithAliasesTest.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/jsp/JspAndDefaultWithAliasesTest.java
index 82ffc33..1cf3100 100644
--- a/tests/test-integration/src/test/java/org/eclipse/jetty/test/jsp/JspAndDefaultWithAliasesTest.java
+++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/jsp/JspAndDefaultWithAliasesTest.java
@@ -30,8 +30,8 @@
 import java.util.List;
 
 import org.eclipse.jetty.security.HashLoginService;
+import org.eclipse.jetty.server.NetworkConnector;
 import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.nio.SelectChannelConnector;
 import org.eclipse.jetty.servlet.DefaultServlet;
 import org.eclipse.jetty.servlet.ServletContextHandler;
 import org.eclipse.jetty.servlet.ServletHolder;
@@ -67,6 +67,7 @@
 
         // @formatter:off
         data.add(new String[] { "false","/dump.jsp" });
+        data.add(new String[] { "false","/dump.jsp/" });
         data.add(new String[] { "true", "/dump.jsp%00" });
         data.add(new String[] { "false","/dump.jsp%00/" });
         data.add(new String[] { "false","/dump.jsp%00x/dump.jsp" });
@@ -86,10 +87,7 @@
     @BeforeClass
     public static void startServer() throws Exception
     {
-        server = new Server();
-        SelectChannelConnector connector = new SelectChannelConnector();
-        connector.setPort(0);
-        server.addConnector(connector);
+        server = new Server(0);  
 
         // Configure LoginService
         HashLoginService login = new HashLoginService();
@@ -118,8 +116,10 @@
         server.setHandler(context);
 
         server.start();
+        
+        int port = ((NetworkConnector)server.getConnectors()[0]).getLocalPort();
 
-        serverURI = new URI("http://localhost:" + connector.getLocalPort() + "/");
+        serverURI = new URI("http://localhost:" + port + "/");
     }
 
     @AfterClass
diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/jsp/JspAndDefaultWithoutAliasesTest.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/jsp/JspAndDefaultWithoutAliasesTest.java
index ab9f484..4cb6b58 100644
--- a/tests/test-integration/src/test/java/org/eclipse/jetty/test/jsp/JspAndDefaultWithoutAliasesTest.java
+++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/jsp/JspAndDefaultWithoutAliasesTest.java
@@ -30,8 +30,8 @@
 import java.util.List;
 
 import org.eclipse.jetty.security.HashLoginService;
+import org.eclipse.jetty.server.NetworkConnector;
 import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.nio.SelectChannelConnector;
 import org.eclipse.jetty.servlet.DefaultServlet;
 import org.eclipse.jetty.servlet.ServletContextHandler;
 import org.eclipse.jetty.servlet.ServletHolder;
@@ -68,6 +68,7 @@
 
         // @formatter:off
         data.add(new Object[] { "/dump.jsp" });
+        data.add(new Object[] { "/dump.jsp/" });
         data.add(new Object[] { "/dump.jsp%00" });
         data.add(new Object[] { "/dump.jsp%00x" });
         data.add(new Object[] { "/dump.jsp%00x/dump.jsp" });
@@ -87,10 +88,7 @@
     @BeforeClass
     public static void startServer() throws Exception
     {
-        server = new Server();
-        SelectChannelConnector connector = new SelectChannelConnector();
-        connector.setPort(0);
-        server.addConnector(connector);
+        server = new Server(0);
 
         // Configure LoginService
         HashLoginService login = new HashLoginService();
@@ -119,8 +117,9 @@
         server.setHandler(context);
 
         server.start();
-
-        serverURI = new URI("http://localhost:" + connector.getLocalPort() + "/");
+        
+        int port = ((NetworkConnector)server.getConnectors()[0]).getLocalPort();
+        serverURI = new URI("http://localhost:" + port + "/");
     }
 
     @AfterClass
diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/rfcs/RFC2616BaseTest.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/rfcs/RFC2616BaseTest.java
index afabaf2..f9b5754 100644
--- a/tests/test-integration/src/test/java/org/eclipse/jetty/test/rfcs/RFC2616BaseTest.java
+++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/rfcs/RFC2616BaseTest.java
@@ -33,9 +33,9 @@
 
 import org.eclipse.jetty.http.HttpFields;
 import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http.HttpTester;
 import org.eclipse.jetty.test.support.StringUtil;
 import org.eclipse.jetty.test.support.TestableJettyServer;
-import org.eclipse.jetty.test.support.rawhttp.HttpResponseTester;
 import org.eclipse.jetty.test.support.rawhttp.HttpSocket;
 import org.eclipse.jetty.test.support.rawhttp.HttpTesting;
 import org.eclipse.jetty.toolchain.test.FS;
@@ -56,8 +56,6 @@
     /** STRICT RFC TESTS */
     private static final boolean STRICT = false;
     private static TestableJettyServer server;
-    private List<HttpResponseTester> responses;
-    private HttpResponseTester response;
     private HttpTesting http;
 
     class TestFile
@@ -167,8 +165,9 @@
         req1.append("123\r\n\r\n");
         req1.append("0;\r\n\r\n");
 
-        response = http.request(req1);
-        response.assertStatus("3.6 Transfer Coding / Bad 400",HttpStatus.BAD_REQUEST_400);
+        HttpTester.Response response = http.request(req1);
+        
+        assertEquals("3.6 Transfer Coding / Bad 400",HttpStatus.BAD_REQUEST_400,response.getStatus());
     }
     
     /**
@@ -208,20 +207,20 @@
         req2.append("Connection: close\n");
         req2.append("\n");
 
-        responses = http.requests(req2);
+        List<HttpTester.Response> responses = http.requests(req2);
         Assert.assertEquals("Response Count",3,responses.size());
 
-        response = responses.get(0); // Response 1
-        response.assertStatusOK("3.6.1 Transfer Codings / Response 1 Code");
-        response.assertBody("3.6.1 Transfer Codings / Chunked String","12345\n");
+        HttpTester.Response response = responses.get(0); // Response 1
+        assertEquals("3.6.1 Transfer Codings / Response 1 Code", HttpStatus.OK_200, response.getStatus());
+        assertTrue("3.6.1 Transfer Codings / Chunked String", response.getContent().contains("12345\n"));
 
         response = responses.get(1); // Response 2
-        response.assertStatusOK("3.6.1 Transfer Codings / Response 2 Code");
-        response.assertBody("3.6.1 Transfer Codings / Chunked String","6789abcde\n");
+        assertEquals("3.6.1 Transfer Codings / Response 2 Code", HttpStatus.OK_200, response.getStatus());
+        assertTrue("3.6.1 Transfer Codings / Chunked String",response.getContent().contains("6789abcde\n"));
 
         response = responses.get(2); // Response 3
-        response.assertStatusOK("3.6.1 Transfer Codings / Response 3 Code");
-        response.assertNoBody("3.6.1 Transfer Codings / No Body");
+        assertEquals("3.6.1 Transfer Codings / Response 3 Code", HttpStatus.OK_200, response.getStatus());
+        assertTrue("3.6.1 Transfer Codings / No Body",response.getContent() == null);
     }
 
     /**
@@ -261,20 +260,20 @@
         req3.append("Connection: close\n");
         req3.append("\n");
 
-        responses = http.requests(req3);
+        List<HttpTester.Response> responses = http.requests(req3);
         Assert.assertEquals("Response Count",3,responses.size());
 
-        response = responses.get(0); // Response 1
-        response.assertStatusOK("3.6.1 Transfer Codings / Response 1 Code");
-        response.assertBody("3.6.1 Transfer Codings / Chunked String","fghIjk\n"); // Complete R1 string
+        HttpTester.Response response = responses.get(0); // Response 1
+        assertEquals("3.6.1 Transfer Codings / Response 1 Code", HttpStatus.OK_200, response.getStatus());
+        assertTrue("3.6.1 Transfer Codings / Chunked String", response.getContent().contains("fghIjk\n")); // Complete R1 string
 
         response = responses.get(1); // Response 2
-        response.assertStatusOK("3.6.1 Transfer Codings / Response 2 Code");
-        response.assertBody("3.6.1 Transfer Codings / Chunked String","lmnoPqrst\n"); // Complete R2 string
+        assertEquals("3.6.1 Transfer Codings / Response 2 Code", HttpStatus.OK_200, response.getStatus());
+        assertTrue("3.6.1 Transfer Codings / Chunked String", response.getContent().contains("lmnoPqrst\n")); // Complete R2 string
 
         response = responses.get(2); // Response 3
-        response.assertStatusOK("3.6.1 Transfer Codings / Response 3 Code");
-        response.assertNoBody("3.6.1 Transfer Codings / No Body");
+        assertEquals("3.6.1 Transfer Codings / Response 3 Code", HttpStatus.OK_200, response.getStatus());
+        assertTrue("3.6.1 Transfer Codings / No Body", response.getContent() == null);
 
     }
 
@@ -305,16 +304,16 @@
         req4.append("Connection: close\n"); // close
         req4.append("\n");
 
-        responses = http.requests(req4);
+        List<HttpTester.Response> responses = http.requests(req4);
         Assert.assertEquals("Response Count",2,responses.size());
 
-        response = responses.get(0); // Response 1
-        response.assertStatusOK("3.6.1 Transfer Codings / Response 1 Code");
-        response.assertBody("3.6.1 Transfer Codings / Chunked String","123456\n"); // Complete R1 string
+        HttpTester.Response response = responses.get(0); // Response 1
+        assertEquals("3.6.1 Transfer Codings / Response 1 Code", HttpStatus.OK_200, response.getStatus());
+        assertTrue("3.6.1 Transfer Codings / Chunked String", response.getContent().contains("123456\n")); // Complete R1 string
 
         response = responses.get(1); // Response 2
-        response.assertStatusOK("3.6.1 Transfer Codings / Response 2 Code");
-        response.assertNoBody("3.6.1 Transfer Codings / No Body");
+        assertEquals("3.6.1 Transfer Codings / Response 2 Code", HttpStatus.OK_200, response.getStatus());
+        assertTrue("3.6.1 Transfer Codings / No Body", response.getContent() == null);
     }
 
     /**
@@ -364,15 +363,15 @@
         req1.append("Connection: close\n");
         req1.append("\n");
 
-        responses = http.requests(req1);
+        List<HttpTester.Response> responses = http.requests(req1);
         Assert.assertEquals("Response Count",2,responses.size());
 
-        response = responses.get(0);
-        response.assertStatusOK("4.4.2 Message Length / Response Code");
-        response.assertBody("4.4.2 Message Length / Body","123\n");
+        HttpTester.Response response = responses.get(0);
+        assertEquals("4.4.2 Message Length / Response Code", HttpStatus.OK_200, response.getStatus());
+        assertTrue("4.4.2 Message Length / Body",response.getContent().contains("123\n"));
         response = responses.get(1);
-        response.assertStatusOK("4.4.2 Message Length / Response Code");
-        response.assertNoBody("4.4.2 Message Length / No Body");
+        assertEquals("4.4.2 Message Length / Response Code", HttpStatus.OK_200, response.getStatus());
+        assertTrue("4.4.2 Message Length / No Body", response.getContent() == null);
 
         // 4.4.3 -
         // Client - do not send 'Content-Length' if entity-length
@@ -405,11 +404,11 @@
         Assert.assertEquals("Response Count",2,responses.size());
 
         response = responses.get(0); // response 1
-        response.assertStatusOK("4.4.3 Ignore Content-Length / Response Code");
-        response.assertBody("4.4.3 Ignore Content-Length / Body","123456\n");
+        assertEquals("4.4.3 Ignore Content-Length / Response Code", HttpStatus.OK_200, response.getStatus());
+        assertTrue("4.4.3 Ignore Content-Length / Body", response.getContent().contains("123456\n"));
         response = responses.get(1); // response 2
-        response.assertStatusOK("4.4.3 Ignore Content-Length / Response Code");
-        response.assertBody("4.4.3 Ignore Content-Length / Body","7890AB\n");
+        assertEquals("4.4.3 Ignore Content-Length / Response Code", HttpStatus.OK_200, response.getStatus());
+        assertTrue("4.4.3 Ignore Content-Length / Body", response.getContent().contains("7890AB\n"));
 
         // 4.4 - Server can request valid Content-Length from client if client
         // fails to provide a Content-Length.
@@ -430,8 +429,8 @@
 
             response = http.request(req3);
 
-            response.assertStatus("4.4 Valid Content-Length Required",HttpStatus.LENGTH_REQUIRED_411);
-            response.assertNoBody("4.4 Valid Content-Length Required");
+            assertEquals("4.4 Valid Content-Length Required",HttpStatus.LENGTH_REQUIRED_411, response.getStatus());
+            assertTrue("4.4 Valid Content-Length Required", response.getContent() == null);
 
             StringBuffer req4 = new StringBuffer();
             req4.append("GET /echo/R2 HTTP/1.0\n");
@@ -441,8 +440,8 @@
 
             response = http.request(req4);
 
-            response.assertStatus("4.4 Valid Content-Length Required",HttpStatus.LENGTH_REQUIRED_411);
-            response.assertNoBody("4.4 Valid Content-Length Required");
+            assertEquals("4.4 Valid Content-Length Required",HttpStatus.LENGTH_REQUIRED_411, response.getStatus());
+            assertTrue("4.4 Valid Content-Length Required", response.getContent() == null);
         }
     }
 
@@ -462,10 +461,10 @@
         req1.append("Connection: close\n");
         req1.append("\r\n");
 
-        response = http.request(req1);
+        HttpTester.Response response = http.request(req1);
 
-        response.assertStatusOK("5.2 Default Host");
-        response.assertBodyContains("5.2 Default Host","Default DOCRoot");
+        assertEquals("5.2 Default Host", HttpStatus.OK_200, response.getStatus());
+        assertTrue("5.2 Default Host",response.getContent().contains("Default DOCRoot"));
     }
 
     /**
@@ -484,10 +483,10 @@
         req2.append("Connection: close\n");
         req2.append("\r\n");
 
-        response = http.request(req2);
+        HttpTester.Response response = http.request(req2);
 
-        response.assertStatusOK("5.2 Virtual Host");
-        response.assertBodyContains("5.2 Virtual Host","VirtualHost DOCRoot");
+        assertEquals("5.2 Virtual Host", HttpStatus.OK_200, response.getStatus());
+        assertTrue("5.2 Virtual Host",response.getContent().contains("VirtualHost DOCRoot"));
     }
 
     /**
@@ -506,10 +505,10 @@
         req3.append("Connection: close\n");
         req3.append("\n");
 
-        response = http.request(req3);
+        HttpTester.Response response = http.request(req3);
 
-        response.assertStatusOK("5.2 Virtual Host (mixed case)");
-        response.assertBodyContains("5.2 Virtual Host (mixed case)","VirtualHost DOCRoot");
+        assertEquals("5.2 Virtual Host (mixed case)", HttpStatus.OK_200, response.getStatus());
+        assertTrue("5.2 Virtual Host (mixed case)",response.getContent().contains("VirtualHost DOCRoot"));
     }
 
     /**
@@ -527,10 +526,10 @@
         req4.append("Connection: close\n");
         req4.append("\n"); // no virtual host
 
-        response = http.request(req4);
+        HttpTester.Response response = http.request(req4);
 
-        response.assertStatus("5.2 No Host",HttpStatus.BAD_REQUEST_400);
-        response.assertNoBody("5.2 No Host");
+        assertEquals("5.2 No Host",HttpStatus.BAD_REQUEST_400,response.getStatus());
+        assertEquals("5.2 No Host","", response.getContent());
     }
 
     /**
@@ -549,10 +548,10 @@
         req5.append("Connection: close\n");
         req5.append("\n");
 
-        response = http.request(req5);
+        HttpTester.Response response = http.request(req5);
 
-        response.assertStatusOK("5.2 Bad Host");
-        response.assertBodyContains("5.2 Bad Host","Default DOCRoot"); // served by default context
+        assertEquals("5.2 Bad Host",HttpStatus.OK_200, response.getStatus());
+        assertTrue("5.2 Bad Host",response.getContent().contains("Default DOCRoot")); // served by default context
     }
 
     /**
@@ -570,10 +569,10 @@
         req6.append("Connection: close\n");
         req6.append("\n");
 
-        response = http.request(req6);
+        HttpTester.Response response = http.request(req6);
 
         // No host header should always return a 400 Bad Request.
-        response.assertStatus("5.2 Virtual Host as AbsoluteURI (No Host Header / HTTP 1.1)",HttpStatus.BAD_REQUEST_400);
+        assertEquals("5.2 Virtual Host as AbsoluteURI (No Host Header / HTTP 1.1)",HttpStatus.BAD_REQUEST_400,response.getStatus());
     }
 
     /**
@@ -591,10 +590,10 @@
         req6.append("Connection: close\n");
         req6.append("\n");
 
-        response = http.request(req6);
+        HttpTester.Response response = http.request(req6);
 
-        response.assertStatusOK("5.2 Virtual Host as AbsoluteURI (No Host Header / HTTP 1.0)");
-        response.assertBodyContains("5.2 Virtual Host as AbsoluteURI (No Host Header / HTTP 1.1)","VirtualHost DOCRoot");
+        assertEquals("5.2 Virtual Host as AbsoluteURI (No Host Header / HTTP 1.0)",HttpStatus.OK_200, response.getStatus());
+        assertTrue("5.2 Virtual Host as AbsoluteURI (No Host Header / HTTP 1.1)",response.getContent().contains("VirtualHost DOCRoot"));
     }
 
     /**
@@ -613,10 +612,11 @@
         req7.append("Connection: close\n");
         req7.append("\n");
 
-        response = http.request(req7);
+        HttpTester.Response response = http.request(req7);
 
-        response.assertStatusOK("5.2 Virtual Host as AbsoluteURI (and Host header)");
-        response.assertBodyContains("5.2 Virtual Host as AbsoluteURI (and Host header)","VirtualHost DOCRoot");
+        assertEquals("5.2 Virtual Host as AbsoluteURI (and Host header)", HttpStatus.OK_200, response.getStatus());
+        System.err.println(response.getContent());
+        assertTrue("5.2 Virtual Host as AbsoluteURI (and Host header)",response.getContent().contains("VirtualHost DOCRoot"));
     }
 
     /**
@@ -633,11 +633,11 @@
         req1.append("Connection: close\n");
         req1.append("\n");
 
-        response = http.request(req1);
+        HttpTester.Response response = http.request(req1);
 
-        response.assertStatusOK("8.1 Persistent Connections");
-        response.assertHeaderExists("8.1 Persistent Connections","Content-Length");
-        response.assertBodyContains("8.1 Persistent Connections","Resource=R1");
+        assertEquals("8.1 Persistent Connections", HttpStatus.OK_200, response.getStatus());
+        assertTrue("8.1 Persistent Connections", response.get("Content-Length") != null);
+        assertTrue("8.1 Persistent Connections",response.getContent().contains("Resource=R1"));
 
         StringBuffer req2 = new StringBuffer();
         req2.append("GET /tests/R1.txt HTTP/1.1\n");
@@ -654,19 +654,19 @@
         req2.append("Connection: close\n");
         req2.append("\n");
 
-        responses = http.requests(req2);
+        List<HttpTester.Response> responses = http.requests(req2);
         Assert.assertEquals("Response Count",2,responses.size()); // Should not have a R3 response.
 
         response = responses.get(0); // response 1
-        response.assertStatusOK("8.1 Persistent Connections");
-        response.assertHeaderExists("8.1 Persistent Connections","Content-Length");
-        response.assertBodyContains("8.1 Peristent Connections","Resource=R1");
+        assertEquals("8.1 Persistent Connections", HttpStatus.OK_200, response.getStatus());
+        assertTrue("8.1 Persistent Connections",response.get("Content-Length") != null);
+        assertTrue("8.1 Peristent Connections", response.getContent().contains("Resource=R1"));
 
         response = responses.get(1); // response 2
-        response.assertStatusOK("8.1.2.2 Persistent Connections / Pipeline");
-        response.assertHeaderExists("8.1.2.2 Persistent Connections / Pipeline","Content-Length");
-        response.assertHeader("8.1.2.2 Persistent Connections / Pipeline","Connection","close");
-        response.assertBodyContains("8.1.2.2 Peristent Connections / Pipeline","Resource=R2");
+        assertEquals("8.1.2.2 Persistent Connections / Pipeline", HttpStatus.OK_200, response.getStatus());
+        assertTrue("8.1.2.2 Persistent Connections / Pipeline", response.get("Content-Length") != null);
+        assertEquals("8.1.2.2 Persistent Connections / Pipeline","close", response.get("Connection"));
+        assertTrue("8.1.2.2 Peristent Connections / Pipeline", response.getContent().contains("Resource=R2"));
     }
 
     /**
@@ -688,9 +688,9 @@
         req2.append("\n"); 
         req2.append("12345678\n"); 
 
-        response = http.request(req2);
+        HttpTester.Response response = http.request(req2);
 
-        response.assertStatus("8.2.3 expect failure",HttpStatus.EXPECTATION_FAILED_417);
+        assertEquals("8.2.3 expect failure",HttpStatus.EXPECTATION_FAILED_417, response.getStatus());
     }
 
     /**
@@ -715,9 +715,9 @@
 
         // Should only expect 1 response.
         // The existence of 2 responses usually means a bad "HTTP/1.1 100" was received.
-        response = http.request(req3);
+        HttpTester.Response response = http.request(req3);
 
-        response.assertStatusOK("8.2.3 expect 100");
+        assertEquals("8.2.3 expect 100", HttpStatus.OK_200, response.getStatus());
     }
     
     
@@ -748,19 +748,19 @@
         req3.append("\n");
         req3.append("87654321"); // Body
 
-        List<HttpResponseTester> responses = http.requests(req3);
+        List<HttpTester.Response> responses = http.requests(req3);
         
         // System.err.println(responses);
         
-        response=responses.get(0);
+        HttpTester.Response response=responses.get(0);
         // System.err.println(response.getRawResponse());
         
-        response.assertStatus("8.2.3 ignored no 100",302);
+        assertEquals("8.2.3 ignored no 100",302, response.getStatus());
         
         response=responses.get(1);
         // System.err.println(response.getRawResponse());
-        response.assertStatus("8.2.3 ignored no 100",200);
-        response.assertBody("87654321\n");
+        assertEquals("8.2.3 ignored no 100",200, response.getStatus());
+        assertTrue(response.getContent().contains("87654321\n"));
     }
 
     /**
@@ -788,14 +788,14 @@
             http.send(sock,req4);
 
             http.setTimeoutMillis(2000);
-            response = http.readAvailable(sock);
-            response.assertStatus("8.2.3 expect 100",HttpStatus.CONTINUE_100);
+            HttpTester.Response response = http.readAvailable(sock);
+            assertEquals("8.2.3 expect 100",HttpStatus.CONTINUE_100,response.getStatus());
 
             http.send(sock,"654321\n"); // Now send the data
             response = http.read(sock);
 
-            response.assertStatusOK("8.2.3 expect 100");
-            response.assertBody("8.2.3 expect 100","654321\n");
+            assertEquals("8.2.3 expect 100", HttpStatus.OK_200, response.getStatus());
+            assertTrue("8.2.3 expect 100",response.getContent().contains("654321\n"));
         }
         finally
         {
@@ -825,20 +825,20 @@
             req1.append("Host: localhost\n");
             req1.append("\n");
 
-            response = http.request(req1);
+            HttpTester.Response response = http.request(req1);
 
-            response.assertStatusOK("9.2 OPTIONS");
-            response.assertHeaderExists("9.2 OPTIONS","Allow");
+            assertEquals("9.2 OPTIONS", HttpStatus.OK_200, response.getStatus());
+            assertTrue("9.2 OPTIONS",response.get("Allow") != null);
             // Header expected ...
             // Allow: GET, HEAD, POST, PUT, DELETE, MOVE, OPTIONS, TRACE
-            String allow = response.getHeader("Allow");
+            String allow = response.get("Allow");
             String expectedMethods[] =
             { "GET", "HEAD", "POST", "PUT", "DELETE", "MOVE", "OPTIONS", "TRACE" };
             for (String expectedMethod : expectedMethods)
             {
                 assertThat(allow,containsString(expectedMethod));
             }
-            response.assertHeader("9.2 OPTIONS","Content-Length","0"); // Required if no response body.
+            assertEquals("9.2 OPTIONS","0", response.get("Content-Length")); // Required if no response body.
         }
     }
 
@@ -870,15 +870,15 @@
         req2.append("Connection: close\n"); // Close this second request
         req2.append("\n");
 
-        responses = http.requests(req2);
+        List<HttpTester.Response> responses = http.requests(req2);
 
         Assert.assertEquals("Response Count",2,responses.size()); // Should have 2 responses
 
-        response = responses.get(0); // Only interested in first response
-        response.assertHeaderExists("9.2 OPTIONS","Allow");
+        HttpTester.Response response = responses.get(0); // Only interested in first response
+        assertTrue("9.2 OPTIONS", response.get("Allow") != null);
         // Header expected ...
         // Allow: GET, HEAD, POST, TRACE, OPTIONS
-        String allow = response.getHeader("Allow");
+        String allow = response.get("Allow");
         String expectedMethods[] =
         { "GET", "HEAD", "POST", "OPTIONS", "TRACE" };
         for (String expectedMethod : expectedMethods)
@@ -886,7 +886,7 @@
             assertThat(allow,containsString(expectedMethod));
         }
 
-        response.assertHeader("9.2 OPTIONS","Content-Length","0"); // Required if no response body.
+        assertEquals("9.2 OPTIONS","0", response.get("Content-Length")); // Required if no response body.
     }
 
     /**
@@ -905,12 +905,12 @@
         req1.append("Connection: close\n");
         req1.append("\n");
 
-        response = http.request(req1);
+        HttpTester.Response response = http.request(req1);
 
-        response.assertStatusOK("9.4 GET / Response Code");
-        response.assertHeader("9.4 GET / Content Type","Content-Type","text/plain");
-        response.assertHeader("9.4 HEAD / Content Type","Content-Length","25");
-        response.assertBody("9.4 GET / Body","Host=Default\nResource=R1\n");
+        assertEquals("9.4 GET / Response Code", HttpStatus.OK_200, response.getStatus());
+        assertEquals("9.4 GET / Content Type","text/plain", response.get("Content-Type"));
+        assertEquals("9.4 HEAD / Content Type","25", response.get("Content-Length"));
+        assertTrue("9.4 GET / Body", response.getContent().contains("Host=Default\nResource=R1\n"));
 
         /* Test HEAD next. (should have no body) */
 
@@ -930,7 +930,7 @@
             String rawHeadResponse = http.readRaw(sock);
             int headResponseLength = rawHeadResponse.length();
             // Only interested in the response header from the GET request above.
-            String rawGetResponse = response.getRawResponse().toString().substring(0,headResponseLength);
+            String rawGetResponse = response.toString().substring(0,headResponseLength);
 
             // As there is a possibility that the time between GET and HEAD requests
             // can cross the second mark. (eg: GET at 11:00:00.999 and HEAD at 11:00:01.001)
@@ -968,12 +968,12 @@
         req1.append("Connection: close\n");
         req1.append("\n");
 
-        HttpResponseTester response = http.request(req1);
+        HttpTester.Response response = http.request(req1);
 
-        response.assertStatusOK("9.8 TRACE / Response Code");
-        response.assertHeader("9.8 TRACE / Content Type","Content-Type","message/http");
-        response.assertBodyContains("9.8 TRACE / echo","TRACE /rfc2616-webapp/httpmethods HTTP/1.1");
-        response.assertBodyContains("9.8 TRACE / echo","Host: localhost");
+        assertEquals("9.8 TRACE / Response Code", HttpStatus.OK_200, response.getStatus());
+        assertEquals("9.8 TRACE / Content Type", "message/http", response.get("Content-Type"));
+        assertTrue("9.8 TRACE / echo", response.getContent().contains("TRACE /rfc2616-webapp/httpmethods HTTP/1.1"));
+        assertTrue("9.8 TRACE / echo", response.getContent().contains("Host: localhost"));
     }
 
     /**
@@ -996,10 +996,10 @@
         req1.append("Connection: close\n");
         req1.append("\n");
 
-        response = http.request(req1);
+        HttpTester.Response response = http.request(req1);
 
-        boolean noRangeHasContentLocation = response.hasHeader("Content-Location");
-        boolean noRangeHasETag = response.hasHeader("ETag");
+        boolean noRangeHasContentLocation = (response.get("Content-Location") != null);
+        boolean noRangeHasETag = (response.get("ETag") != null);
 
         // now try again for the same resource but this time WITH range header
 
@@ -1012,7 +1012,7 @@
 
         response = http.request(req2);
 
-        response.assertStatus("10.2.7 Partial Content",HttpStatus.PARTIAL_CONTENT_206);
+        assertEquals("10.2.7 Partial Content",HttpStatus.PARTIAL_CONTENT_206, response.getStatus());
 
         // (point 1) A 206 response MUST contain either a Content-Range header
         // field (section 14.16) indicating the range included with this
@@ -1021,28 +1021,28 @@
         // in the response, its value MUST match the actual number of OCTETs
         // transmitted in the message-body.
 
-        if (response.hasHeader("Content-Range"))
+        if (response.get("Content-Range") != null)
         {
-            response.assertHeader("10.2.7 Partial Content / Response / Content Range","Content-Range","bytes 1-3/27");
+            assertEquals("10.2.7 Partial Content / Response / Content Range","bytes 1-3/27",response.get("Content-Range"));
         }
 
-        if (response.hasHeader("Content-Length"))
+        if (response.get("Content-Length") != null)
         {
-            response.assertHeader("10.2.7 Patial Content / Response / Content Length","Content-Length","3");
+            assertEquals("10.2.7 Patial Content / Response / Content Length","3", response.get("Content-Length"));
         }
 
         // (point 2) A 206 response MUST contain a Date header
-        response.assertHeaderExists("10.2.7 Partial Content / Response / Date","Date");
+        assertTrue("10.2.7 Partial Content / Response / Date", response.get("Date") != null);
 
         // (point 3) A 206 response MUST contain ETag and/or Content-Location,
         // if the header would have been sent in a 200 response to the same request
         if (noRangeHasContentLocation)
         {
-            response.assertHeaderExists("10.2.7 Partial Content / Content-Location","Content-Location");
+            assertTrue("10.2.7 Partial Content / Content-Location", response.get("Content-Location") != null);
         }
         if (noRangeHasETag)
         {
-            response.assertHeaderExists("10.2.7 Partial Content / Content-Location","ETag");
+            assertTrue("10.2.7 Partial Content / Content-Location", response.get("ETag") != null);
         }
 
         // (point 4) A 206 response MUST contain Expires, Cache-Control, and/or Vary,
@@ -1052,7 +1052,7 @@
         // TODO: Not sure how to test this condition.
 
         // Test the body sent
-        response.assertBody("10.2.7 Partial Content","BCD"); // should only have bytes 1-3
+        assertTrue("10.2.7 Partial Content",response.getContent().contains("BCD")); // should only have bytes 1-3
     }
 
     /**
@@ -1074,11 +1074,11 @@
         req1.append("Connection: Close\n");
         req1.append("\n");
 
-        response = http.request(req1);
+        HttpTester.Response response = http.request(req1);
 
         specId = "10.3 Redirection HTTP/1.0 - basic";
-        response.assertStatus(specId,HttpStatus.FOUND_302);
-        response.assertHeader(specId,"Location",serverURI + "/tests/");
+        assertEquals(specId,HttpStatus.FOUND_302, response.getStatus());
+        assertEquals(specId,serverURI + "/tests/", response.get("Location"));
     }
 
     /**
@@ -1101,20 +1101,19 @@
         req2.append("Connection: close\n");
         req2.append("\n");
 
-        responses = http.requests(req2);
+        List<HttpTester.Response> responses = http.requests(req2);
         Assert.assertEquals("Response Count",2,responses.size());
 
-        response = responses.get(0);
+        HttpTester.Response response = responses.get(0);
         String specId = "10.3 Redirection HTTP/1.1 - basic (response 1)";
-        response.assertStatus(specId,HttpStatus.FOUND_302);
-        response.assertHeader(specId,"Location",server.getScheme() + "://localhost/tests/");
+        assertEquals(specId,HttpStatus.FOUND_302, response.getStatus());
+        assertEquals(specId,server.getScheme() + "://localhost/tests/", response.get("Location"));
 
         response = responses.get(1);
         specId = "10.3 Redirection HTTP/1.1 - basic (response 2)";
-        response.assertStatus(specId,HttpStatus.FOUND_302);
-        response.assertHeader(specId,"Location",server.getScheme() + "://localhost/tests/");
-        response.assertHeader(specId,"Connection","close");
-
+        assertEquals(specId,HttpStatus.FOUND_302, response.getStatus());
+        assertEquals(specId,server.getScheme() + "://localhost/tests/", response.get("Location"));
+        assertEquals(specId,"close", response.get("Connection"));
     }
 
     /**
@@ -1133,11 +1132,11 @@
         req3.append("Connection: close\n");
         req3.append("\n");
 
-        response = http.request(req3);
+        HttpTester.Response response = http.request(req3);
 
         String specId = "10.3 Redirection HTTP/1.0 w/content";
-        response.assertStatus(specId,HttpStatus.FOUND_302);
-        response.assertHeader(specId,"Location",server.getScheme() + "://localhost/tests/R1.txt");
+        assertEquals(specId,HttpStatus.FOUND_302, response.getStatus());
+        assertEquals(specId,server.getScheme() + "://localhost/tests/R1.txt", response.get("Location"));
     }
 
     /**
@@ -1156,13 +1155,13 @@
         req4.append("Connection: close\n");
         req4.append("\n");
 
-        response = http.request(req4);
+        HttpTester.Response response = http.request(req4);
 
         String specId = "10.3 Redirection HTTP/1.1 w/content";
-        response.assertStatus(specId,HttpStatus.FOUND_302);
-        response.assertHeader(specId,"Location",server.getScheme() + "://localhost/tests/R2.txt");
-        response.assertHeader(specId,"Connection","close");
-        response.assertHeaderNotPresent(specId,"Content-Length");
+        assertEquals(specId,HttpStatus.FOUND_302, response.getStatus());
+        assertEquals(specId,server.getScheme() + "://localhost/tests/R2.txt", response.get("Location"));
+        assertEquals(specId,"close", response.get("Connection"));
+        assertTrue(specId,response.get("Content-Length") == null);
     }
 
     /**
@@ -1184,11 +1183,11 @@
         req1.append("Connection: close\n");
         req1.append("\n");
 
-        response = http.request(req1);
+        HttpTester.Response response = http.request(req1);
         specId = "14.3 Accept-Encoding Header";
-        response.assertStatusOK(specId);
-        response.assertHeader(specId,"Content-Encoding","gzip");
-        response.assertHeader(specId,"Content-Type","text/html");
+        assertEquals(specId, HttpStatus.OK_200, response.getStatus());
+        assertEquals(specId,"gzip", response.get("Content-Encoding"));
+        assertEquals(specId,"text/html", response.get("Content-Type"));
     }
 
     /**
@@ -1210,10 +1209,10 @@
         req1.append("Connection: close\n");
         req1.append("\n");
 
-        response = http.request(req1);
+        HttpTester.Response response = http.request(req1);
 
-        response.assertStatusOK();
-        response.assertBody(ALPHA);
+        assertEquals(HttpStatus.OK_200, response.getStatus());
+        assertTrue(response.getContent().contains(ALPHA));
     }
 
 
@@ -1229,12 +1228,12 @@
         req1.append("Connection: close\n");
         req1.append("\n");
 
-        response = http.request(req1);
+        HttpTester.Response response = http.request(req1);
 
         String msg = "Partial Range: '" + rangedef + "'";
-        response.assertStatus(msg,HttpStatus.PARTIAL_CONTENT_206);
-        response.assertHeader(msg,"Content-Range","bytes " + expectedRange);
-        response.assertBody(msg,expectedBody);
+        assertEquals(msg,HttpStatus.PARTIAL_CONTENT_206, response.getStatus());
+        assertEquals(msg,"bytes " + expectedRange, response.get("Content-Range"));
+        assertTrue(msg,response.getContent().contains(expectedBody));
     }
 
     /**
@@ -1288,12 +1287,12 @@
         req1.append("\n");
 
         http.setTimeoutMillis(60000);
-        response = http.request(req1);
+        HttpTester.Response response = http.request(req1);
 
         String msg = "Partial Range (Mixed): 'bytes=a-b,5-8'";
-        response.assertStatus(msg,HttpStatus.PARTIAL_CONTENT_206);
-        response.assertHeader(msg,"Content-Range","bytes 5-8/27");
-        response.assertBody(msg,alpha.substring(5,8 + 1));
+        assertEquals(msg,HttpStatus.PARTIAL_CONTENT_206, response.getStatus());
+        assertEquals(msg,"bytes 5-8/27", response.get("Content-Range"));
+        assertTrue(msg,response.getContent().contains(alpha.substring(5,8 + 1)));
     }
 
     /**
@@ -1328,12 +1327,12 @@
         req1.append("Connection: close\n");
         req1.append("\n");
 
-        response = http.request(req1);
+        HttpTester.Response response = http.request(req1);
 
         String msg = "Partial Range (Mixed): 'bytes=a-b,bytes=5-8'";
-        response.assertStatus(msg,HttpStatus.PARTIAL_CONTENT_206);
-        response.assertHeader(msg,"Content-Range","bytes 5-8/27");
-        response.assertBody(msg,alpha.substring(5,8 + 1));
+        assertEquals(msg,HttpStatus.PARTIAL_CONTENT_206, response.getStatus());
+        assertEquals(msg,"bytes 5-8/27", response.get("Content-Range"));
+        assertTrue(msg,response.getContent().contains(alpha.substring(5,8 + 1)));
     }
 
     /**
@@ -1369,12 +1368,12 @@
         req1.append("Connection: close\n");
         req1.append("\n");
 
-        response = http.request(req1);
+        HttpTester.Response response = http.request(req1);
 
         String msg = "Partial Range (Mixed): 'bytes=a-b' 'bytes=5-8'";
-        response.assertStatus(msg,HttpStatus.PARTIAL_CONTENT_206);
-        response.assertHeader(msg,"Content-Range","bytes 5-8/27");
-        response.assertBody(msg,alpha.substring(5,8 + 1));
+        assertEquals(msg,HttpStatus.PARTIAL_CONTENT_206, response.getStatus());
+        assertEquals(msg,"bytes 5-8/27", response.get("Content-Range"));
+        assertTrue(msg,response.getContent().contains(alpha.substring(5,8 + 1)));
     }
 
     /**
@@ -1385,8 +1384,6 @@
     @Test
     public void test14_23_Http10_NoHostHeader() throws Exception
     {
-        HttpResponseTester response;
-
         // HTTP/1.0 OK with no host
 
         StringBuffer req1 = new StringBuffer();
@@ -1394,8 +1391,8 @@
         req1.append("Connection: close\n");
         req1.append("\n");
 
-        response = http.request(req1);
-        response.assertStatusOK("14.23 HTTP/1.0 - No Host");
+        HttpTester.Response response = http.request(req1);
+        assertEquals("14.23 HTTP/1.0 - No Host", HttpStatus.OK_200, response.getStatus());
     }
 
     /**
@@ -1413,8 +1410,8 @@
         req2.append("Connection: close\n");
         req2.append("\n");
 
-        response = http.request(req2);
-        response.assertStatus("14.23 HTTP/1.1 - No Host",HttpStatus.BAD_REQUEST_400);
+        HttpTester.Response response = http.request(req2);
+        assertEquals("14.23 HTTP/1.1 - No Host",HttpStatus.BAD_REQUEST_400, response.getStatus());
     }
 
     /**
@@ -1433,8 +1430,8 @@
         req3.append("Connection: close\n");
         req3.append("\n");
 
-        response = http.request(req3);
-        response.assertStatusOK("14.23 HTTP/1.1 - Valid Host");
+        HttpTester.Response response = http.request(req3);
+        assertEquals("14.23 HTTP/1.1 - Valid Host", HttpStatus.OK_200, response.getStatus());
     }
 
     /**
@@ -1453,8 +1450,8 @@
         req4.append("Connection: close\n");
         req4.append("\n");
 
-        response = http.request(req4);
-        response.assertStatusOK("14.23 HTTP/1.1 - Empty Host");
+        HttpTester.Response response = http.request(req4);
+        assertEquals("14.23 HTTP/1.1 - Empty Host", HttpStatus.OK_200, response.getStatus());
     }
 
     /**
@@ -1476,14 +1473,14 @@
         req1.append("Connection: close\n");
         req1.append("\n");
 
-        response = http.request(req1);
+        HttpTester.Response response = http.request(req1);
 
         String msg = "Partial (Byte) Range: '" + rangedef + "'";
-        response.assertStatus(msg,HttpStatus.PARTIAL_CONTENT_206);
+        assertEquals(msg,HttpStatus.PARTIAL_CONTENT_206, response.getStatus());
         // It might be strange to see a "Content-Range' response header to a 'Range' request,
         // but this is appropriate per the RFC2616 spec.
-        response.assertHeader(msg,"Content-Range","bytes " + expectedRange);
-        response.assertBody(msg,expectedBody);
+        assertEquals(msg,"bytes " + expectedRange, response.get("Content-Range"));
+        assertTrue(msg,response.getContent().contains(expectedBody));
     }
 
     /**
@@ -1532,12 +1529,12 @@
         req1.append("Connection: close\n");
         req1.append("\n");
 
-        response = http.request(req1);
+        HttpTester.Response response = http.request(req1);
 
         String msg = "Partial (Byte) Range: '" + rangedef + "'";
-        response.assertStatus(msg,HttpStatus.PARTIAL_CONTENT_206);
+        assertEquals(msg,HttpStatus.PARTIAL_CONTENT_206, response.getStatus());
 
-        String contentType = response.getHeader("Content-Type");
+        String contentType = response.get("Content-Type");
         // RFC states that multiple parts should result in multipart/byteranges Content type.
         StringAssert.assertContains(msg + " Content-Type",contentType,"multipart/byteranges");
 
@@ -1557,20 +1554,20 @@
         Assert.assertNotNull(msg + " Should have found boundary in Content-Type header",boundary);
 
         // Find boundary offsets within body
-        List<HttpResponseTester> multiparts = response.findBodyMultiparts(boundary);
+        List<HttpTester.Response> multiparts = HttpTesting.getParts(boundary, response);
         Assert.assertEquals(msg + " multiparts in body (count)",2,multiparts.size());
 
         // Validate multipart #1
-        HttpResponseTester multipart1 = multiparts.get(0);
-        multipart1.assertHeader(msg + " Multipart 1","Content-Type","text/plain");
-        multipart1.assertHeader(msg + " Multipart 1","Content-Range","bytes 23-23/27");
-        multipart1.assertBody(msg + " Multipart 1","X");
+        HttpTester.Response multipart1 = multiparts.get(0);
+        assertEquals(msg + " Multipart 1","text/plain", multipart1.get("Content-Type"));
+        assertEquals(msg + " Multipart 1","bytes 23-23/27", multipart1.get("Content-Range"));
+        assertTrue(msg + " Multipart 1", multipart1.getContent().contains("X"));
 
         // Validate multipart #2
-        HttpResponseTester multipart2 = multiparts.get(1);
-        multipart2.assertHeader(msg + " Multipart 2","Content-Type","text/plain");
-        multipart2.assertHeader(msg + " Multipart 2","Content-Range","bytes 25-26/27");
-        multipart2.assertBody(msg + " Multipart 2","Z\n");
+        HttpTester.Response multipart2 = multiparts.get(1);
+        assertEquals(msg + " Multipart 2","text/plain", multipart2.get("Content-Type"));
+        assertEquals(msg + " Multipart 2","bytes 25-26/27", multipart2.get("Content-Range"));
+        assertTrue(msg + " Multipart 2", multipart2.getContent().contains("Z\n"));
     }
 
     /**
@@ -1593,12 +1590,12 @@
         req1.append("Connection: close\n");
         req1.append("\n");
 
-        response = http.request(req1);
+        HttpTester.Response response = http.request(req1);
 
         String msg = "Partial (Byte) Range: '" + rangedef + "'";
-        response.assertStatus(msg,HttpStatus.PARTIAL_CONTENT_206);
+        assertEquals(msg,HttpStatus.PARTIAL_CONTENT_206,response.getStatus());
 
-        String contentType = response.getHeader("Content-Type");
+        String contentType = response.get("Content-Type");
         // RFC states that multiple parts should result in multipart/byteranges Content type.
         StringAssert.assertContains(msg + " Content-Type",contentType,"multipart/byteranges");
 
@@ -1618,26 +1615,26 @@
         Assert.assertNotNull(msg + " Should have found boundary in Content-Type header",boundary);
 
         // Find boundary offsets within body
-        List<HttpResponseTester> multiparts = response.findBodyMultiparts(boundary);
+        List<HttpTester.Response> multiparts = HttpTesting.getParts(boundary, response);
         Assert.assertEquals(msg + " multiparts in body (count)",3,multiparts.size());
 
         // Validate multipart #1
-        HttpResponseTester multipart1 = multiparts.get(0);
-        multipart1.assertHeader(msg + " Multipart 1","Content-Type","text/plain");
-        multipart1.assertHeader(msg + " Multipart 1","Content-Range","bytes 26-26/27");
-        multipart1.assertBody(msg + " Multipart 1","\n");
+        HttpTester.Response multipart1 = multiparts.get(0);
+        assertEquals(msg + " Multipart 1", "text/plain", multipart1.get("Content-Type"));
+        assertEquals(msg + " Multipart 1","bytes 26-26/27", multipart1.get("Content-Range"));
+        assertTrue(msg + " Multipart 1",multipart1.getContent().contains("\n"));
 
         // Validate multipart #2
-        HttpResponseTester multipart2 = multiparts.get(1);
-        multipart2.assertHeader(msg + " Multipart 2","Content-Type","text/plain");
-        multipart2.assertHeader(msg + " Multipart 2","Content-Range","bytes 25-26/27");
-        multipart2.assertBody(msg + " Multipart 2","Z\n");
+        HttpTester.Response multipart2 = multiparts.get(1);
+        assertEquals(msg + " Multipart 2","text/plain", multipart2.get("Content-Type"));
+        assertEquals(msg + " Multipart 2","bytes 25-26/27", multipart2.get("Content-Range"));
+        assertTrue(msg + " Multipart 2", multipart2.getContent().contains("Z\n"));
 
         // Validate multipart #3
-        HttpResponseTester multipart3 = multiparts.get(2);
-        multipart3.assertHeader(msg + " Multipart 3","Content-Type","text/plain");
-        multipart3.assertHeader(msg + " Multipart 3","Content-Range","bytes 24-26/27");
-        multipart3.assertBody(msg + " Multipart 3","YZ\n");
+        HttpTester.Response multipart3 = multiparts.get(2);
+        assertEquals(msg + " Multipart 3","text/plain", multipart3.get("Content-Type"));
+        assertEquals(msg + " Multipart 3","bytes 24-26/27", multipart3.get("Content-Range"));
+        assertTrue(msg + " Multipart 3", multipart3.getContent().contains("YZ\n"));
     }
 
     /**
@@ -1671,9 +1668,9 @@
         req1.append("Connection: close\n");
         req1.append("\n");
 
-        response = http.request(req1);
+        HttpTester.Response response = http.request(req1);
 
-        response.assertStatus("BadByteRange: '" + rangedef + "'",HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE_416);
+        assertEquals("BadByteRange: '" + rangedef + "'",HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE_416, response.getStatus());
     }
 
     /**
@@ -1714,10 +1711,10 @@
             req1.append("Connection: close\n");
             req1.append("\n");
 
-            response = http.request(req1);
+            HttpTester.Response response = http.request(req1);
             specId = "14.39 TE Header";
-            response.assertStatusOK(specId);
-            response.assertHeader(specId,"Transfer-Encoding","gzip");
+            assertEquals(specId, HttpStatus.OK_200, response.getStatus());
+            assertEquals(specId,"gzip", response.get("Transfer-Encoding"));
         }
     }
 
@@ -1741,9 +1738,9 @@
             req2.append("Connection: close\n");
             req2.append("\n");
 
-            response = http.request(req2);
+            HttpTester.Response response = http.request(req2);
             specId = "14.39 TE Header";
-            response.assertStatus(specId,HttpStatus.NOT_IMPLEMENTED_501); // Error on TE (deflate not supported)
+            assertEquals(specId,HttpStatus.NOT_IMPLEMENTED_501, response.getStatus()); // Error on TE (deflate not supported)
         }
     }
 
@@ -1755,8 +1752,7 @@
     @Test
     public void test19_6() throws Exception
     {
-        List<HttpResponseTester> responses;
-        HttpResponseTester response;
+    
         String specId;
 
         /* Compatibility with HTTP/1.0 */
@@ -1765,10 +1761,10 @@
         req1.append("GET /tests/R1.txt HTTP/1.0\n");
         req1.append("\n");
 
-        response = http.request(req1);
+        HttpTester.Response response = http.request(req1);
         specId = "19.6 Compatibility with HTTP/1.0 - simple request";
-        response.assertStatusOK(specId);
-        response.assertHeaderNotPresent(specId + " - connection closed not assumed","Connection");
+        assertEquals(specId, HttpStatus.OK_200, response.getStatus());
+        assertTrue(specId + " - connection closed not assumed",response.get("Connection") == null);
 
         /* Compatibility with HTTP/1.0 */
 
@@ -1783,26 +1779,25 @@
         req2.append("Connection: close\n"); // Connection closed here
         req2.append("\n");
 
-        req2.append("GET /tests/R3.txt HTTP/1.0\n"); // This request should not
-        // be handled
+        req2.append("GET /tests/R3.txt HTTP/1.0\n"); // This request should not be handled
         req2.append("Host: localhost\n");
         req2.append("Connection: close\n");
         req2.append("\n");
 
-        responses = http.requests(req2);
+        List<HttpTester.Response> responses = http.requests(req2);
         // Since R2 closes the connection, should only get 2 responses (R1 &
         // R2), not (R3)
         Assert.assertEquals("Response Count",2,responses.size());
 
         response = responses.get(0); // response 1
         specId = "19.6.2 Compatibility with previous HTTP - Keep-alive";
-        response.assertStatusOK(specId);
-        response.assertHeader(specId,"Connection","keep-alive");
-        response.assertBodyContains(specId,"Resource=R1");
+        assertEquals(specId, HttpStatus.OK_200, response.getStatus());
+        assertEquals(specId,"keep-alive", response.get("Connection"));
+        assertTrue(specId,response.getContent().contains("Resource=R1"));
 
         response = responses.get(1); // response 2
-        response.assertStatusOK(specId);
-        response.assertBodyContains(specId,"Resource=R2");
+        assertEquals(specId, HttpStatus.OK_200, response.getStatus());
+        assertTrue(specId,response.getContent().contains("Resource=R2"));
 
         /* Compatibility with HTTP/1.0 */
 
@@ -1836,18 +1831,18 @@
 
         specId = "19.6.2 Compatibility with HTTP/1.0- Keep-alive";
         response = responses.get(0);
-        response.assertStatusOK(specId);
-        response.assertHeader(specId,"Connection","keep-alive");
-        response.assertBody(specId,"1234567890\n");
+        assertEquals(specId, HttpStatus.OK_200, response.getStatus());
+        assertEquals(specId,"keep-alive", response.get("Connection"));
+        assertTrue(specId, response.getContent().contains("1234567890\n"));
 
         response = responses.get(1);
-        response.assertStatusOK(specId);
-        response.assertHeader(specId,"Connection","keep-alive");
-        response.assertBody(specId,"ABCDEFGHIJ\n");
+        assertEquals(specId, HttpStatus.OK_200, response.getStatus());
+        assertEquals(specId, "keep-alive", response.get("Connection"));
+        assertTrue(specId,response.getContent().contains("ABCDEFGHIJ\n"));
 
         response = responses.get(2);
-        response.assertStatusOK(specId);
-        response.assertBody(specId,"Host=Default\nResource=R2\n");
+        assertEquals(specId, HttpStatus.OK_200, response.getStatus());
+        assertTrue(specId,response.getContent().contains("Host=Default\nResource=R2\n"));
     }
 
     protected void assertDate(String msg, Calendar expectedTime, long actualTime)
diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/JettyDistro.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/JettyDistro.java
new file mode 100644
index 0000000..99ce5ff
--- /dev/null
+++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/JettyDistro.java
@@ -0,0 +1,874 @@
+//
+//  ========================================================================
+//  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.test.support;
+//
+//========================================================================
+//------------------------------------------------------------------------
+//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.
+//========================================================================
+//
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileFilter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.jetty.toolchain.test.FS;
+import org.eclipse.jetty.toolchain.test.IO;
+import org.eclipse.jetty.toolchain.test.JAR;
+import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+import org.eclipse.jetty.toolchain.test.OS;
+import org.eclipse.jetty.toolchain.test.PathAssert;
+import org.eclipse.jetty.toolchain.test.TestingDir;
+import org.junit.Assert;
+
+/**
+ * Basic process based executor for using the Jetty Distribution along with custom configurations to perform basic
+ * <p>
+ * Allows for a test specific directory, that is a copied jetty-distribution, and then modified for the test specific testing required.
+ * <p>
+ * Requires that you setup the maven-dependency-plugin appropriately for the base distribution you want to use, along with any other dependencies (wars, libs,
+ * etc..) that you may need from other maven projects.
+ * <p>
+ * Maven Dependency Plugin Setup:
+ * 
+ * <pre>
+ *  &lt;project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ *    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"&gt;
+ * 
+ *   &lt;!-- Common Destination Directories --&gt;
+ * 
+ *   &lt;properties&gt;
+ *     &lt;test-wars-dir&gt;${project.build.directory}/test-wars&lt;/test-wars-dir&gt;
+ *     &lt;test-libs-dir&gt;${project.build.directory}/test-libs&lt;/test-libs-dir&gt;
+ *     &lt;test-distro-dir&gt;${project.build.directory}/test-dist&lt;/test-distro-dir&gt;
+ *   &lt;/properties&gt;
+ * 
+ *   &lt;build&gt;
+ *     &lt;plugins&gt;
+ *       &lt;plugin&gt;
+ *         &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
+ *         &lt;artifactId&gt;maven-dependency-plugin&lt;/artifactId&gt;
+ *         &lt;version&gt;2.1&lt;/version&gt;
+ *         &lt;executions&gt;
+ * 
+ *           &lt;!-- Copy LIB and WAR dependencies into place that JettyDistro can use them --&gt;
+ * 
+ *           &lt;execution&gt;
+ *             &lt;id&gt;test-lib-war-copy&lt;/id&gt;
+ *             &lt;phase&gt;process-test-resources&lt;/phase&gt;
+ *             &lt;goals&gt;
+ *               &lt;goal&gt;copy&lt;/goal&gt;
+ *             &lt;/goals&gt;
+ *             &lt;configuration&gt;
+ *               &lt;artifactItems&gt;
+ *                 &lt;artifactItem&gt;
+ *                   &lt;groupId&gt;org.mortbay.jetty.testwars&lt;/groupId&gt;
+ *                   &lt;artifactId&gt;test-war-java_util_logging&lt;/artifactId&gt;
+ *                   &lt;version&gt;7.3.0&lt;/version&gt;
+ *                   &lt;type&gt;war&lt;/type&gt;
+ *                   &lt;outputDirectory&gt;${test-wars-dir}&lt;/outputDirectory&gt;
+ *                 &lt;/artifactItem&gt;
+ *                 &lt;artifactItem&gt;
+ *                   &lt;groupId&gt;org.mortbay.jetty&lt;/groupId&gt;
+ *                   &lt;artifactId&gt;jetty-aspect-servlet-api-2.5&lt;/artifactId&gt;
+ *                   &lt;version&gt;7.3.0&lt;/version&gt;
+ *                   &lt;type&gt;jar&lt;/type&gt;
+ *                   &lt;outputDirectory&gt;${test-libs-dir}&lt;/outputDirectory&gt;
+ *                 &lt;/artifactItem&gt;
+ *               &lt;/artifactItems&gt;
+ *               &lt;overWriteIfNewer&gt;true&lt;/overWriteIfNewer&gt;
+ *               &lt;overWrite&gt;true&lt;/overWrite&gt;
+ *               &lt;stripVersion&gt;true&lt;/stripVersion&gt;
+ *             &lt;/configuration&gt;
+ *           &lt;/execution&gt;
+ * 
+ *           &lt;!-- Extract Jetty DISTRIBUTION into place that JettyDistro can use it --&gt;
+ * 
+ *           &lt;execution&gt;
+ *             &lt;id&gt;unpack-test-dist&lt;/id&gt;
+ *             &lt;phase&gt;process-test-resources&lt;/phase&gt;
+ *             &lt;goals&gt;
+ *               &lt;goal&gt;unpack&lt;/goal&gt;
+ *             &lt;/goals&gt;
+ *             &lt;configuration&gt;
+ *               &lt;artifactItems&gt;
+ *                 &lt;artifactItem&gt;
+ *                   &lt;groupId&gt;org.eclipse.jetty&lt;/groupId&gt;
+ *                   &lt;artifactId&gt;jetty-distribution&lt;/artifactId&gt;
+ *                   &lt;version&gt;7.3.0&lt;/version&gt;
+ *                   &lt;type&gt;zip&lt;/type&gt;
+ *                   &lt;overWrite&gt;true&lt;/overWrite&gt;
+ *                 &lt;/artifactItem&gt;
+ *               &lt;/artifactItems&gt;
+ *               &lt;outputAbsoluteArtifactFilename&gt;true&lt;/outputAbsoluteArtifactFilename&gt;
+ *               &lt;outputDirectory&gt;${test-distro-dir}&lt;/outputDirectory&gt;
+ *               &lt;overWriteSnapshots&gt;true&lt;/overWriteSnapshots&gt;
+ *               &lt;overWriteIfNewer&gt;true&lt;/overWriteIfNewer&gt;
+ *             &lt;/configuration&gt;
+ *           &lt;/execution&gt;
+ *         &lt;/executions&gt;
+ *       &lt;/plugin&gt;
+ *     &lt;/plugins&gt;
+ *   &lt;/build&gt;
+ * 
+ * &lt;/project&gt;
+ * </pre>
+ * <p>
+ * If you have a specific configuration you want to setup, you'll want to prepare this configuration in an overlay directory underneath the
+ * <code>src/test/resources/</code> directory. <br>
+ * Notes:
+ * <ol>
+ * <li>The {@link JettyDistro} sets up a unique test directory (based on the constructor {@link #JettyDistro(Class)} or {@link #JettyDistro(TestingDir)}), by
+ * ensuring the directory is empty, then copying the <code>target/test-dist</code> directory into this new testing directory prior to the test specific changes
+ * to the configuration.<br>
+ * Note: this testing directory is a complete jetty distribution, suitable for executing via the command line for additional testing needs.</li>
+ * <li>The directory name you choose in <code>src/test/resources</code> will be the name you use in the {@link #overlayConfig(String)} method to provide
+ * replacement configurations for the Jetty Distribution.</li>
+ * <li>You'll want to {@link #delete(String)} any files and/or directories from the standard distribution prior to using the {@link #overlayConfig(String)}
+ * method.</li>
+ * <li>Use the {@link #copyLib(String, String)} method to copy JAR files from the <code>target/test-libs</code> directory (created and managed above using the
+ * <code>maven-dependency-plugin</code>) to copy the lib into the test specific.</li>
+ * <li>Use the {@link #copyTestWar(String)} method to copy WAR files from the <code>target/test-wars</code> directory (created and managed above using the
+ * <code>maven-dependency-plugin</code>) to copy the WAR into the test specific directory.</li>
+ * </ol>
+ * <p>
+ * Next you'll want to use Junit 4.8+ and the <code>&#064;BeforeClass</code> and <code>&#064;AfterClass</code> annotations to setup the <code>JettyDistro</code>
+ * class for setting up your testing configuration.
+ * <p>
+ * Example Test Case using {@link JettyDistro} class
+ * 
+ * <pre>
+ * public class MySampleTest
+ * {
+ *     private static JettyDistro jetty;
+ * 
+ *     &#064;BeforeClass
+ *     public static void initJetty() throws Exception
+ *     {
+ *         jetty = new JettyDistro(MySampleTest.class);
+ * 
+ *         jetty.copyTestWar(&quot;test-war-java_util_logging.war&quot;);
+ *         jetty.copyTestWar(&quot;test-war-policy.war&quot;);
+ * 
+ *         jetty.delete(&quot;webapps/test.war&quot;);
+ *         jetty.delete(&quot;contexts/test.d&quot;);
+ *         jetty.delete(&quot;contexts/javadoc.xml&quot;);
+ *         jetty.delete(&quot;contexts/test.xml&quot;);
+ * 
+ *         jetty.overlayConfig(&quot;no_security&quot;);
+ * 
+ *         jetty.setDebug(true);
+ * 
+ *         jetty.start();
+ *     }
+ * 
+ *     &#064;AfterClass
+ *     public static void shutdownJetty() throws Exception
+ *     {
+ *         if (jetty != null)
+ *         {
+ *             jetty.stop();
+ *         }
+ *     }
+ * 
+ *     &#064;Test
+ *     public void testRequest() throws Exception
+ *     {
+ *         SimpleRequest request = new SimpleRequest(jetty.getBaseUri());
+ *         String path = &quot;/test-war-policy/security/PRACTICAL/testFilsystem&quot;);
+ *         String response = request.getString(path);
+ *         Assert.assertEquals(&quot;Success&quot;, response);
+ *     }
+ * }
+ * </pre>
+ */
+public class JettyDistro
+{
+    private String artifactName = "jetty-distribution";
+    private long startTime = 60;
+    private TimeUnit timeUnit = TimeUnit.SECONDS;
+
+    private File jettyHomeDir;
+    private Process pid;
+    private URI baseUri;
+
+    private String jmxUrl;
+
+    private boolean _debug = false;
+
+    /**
+     * Setup the JettyHome as belonging in a testing directory associated with a testing clazz.
+     * 
+     * @param clazz
+     *            the testing class using this JettyDistro
+     * @throws IOException
+     *             if unable to copy unpacked distribution into place for the provided testing directory
+     */
+    public JettyDistro(Class<?> clazz) throws IOException
+    {
+        this(clazz,null);
+    }
+
+    /**
+     * Setup the JettyHome as belonging in a testing directory associated with a testing clazz.
+     * 
+     * @param clazz
+     *            the testing class using this JettyDistro
+     * @param artifact
+     *            name of jetty distribution artifact
+     * @throws IOException
+     *             if unable to copy unpacked distribution into place for the provided testing directory
+     */
+    public JettyDistro(Class<?> clazz, String artifact) throws IOException
+    {
+        this.jettyHomeDir = MavenTestingUtils.getTargetTestingDir(clazz,"jettyHome");
+        if (artifact != null)
+        {
+            this.artifactName = artifact;
+        }
+
+        copyBaseDistro();
+    }
+
+    /**
+     * Setup the JettyHome as belonging to a specific testing method directory
+     * 
+     * @param testdir
+     *            the testing directory to use as the JettyHome for this JettyDistro
+     * @throws IOException
+     *             if unable to copy unpacked distribution into place for the provided testing directory
+     */
+    public JettyDistro(TestingDir testdir) throws IOException
+    {
+        this.jettyHomeDir = testdir.getDir();
+        copyBaseDistro();
+    }
+
+    /**
+     * Setup the JettyHome as belonging to a specific testing method directory
+     * 
+     * @param testdir
+     *            the testing directory to use as the JettyHome for this JettyDistro
+     * @param artifact
+     *            name of jetty distribution artifact
+     * @throws IOException
+     *             if unable to copy unpacked distribution into place for the provided testing directory
+     */
+    public JettyDistro(TestingDir testdir, String artifact) throws IOException
+    {
+        this.jettyHomeDir = testdir.getDir();
+        if (artifact != null)
+        {
+            this.artifactName = artifact;
+        }
+
+        copyBaseDistro();
+    }
+
+    /**
+     * 
+     * @throws IOException
+     *             if unable to copy unpacked distribution into place for the provided testing directory
+     */
+    private void copyBaseDistro() throws IOException
+    {
+        // The outputDirectory for the maven side dependency:unpack goal.
+        File distroUnpackDir = MavenTestingUtils.getTargetFile("test-dist");
+        PathAssert.assertDirExists(artifactName + " dependency:unpack",distroUnpackDir);
+
+        // The actual jetty-distribution-${version} directory is under this directory.
+        // Lets find it.
+        File subdirs[] = distroUnpackDir.listFiles(new FileFilter()
+        {
+            public boolean accept(File path)
+            {
+                if (!path.isDirectory())
+                {
+                    return false;
+                }
+
+                return path.getName().startsWith(artifactName + "-");
+            }
+        });
+
+        if (subdirs.length == 0)
+        {
+            // No jetty-distribution found.
+            StringBuilder err = new StringBuilder();
+            err.append("No target/test-dist/");
+            err.append(artifactName);
+            err.append("-${version} directory found.");
+            err.append("\n  To fix this, run 'mvn process-test-resources' to create the directory.");
+            throw new IOException(err.toString());
+        }
+
+        if (subdirs.length != 1)
+        {
+            // Too many jetty-distributions found.
+            StringBuilder err = new StringBuilder();
+            err.append("Too many target/test-dist/");
+            err.append(artifactName);
+            err.append("-${version} directories found.");
+            for (File dir : subdirs)
+            {
+                err.append("\n  ").append(dir.getAbsolutePath());
+            }
+            err.append("\n  To fix this, run 'mvn clean process-test-resources' to recreate the target/test-dist directory.");
+            throw new IOException(err.toString());
+        }
+
+        File distroSrcDir = subdirs[0];
+        FS.ensureEmpty(jettyHomeDir);
+        System.out.printf("Copying Jetty Distribution: %s%n",distroSrcDir.getAbsolutePath());
+        System.out.printf("            To Testing Dir: %s%n",jettyHomeDir.getAbsolutePath());
+        IO.copyDir(distroSrcDir,jettyHomeDir);
+    }
+
+    /**
+     * Return the $(jetty.home) directory being used for this JettyDistro
+     * 
+     * @return the jetty.home directory being used
+     */
+    public File getJettyHomeDir()
+    {
+        return this.jettyHomeDir;
+    }
+
+    /**
+     * Copy a war file from ${project.basedir}/target/test-wars/${testWarFilename} into the ${jetty.home}/webapps/ directory
+     * 
+     * @param testWarFilename
+     *            the war file to copy (must exist)
+     * @throws IOException
+     *             if unable to copy the war file.
+     */
+    public void copyTestWar(String testWarFilename) throws IOException
+    {
+        File srcWar = MavenTestingUtils.getTargetFile("test-wars/" + testWarFilename);
+        File destWar = new File(jettyHomeDir,OS.separators("webapps/" + testWarFilename));
+        FS.ensureDirExists(destWar.getParentFile());
+        IO.copyFile(srcWar,destWar);
+    }
+
+    /**
+     * Copy an arbitrary file from <code>src/test/resources/${resourcePath}</code> to the testing directory.
+     * 
+     * @param resourcePath
+     *            the relative path for file content within the <code>src/test/resources</code> directory.
+     * @param outputPath
+     *            the testing directory relative output path for the file output (will result in a file with the outputPath name being created)
+     * @throws IOException
+     *             if unable to copy resource file
+     */
+    public void copyResource(String resourcePath, String outputPath) throws IOException
+    {
+        File srcFile = MavenTestingUtils.getTestResourceFile(resourcePath);
+        File destFile = new File(jettyHomeDir,OS.separators(outputPath));
+        FS.ensureDirExists(destFile.getParentFile());
+        IO.copyFile(srcFile,destFile);
+    }
+
+    /**
+     * Copy an arbitrary file from <code>target/test-libs/${libFilename}</code> to the testing directory.
+     * 
+     * @param libFilename
+     *            the <code>target/test-libs/${libFilename}</code> to copy
+     * @param outputPath
+     *            the destination testing directory relative output path for the lib. (will result in a file with the outputPath name being created)
+     * @throws IOException
+     *             if unable to copy lib
+     */
+    public void copyLib(String libFilename, String outputPath) throws IOException
+    {
+        File srcLib = MavenTestingUtils.getTargetFile("test-libs/" + libFilename);
+        File destLib = new File(jettyHomeDir,OS.separators(outputPath));
+        FS.ensureDirExists(destLib.getParentFile());
+        IO.copyFile(srcLib,destLib);
+    }
+
+    /**
+     * Copy the <code>${project.basedir}/src/main/config/</code> tree into the testing directory.
+     * 
+     * @throws IOException
+     *             if unable to copy the directory tree
+     */
+    public void copyProjectMainConfig() throws IOException
+    {
+        File srcDir = MavenTestingUtils.getProjectDir("src/main/config");
+        IO.copyDir(srcDir,jettyHomeDir);
+    }
+
+    /**
+     * Create a <code>${jetty.home}/lib/self/${jarFilename}</code> jar file from the content in the <code>${project.basedir}/target/classes/</code> directory.
+     * 
+     * @throws IOException
+     *             if unable to copy the directory tree
+     */
+    public void createProjectLib(String jarFilename) throws IOException
+    {
+        File srcDir = MavenTestingUtils.getTargetFile("classes");
+        File libSelfDir = new File(jettyHomeDir,OS.separators("lib/self"));
+        FS.ensureDirExists(libSelfDir);
+        File jarFile = new File(libSelfDir,jarFilename);
+        JAR.create(srcDir,jarFile);
+    }
+
+    /**
+     * Unpack an arbitrary config from <code>target/test-configs/${configFilename}</code> to the testing directory.
+     * 
+     * @param configFilename
+     *            the <code>target/test-configs/${configFilename}</code> to copy
+     * @throws IOException
+     *             if unable to unpack config file
+     */
+    public void unpackConfig(String configFilename) throws IOException
+    {
+        File srcConfig = MavenTestingUtils.getTargetFile("test-configs/" + configFilename);
+        JAR.unpack(srcConfig,jettyHomeDir);
+    }
+
+    /**
+     * Delete a File or Directory found in the ${jetty.home} directory.
+     * 
+     * @param path
+     *            the path to delete. (can be a file or directory)
+     */
+    public void delete(String path)
+    {
+        File jettyPath = new File(jettyHomeDir,OS.separators(path));
+        FS.delete(jettyPath);
+    }
+
+    /**
+     * Return the baseUri being used for this Jetty Process Instance.
+     * 
+     * @return the base URI for this Jetty Process Instance.
+     */
+    public URI getBaseUri()
+    {
+        return this.baseUri;
+    }
+
+    /**
+     * Return the JMX URL being used for this Jetty Process Instance.
+     * 
+     * @return the JMX URL for this Jetty Process Instance.
+     */
+    public String getJmxUrl()
+    {
+        return this.jmxUrl;
+    }
+
+    /**
+     * Take the directory contents from ${project.basedir}/src/test/resources/${testConfigName}/ and copy it over whatever happens to be at ${jetty.home}
+     * 
+     * @param testConfigName
+     *            the src/test/resources/ directory name to use as the source diretory for the configuration we are interested in.
+     * @throws IOException
+     *             if unable to copy directory.
+     */
+    public void overlayConfig(String testConfigName) throws IOException
+    {
+        File srcDir = MavenTestingUtils.getTestResourceDir(testConfigName);
+        IO.copyDir(srcDir,jettyHomeDir);
+    }
+
+    /**
+     * Start the jetty server
+     * 
+     * @throws IOException
+     *             if unable to start the server.
+     */
+    public void start() throws IOException
+    {
+        List<String> commands = new ArrayList<String>();
+        commands.add(getJavaBin());
+
+        commands.add("-Djetty.home=" + jettyHomeDir.getAbsolutePath());
+
+        // Do a dry run first to get the exact command line for Jetty process
+        commands.add("-jar");
+        commands.add("start.jar");
+        commands.add("jetty.port=0");
+        if (_debug)
+        {
+            commands.add("-D.DEBUG=true");
+        }
+        commands.add("--dry-run");
+
+        ProcessBuilder pbCmd = new ProcessBuilder(commands);
+        pbCmd.directory(jettyHomeDir);
+
+        String cmdLine = null;
+        Process pidCmd = pbCmd.start();
+        try
+        {
+            cmdLine = readOutputLine(pidCmd);
+        }
+        finally
+        {
+            pidCmd.destroy();
+        }
+
+        if (cmdLine == null || !cmdLine.contains("XmlConfiguration"))
+        {
+            Assert.fail("Unable to get Jetty command line");
+        }
+
+        // Need to breakdown commandline into parts, as spaces in command line will cause failures.
+        List<String> execCommands = splitAndUnescapeCommandLine(cmdLine);
+
+        System.out.printf("Executing: %s%n",cmdLine);
+        System.out.printf("Working Dir: %s%n",jettyHomeDir.getAbsolutePath());
+
+        pbCmd = new ProcessBuilder(execCommands);
+        pid = pbCmd.start();
+
+        ConsoleParser parser = new ConsoleParser();
+        List<String[]> jmxList = parser.newPattern("JMX Remote URL: (.*)",0);
+        List<String[]> connList = parser.newPattern("Started [A-Za-z]*Connector@([0-9]*\\.[0-9]*\\.[0-9]*\\.[0-9]*):([0-9]*)",1);
+        // DISABLED: This is what exists in Jetty 9+
+        // List<String[]> connList = parser.newPattern("Started [A-Za-z]*Connector@.*[\\({]([0-9]*\\.[0-9]*\\.[0-9]*\\.[0-9]*):([0-9]*)[\\)}].*",1);
+
+        startPump("STDOUT",parser,this.pid.getInputStream());
+        startPump("STDERR",parser,this.pid.getErrorStream());
+
+        try
+        {
+            parser.waitForDone(this.startTime,this.timeUnit);
+
+            if (!jmxList.isEmpty())
+            {
+                this.jmxUrl = jmxList.get(0)[0];
+                System.out.printf("## Found JMX connector at %s%n",this.jmxUrl);
+            }
+
+            if (!connList.isEmpty())
+            {
+                String[] params = connList.get(0);
+                if (params.length == 2)
+                {
+                    this.baseUri = URI.create("http://localhost:" + params[1] + "/");
+                }
+                System.out.printf("## Found Jetty connector at host: %s port: %s%n",(Object[])params);
+            }
+
+        }
+        catch (InterruptedException e)
+        {
+            pid.destroy();
+            Assert.fail("Unable to get required information within time limit");
+        }
+    }
+
+    public static List<String> splitAndUnescapeCommandLine(CharSequence rawCmdLine)
+    {
+        List<String> cmds = new ArrayList<String>();
+
+        int len = rawCmdLine.length();
+        StringBuilder arg = new StringBuilder();
+        boolean escaped = false;
+        boolean inQuote = false;
+        char c;
+        for (int i = 0; i < len; i++)
+        {
+            c = rawCmdLine.charAt(i);
+            if (escaped)
+            {
+                switch (c)
+                {
+                    case 'r':
+                        arg.append('\r');
+                        break;
+                    case 'f':
+                        arg.append('\f');
+                        break;
+                    case 't':
+                        arg.append('\t');
+                        break;
+                    case 'n':
+                        arg.append('\n');
+                        break;
+                    case 'b':
+                        arg.append('\b');
+                        break;
+                    default:
+                        arg.append(c);
+                        break;
+                }
+                escaped = false;
+                continue;
+            }
+
+            if (c == '\\')
+            {
+                escaped = true;
+            }
+            else
+            {
+                if ((c == ' ') && (!inQuote))
+                {
+                    // the delim!
+                    cmds.add(String.valueOf(arg.toString()));
+                    arg.setLength(0);
+                }
+                else if (c == '"')
+                {
+                    inQuote = !inQuote;
+                }
+                else
+                {
+                    arg.append(c);
+                }
+            }
+        }
+        cmds.add(String.valueOf(arg.toString()));
+
+        return cmds;
+    }
+
+    private String readOutputLine(Process pidCmd) throws IOException
+    {
+        InputStream in = null;
+        InputStreamReader reader = null;
+        BufferedReader buf = null;
+        try
+        {
+            in = pidCmd.getInputStream();
+            reader = new InputStreamReader(in);
+            buf = new BufferedReader(reader);
+            return buf.readLine();
+        }
+        finally
+        {
+            IO.close(buf);
+            IO.close(reader);
+            IO.close(in);
+        }
+    }
+
+    private static class ConsoleParser
+    {
+        private List<ConsolePattern> patterns = new ArrayList<ConsolePattern>();
+        private CountDownLatch latch;
+        private int count;
+
+        public List<String[]> newPattern(String exp, int cnt)
+        {
+            ConsolePattern pat = new ConsolePattern(exp,cnt);
+            patterns.add(pat);
+            count += cnt;
+
+            return pat.getMatches();
+        }
+
+        public void parse(String line)
+        {
+            for (ConsolePattern pat : patterns)
+            {
+                Matcher mat = pat.getMatcher(line);
+                if (mat.find())
+                {
+                    int num = 0, count = mat.groupCount();
+                    String[] match = new String[count];
+                    while (num++ < count)
+                    {
+                        match[num - 1] = mat.group(num);
+                    }
+                    pat.getMatches().add(match);
+
+                    if (pat.getCount() > 0)
+                    {
+                        getLatch().countDown();
+                    }
+                }
+            }
+        }
+
+        public void waitForDone(long timeout, TimeUnit unit) throws InterruptedException
+        {
+            getLatch().await(timeout,unit);
+        }
+
+        private CountDownLatch getLatch()
+        {
+            synchronized (this)
+            {
+                if (latch == null)
+                {
+                    latch = new CountDownLatch(count);
+                }
+            }
+
+            return latch;
+        }
+    }
+
+    private static class ConsolePattern
+    {
+        private Pattern pattern;
+        private List<String[]> matches;
+        private int count;
+
+        ConsolePattern(String exp, int cnt)
+        {
+            pattern = Pattern.compile(exp);
+            matches = new ArrayList<String[]>();
+            count = cnt;
+        }
+
+        public Matcher getMatcher(String line)
+        {
+            return pattern.matcher(line);
+        }
+
+        public List<String[]> getMatches()
+        {
+            return matches;
+        }
+
+        public int getCount()
+        {
+            return count;
+        }
+    }
+
+    private void startPump(String mode, ConsoleParser parser, InputStream inputStream)
+    {
+        ConsoleStreamer pump = new ConsoleStreamer(mode,inputStream);
+        pump.setParser(parser);
+        Thread thread = new Thread(pump,"ConsoleStreamer/" + mode);
+        thread.start();
+    }
+
+    /**
+     * enable debug on the jetty process
+     * 
+     * @param debug
+     */
+    public void setDebug(boolean debug)
+    {
+        _debug = debug;
+    }
+
+    private String getJavaBin()
+    {
+        String javaexes[] = new String[]
+        { "java", "java.exe" };
+
+        File javaHomeDir = new File(System.getProperty("java.home"));
+        for (String javaexe : javaexes)
+        {
+            File javabin = new File(javaHomeDir,OS.separators("bin/" + javaexe));
+            if (javabin.exists() && javabin.isFile())
+            {
+                return javabin.getAbsolutePath();
+            }
+        }
+
+        Assert.fail("Unable to find java bin");
+        return "java";
+    }
+
+    /**
+     * Stop the jetty server
+     */
+    public void stop()
+    {
+        System.out.println("Stopping JettyDistro ...");
+        if (pid != null)
+        {
+            // TODO: maybe issue a STOP instead?
+            pid.destroy();
+        }
+    }
+
+    /**
+     * Simple streamer for the console output from a Process
+     */
+    private static class ConsoleStreamer implements Runnable
+    {
+        private String mode;
+        private BufferedReader reader;
+        private ConsoleParser parser;
+
+        public ConsoleStreamer(String mode, InputStream is)
+        {
+            this.mode = mode;
+            this.reader = new BufferedReader(new InputStreamReader(is));
+        }
+
+        public void setParser(ConsoleParser connector)
+        {
+            this.parser = connector;
+        }
+
+        public void run()
+        {
+            String line;
+            // System.out.printf("ConsoleStreamer/%s initiated%n",mode);
+            try
+            {
+                while ((line = reader.readLine()) != (null))
+                {
+                    if (parser != null)
+                    {
+                        parser.parse(line);
+                    }
+                    System.out.println("[" + mode + "] " + line);
+                }
+            }
+            catch (IOException ignore)
+            {
+                /* ignore */
+            }
+            finally
+            {
+                IO.close(reader);
+            }
+            // System.out.printf("ConsoleStreamer/%s finished%n",mode);
+        }
+    }
+
+    public void setStartTime(long startTime, TimeUnit timeUnit)
+    {
+        this.startTime = startTime;
+        this.timeUnit = timeUnit;
+    }
+}
\ No newline at end of file
diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/TestableJettyServer.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/TestableJettyServer.java
index cf3b035..7b1e911 100644
--- a/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/TestableJettyServer.java
+++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/TestableJettyServer.java
@@ -120,6 +120,7 @@
         for (int i = 0; i < this._xmlConfigurations.size(); i++)
         {
             URL configURL = this._xmlConfigurations.get(i);
+            System.err.println("configuring: "+configURL);
             XmlConfiguration configuration = new XmlConfiguration(configURL);
             if (last != null)
             {
@@ -176,18 +177,8 @@
         _server.start();
 
         // Find the active server port.
-        this._serverPort = (-1);
-        Connector connectors[] = _server.getConnectors();
-        for (int i = 0; i < connectors.length; i++)
-        {
-            NetworkConnector connector = (NetworkConnector)connectors[i];
-            if (connector.getLocalPort() > 0)
-            {
-                this._serverPort = connector.getLocalPort();
-                break;
-            }
-        }
-
+        this._serverPort = ((NetworkConnector)_server.getConnectors()[0]).getPort();
+        System.err.println("Server Port="+_serverPort);
         Assert.assertTrue("Server Port is between 1 and 65535. Actually <" + _serverPort + ">",(1 <= this._serverPort) && (this._serverPort <= 65535));
     }
 
diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/rawhttp/HttpRequestTester.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/rawhttp/HttpRequestTester.java
deleted file mode 100644
index 0145e20..0000000
--- a/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/rawhttp/HttpRequestTester.java
+++ /dev/null
@@ -1,235 +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.test.support.rawhttp;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-
-import javax.servlet.http.Cookie;
-
-import org.eclipse.jetty.http.HttpFields;
-import org.eclipse.jetty.http.HttpGenerator;
-import org.eclipse.jetty.http.HttpVersion;
-import org.eclipse.jetty.http.MimeTypes;
-import org.eclipse.jetty.util.BufferUtil;
-
-
-/**
- * Assist in Generating Proper Raw HTTP Requests. If you want ultimate control
- * over the Raw HTTP Request, to test non-standard behavior, or partial HTTP
- * Requests, do not use this class.
- * 
- * <pre>
- * HttpRequestTester request = new HttpRequestTester();
- * 
- * request.setMethod(&quot;GET&quot;);
- * request.setURI(&quot;/uri&quot;);
- * request.setHost(&quot;fakehost&quot;);
- * request.setConnectionClosed();
- * 
- * String rawRequest = request.generate();
- * 
- * System.out.println(&quot;--raw-request--\n&quot; + rawRequest);
- * </pre>
- * 
- * <pre>
- * --raw-request--
- * GET /uri HTTP/1.1
- * Host: fakehost
- * Connection: close
- * </pre>
- */
-public class HttpRequestTester
-{
-    private HttpFields fields = new HttpFields();
-    private String method;
-    private String uri;
-    private String version;
-    private byte[] content;
-    private String charset;
-    private String defaultCharset;
-    private String contentType;
-
-    public HttpRequestTester()
-    {
-        this("UTF-8");
-    }
-
-    public HttpRequestTester(String defCharset)
-    {
-        this.defaultCharset = defCharset;
-    }
-
-    public String getMethod()
-    {
-        return method;
-    }
-
-    public void setHost(String host)
-    {
-        addHeader("Host",host);
-    }
-
-    public void setMethod(String method)
-    {
-        this.method = method;
-    }
-
-    public String getURI()
-    {
-        return uri;
-    }
-
-    public void setURI(String uri)
-    {
-        this.uri = uri;
-    }
-
-    public String getVersion()
-    {
-        return version;
-    }
-
-    public void setVersion(String version)
-    {
-        this.version = version;
-    }
-
-    public String getCharset()
-    {
-        return charset;
-    }
-
-    public void setCharset(String charset)
-    {
-        this.charset = charset;
-    }
-
-    public String getContentType()
-    {
-        return contentType;
-    }
-
-    public void setContentType(String contentType)
-    {
-        this.contentType = contentType;
-    }
-
-    public void setConnectionClosed()
-    {
-        fields.add("Connection","close");
-    }
-
-    /**
-     * @param name
-     * @param value
-     * @throws IllegalArgumentException
-     * @see org.eclipse.jetty.http.HttpFields#add(java.lang.String,
-     *      java.lang.String)
-     */
-    public void addHeader(String name, String value) throws IllegalArgumentException
-    {
-        fields.add(name,value);
-    }
-
-    /**
-     * @param name
-     * @param date
-     * @see org.eclipse.jetty.http.HttpFields#addDateField(java.lang.String,
-     *      long)
-     */
-    public void addDateHeader(String name, long date)
-    {
-        fields.addDateField(name,date);
-    }
-
-
-    /**
-     * @param cookie
-     * @see org.eclipse.jetty.http.HttpFields#addSetCookie(org.eclipse.jetty.http.HttpCookie)
-     */
-    public void addSetCookie(Cookie cookie)
-    {
-        fields.addSetCookie(cookie.getName(),cookie.getValue(),cookie.getDomain(),cookie.getPath(),cookie.getMaxAge(),cookie.getComment(),cookie.getSecure(),
-                false,cookie.getVersion());
-    }
-
-    public String generate() throws IOException
-    {
-        
-        ByteArrayOutputStream out = new ByteArrayOutputStream();
-        ByteBuffer header = null;
-        ByteBuffer chunk = null;
-        ByteBuffer content = null;
-        HttpVersion httpVersion = null;
-        if (version == null)
-        {
-            httpVersion = HttpVersion.HTTP_1_1;
-        }
-        else
-        {
-            httpVersion = httpVersion.fromString(version);
-        }
-        
-        HttpGenerator.RequestInfo info = new HttpGenerator.RequestInfo(httpVersion,fields,0,method,uri);
-
-        HttpGenerator generator = new HttpGenerator();
-        loop: while(!generator.isEnd())
-        {
-            HttpGenerator.Result result =  generator.generateRequest(info, header, chunk, content, true);
-            switch(result)
-            {
-                case NEED_HEADER:
-                    header=BufferUtil.allocate(8192);
-                    continue;
-
-                case NEED_CHUNK:
-                    chunk=BufferUtil.allocate(HttpGenerator.CHUNK_SIZE);
-                    continue;
-
-                case NEED_INFO:
-                    throw new IllegalStateException();
-
-                case FLUSH:
-                    if (BufferUtil.hasContent(header))
-                    {
-                        out.write(BufferUtil.toArray(header));
-                        BufferUtil.clear(header);
-                    }
-                    if (BufferUtil.hasContent(chunk))
-                    {
-                        out.write(BufferUtil.toArray(chunk));
-                        BufferUtil.clear(chunk);
-                    }
-                    if (BufferUtil.hasContent(content))
-                    {
-                        out.write(BufferUtil.toArray(content));
-                        BufferUtil.clear(content);
-                    }
-                    break;
-
-                case SHUTDOWN_OUT:
-                    break loop;
-            }
-        }
-
-        return out.toString();
-    }
-}
diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/rawhttp/HttpRequestTesterTest.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/rawhttp/HttpRequestTesterTest.java
index e8c9ef2..5d29bc4 100644
--- a/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/rawhttp/HttpRequestTesterTest.java
+++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/rawhttp/HttpRequestTesterTest.java
@@ -19,7 +19,11 @@
 package org.eclipse.jetty.test.support.rawhttp;
 
 import java.io.IOException;
+import java.nio.ByteBuffer;
 
+import org.eclipse.jetty.http.HttpTester;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.util.BufferUtil;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -28,42 +32,45 @@
     @Test
     public void testBasicHttp10Request() throws IOException
     {
-        HttpRequestTester request = new HttpRequestTester();
+        HttpTester.Request request = HttpTester.newRequest();
         request.setMethod("GET");
         request.setURI("/uri");
         request.setVersion("HTTP/1.0");
-        request.setHost("fakehost");
+        request.put("Host","fakehost");
 
-        String rawRequest = request.generate();
+        ByteBuffer bBuff = request.generate();
 
         StringBuffer expectedRequest = new StringBuffer();
         expectedRequest.append("GET /uri HTTP/1.0\r\n");
         expectedRequest.append("Host: fakehost\r\n");
         expectedRequest.append("\r\n");
 
-        Assert.assertEquals("Basic Request",expectedRequest.toString(),rawRequest);
+        Assert.assertEquals("Basic Request",expectedRequest.toString(),BufferUtil.toString(bBuff));
     }
 
     @Test
     public void testBasicHttp11Request() throws IOException
     {
-        HttpRequestTester request = new HttpRequestTester();
+        HttpTester.Request request = HttpTester.newRequest();
         request.setMethod("GET");
+        request.setVersion(HttpVersion.HTTP_1_1);
         request.setURI("/uri");
-        request.setHost("fakehost");
-        request.setConnectionClosed();
+        request.put("Host","fakehost");
+        request.put("Connection", "close");
+        request.setContent("aaa");
+       
 
-        String rawRequest = request.generate();
+        ByteBuffer bBuff = request.generate();
 
         StringBuffer expectedRequest = new StringBuffer();
         expectedRequest.append("GET /uri HTTP/1.1\r\n");
         expectedRequest.append("Host: fakehost\r\n");
         expectedRequest.append("Connection: close\r\n");
-        expectedRequest.append("Transfer-Encoding: chunked\r\n");
+        expectedRequest.append("Content-Length: 3\r\n");
         expectedRequest.append("\r\n");
-        expectedRequest.append("0\r\n");
-        expectedRequest.append("\r\n");
+        expectedRequest.append("aaa");
+       
 
-        Assert.assertEquals("Basic Request",expectedRequest.toString(),rawRequest);
+        Assert.assertEquals("Basic Request",expectedRequest.toString(),BufferUtil.toString(bBuff));
     }
 }
diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/rawhttp/HttpResponseTester.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/rawhttp/HttpResponseTester.java
deleted file mode 100644
index 4a4f6dd..0000000
--- a/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/rawhttp/HttpResponseTester.java
+++ /dev/null
@@ -1,461 +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.test.support.rawhttp;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.StringReader;
-import java.util.ArrayList;
-import java.util.List;
-
-import junit.framework.Assert;
-
-import org.eclipse.jetty.http.HttpFields;
-import org.eclipse.jetty.http.HttpHeaders;
-import org.eclipse.jetty.http.HttpParser;
-import org.eclipse.jetty.http.HttpStatus;
-import org.eclipse.jetty.http.MimeTypes;
-import org.eclipse.jetty.io.Buffer;
-import org.eclipse.jetty.io.ByteArrayBuffer;
-import org.eclipse.jetty.io.View;
-import org.eclipse.jetty.test.support.StringUtil;
-import org.eclipse.jetty.toolchain.test.StringAssert;
-import org.eclipse.jetty.util.ByteArrayOutputStream2;
-
-/**
- * Assists in testing of HTTP Responses.
- */
-public class HttpResponseTester
-{
-    private class PH extends HttpParser.EventHandler
-    {
-        @Override
-        public void content(Buffer ref) throws IOException
-        {
-            if (content == null)
-                content = new ByteArrayOutputStream2();
-            content.write(ref.asArray());
-        }
-
-        @Override
-        public void headerComplete() throws IOException
-        {
-            contentType = fields.get(HttpHeaders.CONTENT_TYPE_BUFFER);
-            if (contentType != null)
-            {
-                String calcCharset = MimeTypes.getCharsetFromContentType(contentType);
-                if (calcCharset != null)
-                {
-                    charset = calcCharset;
-                }
-            }
-        }
-
-        @Override
-        public void messageComplete(long contextLength) throws IOException
-        {
-        }
-
-        @Override
-        public void parsedHeader(Buffer name, Buffer value) throws IOException
-        {
-            fields.add(name,value);
-        }
-
-        @Override
-        public void startRequest(Buffer method, Buffer url, Buffer version) throws IOException
-        {
-            reset();
-            HttpResponseTester.this.method = getString(method);
-            HttpResponseTester.this.uri = getString(url);
-            HttpResponseTester.this.version = getString(version);
-        }
-
-        @Override
-        public void startResponse(Buffer version, int status, Buffer reason) throws IOException
-        {
-            reset();
-            HttpResponseTester.this.version = getString(version);
-            HttpResponseTester.this.status = status;
-            HttpResponseTester.this.reason = getString(reason);
-        }
-    }
-
-    public static List<HttpResponseTester> parseMulti(CharSequence rawHTTP) throws IOException
-    {
-        List<HttpResponseTester> responses = new ArrayList<HttpResponseTester>();
-        String parse = rawHTTP.toString();
-        while (StringUtil.isNotBlank(parse))
-        {
-            HttpResponseTester response = new HttpResponseTester();
-            parse = response.parse(parse);
-            responses.add(response);
-        }
-
-        return responses;
-    }
-
-    private HttpFields fields = new HttpFields();
-    private CharSequence rawResponse;
-    private String method;
-    private String uri;
-    private String version;
-    private int status;
-    private String reason;
-    private Buffer contentType;
-    private ByteArrayOutputStream2 content;
-    private String charset;
-    private String defaultCharset;
-
-    public HttpResponseTester()
-    {
-        this("UTF-8");
-    }
-
-    public HttpResponseTester(String defCharset)
-    {
-        this.defaultCharset = defCharset;
-    }
-
-    public String getMethod()
-    {
-        return method;
-    }
-
-    public String getURI()
-    {
-        return uri;
-    }
-
-    public String getVersion()
-    {
-        return version;
-    }
-
-    public int getStatus()
-    {
-        return status;
-    }
-
-    public CharSequence getRawResponse()
-    {
-        return rawResponse;
-    }
-
-    public String getReason()
-    {
-        return reason;
-    }
-
-    public String getContentType()
-    {
-        if (contentType == null)
-        {
-            return null;
-        }
-        return contentType.toString();
-    }
-
-    public ByteArrayOutputStream2 getContentBytes()
-    {
-        return content;
-    }
-
-    public String getContent()
-    {
-        return content.toString();
-    }
-
-    public String getBody()
-    {
-        return content.toString();
-    }
-
-    private byte[] getByteArray(CharSequence str)
-    {
-        if (charset == null)
-        {
-            return str.toString().getBytes();
-        }
-
-        try
-        {
-            return str.toString().getBytes(charset);
-        }
-        catch (Exception e)
-        {
-            return str.toString().getBytes();
-        }
-    }
-
-    private String getString(Buffer buffer)
-    {
-        return getString(buffer.asArray());
-    }
-
-    private String getString(byte[] b)
-    {
-        if (charset == null)
-        {
-            return new String(b);
-        }
-
-        try
-        {
-            return new String(b,charset);
-        }
-        catch (Exception e)
-        {
-            return new String(b);
-        }
-    }
-
-    /**
-     * @param name
-     * @return the header value as a date
-     * @see org.eclipse.jetty.http.HttpFields#getDateField(java.lang.String)
-     */
-    public long getDateHeader(String name)
-    {
-        return fields.getDateField(name);
-    }
-
-    /**
-     * @param name
-     * @return the header value as a long
-     * @throws NumberFormatException
-     * @see org.eclipse.jetty.http.HttpFields#getLongField(java.lang.String)
-     */
-    public long getLongHeader(String name) throws NumberFormatException
-    {
-        return fields.getLongField(name);
-    }
-
-    /**
-     * @param name
-     * @return the header value
-     * @see org.eclipse.jetty.http.HttpFields#getStringField(java.lang.String)
-     */
-    public String getHeader(String name)
-    {
-        return fields.getStringField(name);
-    }
-
-    public boolean hasHeader(String headerKey)
-    {
-        return fields.containsKey(headerKey);
-    }
-
-    /**
-     * Parse on HTTP Response
-     * 
-     * @param rawHTTP
-     *            Raw HTTP to parse
-     * @return Any unparsed data in the rawHTTP (eg pipelined requests)
-     * @throws IOException
-     */
-    public String parse(CharSequence rawHTTP) throws IOException
-    {
-        this.charset = defaultCharset;
-        this.rawResponse = rawHTTP;
-        ByteArrayBuffer buf = new ByteArrayBuffer(getByteArray(rawHTTP));
-        View view = new View(buf);
-        HttpParser parser = new HttpParser(view,new PH());
-        parser.parse();
-        return getString(view.asArray());
-    }
-
-    public void reset()
-    {
-        fields.clear();
-        method = null;
-        uri = null;
-        version = null;
-        status = 0;
-        reason = null;
-        content = null;
-    }
-
-    /**
-     * Make sure that status code is "OK"
-     */
-    public void assertStatusOK()
-    {
-        assertStatus(HttpStatus.OK_200,"OK");
-    }
-
-    public void assertStatusOK(String msg)
-    {
-        assertStatus(msg,HttpStatus.OK_200,"OK");
-    }
-
-    public void assertStatus(int expectedStatus, String expectedReason)
-    {
-        Assert.assertEquals("Response.status",expectedStatus,this.status);
-        Assert.assertEquals("Response.reason",expectedReason,this.reason);
-    }
-
-    public void assertStatus(String msg, int expectedStatus, String expectedReason)
-    {
-        Assert.assertEquals(msg + ": Response.status",expectedStatus,this.status);
-        Assert.assertEquals(msg + ": Response.reason",expectedReason,this.reason);
-    }
-
-    public void assertStatus(String msg, int expectedStatus)
-    {
-        assertStatus(msg,expectedStatus,HttpStatus.getMessage(expectedStatus));
-    }
-
-    public void assertContentType(String expectedType)
-    {
-        assertHeader("Content-Type",expectedType);
-    }
-
-    private void assertHeader(String headerKey, String expectedValue)
-    {
-        String actual = fields.getStringField(headerKey);
-        Assert.assertNotNull("Response[" + headerKey + "] should not be null",actual);
-        Assert.assertEquals("Response[" + headerKey + "]",expectedValue,actual);
-    }
-
-    public void assertHeader(String msg, String headerKey, String expectedValue)
-    {
-        String actual = fields.getStringField(headerKey);
-        Assert.assertNotNull(msg + ": Response[" + headerKey + "] should not be null, expecting <" + expectedValue + ">",actual);
-        Assert.assertEquals(msg + ": Response[" + headerKey + "]",expectedValue,actual);
-    }
-
-    public void assertBody(String expected)
-    {
-        Assert.assertNotNull("Response.content should not be null",this.content);
-        String actual = this.content.toString();
-        Assert.assertEquals("Response.content",expected,actual);
-    }
-
-    public void assertBody(String msg, String expected)
-    {
-        Assert.assertNotNull(msg + ": Response.content should not be null",this.content);
-        String actual = this.content.toString();
-        Assert.assertEquals(msg + ": Response.content",expected,actual);
-    }
-    
-    public void assertNoBody(String msg)
-    {
-        Assert.assertNull(msg + ": Response.content should be null",this.content);
-    }
-
-    public void assertBodyContains(String msg, String expectedNeedle)
-    {
-        StringAssert.assertContains(msg + ": Response Content",this.content.toString(),expectedNeedle);
-    }
-
-    public void assertHeaderExists(String msg, String expectedHeaderKey)
-    {
-        Assert.assertTrue(msg + ": header <" + expectedHeaderKey + "> should exist",fields.containsKey(expectedHeaderKey));
-    }
-
-    public void assertHeaderNotPresent(String msg, String headerKey)
-    {
-        Assert.assertFalse(msg + ": header <" + headerKey + "> should NOT exist",fields.containsKey(headerKey));
-    }
-    
-    public List<HttpResponseTester> findBodyMultiparts(String boundary) throws IOException
-    {
-        List<HttpResponseTester> multiparts = new ArrayList<HttpResponseTester>();
-
-        BufferedReader buf = new BufferedReader(new StringReader(getBody()));
-        String line;
-        String startBoundary = "--" + boundary;
-        String endBoundary = "--" + boundary + "--";
-        HttpResponseTester resp = null;
-        boolean parsingHeader = true;
-        boolean previousBodyLine = false;
-
-        while ((line = buf.readLine()) != null)
-        {
-            if (line.equals(startBoundary))
-            {
-                // end of multipart, start a new one.
-                if (resp != null)
-                {
-                    multiparts.add(resp);
-                }
-                resp = new HttpResponseTester();
-                parsingHeader = true;
-                previousBodyLine = false;
-                continue;
-            }
-
-            if (line.equals(endBoundary))
-            {
-                if (resp != null)
-                {
-                    multiparts.add(resp);
-                }
-                break;
-            }
-
-            if (parsingHeader)
-            {
-                if (line.equals(""))
-                {
-                    parsingHeader = false;
-                    continue;
-                }
-
-                resp.parseHeader(line);
-            }
-            else
-            {
-                if (previousBodyLine)
-                {
-                    resp.appendBody("\n");
-                }
-                resp.appendBody(line);
-                previousBodyLine = true;
-            }
-        }
-
-        return multiparts;
-    }
-
-    public void parseHeader(String line)
-    {
-        int idx = line.indexOf(":");
-        String key = line.substring(0,idx).trim();
-        String val = line.substring(idx + 1).trim();
-
-        fields.add(key,val);
-    }
-
-    public void appendBody(String s) throws IOException
-    {
-        appendBody(s.getBytes());
-    }
-
-    public void appendBody(byte buf[]) throws IOException
-    {
-        if (content == null)
-        {
-            content = new ByteArrayOutputStream2();
-        }
-
-        content.write(buf);
-    }
-}
diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/rawhttp/HttpResponseTesterTest.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/rawhttp/HttpResponseTesterTest.java
index 59c97d6..bac0b4a 100644
--- a/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/rawhttp/HttpResponseTesterTest.java
+++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/rawhttp/HttpResponseTesterTest.java
@@ -25,6 +25,9 @@
 import java.io.IOException;
 import java.util.List;
 
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http.HttpTester;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -45,16 +48,15 @@
         rawResponse.append("ABCDEFGHIJKLMNOPQRSTTUVWXYZ\n");
         rawResponse.append("\n");
 
-        HttpResponseTester response = new HttpResponseTester();
-        response.parse(rawResponse);
+        HttpTester.Response response = HttpTester.parseResponse(rawResponse.toString());
 
-        Assert.assertEquals("Response.version","HTTP/1.1",response.getVersion());
+        Assert.assertEquals("Response.version","HTTP/1.1",response.getVersion().asString());
         Assert.assertEquals("Response.status",200,response.getStatus());
         Assert.assertEquals("Response.reason","OK",response.getReason());
 
-        Assert.assertEquals("Response[Content-Type]","text/plain",response.getContentType());
-        Assert.assertEquals("Response[Content-Length]",28,response.getLongHeader("Content-Length"));
-        Assert.assertEquals("Response[Connection]","close",response.getHeader("Connection"));
+        Assert.assertEquals("Response[Content-Type]","text/plain",response.get(HttpHeader.CONTENT_TYPE));
+        Assert.assertEquals("Response[Content-Length]",28,response.getLongField("Content-Length"));
+        Assert.assertEquals("Response[Connection]","close",response.get("Connection"));
 
         String expected = "ABCDEFGHIJKLMNOPQRSTTUVWXYZ\n";
 
@@ -73,6 +75,7 @@
         rawResponse.append("Server: Jetty(7.0.y.z-SNAPSHOT)\n");
         rawResponse.append("\n");
         rawResponse.append("ABCDEFGHIJKLMNOPQRSTTUVWXYZ\n");
+        
         rawResponse.append("HTTP/1.1 200 OK\n");
         rawResponse.append("Date: Mon, 08 Jun 2009 23:05:26 GMT\n");
         rawResponse.append("Content-Type: text/plain\n");
@@ -82,6 +85,7 @@
         rawResponse.append("\n");
         rawResponse.append("Host=Default\n");
         rawResponse.append("Resource=R1\n");
+        
         rawResponse.append("HTTP/1.1 200 OK\n");
         rawResponse.append("Date: Mon, 08 Jun 2009 23:05:26 GMT\n");
         rawResponse.append("Content-Type: text/plain\n");
@@ -93,27 +97,32 @@
         rawResponse.append("Host=Default\n");
         rawResponse.append("Resource=R2\n");
         rawResponse.append("\n");
+        
+       
+        List<HttpTester.Response> responses = HttpTesting.readResponses(rawResponse.toString());
 
-        List<HttpResponseTester> responses = HttpResponseTester.parseMulti(rawResponse);
         Assert.assertNotNull("Responses should not be null",responses);
         Assert.assertEquals("Responses.size",3,responses.size());
 
-        HttpResponseTester resp1 = responses.get(0);
-        resp1.assertStatusOK();
-        resp1.assertContentType("text/plain");
-        resp1.assertBody("ABCDEFGHIJKLMNOPQRSTTUVWXYZ\n");
-        assertThat(resp1.getHeader("Connection"),is(not("close")));
+        HttpTester.Response resp1 = responses.get(0);
+        System.err.println(resp1.toString());
+        Assert.assertEquals(HttpStatus.OK_200, resp1.getStatus());
+        Assert.assertEquals("text/plain", resp1.get("Content-Type"));
+        Assert.assertTrue(resp1.getContent().contains("ABCDEFGHIJKLMNOPQRSTTUVWXYZ\n"));
+        assertThat(resp1.get("Connection"),is(not("close")));
 
-        HttpResponseTester resp2 = responses.get(1);
-        resp2.assertStatusOK();
-        resp2.assertContentType("text/plain");
-        resp2.assertBody("Host=Default\nResource=R1\n");
-        assertThat(resp2.getHeader("Connection"),is(not("close")));
+        HttpTester.Response resp2 = responses.get(1);
+        System.err.println(resp2.toString());
+        Assert.assertEquals(HttpStatus.OK_200, resp2.getStatus());
+        Assert.assertEquals("text/plain", resp2.get("Content-Type"));
+        Assert.assertTrue(resp2.getContent().contains("Host=Default\nResource=R1\n"));
+        assertThat(resp2.get("Connection"),is(not("close")));
 
-        HttpResponseTester resp3 = responses.get(2);
-        resp3.assertStatusOK();
-        resp3.assertContentType("text/plain");
-        resp3.assertBody("Host=Default\nResource=R2\n");
-        assertThat(resp3.getHeader("Connection"),is("close"));
+        HttpTester.Response resp3 = responses.get(2);
+        System.err.println(resp3.toString());
+        Assert.assertEquals(HttpStatus.OK_200, resp3.getStatus());
+        Assert.assertEquals("text/plain", resp3.get("Content-Type"));
+        Assert.assertTrue(resp3.getContent().contains("Host=Default\nResource=R2\n"));
+        assertThat(resp3.get("Connection"),is("close"));
     }
 }
diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/rawhttp/HttpTesting.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/rawhttp/HttpTesting.java
index 2e05e8b..c81fa3f 100644
--- a/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/rawhttp/HttpTesting.java
+++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/rawhttp/HttpTesting.java
@@ -18,19 +18,25 @@
 
 package org.eclipse.jetty.test.support.rawhttp;
 
+import java.io.BufferedReader;
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
+import java.io.StringReader;
 import java.io.StringWriter;
 import java.net.InetAddress;
 import java.net.Socket;
 import java.net.SocketTimeoutException;
 import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
 import java.util.List;
 
-import org.eclipse.jetty.test.support.StringUtil;
+import org.eclipse.jetty.http.HttpTester;
+import org.eclipse.jetty.util.BufferUtil;
 import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.StringUtil;
 
 /**
  * Testing utility for performing RAW HTTP request/response.
@@ -42,6 +48,102 @@
     private InetAddress serverHost;
     private int serverPort;
     private int timeoutMillis = 5000;
+    
+    
+    public static List<HttpTester.Response> getParts (String boundary, HttpTester.Response response) throws IOException
+    {
+        List<HttpTester.Response> parts = new ArrayList<HttpTester.Response>();
+
+        BufferedReader buf = new BufferedReader(new StringReader(response.getContent()));
+        String line;
+        String startBoundary = "--" + boundary;
+        String endBoundary = "--" + boundary + "--";
+      
+        StringBuffer partBuff = null;
+        boolean parsingHeader = true;
+        boolean previousBodyLine = false;
+
+        while ((line = buf.readLine()) != null)
+        {
+            if (line.equals(startBoundary))
+            {
+                // end of multipart, start a new one.
+                if (partBuff != null)
+                {
+                    HttpTester.Response part = HttpTester.parseResponse(partBuff.toString());
+                    parts.add(part);
+                }
+                partBuff = new StringBuffer();
+                parsingHeader = true;
+                previousBodyLine = false;
+                continue;
+            }
+
+            if (line.equals(endBoundary))
+            {
+                if (partBuff != null)
+                {
+                    HttpTester.Response part = HttpTester.parseResponse(partBuff.toString());
+                    parts.add(part);
+                }
+                break;
+            }
+
+            if (parsingHeader)
+            {
+                if (line.equals(""))
+                {
+                    parsingHeader = false;
+                    continue;
+                }
+
+                partBuff.append(line);
+            }
+            else
+            {
+                if (previousBodyLine)
+                {
+                    partBuff.append("\n");
+                }
+                partBuff.append(line);
+                previousBodyLine = true;
+            }
+        }
+
+        return parts;
+
+    }
+
+
+
+    public static List<HttpTester.Response> readResponses(ByteBuffer buffer) throws IOException
+    {
+        List<HttpTester.Response> list = new ArrayList<>();
+
+        while(BufferUtil.hasContent(buffer))
+        {
+            HttpTester.Response response = HttpTester.parseResponse(buffer);
+            if (response == null)
+                break;
+            list.add(HttpTester.parseResponse(buffer));
+        }
+        return list;
+    }
+    
+    public static List<HttpTester.Response> readResponses(String string) throws IOException
+    {
+        List<HttpTester.Response> list = new ArrayList<>();
+
+        ByteBuffer buffer = BufferUtil.toBuffer(string);
+        while(BufferUtil.hasContent(buffer))
+        {
+            HttpTester.Response response = HttpTester.parseResponse(buffer);
+            if (response == null)
+                break;
+            list.add(response);
+        }
+        return list;
+    }
 
     public HttpTesting(HttpSocket httpSocket, InetAddress host, int port)
     {
@@ -115,11 +217,25 @@
      * @return the response object
      * @throws IOException
      */
-    public HttpResponseTester read(Socket sock) throws IOException
+    public HttpTester.Response read(Socket sock) throws IOException
     {
-        HttpResponseTester response = new HttpResponseTester();
-        response.parse(readRaw(sock));
-        return response;
+       return HttpTester.parseResponse(readRaw(sock));
+    }
+
+    
+    public  List<HttpTester.Response> readResponses(Socket sock) throws IOException
+    {
+       List<HttpTester.Response> list = new ArrayList<>();
+       String r = readRaw(sock);
+       ByteBuffer buffer = BufferUtil.toBuffer(r);
+       while(BufferUtil.hasContent(buffer))
+       {
+           HttpTester.Response response = HttpTester.parseResponse(buffer);
+           if (response == null)
+               break;
+           list.add(response);
+       }
+       return list;
     }
 
     /**
@@ -130,16 +246,15 @@
      * @return the response object
      * @throws IOException
      */
-    public HttpResponseTester readAvailable(Socket sock) throws IOException
+    public HttpTester.Response readAvailable(Socket sock) throws IOException
     {
-        HttpResponseTester response = new HttpResponseTester();
+        
         String rawResponse = readRawAvailable(sock);
         if (StringUtil.isBlank(rawResponse))
         {
             return null;
         }
-        response.parse(rawResponse);
-        return response;
+        return HttpTester.parseResponse(rawResponse);
     }
 
     /**
@@ -199,7 +314,7 @@
      * @return the response
      * @throws IOException
      */
-    public HttpResponseTester request(CharSequence rawRequest) throws IOException
+    public HttpTester.Response request(CharSequence rawRequest) throws IOException
     {
         Socket sock = open();
         try
@@ -223,10 +338,10 @@
      * @return the response
      * @throws IOException
      */
-    public HttpResponseTester request(HttpRequestTester request) throws IOException
+    public HttpTester.Response request(HttpTester.Request request) throws IOException
     {
-        String rawRequest = request.generate();
-        return request(rawRequest);
+        ByteBuffer byteBuff = request.generate();
+        return request(BufferUtil.toString(byteBuff));
     }
 
     /**
@@ -237,7 +352,7 @@
      * @return the responses.
      * @throws IOException
      */
-    public List<HttpResponseTester> requests(CharSequence rawRequests) throws IOException
+   public List<HttpTester.Response> requests(CharSequence rawRequests) throws IOException
     {
         Socket sock = open();
         try
@@ -247,7 +362,7 @@
             // Collect response
             String rawResponses = IO.toString(sock.getInputStream());
             DEBUG("--raw-response--\n" + rawResponses);
-            return HttpResponseTester.parseMulti(rawResponses);
+            return readResponses(rawResponses);
         }
         finally
         {
@@ -255,24 +370,6 @@
         }
     }
 
-    /**
-     * Initiate a multiple HTTP requests, parse the responses
-     * 
-     * @param requests
-     *            the request objects.
-     * @return the response objects.
-     * @throws IOException
-     */
-    public List<HttpResponseTester> requests(List<HttpRequestTester> requests) throws IOException
-    {
-        StringBuffer rawRequest = new StringBuffer();
-        for (HttpRequestTester request : requests)
-        {
-            rawRequest.append(request.generate());
-        }
-
-        return requests(rawRequest);
-    }
 
     /**
      * Send a data (as request) to open socket.
diff --git a/tests/test-integration/src/test/resources/DefaultHandler.xml b/tests/test-integration/src/test/resources/DefaultHandler.xml
index dd6805d..644e7f9 100644
--- a/tests/test-integration/src/test/resources/DefaultHandler.xml
+++ b/tests/test-integration/src/test/resources/DefaultHandler.xml
@@ -12,32 +12,23 @@
 
 <Configure id="Server" class="org.eclipse.jetty.server.Server">
 
-    <!-- =========================================================== -->
-    <!-- Server Thread Pool                                          -->
-    <!-- =========================================================== -->
-    <Set name="ThreadPool">
-      <New class="org.eclipse.jetty.util.thread.QueuedThreadPool">
-        <Set name="minThreads">10</Set>
-        <Set name="maxThreads">200</Set>
-      </New>
-    </Set>
+    <New id="httpConfig" class="org.eclipse.jetty.server.HttpConfiguration">
+      <Set name="secureScheme">https</Set>
+      <Set name="securePort"><Property name="jetty.secure.port" default="8443" /></Set>
+      <Set name="outputBufferSize">32768</Set>
+      <Set name="requestHeaderSize">8192</Set>
+      <Set name="responseHeaderSize">8192</Set>
+      <Set name="sendServerVersion">true</Set>
+      <Set name="sendDateHeader">false</Set>
+      <Set name="headerCacheSize">512</Set>
 
-    <!-- =========================================================== -->
-    <!-- Set connectors                                              -->
-    <!-- =========================================================== -->
+      <!-- Uncomment to enable handling of X-Forwarded- style headers
+      <Call name="addCustomizer">
+        <Arg><New class="org.eclipse.jetty.server.ForwardedRequestCustomizer"/></Arg>
+      </Call>
+      -->
+    </New>
 
-    <Call name="addConnector">
-      <Arg>
-          <New class="org.eclipse.jetty.server.ServerConnector">
-            <Set name="host"><SystemProperty name="jetty.host" /></Set>
-            <Set name="port"><SystemProperty name="jetty.port" default="0"/></Set>
-            <Set name="idleTimeout">300000</Set>
-            <Set name="Acceptors">2</Set>
-            <Set name="statsOn">false</Set>
-            <!--<Set name="confidentialPort">8443</Set>-->
-          </New>
-      </Arg>
-    </Call>
 
     <!-- =========================================================== -->
     <!-- Set handler Collection Structure                            -->
diff --git a/tests/test-integration/src/test/resources/NIOHttp.xml b/tests/test-integration/src/test/resources/NIOHttp.xml
index 4372a16..793da7a 100644
--- a/tests/test-integration/src/test/resources/NIOHttp.xml
+++ b/tests/test-integration/src/test/resources/NIOHttp.xml
@@ -6,17 +6,27 @@
     <!-- =========================================================== -->
     <!-- Set connectors                                              -->
     <!-- =========================================================== -->
-
+    
     <Call name="addConnector">
-      <Arg>
-          <New class="org.eclipse.jetty.server.ServerConnector">
-            <Set name="host"><SystemProperty name="jetty.host" /></Set>
-            <Set name="port"><SystemProperty name="jetty.port" default="0"/></Set>
-            <Set name="idleTimeout">300000</Set>
-            <Set name="Acceptors">2</Set>
-            <Set name="statsOn">false</Set>
-          </New>
-      </Arg>
-    </Call>
+    <Arg>
+      <New class="org.eclipse.jetty.server.ServerConnector">
+        <Arg name="server"><Ref refid="Server" /></Arg>
+        <Arg name="factories">
+          <Array type="org.eclipse.jetty.server.ConnectionFactory">
+            <Item>
+              <New class="org.eclipse.jetty.server.HttpConnectionFactory">
+                <Arg name="config"><Ref refid="httpConfig" /></Arg>
+              </New>
+            </Item>
+          </Array>
+        </Arg>
+        <Set name="host"><Property name="jetty.host" /></Set>
+        <Set name="port"><Property name="jetty.port" default="8080" /></Set>
+        <Set name="idleTimeout"><Property name="http.timeout" default="30000"/></Set>
+      </New>
+    </Arg>
+  </Call>
+    
+    
 
 </Configure>
diff --git a/tests/test-integration/src/test/resources/NIOHttps.xml b/tests/test-integration/src/test/resources/NIOHttps.xml
index d99d67e..ff42c72 100644
--- a/tests/test-integration/src/test/resources/NIOHttps.xml
+++ b/tests/test-integration/src/test/resources/NIOHttps.xml
@@ -5,21 +5,33 @@
 
     <!-- =========================================================== -->
     <!-- Set connectors                                              -->
-    <!-- =========================================================== -->
-
-    <Call name="addConnector">
-      <Arg>
-          <New class="org.eclipse.jetty.server.ServerConnector">
-            <Set name="host"><SystemProperty name="jetty.host" /></Set>
-            <Set name="port"><SystemProperty name="jetty.port" default="0"/></Set>
-            <Set name="idleTimeout">300000</Set>
-            <Set name="Acceptors">2</Set>
-            <Set name="statsOn">false</Set>
-            <Set name="keystore"><Property name="test.resourcesdir" default="src/test/resources" />/keystore</Set>
-            <Set name="password">OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4</Set>
-            <Set name="keyPassword">OBF:1u2u1wml1z7s1z7a1wnl1u2g</Set>
-          </New>
-      </Arg>
-    </Call>
+    <!-- =========================================================== --> 
+   <Call id="httpsConnector" name="addConnector">
+    <Arg>
+      <New class="org.eclipse.jetty.server.ServerConnector">
+        <Arg name="server"><Ref refid="Server" /></Arg>
+          <Arg name="factories">
+            <Array type="org.eclipse.jetty.server.ConnectionFactory">
+              <Item>
+                <New class="org.eclipse.jetty.server.SslConnectionFactory">
+                  <Arg name="next">http/1.1</Arg>
+                  <Arg name="sslContextFactory"><Ref refid="sslContextFactory"/></Arg>
+                </New>
+              </Item>
+              <Item>
+                <New class="org.eclipse.jetty.server.HttpConnectionFactory">
+                  <Arg name="config"><Ref refid="sslHttpConfig"/></Arg>
+                </New>
+              </Item>
+            </Array>
+          </Arg>
+          <Set name="host"><Property name="jetty.host" /></Set>
+          <Set name="port"><Property name="jetty.https.port" default="8443" /></Set>
+          <Set name="idleTimeout">30000</Set>
+        </New>
+    </Arg>
+  </Call>
+    
+    
 
 </Configure>
diff --git a/tests/test-integration/src/test/resources/RFC2616Base.xml b/tests/test-integration/src/test/resources/RFC2616Base.xml
index b110e94..3cc7a4b 100644
--- a/tests/test-integration/src/test/resources/RFC2616Base.xml
+++ b/tests/test-integration/src/test/resources/RFC2616Base.xml
@@ -12,38 +12,22 @@
 
 <Configure id="Server" class="org.eclipse.jetty.server.Server">
 
-    <!-- =========================================================== -->
-    <!-- Server Thread Pool                                          -->
-    <!-- =========================================================== -->
-    <Set name="ThreadPool">
-      <New class="org.eclipse.jetty.util.thread.QueuedThreadPool">
-        <Set name="minThreads">10</Set>
-        <Set name="maxThreads">200</Set>
-      </New>
-    </Set>
+    <New id="httpConfig" class="org.eclipse.jetty.server.HttpConfiguration">
+      <Set name="secureScheme">https</Set>
+      <Set name="securePort"><Property name="jetty.secure.port" default="8443" /></Set>
+      <Set name="outputBufferSize">32768</Set>
+      <Set name="requestHeaderSize">8192</Set>
+      <Set name="responseHeaderSize">8192</Set>
+      <Set name="sendServerVersion">true</Set>
+      <Set name="sendDateHeader">false</Set>
+      <Set name="headerCacheSize">512</Set>
 
-    <!-- =========================================================== -->
-    <!-- No connectors Set Here.                                     -->
-    <!-- See:                                                        -->
-    <!--   BIOHttp.xml                                               -->
-    <!--   BIOHttps.xml                                              -->
-    <!--   NIOHttp.xml                                               -->
-    <!--   NIOHttps.xml                                              -->
-    <!-- =========================================================== -->
-
-    <!--
-    <Call name="addConnector">
-      <Arg>
-          <New class="org.eclipse.jetty.server.ServerConnector">
-            <Set name="host"><SystemProperty name="jetty.host" /></Set>
-            <Set name="port"><SystemProperty name="jetty.port" default="0"/></Set>
-            <Set name="idleTimeout">300000</Set>
-            <Set name="Acceptors">2</Set>
-            <Set name="statsOn">false</Set>
-          </New>
-      </Arg>
-    </Call>
-     -->
+      <!-- Uncomment to enable handling of X-Forwarded- style headers
+      <Call name="addCustomizer">
+        <Arg><New class="org.eclipse.jetty.server.ForwardedRequestCustomizer"/></Arg>
+      </Call>
+      -->
+    </New>
 
     <!-- =========================================================== -->
     <!-- Set handler Collection Structure                            -->
@@ -88,52 +72,37 @@
       </New>
     </Set>
 
-    <Call name="addBean">
-      <Arg>
-        <New class="org.eclipse.jetty.deploy.ContextDeployer">
-          <Set name="contexts"><Ref refid="WebappContexts"/></Set>
-          <Set name="configurationDir"><Property name="test.resourcesdir" default="src/test/resources"/>/webapp-contexts/RFC2616</Set>
-          <Set name="scanInterval">0</Set>
-          <Set name="configurationManager">
-            <New class="org.eclipse.jetty.deploy.PropertiesConfigurationManager">
-              <Set name="file"><Property name="test.targetdir" default="target"/>/testable-jetty-server-config.properties</Set>
-            </New>
-          </Set>
-        </New>
-      </Arg>
-    </Call>
+   <Call name="addBean">
+    <Arg>
+      <New id="DeploymentManager" class="org.eclipse.jetty.deploy.DeploymentManager">
+        <Set name="contexts">
+          <Ref refid="Contexts" />
+        </Set>
+        <Call name="setContextAttribute">
+          <Arg>org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern</Arg>
+          <Arg>.*/servlet-api-[^/]*\.jar$</Arg>
+        </Call>
 
-    <!-- =========================================================== -->
-    <!-- Configure the webapp deployer.                              -->
-    <!-- A webapp  deployer will deploy standard webapps discovered  -->
-    <!-- in a directory at startup, without the need for additional  -->
-    <!-- configuration files.    It does not support hot deploy or   -->
-    <!-- non standard contexts (see ContextDeployer above).          -->
-    <!--                                                             -->
-    <!-- This deployer is configured to deploy webapps from the      -->
-    <!-- $JETTY_HOME/webapps directory                               -->
-    <!--                                                             -->
-    <!-- Normally only one type of deployer need be used.            -->
-    <!--                                                             -->
-    <!-- =========================================================== -->
-    <!--
-    <Call name="addBean">
-      <Arg>
-        <New class="org.eclipse.jetty.deploy.WebAppDeployer">
-          <Set name="contexts"><Ref refid="WebappContexts"/></Set>
-          <Set name="webAppDir"><Property name="test.targetdir" default="target"/>/webapps</Set>
-          <Set name="parentLoaderPriority">false</Set>
-          <Set name="extract">true</Set>
-          <Set name="allowDuplicates">false</Set>
-          <Set name="defaultsDescriptor"><Property name="test.resourcesdir" default="src/test/resources"/>/webdefault.xml</Set>
-          <Call name="setAttribute">
-            <Arg>org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern</Arg>
-            <Arg>.*/jsp-api-[^/]*\.jar$|.*/jsp-[^/]*\.jar$</Arg>
-          </Call>
-        </New>
-      </Arg>
-    </Call>
-     -->
+        <Call id="webappprovider" name="addAppProvider">
+          <Arg>
+            <New class="org.eclipse.jetty.deploy.providers.WebAppProvider">
+              <Set name="monitoredDirName"><Property name="test.resourcesdir" default="src/test/resources" />/webapps-contexts/RFC2616</Set>
+              <Set name="scanInterval">1</Set>
+              <Set name="extractWars">true</Set>
+              <Set name="configurationManager">
+                <New class="org.eclipse.jetty.deploy.PropertiesConfigurationManager">
+                  <Set name="file"><Property name="test.targetdir" default="target"/>/testable-jetty-server-config.properties</Set>
+                </New>
+              </Set>
+            </New>
+          </Arg>
+        </Call>
+
+      </New>
+    </Arg>
+  </Call>
+
+
 
     <!-- =========================================================== -->
     <!-- extra options                                               -->
diff --git a/tests/test-integration/src/test/resources/ssl.xml b/tests/test-integration/src/test/resources/ssl.xml
new file mode 100644
index 0000000..84de070
--- /dev/null
+++ b/tests/test-integration/src/test/resources/ssl.xml
@@ -0,0 +1,33 @@
+<Configure id="sslContextFactory" class="org.eclipse.jetty.util.ssl.SslContextFactory">
+  <Set name="KeyStorePath"><Property name="jetty.home" default="." />/<Property name="jetty.keystore" default="keystore"/></Set>
+  <Set name="KeyStorePassword"><Property name="jetty.keystore.password" default="OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"/></Set>
+  <Set name="KeyManagerPassword"><Property name="jetty.keymanager.password" default="OBF:1u2u1wml1z7s1z7a1wnl1u2g"/></Set>
+  <Set name="TrustStorePath"><Property name="jetty.home" default="." />/<Property name="jetty.truststore" default="keystore"/></Set>
+  <Set name="TrustStorePassword"><Property name="jetty.truststore.password" default="OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"/></Set>
+  <Set name="EndpointIdentificationAlgorithm"></Set>
+  <Set name="ExcludeCipherSuites">
+    <Array type="String">
+      <Item>SSL_RSA_WITH_DES_CBC_SHA</Item>
+      <Item>SSL_DHE_RSA_WITH_DES_CBC_SHA</Item>
+      <Item>SSL_DHE_DSS_WITH_DES_CBC_SHA</Item>
+      <Item>SSL_RSA_EXPORT_WITH_RC4_40_MD5</Item>
+      <Item>SSL_RSA_EXPORT_WITH_DES40_CBC_SHA</Item>
+      <Item>SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA</Item>
+      <Item>SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA</Item>
+    </Array>
+  </Set>
+
+  <!-- =========================================================== -->
+  <!-- Create a TLS specific HttpConfiguration based on the        -->
+  <!-- common HttpConfiguration defined in jetty.xml               -->
+  <!-- Add a SecureRequestCustomizer to extract certificate and    -->
+  <!-- session information                                         -->
+  <!-- =========================================================== -->
+  <New id="sslHttpConfig" class="org.eclipse.jetty.server.HttpConfiguration">
+    <Arg><Ref refid="httpConfig"/></Arg>
+    <Call name="addCustomizer">
+      <Arg><New class="org.eclipse.jetty.server.SecureRequestCustomizer"/></Arg>
+    </Call>
+  </New>
+
+</Configure>
diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractNewSessionTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractNewSessionTest.java
index a3d3af0..fc8fa98 100644
--- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractNewSessionTest.java
+++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractNewSessionTest.java
@@ -57,12 +57,12 @@
     @Test
     public void testNewSession() throws Exception
     {
-        String contextPath = "";
         String servletMapping = "/server";
         int scavengePeriod = 3;
         AbstractTestServer server = createServer(0, 1, scavengePeriod);
-        ServletContextHandler context = server.addContext(contextPath);
+        ServletContextHandler context = server.addContext("/");
         context.addServlet(TestServlet.class, servletMapping);
+        String contextPath = "";
 
         try
         {
diff --git a/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/main/java/com/acme/RoleAnnotationTest.java b/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/main/java/com/acme/RoleAnnotationTest.java
index 303f35c..d16b737 100644
--- a/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/main/java/com/acme/RoleAnnotationTest.java
+++ b/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/main/java/com/acme/RoleAnnotationTest.java
@@ -75,7 +75,7 @@
             result = request.isUserInRole("manager");
             out.println("<br/><b>Result: isUserInRole(\"manager\")="+result+":"+ (result?" <span class=\"pass\">PASS":" <span class=\"fail\">FAIL")+"</span></b>");
             result = request.isUserInRole("user");
-            out.println("<br/><b>Result: isUserInRole(\"user\")="+result+":"+ (result==false?" <span class=\"pass\">PASS":" <span class=\"fail\">FAIL")+"</span></b>");
+            out.println("<br/><b>Result: isUserInRole(\"user\")="+result+":"+ (result?" <span class=\"pass\">PASS":" <span class=\"fail\">FAIL")+"</span></b>");
             String context = _config.getServletContext().getContextPath();
             if (!context.endsWith("/"))
                 context += "/";