Merged branch 'master' into '431642'.
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 5587c2f..41b1627 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
@@ -69,6 +69,8 @@
     private static final Logger LOG = Log.getLogger(AnnotationParser.class);
 
     protected Set<String> _parsedClassNames = new ConcurrentHashSet<String>();
+    
+    protected static int ASM_OPCODE_VERSION = Opcodes.ASM5; //compatibility of api
    
 
     /**
@@ -373,7 +375,7 @@
                                final String signature,
                                final String[] exceptions)
         {
-            super(Opcodes.ASM4);
+            super(ASM_OPCODE_VERSION);
             _handlers = handlers;
             _mi = new MethodInfo(classInfo, name, access, methodDesc,signature, exceptions);
         }
@@ -417,7 +419,7 @@
                               final String signature,
                               final Object value)
         {
-            super(Opcodes.ASM4);
+            super(ASM_OPCODE_VERSION);
             _handlers = handlers;
             _fieldInfo = new FieldInfo(classInfo, fieldName, access, fieldType, signature, value);
         }
@@ -456,7 +458,7 @@
         
         public MyClassVisitor(Set<? extends Handler> handlers, Resource containingResource)
         {
-            super(Opcodes.ASM4);
+            super(ASM_OPCODE_VERSION);
             _handlers = handlers;
             _containingResource = containingResource;
         }
@@ -702,6 +704,7 @@
                     }                  
                     catch (Exception ex)
                     {
+                        if (LOG.isDebugEnabled()) LOG.debug("Error scanning file "+files[f], ex);
                         me.add(new RuntimeException("Error scanning file "+files[f],ex));
                     }
                 }
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 15cd719..5649b29 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
@@ -228,7 +228,6 @@
     protected void doStop() throws Exception
     {
         cookieStore.removeAll();
-        cookieStore = null;
         decoderFactories.clear();
         handlers.clear();
 
diff --git a/jetty-distribution/pom.xml b/jetty-distribution/pom.xml
index 5003e02..d81eb57 100644
--- a/jetty-distribution/pom.xml
+++ b/jetty-distribution/pom.xml
@@ -570,7 +570,7 @@
                 <argument>jetty.home=${assembly-directory}</argument>
                 <argument>jetty.base=${assembly-directory}/demo-base</argument>
                 <argument>--add-to-start=server,continuation,deploy,websocket,ext,resources,client,annotations,jndi,servlets</argument>
-                <argument>--add-to-startd-ini=jsp,jstl,http,https</argument>
+                <argument>--add-to-startd=jsp,jstl,http,https</argument>
               </arguments>
             </configuration>
             <goals>
diff --git a/jetty-distribution/src/main/resources/bin/jetty.sh b/jetty-distribution/src/main/resources/bin/jetty.sh
index ce489db..3190332 100644
--- a/jetty-distribution/src/main/resources/bin/jetty.sh
+++ b/jetty-distribution/src/main/resources/bin/jetty.sh
@@ -105,8 +105,14 @@
 
 running()
 {
-  local PID=$(cat "$1" 2>/dev/null) || return 1
-  kill -0 "$PID" 2>/dev/null
+  if [ -f "$1" ]
+  then
+    local PID=$(cat "$1" 2>/dev/null) || return 1
+    kill -0 "$PID" 2>/dev/null
+    return
+  fi
+  rm -f "$1"
+  return 1
 }
 
 started()
@@ -408,16 +414,10 @@
 
     else
 
-      if [ -f "$JETTY_PID" ]
+      if running $JETTY_PID
       then
-        if running $JETTY_PID
-        then
-          echo "Already Running!"
-          exit 1
-        else
-          # dead pid file - remove
-          rm -f "$JETTY_PID"
-        fi
+        echo "Already Running $(cat $JETTY_PID)!"
+        exit 1
       fi
 
       if [ "$JETTY_USER" ] 
@@ -519,16 +519,10 @@
   run|demo)
     echo "Running Jetty: "
 
-    if [ -f "$JETTY_PID" ]
+    if running "$JETTY_PID"
     then
-      if running "$JETTY_PID"
-      then
-        echo "Already Running!"
-        exit 1
-      else
-        # dead pid file - remove
-        rm -f "$JETTY_PID"
-      fi
+      echo Already Running $(cat "$JETTY_PID")!
+      exit 1
     fi
 
     exec "${RUN_CMD[@]}"
@@ -550,7 +544,7 @@
     echo "RUN_CMD        =  ${RUN_CMD[*]}"
     echo
     
-    if [ -f "$JETTY_PID" ]
+    if running "$JETTY_PID"
     then
       echo "Jetty running pid=$(< "$JETTY_PID")"
       exit 0
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractEndPoint.java b/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractEndPoint.java
index bfcb1aa..8fa2cc8 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractEndPoint.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractEndPoint.java
@@ -44,6 +44,7 @@
             return AbstractEndPoint.this.needsFill();
         }
     };
+    
     private final WriteFlusher _writeFlusher = new WriteFlusher(this)
     {
         @Override
@@ -142,9 +143,22 @@
     @Override
     protected void onIdleExpired(TimeoutException timeout)
     {
-        // Note: Rely on fillInterest to notify onReadTimeout to close connection.
-        _fillInterest.onFail(timeout);
-        _writeFlusher.onFail(timeout);
+        boolean output_shutdown=isOutputShutdown();
+        boolean input_shutdown=isInputShutdown();
+        boolean fillFailed = _fillInterest.onFail(timeout);
+        boolean writeFailed = _writeFlusher.onFail(timeout);
+        
+        // If the endpoint is half closed and there was no onFail handling, the close here
+        // This handles the situation where the connection has completed its close handling 
+        // and the endpoint is half closed, but the other party does not complete the close.
+        // This perhaps should not check for half closed, however the servlet spec case allows
+        // for a dispatched servlet or suspended request to extend beyond the connections idle 
+        // time.  So if this test would always close an idle endpoint that is not handled, then 
+        // we would need a mode to ignore timeouts for some HTTP states
+        if (isOpen() && (output_shutdown || input_shutdown) && !(fillFailed || writeFailed))
+            close();
+        else 
+            LOG.debug("Ignored idle endpoint {}",this);
     }
 
     @Override
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/FillInterest.java b/jetty-io/src/main/java/org/eclipse/jetty/io/FillInterest.java
index 0f3c2e5..b2c3f68 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/FillInterest.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/FillInterest.java
@@ -93,12 +93,17 @@
     
     /* ------------------------------------------------------------ */
     /** Call to signal a failure to a registered interest
+     * @return true if the cause was passed to a {@link Callback} instance
      */
-    public void onFail(Throwable cause)
+    public boolean onFail(Throwable cause)
     {
         Callback callback=_interested.get();
         if (callback!=null && _interested.compareAndSet(callback,null))
+        {
             callback.failed(cause);
+            return true;
+        }
+        return false;
     }
     
     /* ------------------------------------------------------------ */
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/WriteFlusher.java b/jetty-io/src/main/java/org/eclipse/jetty/io/WriteFlusher.java
index 326ef3f..dd44e53 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/WriteFlusher.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/WriteFlusher.java
@@ -253,10 +253,14 @@
             return _buffers;
         }
 
-        protected void fail(Throwable cause)
+        protected boolean fail(Throwable cause)
         {
             if (_callback!=null)
+            {
                 _callback.failed(cause);
+                return true;
+            }
+            return false;
         }
 
         protected void complete()
@@ -430,7 +434,12 @@
         }
     }
 
-    public void onFail(Throwable cause)
+    /* ------------------------------------------------------------ */
+    /** Notify the flusher of a failure
+     * @param cause The cause of the failure
+     * @return true if the flusher passed the failure to a {@link Callback} instance
+     */
+    public boolean onFail(Throwable cause)
     {
         // Keep trying to handle the failure until we get to IDLE or FAILED state
         while(true)
@@ -442,7 +451,7 @@
                 case FAILED:
                     if (DEBUG)
                         LOG.debug("ignored: {} {}", this, cause);
-                    return;
+                    return false;
 
                 case PENDING:
                     if (DEBUG)
@@ -450,10 +459,7 @@
 
                     PendingState pending = (PendingState)current;
                     if (updateState(pending,__IDLE))
-                    {
-                        pending.fail(cause);
-                        return;
-                    }
+                        return pending.fail(cause);
                     break;
 
                 default:
@@ -461,7 +467,7 @@
                         LOG.debug("failed: {} {}", this, cause);
 
                     if (updateState(current,new FailedState(cause)))
-                        return;
+                        return false;
                     break;
             }
         }
diff --git a/jetty-io/src/test/java/org/eclipse/jetty/io/ByteArrayEndPointTest.java b/jetty-io/src/test/java/org/eclipse/jetty/io/ByteArrayEndPointTest.java
index d23be98..f6cefa1 100644
--- a/jetty-io/src/test/java/org/eclipse/jetty/io/ByteArrayEndPointTest.java
+++ b/jetty-io/src/test/java/org/eclipse/jetty/io/ByteArrayEndPointTest.java
@@ -19,8 +19,6 @@
 package org.eclipse.jetty.io;
 
 import static org.hamcrest.Matchers.*;
-import static org.hamcrest.Matchers.containsString;
-import static org.hamcrest.Matchers.greaterThan;
 import static org.hamcrest.Matchers.instanceOf;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -37,8 +35,6 @@
 import org.eclipse.jetty.toolchain.test.annotation.Slow;
 import org.eclipse.jetty.util.BufferUtil;
 import org.eclipse.jetty.util.FutureCallback;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
 import org.eclipse.jetty.util.thread.Scheduler;
 import org.eclipse.jetty.util.thread.TimerScheduler;
 import org.junit.After;
@@ -132,6 +128,7 @@
 
         assertEquals(true,endp.flush(BufferUtil.EMPTY_BUFFER,BufferUtil.toBuffer(" and"),BufferUtil.toBuffer(" more")));
         assertEquals("some output some more and more",endp.getOutputString());
+        endp.close();
     }
 
     @Test
@@ -150,6 +147,7 @@
 
         assertEquals(true,endp.flush(data));
         assertEquals("data.",BufferUtil.toString(endp.takeOutput()));
+        endp.close();
     }
 
 
@@ -237,6 +235,7 @@
         assertTrue(fcb.isDone());
         assertEquals(null, fcb.get());
         assertEquals(" more.", endp.getOutputString());
+        endp.close();
     }
     
     /**
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationParser.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationParser.java
index b1ccba2..8887036 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationParser.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationParser.java
@@ -35,6 +35,7 @@
 import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelper;
 import org.eclipse.jetty.util.ConcurrentHashSet;
 import org.eclipse.jetty.util.resource.Resource;
+import org.objectweb.asm.Opcodes;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.Constants;
 
@@ -50,6 +51,14 @@
     private ConcurrentHashMap<Resource, Bundle> _resourceToBundle = new ConcurrentHashMap<Resource, Bundle>();
     private ConcurrentHashMap<Bundle,URI> _bundleToUri = new ConcurrentHashMap<Bundle, URI>();
     
+    static
+    {
+        //As of jetty 9.2.0, the impl of asm visitor classes is compatible with both asm4 and asm5.
+        //We need to use asm4 with osgi, because we need to use aries spifly to support annotations,
+        //and currently this only supports asm4. Therefore, we set the asm api version to be 4 for osgi.
+        ASM_OPCODE_VERSION = Opcodes.ASM4;
+    }
+    
     /**
      * Keep track of a jetty URI Resource and its associated OSGi bundle.
      * @param uri
diff --git a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ProxyServlet.java b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ProxyServlet.java
index 3323f1c..8d7c543 100644
--- a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ProxyServlet.java
+++ b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ProxyServlet.java
@@ -48,7 +48,6 @@
 import org.eclipse.jetty.client.util.InputStreamContentProvider;
 import org.eclipse.jetty.http.HttpField;
 import org.eclipse.jetty.http.HttpHeader;
-import org.eclipse.jetty.http.HttpMethod;
 import org.eclipse.jetty.http.HttpVersion;
 import org.eclipse.jetty.util.Callback;
 import org.eclipse.jetty.util.HttpCookieStore;
@@ -392,7 +391,7 @@
         }
 
         final Request proxyRequest = _client.newRequest(rewrittenURI)
-                .method(HttpMethod.fromString(request.getMethod()))
+                .method(request.getMethod())
                 .version(HttpVersion.fromString(request.getProtocol()));
 
         // Copy headers
@@ -434,10 +433,10 @@
         asyncContext.setTimeout(0);
         request.setAttribute(ASYNC_CONTEXT, asyncContext);
 
-        customizeProxyRequest(proxyRequest, request);
-
         if (hasContent)
-            proxyRequest.content(proxyRequestContent(asyncContext, requestId));
+            proxyRequest.content(proxyRequestContent(proxyRequest, request));
+
+        customizeProxyRequest(proxyRequest, request);
 
         if (_log.isDebugEnabled())
         {
@@ -476,9 +475,8 @@
         proxyRequest.send(new ProxyResponseListener(request, response));
     }
 
-    protected ContentProvider proxyRequestContent(final AsyncContext asyncContext, final int requestId) throws IOException
+    protected ContentProvider proxyRequestContent(Request proxyRequest, final HttpServletRequest request) throws IOException
     {
-        final HttpServletRequest request = (HttpServletRequest)asyncContext.getRequest();
         return new InputStreamContentProvider(request.getInputStream())
         {
             @Override
@@ -490,7 +488,7 @@
             @Override
             protected ByteBuffer onRead(byte[] buffer, int offset, int length)
             {
-                _log.debug("{} proxying content to upstream: {} bytes", requestId, length);
+                _log.debug("{} proxying content to upstream: {} bytes", getRequestId(request), length);
                 return super.onRead(buffer, offset, length);
             }
         };
@@ -609,7 +607,8 @@
      * <li>proxyTo - a mandatory URI like http://host:80/context to which the request is proxied.</li>
      * <li>prefix - an optional URI prefix that is stripped from the start of the forwarded URI.</li>
      * </ul>
-     * For example, if a request is received at /foo/bar and the 'proxyTo' parameter is "http://host:80/context"
+     * <p/>
+     * For example, if a request is received at "/foo/bar", the 'proxyTo' parameter is "http://host:80/context"
      * and the 'prefix' parameter is "/foo", then the request would be proxied to "http://host:80/context/bar".
      */
     public static class Transparent extends ProxyServlet
diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintSecurityHandler.java b/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintSecurityHandler.java
index d8ad639..201618d 100644
--- a/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintSecurityHandler.java
+++ b/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintSecurityHandler.java
@@ -45,6 +45,7 @@
 import org.eclipse.jetty.server.Request;
 import org.eclipse.jetty.server.Response;
 import org.eclipse.jetty.server.UserIdentity;
+import org.eclipse.jetty.server.handler.ContextHandler;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 import org.eclipse.jetty.util.security.Constraint;
@@ -820,7 +821,9 @@
         if (paths != null && !paths.isEmpty())
         {
             for (String p:paths)
-                LOG.warn("Path with uncovered http methods: {}",p);
+                LOG.warn("{} has uncovered http methods for path: {}",ContextHandler.getCurrentContext(), p);
+            if (LOG.isDebugEnabled())
+                LOG.debug(new Throwable());
             return true;
         }
         return false; 
diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/HashLoginService.java b/jetty-security/src/main/java/org/eclipse/jetty/security/HashLoginService.java
index 55f7ed2..335aabd 100644
--- a/jetty-security/src/main/java/org/eclipse/jetty/security/HashLoginService.java
+++ b/jetty-security/src/main/java/org/eclipse/jetty/security/HashLoginService.java
@@ -133,6 +133,7 @@
     /**
      * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart()
      */
+    @Override
     protected void doStart() throws Exception
     {
         super.doStart();
@@ -154,6 +155,7 @@
     /**
      * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStop()
      */
+    @Override
     protected void doStop() throws Exception
     {
         super.doStop();
@@ -163,6 +165,7 @@
     }
     
     /* ------------------------------------------------------------ */
+    @Override
     public void update(String userName, Credential credential, String[] roleArray)
     {
         if (LOG.isDebugEnabled())
@@ -171,6 +174,7 @@
     }
 
     /* ------------------------------------------------------------ */
+    @Override
     public void remove(String userName)
     {
         if (LOG.isDebugEnabled())
diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/SecurityHandler.java b/jetty-security/src/main/java/org/eclipse/jetty/security/SecurityHandler.java
index 90c0d1b..a6e108e 100644
--- a/jetty-security/src/main/java/org/eclipse/jetty/security/SecurityHandler.java
+++ b/jetty-security/src/main/java/org/eclipse/jetty/security/SecurityHandler.java
@@ -43,6 +43,7 @@
 import org.eclipse.jetty.server.handler.ContextHandler.Context;
 import org.eclipse.jetty.server.handler.HandlerWrapper;
 import org.eclipse.jetty.server.session.AbstractSession;
+import org.eclipse.jetty.util.component.LifeCycle;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 
@@ -74,8 +75,6 @@
     private LoginService _loginService;
     private IdentityService _identityService;
     private boolean _renewSession=true;
-    private boolean _discoveredIdentityService = false;
-    private boolean _discoveredLoginService = false;
 
     /* ------------------------------------------------------------ */
     protected SecurityHandler()
@@ -266,20 +265,24 @@
     }
 
     /* ------------------------------------------------------------ */
-    protected LoginService findLoginService()
+    protected LoginService findLoginService() throws Exception
     {
         Collection<LoginService> list = getServer().getBeans(LoginService.class);
-
+        LoginService service = null;
         String realm=getRealmName();
         if (realm!=null)
         {
-            for (LoginService service : list)
-                if (service.getName()!=null && service.getName().equals(realm))
-                    return service;
+            for (LoginService s : list)
+                if (s.getName()!=null && s.getName().equals(realm))
+                {
+                    service=s;
+                    break;
+                }
         }
         else if (list.size()==1)
-            return list.iterator().next();
-        return null;
+            service =  list.iterator().next();
+        
+        return service;
     }
 
     /* ------------------------------------------------------------ */
@@ -342,9 +345,10 @@
         if (_loginService==null)
         {
             setLoginService(findLoginService());
-            _discoveredLoginService = true;
+            if (_loginService!=null)
+                unmanage(_loginService);
         }
-
+        
         if (_identityService==null)
         {
             if (_loginService!=null)
@@ -353,10 +357,16 @@
             if (_identityService==null)
                 setIdentityService(findIdentityService());
 
-            if (_identityService==null && _realmName!=null)
-                setIdentityService(new DefaultIdentityService());
-
-            _discoveredIdentityService = true;
+            if (_identityService==null)
+            {
+                if (_realmName!=null)
+                { 
+                    setIdentityService(new DefaultIdentityService());
+                    manage(_identityService);
+                }
+            }
+            else
+                unmanage(_identityService);
         }
 
         if (_loginService!=null)
@@ -387,17 +397,16 @@
     protected void doStop() throws Exception
     {
         //if we discovered the services (rather than had them explicitly configured), remove them.
-        if (_discoveredIdentityService)
+        if (!isManaged(_identityService))
         {
             removeBean(_identityService);
-            _identityService = null;
-            
+            _identityService = null;   
         }
         
-        if (_discoveredLoginService)
+        if (!isManaged(_loginService))
         {
             removeBean(_loginService);
-            _loginService = null;
+            _loginService=null;
         }
         
         super.doStop();
@@ -427,6 +436,7 @@
     /**
      * @see org.eclipse.jetty.security.Authenticator.AuthConfiguration#isSessionRenewedOnAuthentication()
      */
+    @Override
     public boolean isSessionRenewedOnAuthentication()
     {
         return _renewSession;
@@ -473,7 +483,7 @@
             {
                 if (!baseRequest.isHandled())
                 {
-                    response.sendError(Response.SC_FORBIDDEN);
+                    response.sendError(HttpServletResponse.SC_FORBIDDEN);
                     baseRequest.setHandled(true);
                 }
                 return;
@@ -488,7 +498,7 @@
                 LOG.warn("No authenticator for: "+roleInfo);
                 if (!baseRequest.isHandled())
                 {
-                    response.sendError(Response.SC_FORBIDDEN);
+                    response.sendError(HttpServletResponse.SC_FORBIDDEN);
                     baseRequest.setHandled(true);
                 }
                 return;
@@ -524,7 +534,7 @@
                         boolean authorized=checkWebResourcePermissions(pathInContext, baseRequest, base_response, roleInfo, userAuth.getUserIdentity());
                         if (!authorized)
                         {
-                            response.sendError(Response.SC_FORBIDDEN, "!role");
+                            response.sendError(HttpServletResponse.SC_FORBIDDEN, "!role");
                             baseRequest.setHandled(true);
                             return;
                         }
@@ -574,7 +584,7 @@
             {
                 // jaspi 3.8.3 send HTTP 500 internal server error, with message
                 // from AuthException
-                response.sendError(Response.SC_INTERNAL_SERVER_ERROR, e.getMessage());
+                response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
             }
             finally
             {
@@ -634,6 +644,7 @@
     /* ------------------------------------------------------------ */
     public class NotChecked implements Principal
     {
+        @Override
         public String getName()
         {
             return null;
@@ -656,6 +667,7 @@
     /* ------------------------------------------------------------ */
     public static final Principal __NO_USER = new Principal()
     {
+        @Override
         public String getName()
         {
             return null;
@@ -680,6 +692,7 @@
      */
     public static final Principal __NOBODY = new Principal()
     {
+        @Override
         public String getName()
         {
             return "Nobody";
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 87f62c2..352af25 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
@@ -52,7 +52,7 @@
     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
+    private boolean _sendDateHeader = true; //send Date: header
 
     public interface Customizer
     {
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java
index a1f9a8b..e5d31f0 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java
@@ -288,6 +288,14 @@
         }
     }
 
+    public boolean isAsync()
+    {
+        synchronized (lock())
+        {
+            return _contentState==ASYNC;
+        }
+    }
+    
     /**
      * @return whether an EOF has been detected, even though there may be content to consume.
      */
@@ -436,6 +444,7 @@
             input.blockForContent();
         }
 
+        @Override
         public String toString()
         {
             return "STREAM";
@@ -471,6 +480,7 @@
             return true;
         }
 
+        @Override
         public String toString()
         {
             return "EARLY_EOF";
@@ -485,6 +495,7 @@
             return true;
         }
 
+        @Override
         public String toString()
         {
             return "EOF";
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 610db58..7c0fd89 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
@@ -74,7 +74,7 @@
     write completed    -          -          -          ASYNC         READY->owp    -
     
     */
-    enum OutputState { OPEN, ASYNC, READY, PENDING, UNREADY, CLOSED }
+    enum OutputState { OPEN, ASYNC, READY, PENDING, UNREADY, ERROR, CLOSED }
     private final AtomicReference<OutputState> _state=new AtomicReference<>(OutputState.OPEN);
 
     public HttpOutput(HttpChannel<?> channel)
@@ -146,7 +146,9 @@
                     break loop;
                     
                 case UNREADY:
-                    throw new WritePendingException(); // TODO ?
+                    if (_state.compareAndSet(state,OutputState.ERROR))
+                        _writeListener.onError(_onError==null?new EofException("Async close"):_onError);
+                    continue;
                     
                 default:
                     if (_state.compareAndSet(state,OutputState.CLOSED))
@@ -179,7 +181,9 @@
                     break loop;
                     
                 case UNREADY:
-                    throw new WritePendingException(); // TODO ?
+                    if (_state.compareAndSet(state,OutputState.ERROR))
+                        _writeListener.onError(_onError==null?new EofException("Async closed"):_onError);
+                    continue;
                     
                 default:
                     if (_state.compareAndSet(state,OutputState.CLOSED))
@@ -238,6 +242,9 @@
                 case UNREADY:
                     throw new WritePendingException();
 
+                case ERROR:
+                    throw new EofException(_onError);
+                    
                 case CLOSED:
                     return;
             }
@@ -298,6 +305,9 @@
                 case UNREADY:
                     throw new WritePendingException();
 
+                case ERROR:
+                    throw new EofException(_onError);
+                    
                 case CLOSED:
                     throw new EofException("Closed");
             }
@@ -396,6 +406,9 @@
                 case UNREADY:
                     throw new WritePendingException();
 
+                case ERROR:
+                    throw new EofException(_onError);
+                    
                 case CLOSED:
                     throw new EofException("Closed");
             }
@@ -476,6 +489,9 @@
                 case UNREADY:
                     throw new WritePendingException();
 
+                case ERROR:
+                    throw new EofException(_onError);
+                    
                 case CLOSED:
                     throw new EofException("Closed");
             }
@@ -615,6 +631,8 @@
                     if (!_state.compareAndSet(OutputState.OPEN, OutputState.PENDING))
                         continue;
                     break;
+                case ERROR:
+                    throw new EofException(_onError);
                 case CLOSED:
                     throw new EofException("Closed");
                 default:
@@ -706,6 +724,9 @@
                     return false;
                 case UNREADY:
                     return false;
+
+                case ERROR:
+                    return true;
                     
                 case CLOSED:
                     return true;
@@ -716,45 +737,55 @@
     @Override
     public void run()
     {
-        if(_onError!=null)
+        loop: while (true)
         {
-            Throwable th=_onError;
-            _onError=null;
-            _writeListener.onError(new IOException(th));
-            close();
-        }
-        
-        switch(_state.get())
-        {
-            case READY:
-                try
+            OutputState state = _state.get();
+
+            if(_onError!=null)
+            {
+                switch(state)
                 {
-                    _writeListener.onWritePossible();
+                    case CLOSED:
+                    case ERROR:
+                        _onError=null;
+                        break loop;
+
+                    default:
+                        if (_state.compareAndSet(state, OutputState.ERROR))
+                        {
+                            Throwable th=_onError;
+                            _onError=null;
+                            LOG.debug("onError",th);
+                            _writeListener.onError(th);
+                            close();
+
+                            break loop;
+                        }
+
                 }
-                catch (Throwable e)
-                {
-                    _writeListener.onError(e);
-                    close();
-                }
-                break;
-                
-            case CLOSED:
-                try
-                {
-                    new Throwable().printStackTrace();
+                continue loop;
+            }
+
+            switch(_state.get())
+            {
+                case READY:
+                case CLOSED:
                     // even though a write is not possible, because a close has 
                     // occurred, we need to call onWritePossible to tell async
                     // producer that the last write completed.
-                    _writeListener.onWritePossible();
-                }
-                catch (Throwable e)
-                {
-                    _writeListener.onError(e);
-                }
-                break;
-                
-            default:
-                    
+                    try
+                    {
+                        _writeListener.onWritePossible();
+                        break loop;
+                    }
+                    catch (Throwable e)
+                    {
+                        _onError=e;
+                    }
+                    break;
+                default:
+
+            }
         }
     }
     
@@ -769,37 +800,29 @@
         @Override
         protected void completed()
         {
-            try
+            while(true)
             {
-                while(true)
+                OutputState last=_state.get();
+                switch(last)
                 {
-                    HttpOutput.OutputState last=_state.get();
-                    switch(last)
-                    {
-                        case PENDING:
-                            if (!_state.compareAndSet(HttpOutput.OutputState.PENDING, HttpOutput.OutputState.ASYNC))
-                                continue;
-                            break;
+                    case PENDING:
+                        if (!_state.compareAndSet(OutputState.PENDING, OutputState.ASYNC))
+                            continue;
+                        break;
 
-                        case UNREADY:
-                            if (!_state.compareAndSet(HttpOutput.OutputState.UNREADY, HttpOutput.OutputState.READY))
-                                continue;
-                            _channel.getState().onWritePossible();
-                            break;
+                    case UNREADY:
+                        if (!_state.compareAndSet(OutputState.UNREADY, OutputState.READY))
+                            continue;
+                        _channel.getState().onWritePossible();
+                        break;
 
-                        case CLOSED:
-                            break;
+                    case CLOSED:
+                        break;
 
-                        default:
-                            throw new IllegalStateException();
-                    }
-                    break;
+                    default:
+                        throw new IllegalStateException();
                 }
-            }
-            catch (Exception e)
-            {
-                _onError=e;
-                _channel.getState().onWritePossible();
+                break;
             }
         }
 
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 df679b4..adf325e 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
@@ -339,6 +339,8 @@
                                 throw new IllegalStateException("Form too large " + content_length + ">" + maxFormContentSize);
                             }
                             InputStream in = getInputStream();
+                            if (_input.isAsync())
+                                throw new IllegalStateException("Cannot extract parameters with async IO");
 
                             // Add form params to query params
                             UrlEncoded.decodeTo(in,_baseParameters,encoding,content_length < 0?maxFormContentSize:-1,maxFormKeys);
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 b6b3331..333aecd 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
@@ -1384,8 +1384,9 @@
 
     /* ------------------------------------------------------------ */
     /**
-     * @param base
-     *            The resourceBase to set.
+     * Set the base resource for this context.
+     * @param base The resource used as the base for all static content of this context.
+     * @see #setResourceBase(String)
      */
     public void setBaseResource(Resource base)
     {
@@ -1393,9 +1394,11 @@
     }
 
     /* ------------------------------------------------------------ */
-    /**
-     * @param resourceBase
-     *            The base resource as a string.
+    /** 
+     * Set the base resource for this context.
+     * @param resourceBase A string representing the base resource for the context. Any string accepted 
+     * by {@link Resource#newResource(String)} may be passed and the call is equivalent to 
+     * <code>setBaseResource(newResource(resourceBase));</code>
      */
     public void setResourceBase(String resourceBase)
     {
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 f76df68..1f966c5 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
@@ -26,7 +26,6 @@
 import java.util.EventListener;
 import java.util.HashSet;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.CopyOnWriteArrayList;
 
@@ -49,7 +48,7 @@
 import org.eclipse.jetty.util.annotation.ManagedAttribute;
 import org.eclipse.jetty.util.annotation.ManagedObject;
 import org.eclipse.jetty.util.annotation.ManagedOperation;
-import org.eclipse.jetty.util.component.AbstractLifeCycle;
+import org.eclipse.jetty.util.component.ContainerLifeCycle;
 import org.eclipse.jetty.util.log.Logger;
 import org.eclipse.jetty.util.statistic.CounterStatistic;
 import org.eclipse.jetty.util.statistic.SampleStatistic;
@@ -66,7 +65,7 @@
  */
 @SuppressWarnings("deprecation")
 @ManagedObject("Abstract Session Manager")
-public abstract class AbstractSessionManager extends AbstractLifeCycle implements SessionManager
+public abstract class AbstractSessionManager extends ContainerLifeCycle implements SessionManager
 {
     final static Logger __log = SessionHandler.LOG;
 
@@ -82,11 +81,13 @@
 
     static final HttpSessionContext __nullSessionContext=new HttpSessionContext()
     {
+        @Override
         public HttpSession getSession(String sessionId)
         {
             return null;
         }
 
+        @Override
         @SuppressWarnings({ "rawtypes", "unchecked" })
         public Enumeration getIds()
         {
@@ -162,6 +163,7 @@
     }
 
     /* ------------------------------------------------------------ */
+    @Override
     public HttpCookie access(HttpSession session,boolean secure)
     {
         long now=System.currentTimeMillis();
@@ -187,6 +189,7 @@
     }
 
     /* ------------------------------------------------------------ */
+    @Override
     public void addEventListener(EventListener listener)
     {
         if (listener instanceof HttpSessionAttributeListener)
@@ -195,17 +198,22 @@
             _sessionListeners.add((HttpSessionListener)listener);
         if (listener instanceof HttpSessionIdListener)
             _sessionIdListeners.add((HttpSessionIdListener)listener);
+        addBean(listener,false);
     }
 
     /* ------------------------------------------------------------ */
+    @Override
     public void clearEventListeners()
     {
+        for (EventListener e :getBeans(EventListener.class))
+            removeBean(e);
         _sessionAttributeListeners.clear();
         _sessionListeners.clear();
         _sessionIdListeners.clear();
     }
 
     /* ------------------------------------------------------------ */
+    @Override
     public void complete(HttpSession session)
     {
         AbstractSession s = ((SessionIf)session).getSession();
@@ -237,17 +245,20 @@
                         Thread.currentThread().setContextClassLoader(serverLoader);
                         _sessionIdManager=new HashSessionIdManager();
                         server.setSessionIdManager(_sessionIdManager);
+                        server.manage(_sessionIdManager);
+                        _sessionIdManager.start();
                     }
                     finally
                     {
                         Thread.currentThread().setContextClassLoader(_loader);
                     }
                 }
+
+                // server session id is never managed by this manager
+                addBean(_sessionIdManager,false);
             }
         }
         
-        if (!_sessionIdManager.isStarted())
-            _sessionIdManager.start();
 
         // Look for a session cookie name
         if (_context!=null)
@@ -299,6 +310,7 @@
     /**
      * @return Returns the httpOnly.
      */
+    @Override
     @ManagedAttribute("true if cookies use the http only flag")
     public boolean getHttpOnly()
     {
@@ -306,6 +318,7 @@
     }
 
     /* ------------------------------------------------------------ */
+    @Override
     public HttpSession getHttpSession(String nodeId)
     {
         String cluster_id = getSessionIdManager().getClusterId(nodeId);
@@ -318,18 +331,9 @@
 
     /* ------------------------------------------------------------ */
     /**
-     * @return Returns the metaManager used for cross context session management
-     * @deprecated Use {@link #getSessionIdManager()}
-     */
-    public SessionIdManager getIdManager()
-    {
-        return getSessionIdManager();
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
      * @return Returns the SessionIdManager used for cross context session management
      */
+    @Override
     @ManagedAttribute("Session ID Manager")
     public SessionIdManager getSessionIdManager()
     {
@@ -350,16 +354,6 @@
 
     /* ------------------------------------------------------------ */
     /**
-     * @see #getSessionsMax()
-     */
-    @Deprecated
-    public int getMaxSessions()
-    {
-        return getSessionsMax();
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
      * @return maximum number of sessions
      */
     @ManagedAttribute("maximum number of simultaneous sessions")
@@ -379,33 +373,12 @@
     }
 
     /* ------------------------------------------------------------ */
-    /**
-     * @deprecated use {@link #getSessionIdManager()}
-     */
-    @Deprecated
-    public SessionIdManager getMetaManager()
-    {
-        return getSessionIdManager();
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
-     * @deprecated always returns 0. no replacement available.
-     */
-    @Deprecated
-    public int getMinSessions()
-    {
-        return 0;
-    }
-
-    /* ------------------------------------------------------------ */
     @ManagedAttribute("time before a session cookie is re-set (in s)")
     public int getRefreshCookieAge()
     {
         return _refreshCookieAge;
     }
 
-
     /* ------------------------------------------------------------ */
     /**
      * @return same as SessionCookieConfig.getSecure(). If true, session
@@ -438,8 +411,6 @@
         _secureRequestOnly = secureRequestOnly;
     }
 
-
-
     /* ------------------------------------------------------------ */
     @ManagedAttribute("the set session cookie")
     public String getSessionCookie()
@@ -473,6 +444,7 @@
      *
      * @see org.eclipse.jetty.server.SessionManager#getSessionCookie(javax.servlet.http.HttpSession, java.lang.String, boolean)
      */
+    @Override
     public HttpCookie getSessionCookie(HttpSession session, String contextPath, boolean requestIsSecure)
     {
         if (isUsingCookies())
@@ -527,18 +499,6 @@
     }
 
     /* ------------------------------------------------------------ */
-    /**
-     * @deprecated  Need to review if it is needed.
-     */
-    @SuppressWarnings("rawtypes")
-    public Map getSessionMap()
-    {
-        throw new UnsupportedOperationException();
-    }
-
-
-
-    /* ------------------------------------------------------------ */
     @ManagedAttribute("number of currently active sessions")
     public int getSessions()
     {
@@ -546,6 +506,7 @@
     }
 
     /* ------------------------------------------------------------ */
+    @Override
     @ManagedAttribute("name of use for URL session tracking")
     public String getSessionIdPathParameterName()
     {
@@ -553,6 +514,7 @@
     }
 
     /* ------------------------------------------------------------ */
+    @Override
     public String getSessionIdPathParameterNamePrefix()
     {
         return _sessionIdPathParameterNamePrefix;
@@ -562,12 +524,14 @@
     /**
      * @return Returns the usingCookies.
      */
+    @Override
     public boolean isUsingCookies()
     {
         return _usingCookies;
     }
 
     /* ------------------------------------------------------------ */
+    @Override
     public boolean isValid(HttpSession session)
     {
         AbstractSession s = ((SessionIf)session).getSession();
@@ -575,6 +539,7 @@
     }
 
     /* ------------------------------------------------------------ */
+    @Override
     public String getClusterId(HttpSession session)
     {
         AbstractSession s = ((SessionIf)session).getSession();
@@ -582,6 +547,7 @@
     }
 
     /* ------------------------------------------------------------ */
+    @Override
     public String getNodeId(HttpSession session)
     {
         AbstractSession s = ((SessionIf)session).getSession();
@@ -592,6 +558,7 @@
     /**
      * Create a new HttpSession for a request
      */
+    @Override
     public HttpSession newHttpSession(HttpServletRequest request)
     {
         AbstractSession session=newSession(request);
@@ -601,6 +568,7 @@
     }
 
     /* ------------------------------------------------------------ */
+    @Override
     public void removeEventListener(EventListener listener)
     {
         if (listener instanceof HttpSessionAttributeListener)
@@ -608,17 +576,7 @@
         if (listener instanceof HttpSessionListener)
             _sessionListeners.remove(listener);
     }
-
-    /* ------------------------------------------------------------ */
-    /**
-     * @see #statsReset()
-     */
-    @Deprecated
-    public void resetStats()
-    {
-        statsReset();
-    }
-
+    
     /* ------------------------------------------------------------ */
     /**
      * Reset statistics values
@@ -643,54 +601,42 @@
     /* ------------------------------------------------------------ */
     /**
      * @param metaManager The metaManager used for cross context session management.
-     * @deprecated use {@link #setSessionIdManager(SessionIdManager)}
      */
-    public void setIdManager(SessionIdManager metaManager)
-    {
-        setSessionIdManager(metaManager);
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
-     * @param metaManager The metaManager used for cross context session management.
-     */
+    @Override
     public void setSessionIdManager(SessionIdManager metaManager)
     {
+        updateBean(_sessionIdManager, metaManager);
         _sessionIdManager=metaManager;
     }
 
-
-
     /* ------------------------------------------------------------ */
     /**
      * @param seconds
      */
+    @Override
     public void setMaxInactiveInterval(int seconds)
     {
         _dftMaxIdleSecs=seconds;
     }
 
-
     /* ------------------------------------------------------------ */
     public void setRefreshCookieAge(int ageInSeconds)
     {
         _refreshCookieAge=ageInSeconds;
     }
 
-
-
+    /* ------------------------------------------------------------ */
     public void setSessionCookie(String cookieName)
     {
         _sessionCookie=cookieName;
     }
 
-
-
     /* ------------------------------------------------------------ */
     /**
      * @param sessionHandler
      *            The sessionHandler to set.
      */
+    @Override
     public void setSessionHandler(SessionHandler sessionHandler)
     {
         _sessionHandler=sessionHandler;
@@ -698,6 +644,7 @@
 
 
     /* ------------------------------------------------------------ */
+    @Override
     public void setSessionIdPathParameterName(String param)
     {
         _sessionIdPathParameterName =(param==null||"none".equals(param))?null:param;
@@ -844,12 +791,14 @@
     }
 
     /* ------------------------------------------------------------ */
+    @Override
     public Set<SessionTrackingMode> getDefaultSessionTrackingModes()
     {
         return __defaultSessionTrackingModes;
     }
 
     /* ------------------------------------------------------------ */
+    @Override
     public Set<SessionTrackingMode> getEffectiveSessionTrackingModes()
     {
         return Collections.unmodifiableSet(_sessionTrackingModes);
@@ -871,8 +820,8 @@
         return _usingURLs;
     }
 
-
     /* ------------------------------------------------------------ */
+    @Override
     public SessionCookieConfig getSessionCookieConfig()
     {
         return _cookieConfig;
@@ -917,6 +866,7 @@
     /**
      * @see org.eclipse.jetty.server.SessionManager#isCheckingRemoteSessionIdEncoding()
      */
+    @Override
     @ManagedAttribute("check remote session id encoding")
     public boolean isCheckingRemoteSessionIdEncoding()
     {
@@ -927,6 +877,7 @@
     /**
      * @see org.eclipse.jetty.server.SessionManager#setCheckingRemoteSessionIdEncoding(boolean)
      */
+    @Override
     public void setCheckingRemoteSessionIdEncoding(boolean remote)
     {
         _checkingRemoteSessionIdEncoding=remote;
@@ -1091,4 +1042,11 @@
             }
         }
     }
+
+    @Override
+    @Deprecated
+    public SessionIdManager getMetaManager()
+    {
+        throw new UnsupportedOperationException();
+    }
 }
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionManager.java
index beda6ce..1effd32 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionManager.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionManager.java
@@ -34,6 +34,7 @@
 import javax.servlet.ServletContext;
 import javax.servlet.http.HttpServletRequest;
 
+import org.eclipse.jetty.server.SessionIdManager;
 import org.eclipse.jetty.server.handler.ContextHandler;
 import org.eclipse.jetty.util.ClassLoadingObjectInputStream;
 import org.eclipse.jetty.util.IO;
@@ -60,9 +61,7 @@
     final static Logger LOG = SessionHandler.LOG;
 
     protected final ConcurrentMap<String,HashedSession> _sessions=new ConcurrentHashMap<String,HashedSession>();
-    private static int __id;
     private Scheduler _timer;
-    private boolean _timerStop=false;
     private Scheduler.Task _task;
     long _scavengePeriodMs=30000;
     long _savePeriodMs=0; //don't do period saves by default
@@ -134,14 +133,11 @@
     @Override
     public void doStart() throws Exception
     {
-        super.doStart();
-
-        _timerStop=false;
         //try shared scheduler from Server first
         _timer = getSessionHandler().getServer().getBean(Scheduler.class);
         if (_timer == null)
         {
-            //try one passwed into the context
+            //try one passed into the context
             ServletContext context = ContextHandler.getCurrentContext();
             if (context!=null)
                 _timer = (Scheduler)context.getAttribute("org.eclipse.jetty.server.session.timer");   
@@ -149,12 +145,14 @@
       
         if (_timer == null)
         {
-          //make a scheduler if none useable
-            _timerStop=true;
-            _timer=new ScheduledExecutorScheduler();
-            _timer.start();
+            //make a scheduler if none useable
+            _timer=new ScheduledExecutorScheduler(toString()+"Timer",true);
+            addBean(_timer,true);
         }
-     
+        else
+            addBean(_timer,false);
+            
+        super.doStart();
 
         setScavengePeriod(getScavengePeriod());
 
@@ -186,8 +184,6 @@
             if (_task!=null)
                 _task.cancel();
             _task=null;
-            if (_timer!=null && _timerStop)
-                _timer.stop();
             _timer=null;
         }
 
@@ -427,7 +423,7 @@
     /* ------------------------------------------------------------ */
     @Override
     protected void shutdownSessions() throws Exception
-    {
+    {   
         // Invalidate all sessions to cause unbind events
         ArrayList<HashedSession> sessions=new ArrayList<HashedSession>(_sessions.values());
         int loop=100;
@@ -672,7 +668,6 @@
         if (size>0)
         {
             ClassLoadingObjectInputStream ois =  new ClassLoadingObjectInputStream(is);
-        
             for (int i=0; i<size;i++)
             {
                 String key = ois.readUTF();
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/AsyncRequestReadTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/AsyncRequestReadTest.java
index c8a4321..2c9fd57 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/AsyncRequestReadTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/AsyncRequestReadTest.java
@@ -59,6 +59,7 @@
         server = new Server();
         connector = new ServerConnector(server);
         connector.setIdleTimeout(10000);
+        connector.getConnectionFactory(HttpConnectionFactory.class).getHttpConfiguration().setSendDateHeader(false);
         server.addConnector(connector);
     }
 
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestFixture.java b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestFixture.java
index 1636cd4..abce05f 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestFixture.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestFixture.java
@@ -68,6 +68,7 @@
     protected void startServer(ServerConnector connector) throws Exception
     {
         _connector = connector;
+        _connector.getConnectionFactory(HttpConnectionFactory.class).getHttpConfiguration().setSendDateHeader(false);
         _server.addConnector(_connector);
         _server.setHandler(new HandlerWrapper());
         _server.start();
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 451cb00..ebd686d 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
@@ -23,6 +23,7 @@
 import org.eclipse.jetty.server.Request;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+import org.eclipse.jetty.util.IO;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.StdErrLog;
 import org.junit.After;
@@ -94,6 +95,7 @@
     public void testHashSession() throws Exception
     {
         File testDir = MavenTestingUtils.getTargetTestingDir("saved");
+        IO.delete(testDir);
         testDir.mkdirs();
         
         Server server = new Server();
@@ -109,10 +111,12 @@
         AbstractSessionIdManager idManager = new HashSessionIdManager();
         idManager.setWorkerName("foo");
         manager.setSessionIdManager(idManager);
+        server.setSessionIdManager(idManager);
         
-        idManager.start();
+        server.start();
         manager.start();
         
+        
         HashedSession session = (HashedSession)manager.newHttpSession(new Request(null, null));
         String sessionId = session.getId();
         
@@ -120,14 +124,12 @@
         session.setAttribute("two", new Integer(2));    
         
         //stop will persist sessions
-        idManager.stop();
         manager.setMaxInactiveInterval(30); //change max inactive interval for *new* sessions
         manager.stop();
         
         Assert.assertTrue("File should exist!", new File(testDir, session.getId()).exists());
         
         //start will restore sessions
-        idManager.start();
         manager.start();
         
         HashedSession restoredSession = (HashedSession)manager.getSession(sessionId);
@@ -138,5 +140,7 @@
         
         Assert.assertEquals(1, ((Integer)o).intValue());
         Assert.assertEquals(5, restoredSession.getMaxInactiveInterval());     
+        
+        server.stop();
     }
 }
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SSLEngineTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SSLEngineTest.java
index 29db142..564e9c1 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SSLEngineTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SSLEngineTest.java
@@ -109,6 +109,7 @@
         http.getHttpConfiguration().setRequestHeaderSize(512);
         connector=new ServerConnector(server, sslContextFactory, http);
         connector.setPort(0);
+        connector.getConnectionFactory(HttpConnectionFactory.class).getHttpConfiguration().setSendDateHeader(false);
 
         server.addConnector(connector);
     }
diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java
index 09e57cb..6f0e5b1 100644
--- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java
+++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java
@@ -89,6 +89,10 @@
  * Unless run as part of a {@link ServletContextHandler} or derivative, the {@link #initialize()}
  * method must be called manually after start().
  */
+
+/* ------------------------------------------------------------ */
+/**
+ */
 @ManagedObject("Servlet Handler")
 public class ServletHandler extends ScopedHandler
 {
@@ -107,6 +111,7 @@
     private boolean _filterChainsCached=true;
     private int _maxFilterChainsCacheSize=512;
     private boolean _startWithUnavailable=false;
+    private boolean _ensureDefaultServlet=true;
     private IdentityService _identityService;
 
     private ServletHolder[] _servlets=new ServletHolder[0];
@@ -153,7 +158,7 @@
         updateNameMappings();
         updateMappings();        
         
-        if (getServletMapping("/")==null)
+        if (getServletMapping("/")==null && _ensureDefaultServlet)
         {
             LOG.debug("Adding Default404Servlet to {}",this);
             addServletWithMapping(Default404Servlet.class,"/");
@@ -183,6 +188,26 @@
     }
     
     
+    /* ------------------------------------------------------------ */
+    /**
+     * @return true if ServletHandler always has a default servlet, using {@link Default404Servlet} if no other
+     * default servlet is configured.
+     */
+    public boolean isEnsureDefaultServlet()
+    {
+        return _ensureDefaultServlet;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param ensureDefaultServlet true if ServletHandler always has a default servlet, using {@link Default404Servlet} if no other
+     * default servlet is configured.
+     */
+    public void setEnsureDefaultServlet(boolean ensureDefaultServlet)
+    {
+        _ensureDefaultServlet=ensureDefaultServlet;
+    }
+
     /* ----------------------------------------------------------------- */
     @Override
     protected void start(LifeCycle l) throws Exception
@@ -379,8 +404,6 @@
     {
         return _servletMappings;
     }
-
-   
     
     /* ------------------------------------------------------------ */
     /**
@@ -397,9 +420,6 @@
         return _servletPathMappings.get(pathSpec);
     }
     
-
- 
-    
     /* ------------------------------------------------------------ */
     /** Get Servlets.
      * @return Array of defined servlets
@@ -528,12 +548,7 @@
         try
         {
             if (servlet_holder==null)
-            {
-                if (getHandler()==null)
-                    notFound(request, response);
-                else
-                    nextHandle(target,baseRequest,request,response);
-            }
+                notFound(baseRequest,request, response);
             else
             {
                 // unwrap any tunnelling of base Servlet request/responses
@@ -1511,11 +1526,11 @@
     }
 
     /* ------------------------------------------------------------ */
-    protected void notFound(HttpServletRequest request, HttpServletResponse response) throws IOException
+    protected void notFound(Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
     {
-        if(LOG.isDebugEnabled())
-            LOG.debug("Not Found "+request.getRequestURI());
-        //Override to send an error back, eg with: response.sendError(HttpServletResponse.SC_NOT_FOUND);
+        LOG.debug("Not Found {}",request.getRequestURI());
+        if (getHandler()!=null)
+            nextHandle(URIUtil.addPaths(request.getServletPath(),request.getPathInfo()),baseRequest,request,response);
     }
 
     /* ------------------------------------------------------------ */
@@ -1615,8 +1630,7 @@
             // pass to next filter
             if (_filterHolder!=null)
             {
-                if (LOG.isDebugEnabled())
-                    LOG.debug("call filter " + _filterHolder);
+                LOG.debug("call filter {}", _filterHolder);
                 Filter filter= _filterHolder.getFilter();
                 if (_filterHolder.isAsyncSupported())
                     filter.doFilter(request, response, _next);
@@ -1642,20 +1656,15 @@
             }
 
             // Call servlet
-
             HttpServletRequest srequest = (HttpServletRequest)request;
-            if (_servletHolder != null)
+            if (_servletHolder == null)
+                notFound(baseRequest, srequest, (HttpServletResponse)response);
+            else
             {
                 if (LOG.isDebugEnabled())
                     LOG.debug("call servlet " + _servletHolder);
                 _servletHolder.handle(baseRequest,request, response);
             }
-            else if (getHandler()==null)
-                notFound(srequest, (HttpServletResponse)response);
-            else
-                nextHandle(URIUtil.addPaths(srequest.getServletPath(),srequest.getPathInfo()),
-                           baseRequest,srequest,(HttpServletResponse)response);
-
         }
 
         @Override
@@ -1724,20 +1733,13 @@
 
             // Call servlet
             HttpServletRequest srequest = (HttpServletRequest)request;
-            if (_servletHolder != null)
-            {
-                if (LOG.isDebugEnabled())
-                    LOG.debug("call servlet " + _servletHolder);
-                _servletHolder.handle(_baseRequest,request, response);
-            }
-            else if (getHandler()==null)
-                notFound(srequest, (HttpServletResponse)response);
+            if (_servletHolder == null)
+                notFound((request instanceof Request)?((Request)request):HttpChannel.getCurrentHttpChannel().getRequest(), srequest, (HttpServletResponse)response);
             else
             {
-                Request baseRequest=(request instanceof Request)?((Request)request):HttpChannel.getCurrentHttpChannel().getRequest();
-                nextHandle(URIUtil.addPaths(srequest.getServletPath(),srequest.getPathInfo()),
-                           baseRequest,srequest,(HttpServletResponse)response);
-            }
+                LOG.debug("call servlet {}", _servletHolder);
+                _servletHolder.handle(_baseRequest,request, response);
+            }    
         }
 
         /* ------------------------------------------------------------ */
@@ -1795,6 +1797,7 @@
     /* ------------------------------------------------------------ */
     public static class Default404Servlet extends HttpServlet
     {
+        @Override
         protected void doGet(HttpServletRequest req, HttpServletResponse resp)
                 throws ServletException, IOException
         {
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncContextTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncContextTest.java
index bbcb708..abfe555 100644
--- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncContextTest.java
+++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncContextTest.java
@@ -40,6 +40,7 @@
 
 import org.eclipse.jetty.server.Connector;
 import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.HttpConnectionFactory;
 import org.eclipse.jetty.server.LocalConnector;
 import org.eclipse.jetty.server.QuietServletException;
 import org.eclipse.jetty.server.Request;
@@ -70,6 +71,7 @@
         _contextHandler = new ServletContextHandler(ServletContextHandler.NO_SESSIONS);
         _connector = new LocalConnector(_server);
         _connector.setIdleTimeout(5000);
+        _connector.getConnectionFactory(HttpConnectionFactory.class).getHttpConfiguration().setSendDateHeader(false);
         _server.setConnectors(new Connector[]
         { _connector });
 
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncIOServletTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncIOServletTest.java
index 563b895..b37f94c 100644
--- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncIOServletTest.java
+++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncIOServletTest.java
@@ -18,11 +18,17 @@
 
 package org.eclipse.jetty.servlet;
 
+import static org.hamcrest.Matchers.*;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertThat;
+
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.OutputStream;
 import java.net.Socket;
+import java.nio.charset.StandardCharsets;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -30,6 +36,7 @@
 import javax.servlet.AsyncContext;
 import javax.servlet.ReadListener;
 import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
 import javax.servlet.WriteListener;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
@@ -39,6 +46,7 @@
 import org.eclipse.jetty.server.ServerConnector;
 import org.eclipse.jetty.toolchain.test.http.SimpleHttpParser;
 import org.eclipse.jetty.toolchain.test.http.SimpleHttpResponse;
+import org.hamcrest.Matchers;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Test;
@@ -56,7 +64,7 @@
         connector = new ServerConnector(server);
         server.addConnector(connector);
 
-        context = new ServletContextHandler(server, "", false, false);
+        context = new ServletContextHandler(server, "/", false, false);
         ServletHolder holder = new ServletHolder(servlet);
         holder.setAsyncSupported(true);
         context.addServlet(holder, path);
@@ -111,7 +119,8 @@
                     @Override
                     public void onError(Throwable t)
                     {
-                        Assert.assertSame(throwable, t);
+                        Assert.assertThat("onError type",t,instanceOf(throwable.getClass()));
+                        Assert.assertThat("onError message",t.getMessage(),is(throwable.getMessage()));
                         latch.countDown();
                         response.setStatus(500);
                         asyncContext.complete();
@@ -231,10 +240,10 @@
                     @Override
                     public void onError(Throwable t)
                     {
-                        Assert.assertSame(throwable, t);
                         latch.countDown();
                         response.setStatus(500);
                         asyncContext.complete();
+                        Assert.assertSame(throwable, t);
                     }
                 });
             }
@@ -257,4 +266,81 @@
             Assert.assertEquals("500", response.getCode());
         }
     }
+    
+
+    @Test
+    public void testAsyncWriteClosed() throws Exception
+    {
+        final CountDownLatch latch = new CountDownLatch(1);
+        String text = "Now is the winter of our discontent. How Now Brown Cow. The quick brown fox jumped over the lazy dog.\n";
+        for (int i=0;i<10;i++)
+            text=text+text;
+        final byte[] data = text.getBytes(StandardCharsets.ISO_8859_1);
+        
+        startServer(new HttpServlet()
+        {
+            @Override
+            protected void service(HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException
+            {
+                response.flushBuffer();
+                
+                final AsyncContext async = request.startAsync();
+                final ServletOutputStream out = response.getOutputStream();
+                out.setWriteListener(new WriteListener()
+                {
+                    @Override
+                    public void onWritePossible() throws IOException
+                    {
+                        while (out.isReady())
+                        {
+                            try
+                            {
+                                Thread.sleep(100);
+                                out.write(data);
+                            }
+                            catch(IOException e)
+                            {
+                                throw e;
+                            }
+                            catch(Exception e)
+                            {
+                                e.printStackTrace();
+                            }
+                        }
+                    }
+
+                    @Override
+                    public void onError(Throwable t)
+                    {
+                        async.complete();
+                        latch.countDown();
+                    }
+                });
+            }
+        });
+
+        String request = "GET " + path + " HTTP/1.1\r\n" +
+                "Host: localhost:" + connector.getLocalPort() + "\r\n" +
+                "\r\n";
+
+        try (Socket client = new Socket("localhost", connector.getLocalPort()))
+        {
+            OutputStream output = client.getOutputStream();
+            output.write(request.getBytes("UTF-8"));
+            output.flush();
+
+            BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
+            String line=in.readLine();
+            assertThat(line, containsString("200 OK"));
+            while (line.length()>0)
+                line=in.readLine();
+            line=in.readLine();
+            assertThat(line, not(containsString(" ")));
+            line=in.readLine();
+            assertThat(line, containsString("discontent. How Now Brown Cow. The "));
+        }
+        
+        if (!latch.await(5, TimeUnit.SECONDS))
+            Assert.fail();
+    }
 }
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DispatcherTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DispatcherTest.java
index 6bc920f..8ed551d 100644
--- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DispatcherTest.java
+++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DispatcherTest.java
@@ -78,6 +78,7 @@
         _server = new Server();
         _connector = new LocalConnector(_server);
         _connector.getConnectionFactory(HttpConfiguration.ConnectionFactory.class).getHttpConfiguration().setSendServerVersion(false);
+        _connector.getConnectionFactory(HttpConfiguration.ConnectionFactory.class).getHttpConfiguration().setSendDateHeader(false);
 
         _contextCollection = new ContextHandlerCollection();
         _contextHandler = new ServletContextHandler();
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/InvokerTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/InvokerTest.java
index 13dface..d94f6cd 100644
--- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/InvokerTest.java
+++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/InvokerTest.java
@@ -49,6 +49,7 @@
         _server = new Server();
         _connector = new LocalConnector(_server);
         _connector.getConnectionFactory(HttpConfiguration.ConnectionFactory.class).getHttpConfiguration().setSendServerVersion(false);
+        _connector.getConnectionFactory(HttpConfiguration.ConnectionFactory.class).getHttpConfiguration().setSendDateHeader(false);
         ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
 
         _server.addConnector(_connector);
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletContextHandlerTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletContextHandlerTest.java
index c4b4699..7ebbe2b 100644
--- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletContextHandlerTest.java
+++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletContextHandlerTest.java
@@ -40,13 +40,18 @@
 
 import org.eclipse.jetty.security.ConstraintSecurityHandler;
 import org.eclipse.jetty.security.SecurityHandler;
+import org.eclipse.jetty.server.Handler;
 import org.eclipse.jetty.server.LocalConnector;
+import org.eclipse.jetty.server.Request;
 import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.handler.AbstractHandler;
 import org.eclipse.jetty.server.handler.AbstractHandlerContainer;
 import org.eclipse.jetty.server.handler.ContextHandler;
 import org.eclipse.jetty.server.handler.ContextHandlerCollection;
+import org.eclipse.jetty.server.handler.HandlerList;
 import org.eclipse.jetty.server.handler.ResourceHandler;
 import org.eclipse.jetty.server.session.SessionHandler;
+import org.eclipse.jetty.util.component.LifeCycle.Listener;
 import org.hamcrest.Matchers;
 import org.junit.After;
 import org.junit.Assert;
@@ -276,7 +281,37 @@
         assertTrue(contextDestroy.get());
     }
     
-    
+
+    @Test
+    public void testFallThrough() throws Exception
+    {
+        HandlerList list = new HandlerList();
+        _server.setHandler(list);
+
+        ServletContextHandler root = new ServletContextHandler(list,"/",ServletContextHandler.SESSIONS);
+
+        ServletHandler servlet = root.getServletHandler();
+        servlet.setEnsureDefaultServlet(false);
+        servlet.addServletWithMapping(HelloServlet.class, "/hello/*");
+        
+        list.addHandler(new AbstractHandler()
+        {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+            {
+                response.sendError(404, "Fell Through");
+            }
+        });
+
+        _server.start();
+
+        String response= _connector.getResponses("GET /hello HTTP/1.0\r\n\r\n");
+        Assert.assertThat(response, Matchers.containsString("200 OK"));
+        
+        response= _connector.getResponses("GET /other HTTP/1.0\r\n\r\n");
+        Assert.assertThat(response, Matchers.containsString("404 Fell Through"));
+        
+    }
 
     private int assertResponseContains(String expected, String response)
     {
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/PathMatchers.java b/jetty-start/src/main/java/org/eclipse/jetty/start/PathMatchers.java
index 609c8b2..58ce0ca 100644
--- a/jetty-start/src/main/java/org/eclipse/jetty/start/PathMatchers.java
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/PathMatchers.java
@@ -47,7 +47,7 @@
             }
         }
     }
-    
+
     private static final char GLOB_CHARS[] = "*?".toCharArray();
     private static final char SYNTAXED_GLOB_CHARS[] = "{}[]|:".toCharArray();
     private static final Path EMPTY_PATH = new File(".").toPath();
@@ -167,7 +167,12 @@
      */
     public static boolean isAbsolute(final String pattern)
     {
-        return asPath(pattern).isAbsolute();
+        Path searchRoot = getSearchRoot(pattern);
+        if (searchRoot == EMPTY_PATH)
+        {
+            return false;
+        }
+        return searchRoot.isAbsolute();
     }
 
     /**
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/log/Log.java b/jetty-util/src/main/java/org/eclipse/jetty/util/log/Log.java
index 7d998b3..57838f2 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/log/Log.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/log/Log.java
@@ -151,33 +151,32 @@
 
     public static void initialized()
     {   
-       
         synchronized (Log.class)
         {
             if (__initialized)
                 return;
             __initialized = true;
-        }
-        
-        final long uptime=ManagementFactory.getRuntimeMXBean().getUptime();
 
-        try
-        {
-            Class<?> log_class = Loader.loadClass(Log.class, __logClass);
-            if (LOG == null || !LOG.getClass().equals(log_class))
+            final long uptime=ManagementFactory.getRuntimeMXBean().getUptime();
+
+            try
             {
-                LOG = (Logger)log_class.newInstance();
-                LOG.debug("Logging to {} via {}", LOG, log_class.getName());
+                Class<?> log_class = Loader.loadClass(Log.class, __logClass);
+                if (LOG == null || !LOG.getClass().equals(log_class))
+                {
+                    LOG = (Logger)log_class.newInstance();
+                    LOG.debug("Logging to {} via {}", LOG, log_class.getName());
+                }
             }
+            catch(Throwable e)
+            {
+                // Unable to load specified Logger implementation, default to standard logging.
+                initStandardLogging(e);
+            }
+
+            if (LOG!=null)
+                LOG.info(String.format("Logging initialized @%dms",uptime));
         }
-        catch(Throwable e)
-        {
-            // Unable to load specified Logger implementation, default to standard logging.
-            initStandardLogging(e);
-        }
-        
-        if (LOG!=null)
-            LOG.info(String.format("Logging initialized @%dms",uptime));
     }
 
     private static void initStandardLogging(Throwable e)
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 401e75a..623731a 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
@@ -46,6 +46,10 @@
 /* ------------------------------------------------------------ */
 /** 
  * Abstract resource class.
+ * <p>
+ * This class provides a resource abstraction, where a resource may be
+ * a file, a URL or an entry in a jar file.
+ * </p>
  */
 public abstract class Resource implements ResourceFactory, Closeable
 {
diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/StandardDescriptorProcessor.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/StandardDescriptorProcessor.java
index 06cd46f..ad7bbd7 100644
--- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/StandardDescriptorProcessor.java
+++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/StandardDescriptorProcessor.java
@@ -1417,12 +1417,13 @@
             {
                 jsp_pg_servlet=new ServletHolder(JspPropertyGroupServlet.NAME,new JspPropertyGroupServlet(context,handler));
                 _servletHolderMap.put(JspPropertyGroupServlet.NAME,jsp_pg_servlet);
+                _servletHolders.add(jsp_pg_servlet);
             }
 
             ServletMapping mapping = new ServletMapping();
             mapping.setServletName(JspPropertyGroupServlet.NAME);
             mapping.setPathSpecs(paths.toArray(new String[paths.size()]));
-            context.getServletHandler().addServletMapping(mapping);
+            _servletMappings.add(mapping);
         }
     }
 
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/AbstractJsrEventDriver.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/AbstractJsrEventDriver.java
index 3b91b38..d7b9ced 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/AbstractJsrEventDriver.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/AbstractJsrEventDriver.java
@@ -31,11 +31,10 @@
 import org.eclipse.jetty.websocket.common.CloseInfo;
 import org.eclipse.jetty.websocket.common.WebSocketSession;
 import org.eclipse.jetty.websocket.common.events.AbstractEventDriver;
-import org.eclipse.jetty.websocket.common.events.EventDriver;
 import org.eclipse.jetty.websocket.jsr356.JsrSession;
 import org.eclipse.jetty.websocket.jsr356.metadata.EndpointMetadata;
 
-public abstract class AbstractJsrEventDriver extends AbstractEventDriver implements EventDriver
+public abstract class AbstractJsrEventDriver extends AbstractEventDriver
 {
     protected final EndpointMetadata metadata;
     protected final EndpointConfig config;
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/JsrAnnotatedEventDriver.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/JsrAnnotatedEventDriver.java
index a550c20..0cf9df7 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/JsrAnnotatedEventDriver.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/JsrAnnotatedEventDriver.java
@@ -23,6 +23,7 @@
 import java.io.Reader;
 import java.nio.ByteBuffer;
 import java.util.Map;
+
 import javax.websocket.CloseReason;
 import javax.websocket.DecodeException;
 
@@ -31,7 +32,6 @@
 import org.eclipse.jetty.util.log.Logger;
 import org.eclipse.jetty.websocket.api.WebSocketPolicy;
 import org.eclipse.jetty.websocket.api.extensions.Frame;
-import org.eclipse.jetty.websocket.common.events.EventDriver;
 import org.eclipse.jetty.websocket.common.message.MessageInputStream;
 import org.eclipse.jetty.websocket.common.message.MessageReader;
 import org.eclipse.jetty.websocket.common.message.SimpleBinaryMessage;
@@ -44,7 +44,7 @@
 /**
  * Base implementation for JSR-356 Annotated event drivers.
  */
-public class JsrAnnotatedEventDriver extends AbstractJsrEventDriver implements EventDriver
+public class JsrAnnotatedEventDriver extends AbstractJsrEventDriver
 {
     private static final Logger LOG = Log.getLogger(JsrAnnotatedEventDriver.class);
     private final JsrEvents<?, ?> events;
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/JsrEndpointEventDriver.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/JsrEndpointEventDriver.java
index b977147..9ac4906 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/JsrEndpointEventDriver.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/JsrEndpointEventDriver.java
@@ -23,6 +23,7 @@
 import java.io.Reader;
 import java.nio.ByteBuffer;
 import java.util.Map;
+
 import javax.websocket.CloseReason;
 import javax.websocket.Endpoint;
 import javax.websocket.MessageHandler;
@@ -34,7 +35,6 @@
 import org.eclipse.jetty.util.log.Logger;
 import org.eclipse.jetty.websocket.api.WebSocketPolicy;
 import org.eclipse.jetty.websocket.api.extensions.Frame;
-import org.eclipse.jetty.websocket.common.events.EventDriver;
 import org.eclipse.jetty.websocket.common.message.MessageInputStream;
 import org.eclipse.jetty.websocket.common.message.MessageReader;
 import org.eclipse.jetty.websocket.jsr356.JsrPongMessage;
@@ -49,7 +49,7 @@
 /**
  * EventDriver for websocket that extend from {@link javax.websocket.Endpoint}
  */
-public class JsrEndpointEventDriver extends AbstractJsrEventDriver implements EventDriver
+public class JsrEndpointEventDriver extends AbstractJsrEventDriver
 {
     private static final Logger LOG = Log.getLogger(JsrEndpointEventDriver.class);
 
diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/ConfiguratorTest.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/ConfiguratorTest.java
index 045118a..f6cdb3b 100644
--- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/ConfiguratorTest.java
+++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/ConfiguratorTest.java
@@ -37,6 +37,7 @@
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.ServerConnector;
 import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.toolchain.test.EventQueue;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 import org.eclipse.jetty.websocket.api.util.QuoteUtil;
@@ -44,7 +45,6 @@
 import org.eclipse.jetty.websocket.common.frames.TextFrame;
 import org.eclipse.jetty.websocket.common.test.BlockheadClient;
 import org.eclipse.jetty.websocket.common.test.HttpResponse;
-import org.eclipse.jetty.websocket.common.test.IncomingFramesCapture;
 import org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer;
 import org.junit.AfterClass;
 import org.junit.Assert;
@@ -210,8 +210,8 @@
             client.expectUpgradeResponse();
 
             client.write(new TextFrame().setPayload("X-Dummy"));
-            IncomingFramesCapture capture = client.readFrames(1,TimeUnit.SECONDS,1);
-            WebSocketFrame frame = capture.getFrames().poll();
+            EventQueue<WebSocketFrame> frames = client.readFrames(1,1,TimeUnit.SECONDS);
+            WebSocketFrame frame = frames.poll();
             Assert.assertThat("Frame Response", frame.getPayloadAsUTF8(), is("Request Header [X-Dummy]: \"Bogus\""));
         }
     }
diff --git a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/ConnectionManager.java b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/ConnectionManager.java
index da112f9..ab20d77 100644
--- a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/ConnectionManager.java
+++ b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/ConnectionManager.java
@@ -32,6 +32,7 @@
 import org.eclipse.jetty.util.component.ContainerLifeCycle;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.websocket.api.StatusCode;
 import org.eclipse.jetty.websocket.api.WebSocketException;
 import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
 import org.eclipse.jetty.websocket.client.WebSocketClient;
@@ -158,7 +159,7 @@
         sessions.add(session);
     }
 
-    private void closeAllConnections()
+    private void shutdownAllConnections()
     {
         for (WebSocketSession session : sessions)
         {
@@ -166,11 +167,13 @@
             {
                 try
                 {
-                    session.getConnection().close();
+                    session.getConnection().close(
+                            StatusCode.SHUTDOWN,
+                            "Shutdown");
                 }
                 catch (Throwable t)
                 {
-                    LOG.debug("During Close All Connections",t);
+                    LOG.debug("During Shutdown All Connections",t);
                 }
             }
         }
@@ -203,7 +206,7 @@
     @Override
     protected void doStop() throws Exception
     {
-        closeAllConnections();
+        shutdownAllConnections();
         sessions.clear();
         super.doStop();
         removeBean(selector);
diff --git a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/WebSocketClientSelectorManager.java b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/WebSocketClientSelectorManager.java
index 928e911..6616f54 100644
--- a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/WebSocketClientSelectorManager.java
+++ b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/WebSocketClientSelectorManager.java
@@ -98,7 +98,7 @@
             else
             {
                 // Standard "ws://"
-                endPoint.setIdleTimeout(connectPromise.getClient().getMaxIdleTimeout());
+                endPoint.setIdleTimeout(connectPromise.getDriver().getPolicy().getIdleTimeout());
                 return newUpgradeConnection(channel,endPoint,connectPromise);
             }
         }
@@ -139,4 +139,9 @@
     {
         this.sslContextFactory = sslContextFactory;
     }
+    
+    public WebSocketPolicy getPolicy()
+    {
+        return policy;
+    }
 }
diff --git a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/ClientCloseTest.java b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/ClientCloseTest.java
new file mode 100644
index 0000000..2b1f824
--- /dev/null
+++ b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/ClientCloseTest.java
@@ -0,0 +1,624 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2014 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.websocket.client;
+
+import static org.hamcrest.Matchers.*;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.nio.ByteBuffer;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.SocketChannel;
+import java.util.Arrays;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.EofException;
+import org.eclipse.jetty.io.SelectChannelEndPoint;
+import org.eclipse.jetty.io.SelectorManager.ManagedSelector;
+import org.eclipse.jetty.toolchain.test.EventQueue;
+import org.eclipse.jetty.toolchain.test.TestTracker;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.thread.Scheduler;
+import org.eclipse.jetty.websocket.api.ProtocolException;
+import org.eclipse.jetty.websocket.api.Session;
+import org.eclipse.jetty.websocket.api.StatusCode;
+import org.eclipse.jetty.websocket.api.WebSocketAdapter;
+import org.eclipse.jetty.websocket.client.io.ConnectionManager;
+import org.eclipse.jetty.websocket.client.io.WebSocketClientSelectorManager;
+import org.eclipse.jetty.websocket.common.CloseInfo;
+import org.eclipse.jetty.websocket.common.OpCode;
+import org.eclipse.jetty.websocket.common.WebSocketFrame;
+import org.eclipse.jetty.websocket.common.WebSocketSession;
+import org.eclipse.jetty.websocket.common.frames.TextFrame;
+import org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection;
+import org.eclipse.jetty.websocket.common.test.BlockheadServer;
+import org.eclipse.jetty.websocket.common.test.BlockheadServer.ServerConnection;
+import org.eclipse.jetty.websocket.common.test.IncomingFramesCapture;
+import org.eclipse.jetty.websocket.common.test.RawFrameBuilder;
+import org.hamcrest.Matcher;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class ClientCloseTest
+{
+    private static final Logger LOG = Log.getLogger(ClientCloseTest.class);
+
+    private static class CloseTrackingSocket extends WebSocketAdapter
+    {
+        private static final Logger LOG = Log.getLogger(ClientCloseTest.CloseTrackingSocket.class);
+
+        public int closeCode = -1;
+        public String closeReason = null;
+        public CountDownLatch closeLatch = new CountDownLatch(1);
+        public CountDownLatch openLatch = new CountDownLatch(1);
+
+        public EventQueue<String> messageQueue = new EventQueue<>();
+        public EventQueue<Throwable> errorQueue = new EventQueue<>();
+
+        public void assertNoCloseEvent()
+        {
+            Assert.assertThat("Client Close Event",closeLatch.getCount(),is(1L));
+            Assert.assertThat("Client Close Event Status Code ",closeCode,is(-1));
+        }
+
+        public void assertReceivedCloseEvent(int clientTimeoutMs, Matcher<Integer> statusCodeMatcher, Matcher<String> reasonMatcher)
+                throws InterruptedException
+        {
+            long maxTimeout = clientTimeoutMs * 2;
+
+            Assert.assertThat("Client Close Event Occurred",closeLatch.await(maxTimeout,TimeUnit.MILLISECONDS),is(true));
+            Assert.assertThat("Client Close Event Status Code",closeCode,statusCodeMatcher);
+            if (reasonMatcher == null)
+            {
+                Assert.assertThat("Client Close Event Reason",closeReason,nullValue());
+            }
+            else
+            {
+                Assert.assertThat("Client Close Event Reason",closeReason,reasonMatcher);
+            }
+        }
+
+        public void assertReceivedError(Class<? extends Throwable> expectedThrownClass, Matcher<String> messageMatcher) throws TimeoutException,
+                InterruptedException
+        {
+            errorQueue.awaitEventCount(1,500,TimeUnit.MILLISECONDS);
+            Throwable actual = errorQueue.poll();
+            Assert.assertThat("Client Error Event",actual,instanceOf(expectedThrownClass));
+            if (messageMatcher == null)
+            {
+                Assert.assertThat("Client Error Event Message",actual.getMessage(),nullValue());
+            }
+            else
+            {
+                Assert.assertThat("Client Error Event Message",actual.getMessage(),messageMatcher);
+            }
+        }
+
+        public void clearQueues()
+        {
+            messageQueue.clear();
+            errorQueue.clear();
+        }
+
+        @Override
+        public void onWebSocketClose(int statusCode, String reason)
+        {
+            LOG.debug("onWebSocketClose({},{})",statusCode,reason);
+            super.onWebSocketClose(statusCode,reason);
+            closeCode = statusCode;
+            closeReason = reason;
+            closeLatch.countDown();
+        }
+
+        @Override
+        public void onWebSocketConnect(Session session)
+        {
+            super.onWebSocketConnect(session);
+            openLatch.countDown();
+        }
+
+        @Override
+        public void onWebSocketError(Throwable cause)
+        {
+            LOG.debug("onWebSocketError",cause);
+            Assert.assertThat("Error capture",errorQueue.offer(cause),is(true));
+        }
+
+        @Override
+        public void onWebSocketText(String message)
+        {
+            LOG.debug("onWebSocketText({})",message);
+            messageQueue.offer(message);
+        }
+
+        public EndPoint getEndPoint() throws Exception
+        {
+            Session session = getSession();
+            Assert.assertThat("Session type",session,instanceOf(WebSocketSession.class));
+
+            WebSocketSession wssession = (WebSocketSession)session;
+            Field fld = wssession.getClass().getDeclaredField("connection");
+            fld.setAccessible(true);
+            Assert.assertThat("Field: connection",fld,notNullValue());
+
+            Object val = fld.get(wssession);
+            Assert.assertThat("Connection type",val,instanceOf(AbstractWebSocketConnection.class));
+            @SuppressWarnings("resource")
+            AbstractWebSocketConnection wsconn = (AbstractWebSocketConnection)val;
+            return wsconn.getEndPoint();
+        }
+    }
+
+    @Rule
+    public TestTracker tt = new TestTracker();
+
+    private BlockheadServer server;
+    private WebSocketClient client;
+
+    private void confirmConnection(CloseTrackingSocket clientSocket, Future<Session> clientFuture, ServerConnection serverConn) throws Exception
+    {
+        // Wait for client connect on via future
+        clientFuture.get(500,TimeUnit.MILLISECONDS);
+
+        // Wait for client connect via client websocket
+        Assert.assertThat("Client WebSocket is Open",clientSocket.openLatch.await(500,TimeUnit.MILLISECONDS),is(true));
+
+        try
+        {
+            // Send message from client to server
+            final String echoMsg = "echo-test";
+            Future<Void> testFut = clientSocket.getRemote().sendStringByFuture(echoMsg);
+
+            // Wait for send future
+            testFut.get(500,TimeUnit.MILLISECONDS);
+
+            // Read Frame on server side
+            IncomingFramesCapture serverCapture = serverConn.readFrames(1,500,TimeUnit.MILLISECONDS);
+            serverCapture.assertNoErrors();
+            serverCapture.assertFrameCount(1);
+            WebSocketFrame frame = serverCapture.getFrames().poll();
+            Assert.assertThat("Server received frame",frame.getOpCode(),is(OpCode.TEXT));
+            Assert.assertThat("Server received frame payload",frame.getPayloadAsUTF8(),is(echoMsg));
+
+            // Server send echo reply
+            serverConn.write(new TextFrame().setPayload(echoMsg));
+
+            // Wait for received echo
+            clientSocket.messageQueue.awaitEventCount(1,1,TimeUnit.SECONDS);
+
+            // Verify received message
+            String recvMsg = clientSocket.messageQueue.poll();
+            Assert.assertThat("Received message",recvMsg,is(echoMsg));
+
+            // Verify that there are no errors
+            Assert.assertThat("Error events",clientSocket.errorQueue,empty());
+        }
+        finally
+        {
+            clientSocket.clearQueues();
+        }
+    }
+
+    private void confirmServerReceivedCloseFrame(ServerConnection serverConn, int expectedCloseCode, Matcher<String> closeReasonMatcher) throws IOException,
+            TimeoutException
+    {
+        IncomingFramesCapture serverCapture = serverConn.readFrames(1,500,TimeUnit.MILLISECONDS);
+        serverCapture.assertNoErrors();
+        serverCapture.assertFrameCount(1);
+        serverCapture.assertHasFrame(OpCode.CLOSE,1);
+        WebSocketFrame frame = serverCapture.getFrames().poll();
+        Assert.assertThat("Server received close frame",frame.getOpCode(),is(OpCode.CLOSE));
+        CloseInfo closeInfo = new CloseInfo(frame);
+        Assert.assertThat("Server received close code",closeInfo.getStatusCode(),is(expectedCloseCode));
+        if (closeReasonMatcher == null)
+        {
+            Assert.assertThat("Server received close reason",closeInfo.getReason(),nullValue());
+        }
+        else
+        {
+            Assert.assertThat("Server received close reason",closeInfo.getReason(),closeReasonMatcher);
+        }
+    }
+
+    public static class TestWebSocketClient extends WebSocketClient
+    {
+        @Override
+        protected ConnectionManager newConnectionManager()
+        {
+            return new TestConnectionManager(this);
+        }
+    }
+
+    public static class TestConnectionManager extends ConnectionManager
+    {
+        public TestConnectionManager(WebSocketClient client)
+        {
+            super(client);
+        }
+
+        @Override
+        protected WebSocketClientSelectorManager newWebSocketClientSelectorManager(WebSocketClient client)
+        {
+            return new TestSelectorManager(client);
+        }
+    }
+
+    public static class TestSelectorManager extends WebSocketClientSelectorManager
+    {
+        public TestSelectorManager(WebSocketClient client)
+        {
+            super(client);
+        }
+
+        @Override
+        protected EndPoint newEndPoint(SocketChannel channel, ManagedSelector selectSet, SelectionKey selectionKey) throws IOException
+        {
+            return new TestEndPoint(channel,selectSet,selectionKey,getScheduler(),getPolicy().getIdleTimeout());
+        }
+    }
+
+    public static class TestEndPoint extends SelectChannelEndPoint
+    {
+        public AtomicBoolean congestedFlush = new AtomicBoolean(false);
+
+        public TestEndPoint(SocketChannel channel, ManagedSelector selector, SelectionKey key, Scheduler scheduler, long idleTimeout)
+        {
+            super(channel,selector,key,scheduler,idleTimeout);
+        }
+
+        @Override
+        public boolean flush(ByteBuffer... buffers) throws IOException
+        {
+            boolean flushed = super.flush(buffers);
+            congestedFlush.set(!flushed);
+            return flushed;
+        }
+    }
+
+    @Before
+    public void startClient() throws Exception
+    {
+        client = new TestWebSocketClient();
+        client.start();
+    }
+
+    @Before
+    public void startServer() throws Exception
+    {
+        server = new BlockheadServer();
+        server.start();
+    }
+
+    @After
+    public void stopClient() throws Exception
+    {
+        if (client.isRunning())
+        {
+            client.stop();
+        }
+    }
+
+    @After
+    public void stopServer() throws Exception
+    {
+        server.stop();
+    }
+
+    @Test
+    public void testHalfClose() throws Exception
+    {
+        // Set client timeout
+        final int timeout = 1000;
+        client.setMaxIdleTimeout(timeout);
+
+        // Client connects
+        CloseTrackingSocket clientSocket = new CloseTrackingSocket();
+        Future<Session> clientConnectFuture = client.connect(clientSocket,server.getWsUri());
+
+        // Server accepts connect
+        ServerConnection serverConn = server.accept();
+        serverConn.upgrade();
+
+        // client confirms connection via echo
+        confirmConnection(clientSocket,clientConnectFuture,serverConn);
+
+        // client sends close frame (code 1000, normal)
+        final String origCloseReason = "Normal Close";
+        clientSocket.getSession().close(StatusCode.NORMAL,origCloseReason);
+
+        // server receives close frame
+        confirmServerReceivedCloseFrame(serverConn,StatusCode.NORMAL,is(origCloseReason));
+
+        // server sends 2 messages
+        serverConn.write(new TextFrame().setPayload("Hello"));
+        serverConn.write(new TextFrame().setPayload("World"));
+
+        // server sends close frame (code 1000, no reason)
+        CloseInfo sclose = new CloseInfo(StatusCode.NORMAL,"From Server");
+        serverConn.write(sclose.asFrame());
+
+        // client receives 2 messages
+        clientSocket.messageQueue.awaitEventCount(2,1,TimeUnit.SECONDS);
+
+        // Verify received messages
+        String recvMsg = clientSocket.messageQueue.poll();
+        Assert.assertThat("Received message 1",recvMsg,is("Hello"));
+        recvMsg = clientSocket.messageQueue.poll();
+        Assert.assertThat("Received message 2",recvMsg,is("World"));
+
+        // Verify that there are no errors
+        Assert.assertThat("Error events",clientSocket.errorQueue,empty());
+
+        // client close event on ws-endpoint
+        clientSocket.assertReceivedCloseEvent(timeout,is(StatusCode.NORMAL),containsString("From Server"));
+    }
+
+    @Test
+    public void testNetworkCongestion() throws Exception
+    {
+        // Set client timeout
+        final int timeout = 1000;
+        client.setMaxIdleTimeout(timeout);
+
+        // Client connects
+        CloseTrackingSocket clientSocket = new CloseTrackingSocket();
+        Future<Session> clientConnectFuture = client.connect(clientSocket,server.getWsUri());
+
+        // Server accepts connect
+        ServerConnection serverConn = server.accept();
+        serverConn.upgrade();
+
+        // client confirms connection via echo
+        confirmConnection(clientSocket,clientConnectFuture,serverConn);
+
+        // client sends BIG frames (until it cannot write anymore)
+        // server must not read (for test purpose, in order to congest connection)
+        // when write is congested, client enqueue close frame
+        // client initiate write, but write never completes
+        EndPoint endp = clientSocket.getEndPoint();
+        Assert.assertThat("EndPoint is testable",endp,instanceOf(TestEndPoint.class));
+        TestEndPoint testendp = (TestEndPoint)endp;
+
+        char msg[] = new char[10240];
+        int writeCount = 0;
+        long writeSize = 0;
+        int i = 0;
+        while (!testendp.congestedFlush.get())
+        {
+            int z = i - ((i / 26) * 26);
+            char c = (char)('a' + z);
+            Arrays.fill(msg,c);
+            clientSocket.getRemote().sendStringByFuture(String.valueOf(msg));
+            writeCount++;
+            writeSize += msg.length;
+        }
+        LOG.debug("Wrote {} frames totalling {} bytes of payload before congestion kicked in",writeCount,writeSize);
+
+        // Verify that there are no errors
+        Assert.assertThat("Error events",clientSocket.errorQueue,empty());
+
+        // client idle timeout triggers close event on client ws-endpoint
+        // client close event on ws-endpoint
+        clientSocket.assertReceivedCloseEvent(timeout,is(StatusCode.SHUTDOWN),containsString("Timeout"));
+    }
+
+    @Test
+    public void testProtocolException() throws Exception
+    {
+        // Set client timeout
+        final int timeout = 1000;
+        client.setMaxIdleTimeout(timeout);
+
+        // Client connects
+        CloseTrackingSocket clientSocket = new CloseTrackingSocket();
+        Future<Session> clientConnectFuture = client.connect(clientSocket,server.getWsUri());
+
+        // Server accepts connect
+        ServerConnection serverConn = server.accept();
+        serverConn.upgrade();
+
+        // client confirms connection via echo
+        confirmConnection(clientSocket,clientConnectFuture,serverConn);
+
+        // client should not have received close message (yet)
+        clientSocket.assertNoCloseEvent();
+
+        // server sends bad close frame (too big of a reason message)
+        byte msg[] = new byte[400];
+        Arrays.fill(msg,(byte)'x');
+        ByteBuffer bad = ByteBuffer.allocate(500);
+        RawFrameBuilder.putOpFin(bad,OpCode.CLOSE,true);
+        RawFrameBuilder.putLength(bad,msg.length + 2,false);
+        bad.putShort((short)StatusCode.NORMAL);
+        bad.put(msg);
+        BufferUtil.flipToFlush(bad,0);
+        serverConn.write(bad);
+
+        // client should have noticed the error
+        clientSocket.assertReceivedError(ProtocolException.class,containsString("Invalid control frame"));
+
+        // client parse invalid frame, notifies server of close (protocol error)
+        confirmServerReceivedCloseFrame(serverConn,StatusCode.PROTOCOL,allOf(containsString("Invalid control frame"),containsString("length")));
+
+        // server disconnects
+        serverConn.disconnect();
+
+        // client triggers close event on client ws-endpoint
+        clientSocket.assertReceivedCloseEvent(timeout,is(StatusCode.PROTOCOL),allOf(containsString("Invalid control frame"),containsString("length")));
+    }
+
+    @Test
+    public void testReadEOF() throws Exception
+    {
+        // Set client timeout
+        final int timeout = 1000;
+        client.setMaxIdleTimeout(timeout);
+
+        // Client connects
+        CloseTrackingSocket clientSocket = new CloseTrackingSocket();
+        Future<Session> clientConnectFuture = client.connect(clientSocket,server.getWsUri());
+
+        // Server accepts connect
+        ServerConnection serverConn = server.accept();
+        serverConn.upgrade();
+
+        // client confirms connection via echo
+        confirmConnection(clientSocket,clientConnectFuture,serverConn);
+
+        // client sends close frame
+        final String origCloseReason = "Normal Close";
+        clientSocket.getSession().close(StatusCode.NORMAL,origCloseReason);
+
+        // server receives close frame
+        confirmServerReceivedCloseFrame(serverConn,StatusCode.NORMAL,is(origCloseReason));
+
+        // client should not have received close message (yet)
+        clientSocket.assertNoCloseEvent();
+
+        // server shuts down connection (no frame reply)
+        serverConn.disconnect();
+
+        // client reads -1 (EOF)
+        // client triggers close event on client ws-endpoint
+        clientSocket.assertReceivedCloseEvent(timeout,is(StatusCode.ABNORMAL),containsString("EOF"));
+    }
+
+    @Test
+    public void testServerNoCloseHandshake() throws Exception
+    {
+        // Set client timeout
+        final int timeout = 1000;
+        client.setMaxIdleTimeout(timeout);
+
+        // Client connects
+        CloseTrackingSocket clientSocket = new CloseTrackingSocket();
+        Future<Session> clientConnectFuture = client.connect(clientSocket,server.getWsUri());
+
+        // Server accepts connect
+        ServerConnection serverConn = server.accept();
+        serverConn.upgrade();
+
+        // client confirms connection via echo
+        confirmConnection(clientSocket,clientConnectFuture,serverConn);
+
+        // client sends close frame
+        final String origCloseReason = "Normal Close";
+        clientSocket.getSession().close(StatusCode.NORMAL,origCloseReason);
+
+        // server receives close frame
+        confirmServerReceivedCloseFrame(serverConn,StatusCode.NORMAL,is(origCloseReason));
+
+        // client should not have received close message (yet)
+        clientSocket.assertNoCloseEvent();
+
+        // server never sends close frame handshake
+        // server sits idle
+
+        // client idle timeout triggers close event on client ws-endpoint
+        clientSocket.assertReceivedCloseEvent(timeout,is(StatusCode.SHUTDOWN),containsString("Timeout"));
+    }
+
+    @Test
+    public void testStopLifecycle() throws Exception
+    {
+        // Set client timeout
+        final int timeout = 1000;
+        client.setMaxIdleTimeout(timeout);
+
+        int clientCount = 3;
+        CloseTrackingSocket clientSockets[] = new CloseTrackingSocket[clientCount];
+        ServerConnection serverConns[] = new ServerConnection[clientCount];
+
+        // Connect Multiple Clients
+        for (int i = 0; i < clientCount; i++)
+        {
+            // Client Request Upgrade
+            clientSockets[i] = new CloseTrackingSocket();
+            Future<Session> clientConnectFuture = client.connect(clientSockets[i],server.getWsUri());
+
+            // Server accepts connection
+            serverConns[i] = server.accept();
+            serverConns[i].upgrade();
+
+            // client confirms connection via echo
+            confirmConnection(clientSockets[i],clientConnectFuture,serverConns[i]);
+        }
+
+        // client lifecycle stop
+        client.stop();
+
+        // clients send close frames (code 1001, shutdown)
+        for (int i = 0; i < clientCount; i++)
+        {
+            // server receives close frame
+            confirmServerReceivedCloseFrame(serverConns[i],StatusCode.SHUTDOWN,containsString("Shutdown"));
+        }
+
+        // clients disconnect
+        for (int i = 0; i < clientCount; i++)
+        {
+            clientSockets[i].assertReceivedCloseEvent(timeout,is(StatusCode.SHUTDOWN),containsString("Shutdown"));
+        }
+    }
+
+    @Test
+    public void testWriteException() throws Exception
+    {
+        // Set client timeout
+        final int timeout = 1000;
+        client.setMaxIdleTimeout(timeout);
+
+        // Client connects
+        CloseTrackingSocket clientSocket = new CloseTrackingSocket();
+        Future<Session> clientConnectFuture = client.connect(clientSocket,server.getWsUri());
+
+        // Server accepts connect
+        ServerConnection serverConn = server.accept();
+        serverConn.upgrade();
+
+        // client confirms connection via echo
+        confirmConnection(clientSocket,clientConnectFuture,serverConn);
+
+        // setup client endpoint for write failure (test only)
+        EndPoint endp = clientSocket.getEndPoint();
+        endp.shutdownOutput();
+
+        // client enqueue close frame
+        // client write failure
+        final String origCloseReason = "Normal Close";
+        clientSocket.getSession().close(StatusCode.NORMAL,origCloseReason);
+
+        clientSocket.assertReceivedError(EofException.class,null);
+
+        // client triggers close event on client ws-endpoint
+        // assert - close code==1006 (abnormal)
+        // assert - close reason message contains (write failure)
+        clientSocket.assertReceivedCloseEvent(timeout,is(StatusCode.ABNORMAL),containsString("EOF"));
+    }
+}
diff --git a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/ServerWriteThread.java b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/ServerWriteThread.java
index e06e883..2cea2fd 100644
--- a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/ServerWriteThread.java
+++ b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/ServerWriteThread.java
@@ -19,7 +19,6 @@
 package org.eclipse.jetty.websocket.client;
 
 import java.io.IOException;
-import java.util.concurrent.Exchanger;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 
@@ -32,7 +31,6 @@
 {
     private static final Logger LOG = Log.getLogger(ServerWriteThread.class);
     private final ServerConnection conn;
-    private Exchanger<String> exchanger;
     private int slowness = -1;
     private int messageCount = 100;
     private String message = "Hello";
@@ -42,11 +40,6 @@
         this.conn = conn;
     }
 
-    public Exchanger<String> getExchanger()
-    {
-        return exchanger;
-    }
-
     public String getMessage()
     {
         return message;
@@ -73,12 +66,6 @@
             {
                 conn.write(new TextFrame().setPayload(message));
 
-                if (exchanger != null)
-                {
-                    // synchronized on exchange
-                    exchanger.exchange(message);
-                }
-
                 m.incrementAndGet();
 
                 if (slowness > 0)
@@ -93,11 +80,6 @@
         }
     }
 
-    public void setExchanger(Exchanger<String> exchanger)
-    {
-        this.exchanger = exchanger;
-    }
-
     public void setMessage(String message)
     {
         this.message = message;
diff --git a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/SlowServerTest.java b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/SlowServerTest.java
index 80720f3..4f56ec6 100644
--- a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/SlowServerTest.java
+++ b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/SlowServerTest.java
@@ -49,7 +49,7 @@
     public void startClient() throws Exception
     {
         client = new WebSocketClient();
-        client.getPolicy().setIdleTimeout(60000);
+        client.setMaxIdleTimeout(60000);
         client.start();
     }
 
@@ -78,7 +78,7 @@
     {
         JettyTrackingSocket tsocket = new JettyTrackingSocket();
         client.setMasker(new ZeroMasker());
-        client.getPolicy().setIdleTimeout(60000);
+        client.setMaxIdleTimeout(60000);
 
         URI wsUri = server.getWsUri();
         Future<Session> future = client.connect(tsocket,wsUri);
@@ -123,41 +123,38 @@
     @Slow
     public void testServerSlowToSend() throws Exception
     {
-        // final Exchanger<String> exchanger = new Exchanger<String>();
-        JettyTrackingSocket tsocket = new JettyTrackingSocket();
-        // tsocket.messageExchanger = exchanger;
+        JettyTrackingSocket clientSocket = new JettyTrackingSocket();
         client.setMasker(new ZeroMasker());
-        client.getPolicy().setIdleTimeout(60000);
+        client.setMaxIdleTimeout(60000);
 
         URI wsUri = server.getWsUri();
-        Future<Session> future = client.connect(tsocket,wsUri);
+        Future<Session> clientConnectFuture = client.connect(clientSocket,wsUri);
 
-        ServerConnection sconnection = server.accept();
-        sconnection.setSoTimeout(60000);
-        sconnection.upgrade();
+        ServerConnection serverConn = server.accept();
+        serverConn.setSoTimeout(60000);
+        serverConn.upgrade();
 
         // Confirm connected
-        future.get(500,TimeUnit.MILLISECONDS);
-        tsocket.waitForConnected(500,TimeUnit.MILLISECONDS);
+        clientConnectFuture.get(500,TimeUnit.MILLISECONDS);
+        clientSocket.waitForConnected(500,TimeUnit.MILLISECONDS);
 
         // Have server write slowly.
         int messageCount = 1000;
 
-        ServerWriteThread writer = new ServerWriteThread(sconnection);
+        ServerWriteThread writer = new ServerWriteThread(serverConn);
         writer.setMessageCount(messageCount);
         writer.setMessage("Hello");
-        // writer.setExchanger(exchanger);
         writer.setSlowness(10);
         writer.start();
         writer.join();
 
         // Verify receive
-        Assert.assertThat("Message Receive Count",tsocket.messageQueue.size(),is(messageCount));
+        Assert.assertThat("Message Receive Count",clientSocket.messageQueue.size(),is(messageCount));
 
         // Close
-        sconnection.close(StatusCode.NORMAL);
+        serverConn.close(StatusCode.NORMAL);
 
-        Assert.assertTrue("Client Socket Closed",tsocket.closeLatch.await(10,TimeUnit.SECONDS));
-        tsocket.assertCloseCode(StatusCode.NORMAL);
+        Assert.assertTrue("Client Socket Closed",clientSocket.closeLatch.await(10,TimeUnit.SECONDS));
+        clientSocket.assertCloseCode(StatusCode.NORMAL);
     }
 }
diff --git a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/TimeoutTest.java b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/TimeoutTest.java
deleted file mode 100644
index 72c1fc1..0000000
--- a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/TimeoutTest.java
+++ /dev/null
@@ -1,115 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2014 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.websocket.client;
-
-import static org.hamcrest.Matchers.lessThanOrEqualTo;
-
-import java.net.URI;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-
-import org.eclipse.jetty.toolchain.test.TestTracker;
-import org.eclipse.jetty.websocket.api.Session;
-import org.eclipse.jetty.websocket.api.StatusCode;
-import org.eclipse.jetty.websocket.common.test.BlockheadServer;
-import org.eclipse.jetty.websocket.common.test.BlockheadServer.ServerConnection;
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-
-/**
- * Various tests for Timeout handling
- */
-public class TimeoutTest
-{
-    @Rule
-    public TestTracker tt = new TestTracker();
-
-    private BlockheadServer server;
-    private WebSocketClient client;
-
-    @Before
-    public void startClient() throws Exception
-    {
-        client = new WebSocketClient();
-        client.getPolicy().setIdleTimeout(250); // idle timeout (for all tests here)
-        client.start();
-    }
-
-    @Before
-    public void startServer() throws Exception
-    {
-        server = new BlockheadServer();
-        server.start();
-    }
-
-    @After
-    public void stopClient() throws Exception
-    {
-        client.stop();
-    }
-
-    @After
-    public void stopServer() throws Exception
-    {
-        server.stop();
-    }
-
-    /**
-     * In a situation where the upgrade/connection is successful, and there is no activity for a while, the idle timeout triggers on the client side and
-     * automatically initiates a close handshake.
-     */
-    @Test
-    public void testIdleDetectedByClient() throws Exception
-    {
-        JettyTrackingSocket wsocket = new JettyTrackingSocket();
-
-        URI wsUri = server.getWsUri();
-        client.setMaxIdleTimeout(1000);
-        Future<Session> future = client.connect(wsocket,wsUri);
-
-        ServerConnection ssocket = server.accept();
-        ssocket.upgrade();
-
-        try
-        {
-            ssocket.startEcho();
-            // Validate that connect occurred
-            future.get(500,TimeUnit.MILLISECONDS);
-            wsocket.waitForConnected(500,TimeUnit.MILLISECONDS);
-
-            // Wait for inactivity idle timeout.
-            long start = System.currentTimeMillis();
-            wsocket.waitForClose(2,TimeUnit.SECONDS);
-            long end = System.currentTimeMillis();
-            long dur = (end - start);
-            // Make sure idle timeout takes less than 5 total seconds
-            Assert.assertThat("Idle Timeout",dur,lessThanOrEqualTo(3000L));
-
-            // Client should see a close event, with abnormal status NO_CLOSE
-            wsocket.assertCloseCode(StatusCode.ABNORMAL);
-        }
-        finally
-        {
-            ssocket.stopEcho();
-        }
-    }
-}
diff --git a/jetty-websocket/websocket-client/src/test/resources/jetty-logging.properties b/jetty-websocket/websocket-client/src/test/resources/jetty-logging.properties
index 7c9bd36..9668b13 100644
--- a/jetty-websocket/websocket-client/src/test/resources/jetty-logging.properties
+++ b/jetty-websocket/websocket-client/src/test/resources/jetty-logging.properties
@@ -1,12 +1,17 @@
 org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
 org.eclipse.jetty.LEVEL=WARN
 # org.eclipse.jetty.LEVEL=DEBUG
-# org.eclipse.jetty.io.ChannelEndPoint.LEVEL=INFO
+# org.eclipse.jetty.io.ChannelEndPoint.LEVEL=DEBUG
+# org.eclipse.jetty.io.SelectChannelEndPoint.LEVEL=DEBUG
+# org.eclipse.jetty.io.IdleTimeout.LEVEL=DEBUG
+# org.eclipse.jetty.io.FillInterest.LEVEL=DEBUG
+# org.eclipse.jetty.io.AbstractConnection.LEVEL=DEBUG
 # org.eclipse.jetty.websocket.LEVEL=WARN
 # org.eclipse.jetty.websocket.LEVEL=DEBUG
 # org.eclipse.jetty.websocket.client.LEVEL=DEBUG
 # org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection.LEVEL=DEBUG
-# org.eclipse.jetty.websocket.common.io.WriteBytesProvider.LEVEL=DEBUG
+# org.eclipse.jetty.websocket.common.io.IOState.LEVEL=DEBUG
+
 # org.eclipse.jetty.websocket.common.Generator.LEVEL=DEBUG
 # org.eclipse.jetty.websocket.common.Parser.LEVEL=DEBUG
 # org.eclipse.jetty.websocket.client.TrackingSocket.LEVEL=DEBUG
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/CloseInfo.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/CloseInfo.java
index 03a737c..f14c252 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/CloseInfo.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/CloseInfo.java
@@ -20,6 +20,7 @@
 
 import java.nio.ByteBuffer;
 
+import org.eclipse.jetty.util.BufferUtil;
 import org.eclipse.jetty.util.StringUtil;
 import org.eclipse.jetty.util.Utf8Appendable.NotUtf8Exception;
 import org.eclipse.jetty.util.Utf8StringBuilder;
@@ -65,7 +66,8 @@
             statusCode |= (data.get() & 0xFF) << 8;
             statusCode |= (data.get() & 0xFF);
 
-            if(validate) {
+            if (validate)
+            {
                 if ((statusCode < StatusCode.NORMAL) || (statusCode == StatusCode.UNDEFINED) || (statusCode == StatusCode.NO_CLOSE)
                         || (statusCode == StatusCode.NO_CODE) || ((statusCode > 1011) && (statusCode <= 2999)) || (statusCode >= 5000))
                 {
@@ -120,7 +122,7 @@
 
     public CloseInfo(int statusCode)
     {
-        this(statusCode, null);
+        this(statusCode,null);
     }
 
     public CloseInfo(int statusCode, String reason)
@@ -144,8 +146,9 @@
             utf = StringUtil.getUtf8Bytes(reason);
             len += utf.length;
         }
-        
-        ByteBuffer buf = ByteBuffer.allocate(len);
+
+        ByteBuffer buf = BufferUtil.allocate(len);
+        BufferUtil.flipToFill(buf);
         buf.put((byte)((statusCode >>> 8) & 0xFF));
         buf.put((byte)((statusCode >>> 0) & 0xFF));
 
@@ -153,7 +156,7 @@
         {
             buf.put(utf,0,utf.length);
         }
-        buf.flip();
+        BufferUtil.flipToFlush(buf,0);
 
         return buf;
     }
@@ -162,7 +165,14 @@
     {
         CloseFrame frame = new CloseFrame();
         frame.setFin(true);
-        frame.setPayload(asByteBuffer());
+        if ((statusCode >= 1000) && (statusCode != StatusCode.NO_CLOSE) && (statusCode != StatusCode.NO_CODE))
+        {
+            if (statusCode == StatusCode.FAILED_TLS_HANDSHAKE)
+            {
+                throw new ProtocolException("Close Frame with status code " + statusCode + " not allowed (per RFC6455)");
+            }
+            frame.setPayload(asByteBuffer());
+        }
         return frame;
     }
 
@@ -180,10 +190,10 @@
     {
         return !((statusCode == StatusCode.NORMAL) || (statusCode == StatusCode.NO_CODE));
     }
-    
+
     public boolean isAbnormal()
     {
-        return (statusCode == StatusCode.ABNORMAL);
+        return (statusCode != StatusCode.NORMAL);
     }
 
     @Override
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/Parser.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/Parser.java
index 0cf7e5f..86195a0 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/Parser.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/Parser.java
@@ -96,8 +96,9 @@
 
     private void assertSanePayloadLength(long len)
     {
-        if (LOG.isDebugEnabled())
-            LOG.debug("Payload Length: {} - {}",len,this);
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("{} Payload Length: {} - {}",policy.getBehavior(),len,this);
+        }
 
         // Since we use ByteBuffer so often, having lengths over Integer.MAX_VALUE is really impossible.
         if (len > Integer.MAX_VALUE)
@@ -239,7 +240,7 @@
         incomingFramesHandler.incomingError(e);
     }
 
-    public void parse(ByteBuffer buffer)
+    public void parse(ByteBuffer buffer) throws WebSocketException
     {
         if (buffer.remaining() <= 0)
         {
@@ -266,13 +267,20 @@
         {
             buffer.position(buffer.limit()); // consume remaining
             reset();
+            // let session know
             notifyWebSocketException(e);
+            // need to throw for proper close behavior in connection
+            throw e;
         }
         catch (Throwable t)
         {
             buffer.position(buffer.limit()); // consume remaining
             reset();
-            notifyWebSocketException(new WebSocketException(t));
+            // let session know
+            WebSocketException e = new WebSocketException(t);
+            notifyWebSocketException(e);
+            // need to throw for proper close behavior in connection
+            throw e;
         }
     }
 
@@ -299,7 +307,9 @@
     private boolean parseFrame(ByteBuffer buffer)
     {
         if (LOG.isDebugEnabled())
+        {
             LOG.debug("{} Parsing {} bytes",policy.getBehavior(),buffer.remaining());
+        }
         while (buffer.hasRemaining())
         {
             switch (state)
@@ -318,7 +328,8 @@
                     }
                     
                     if (LOG.isDebugEnabled())
-                        LOG.debug("OpCode {}, fin={} rsv={}{}{}",
+                        LOG.debug("{} OpCode {}, fin={} rsv={}{}{}",
+                                policy.getBehavior(),
                                 OpCode.name(opcode),
                                 fin,
                                 (isRsv1InUse()?'1':'.'),
@@ -412,11 +423,6 @@
                                 throw new ProtocolException("RSV3 not allowed to be set");   
                         }
                     }
-                    else
-                    {
-                        if (LOG.isDebugEnabled())
-                            LOG.debug("OpCode {}, fin={} rsv=000",OpCode.name(opcode),fin);
-                    }
                     
                     state = State.PAYLOAD_LEN;
                     break;
@@ -591,8 +597,9 @@
             buffer.limit(limit);
             buffer.position(buffer.position() + window.remaining());
 
-            if (LOG.isDebugEnabled())
-                LOG.debug("Window: {}",BufferUtil.toDetailString(window));
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("{} Window: {}",policy.getBehavior(),BufferUtil.toDetailString(window));
+            }
 
             maskProcessor.process(window);
 
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSession.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSession.java
index 460d1d7..04637e3 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSession.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSession.java
@@ -41,6 +41,7 @@
 import org.eclipse.jetty.websocket.api.SuspendToken;
 import org.eclipse.jetty.websocket.api.UpgradeRequest;
 import org.eclipse.jetty.websocket.api.UpgradeResponse;
+import org.eclipse.jetty.websocket.api.WebSocketBehavior;
 import org.eclipse.jetty.websocket.api.WebSocketException;
 import org.eclipse.jetty.websocket.api.WebSocketPolicy;
 import org.eclipse.jetty.websocket.api.extensions.ExtensionFactory;
@@ -90,20 +91,19 @@
     @Override
     public void close()
     {
-        this.close(StatusCode.NORMAL, null);
+        this.close(StatusCode.NORMAL,null);
     }
 
     @Override
     public void close(CloseStatus closeStatus)
     {
-        this.close(closeStatus.getCode(), closeStatus.getPhrase());
+        this.close(closeStatus.getCode(),closeStatus.getPhrase());
     }
 
     @Override
     public void close(int statusCode, String reason)
     {
-        connection.close(statusCode, reason);
-        notifyClose(statusCode, reason);
+        connection.close(statusCode,reason);
     }
 
     /**
@@ -115,7 +115,7 @@
         connection.disconnect();
 
         // notify of harsh disconnect
-        notifyClose(StatusCode.NO_CLOSE, "Harsh disconnect");
+        notifyClose(StatusCode.NO_CLOSE,"Harsh disconnect");
     }
 
     public void dispatch(Runnable runnable)
@@ -130,7 +130,7 @@
         out.append(indent).append(" +- incomingHandler : ");
         if (incomingHandler instanceof Dumpable)
         {
-            ((Dumpable)incomingHandler).dump(out, indent + "    ");
+            ((Dumpable)incomingHandler).dump(out,indent + "    ");
         }
         else
         {
@@ -140,7 +140,7 @@
         out.append(indent).append(" +- outgoingHandler : ");
         if (outgoingHandler instanceof Dumpable)
         {
-            ((Dumpable)outgoingHandler).dump(out, indent + "    ");
+            ((Dumpable)outgoingHandler).dump(out,indent + "    ");
         }
         else
         {
@@ -273,7 +273,7 @@
     {
         final int prime = 31;
         int result = 1;
-        result = (prime * result) + ((connection == null) ? 0 : connection.hashCode());
+        result = (prime * result) + ((connection == null)?0:connection.hashCode());
         return result;
     }
 
@@ -328,7 +328,11 @@
 
     public void notifyClose(int statusCode, String reason)
     {
-        websocket.onClose(new CloseInfo(statusCode, reason));
+        if (LOG.isDebugEnabled())
+        {
+            LOG.debug("notifyClose({},{})",statusCode,reason);
+        }
+        websocket.onClose(new CloseInfo(statusCode,reason));
     }
 
     public void notifyError(Throwable cause)
@@ -342,12 +346,13 @@
     {
         switch (state)
         {
-            case CLOSING:
+            case CLOSED:
                 // notify session listeners
                 for (SessionListener listener : sessionListeners)
                 {
                     try
                     {
+                        LOG.debug("{}.onSessionClosed()",listener.getClass().getSimpleName());
                         listener.onSessionClosed(this);
                     }
                     catch (Throwable t)
@@ -355,12 +360,10 @@
                         LOG.ignore(t);
                     }
                 }
-                break;
-            case CLOSED:
                 IOState ioState = this.connection.getIOState();
                 CloseInfo close = ioState.getCloseInfo();
                 // confirmed close of local endpoint
-                notifyClose(close.getStatusCode(), close.getReason());
+                notifyClose(close.getStatusCode(),close.getReason());
                 break;
             case OPEN:
                 // notify session listeners
@@ -394,17 +397,32 @@
         connection.getIOState().onConnected();
 
         // Connect remote
-        remote = new WebSocketRemoteEndpoint(connection, outgoingHandler, getBatchMode());
+        remote = new WebSocketRemoteEndpoint(connection,outgoingHandler,getBatchMode());
 
-        // Open WebSocket
-        websocket.openSession(this);
-
-        // Open connection
-        connection.getIOState().onOpened();
-
-        if (LOG.isDebugEnabled())
+        try
         {
-            LOG.debug("open -> {}", dump());
+            // Open WebSocket
+            websocket.openSession(this);
+
+            // Open connection
+            connection.getIOState().onOpened();
+
+            if (LOG.isDebugEnabled())
+            {
+                LOG.debug("open -> {}",dump());
+            }
+        }
+        catch (Throwable t)
+        {
+            // Exception on end-user WS-Endpoint.
+            // Fast-fail & close connection with reason.
+            int statusCode = StatusCode.SERVER_ERROR;
+            if(policy.getBehavior() == WebSocketBehavior.CLIENT)
+            {
+                statusCode = StatusCode.POLICY_VIOLATION;
+            }
+            
+            close(statusCode,t.getMessage());
         }
     }
 
@@ -444,11 +462,11 @@
                 List<String> values = entry.getValue();
                 if (values != null)
                 {
-                    this.parameterMap.put(entry.getKey(), values.toArray(new String[values.size()]));
+                    this.parameterMap.put(entry.getKey(),values.toArray(new String[values.size()]));
                 }
                 else
                 {
-                    this.parameterMap.put(entry.getKey(), new String[0]);
+                    this.parameterMap.put(entry.getKey(),new String[0]);
                 }
             }
         }
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/events/AbstractEventDriver.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/events/AbstractEventDriver.java
index 8cc60f0..7a55b2d 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/events/AbstractEventDriver.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/events/AbstractEventDriver.java
@@ -88,13 +88,7 @@
     {
         if (LOG.isDebugEnabled())
         {
-            LOG.debug("incoming(WebSocketException)",e);
-        }
-
-        if (e instanceof CloseException)
-        {
-            CloseException close = (CloseException)e;
-            terminateConnection(close.getStatusCode(),close.getMessage());
+            LOG.debug("incomingError(" + e.getClass().getName() + ")",e);
         }
 
         onError(e);
@@ -105,7 +99,7 @@
     {
         if (LOG.isDebugEnabled())
         {
-            LOG.debug("{}.onFrame({})",websocket.getClass().getSimpleName(),frame);
+            LOG.debug("incomingFrame({})",frame);
         }
 
         try
@@ -226,6 +220,7 @@
         catch (Throwable t)
         {
             unhandled(t);
+            throw t;
         }
     }
 
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/ExtensionStack.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/ExtensionStack.java
index 18e4a9d..a249c0e 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/ExtensionStack.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/ExtensionStack.java
@@ -370,10 +370,13 @@
         protected Action process() throws Exception
         {
             current = entries.poll();
-            LOG.debug("Processing {}", current);
             if (current == null)
+            {
+                LOG.debug("Entering IDLE");
                 return Action.IDLE;
-            nextOutgoing.outgoingFrame(current.frame, this, current.batchMode);
+            }
+            LOG.debug("Processing {}",current);
+            nextOutgoing.outgoingFrame(current.frame,this,current.batchMode);
             return Action.SCHEDULED;
         }
 
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/AbstractWebSocketConnection.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/AbstractWebSocketConnection.java
index c9f98bb..7494aba 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/AbstractWebSocketConnection.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/AbstractWebSocketConnection.java
@@ -32,7 +32,6 @@
 
 import org.eclipse.jetty.io.AbstractConnection;
 import org.eclipse.jetty.io.ByteBufferPool;
-import org.eclipse.jetty.io.Connection;
 import org.eclipse.jetty.io.EndPoint;
 import org.eclipse.jetty.util.BufferUtil;
 import org.eclipse.jetty.util.StringUtil;
@@ -59,8 +58,7 @@
 import org.eclipse.jetty.websocket.common.io.IOState.ConnectionStateListener;
 
 /**
- * Provides the implementation of {@link LogicalConnection} within the
- * framework of the new {@link Connection} framework of {@code jetty-io}.
+ * Provides the implementation of {@link LogicalConnection} within the framework of the new {@link Connection} framework of {@code jetty-io}.
  */
 public abstract class AbstractWebSocketConnection extends AbstractConnection implements LogicalConnection, ConnectionStateListener, Dumpable
 {
@@ -68,7 +66,7 @@
     {
         private Flusher(ByteBufferPool bufferPool, Generator generator, EndPoint endpoint)
         {
-            super(bufferPool, generator, endpoint, getPolicy().getMaxBinaryMessageBufferSize(), 8);
+            super(bufferPool,generator,endpoint,getPolicy().getMaxBinaryMessageBufferSize(),8);
         }
 
         @Override
@@ -106,7 +104,7 @@
             // Abnormal Close
             reason = CloseStatus.trimMaxReasonLength(reason);
             session.notifyError(x);
-            session.notifyClose(StatusCode.NO_CLOSE,reason);
+            session.notifyClose(StatusCode.ABNORMAL,reason);
 
             disconnect(); // disconnect endpoint & connection
         }
@@ -116,10 +114,11 @@
     {
         private final boolean outputOnly;
 
-        public OnDisconnectCallback(boolean outputOnly) {
+        public OnDisconnectCallback(boolean outputOnly)
+        {
             this.outputOnly = outputOnly;
         }
-        
+
         @Override
         public void writeFailed(Throwable x)
         {
@@ -133,6 +132,68 @@
         }
     }
 
+    public class OnCloseLocalCallback implements WriteCallback
+    {
+        private final WriteCallback callback;
+        private final CloseInfo close;
+
+        public OnCloseLocalCallback(WriteCallback callback, CloseInfo close)
+        {
+            this.callback = callback;
+            this.close = close;
+        }
+
+        public OnCloseLocalCallback(CloseInfo close)
+        {
+            this(null,close);
+        }
+
+        @Override
+        public void writeFailed(Throwable x)
+        {
+            try
+            {
+                if (callback != null)
+                {
+                    callback.writeFailed(x);
+                }
+            }
+            finally
+            {
+                onLocalClose();
+            }
+        }
+
+        @Override
+        public void writeSuccess()
+        {
+            try
+            {
+                if (callback != null)
+                {
+                    callback.writeSuccess();
+                }
+            }
+            finally
+            {
+                onLocalClose();
+            }
+        }
+
+        private void onLocalClose()
+        {
+            LOG.debug("Local Close Confirmed {}",close);
+            if (close.isAbnormal())
+            {
+                ioState.onAbnormalClose(close);
+            }
+            else
+            {
+                ioState.onCloseLocal(close);
+            }
+        }
+    }
+
     public static class Stats
     {
         private AtomicLong countFillInterestedEvents = new AtomicLong(0);
@@ -218,29 +279,22 @@
     @Override
     public void close(int statusCode, String reason)
     {
+        LOG.debug("close({},{})",statusCode,reason);
         CloseInfo close = new CloseInfo(statusCode,reason);
-        if (statusCode == StatusCode.ABNORMAL)
-        {
-            flusher.close(); // TODO this makes the IdleTimeoutTest pass, but I'm dubious it is the correct way
-            ioState.onAbnormalClose(close);
-        }
-        else
-        {
-            ioState.onCloseLocal(close);
-        }
+        this.outgoingFrame(close.asFrame(),new OnCloseLocalCallback(close),BatchMode.OFF);
     }
 
-
     @Override
     public void disconnect()
     {
-        LOG.debug("{} disconnect()",policy.getBehavior());
-        flusher.close();
         disconnect(false);
     }
 
     private void disconnect(boolean onlyOutput)
     {
+        LOG.debug("{} disconnect({})",policy.getBehavior(),onlyOutput?"outputOnly":"both");
+        // close FrameFlusher, we cannot write anymore at this point.
+        flusher.close();
         EndPoint endPoint = getEndPoint();
         // We need to gently close first, to allow
         // SSL close alerts to be sent by Jetty
@@ -366,7 +420,9 @@
     @Override
     public void onClose()
     {
+        LOG.debug("{} onClose()",policy.getBehavior());
         super.onClose();
+        // ioState.onDisconnected();
         flusher.close();
     }
 
@@ -385,7 +441,7 @@
                 {
                     // Fire out a close frame, indicating abnormal shutdown, then disconnect
                     CloseInfo abnormal = new CloseInfo(StatusCode.SHUTDOWN,"Abnormal Close - " + ioState.getCloseInfo().getReason());
-                    outgoingFrame(abnormal.asFrame(),new OnDisconnectCallback(false), BatchMode.OFF);
+                    outgoingFrame(abnormal.asFrame(),new OnDisconnectCallback(false),BatchMode.OFF);
                 }
                 else
                 {
@@ -394,9 +450,13 @@
                 }
                 break;
             case CLOSING:
-                CloseInfo close = ioState.getCloseInfo();
-                // reply to close handshake from remote
-                outgoingFrame(close.asFrame(),new OnDisconnectCallback(true), BatchMode.OFF);
+                // First occurrence of .onCloseLocal or .onCloseRemote use
+                if (ioState.wasRemoteCloseInitiated())
+                {
+                    CloseInfo close = ioState.getCloseInfo();
+                    // reply to close handshake from remote
+                    outgoingFrame(close.asFrame(),new OnCloseLocalCallback(new OnDisconnectCallback(true),close),BatchMode.OFF);
+                }
             default:
                 break;
         }
@@ -447,20 +507,26 @@
     @Override
     protected boolean onReadTimeout()
     {
-        LOG.debug("{} Read Timeout",policy.getBehavior());
-
         IOState state = getIOState();
-        if ((state.getConnectionState() == ConnectionState.CLOSING) || (state.getConnectionState() == ConnectionState.CLOSED))
+        ConnectionState cstate = state.getConnectionState();
+        LOG.debug("{} Read Timeout - {}",policy.getBehavior(),cstate);
+
+        if (cstate == ConnectionState.CLOSED)
         {
-            // close already initiated, extra timeouts not relevant
+            // close already completed, extra timeouts not relevant
             // allow underlying connection and endpoint to disconnect on its own
             return true;
         }
 
-        // Initiate close - politely send close frame.
-        session.notifyError(new SocketTimeoutException("Timeout on Read"));
-        // This is an Abnormal Close condition
-        close(StatusCode.ABNORMAL,"Idle Timeout");
+        try
+        {
+            session.notifyError(new SocketTimeoutException("Timeout on Read"));
+        }
+        finally
+        {
+            // This is an Abnormal Close condition
+            close(StatusCode.SHUTDOWN,"Idle Timeout");
+        }
 
         return false;
     }
@@ -476,7 +542,7 @@
             LOG.debug("outgoingFrame({}, {})",frame,callback);
         }
 
-        flusher.enqueue(frame,callback, batchMode);
+        flusher.enqueue(frame,callback,batchMode);
     }
 
     private int read(ByteBuffer buffer)
@@ -504,7 +570,6 @@
                         LOG.debug("Filled {} bytes - {}",filled,BufferUtil.toDetailString(buffer));
                     }
                     parser.parse(buffer);
-                    // TODO: has the end user application already consumed what it was given?
                 }
             }
         }
@@ -520,6 +585,12 @@
             close(e.getStatusCode(),e.getMessage());
             return -1;
         }
+        catch (Throwable t)
+        {
+            LOG.warn(t);
+            close(StatusCode.ABNORMAL,t.getMessage());
+            return -1;
+        }
     }
 
     @Override
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/FrameFlusher.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/FrameFlusher.java
index a7bb3cb..6e995e5 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/FrameFlusher.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/FrameFlusher.java
@@ -29,7 +29,6 @@
 import org.eclipse.jetty.io.EndPoint;
 import org.eclipse.jetty.util.ArrayQueue;
 import org.eclipse.jetty.util.BufferUtil;
-import org.eclipse.jetty.util.Callback;
 import org.eclipse.jetty.util.IteratingCallback;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
@@ -45,246 +44,22 @@
  */
 public class FrameFlusher
 {
-    public static final BinaryFrame FLUSH_FRAME = new BinaryFrame();
-    private static final Logger LOG = Log.getLogger(FrameFlusher.class);
-
-    private final ByteBufferPool bufferPool;
-    private final EndPoint endpoint;
-    private final int bufferSize;
-    private final Generator generator;
-    private final int maxGather;
-    private final Object lock = new Object();
-    private final ArrayQueue<FrameEntry> queue = new ArrayQueue<>(16, 16, lock);
-    private final Flusher flusher = new Flusher();
-    private final AtomicBoolean closed = new AtomicBoolean();
-    private volatile Throwable failure;
-
-    public FrameFlusher(ByteBufferPool bufferPool, Generator generator, EndPoint endpoint, int bufferSize, int maxGather)
-    {
-        this.bufferPool = bufferPool;
-        this.endpoint = endpoint;
-        this.bufferSize = bufferSize;
-        this.generator = Objects.requireNonNull(generator);
-        this.maxGather = maxGather;
-    }
-
-    public void enqueue(Frame frame, WriteCallback callback, BatchMode batchMode)
-    {
-        if (closed.get())
-        {
-            notifyCallbackFailure(callback, new EOFException("Connection has been closed locally"));
-            return;
-        }
-        if (flusher.isFailed())
-        {
-            notifyCallbackFailure(callback, failure);
-            return;
-        }
-
-        FrameEntry entry = new FrameEntry(frame, callback, batchMode);
-
-        synchronized (lock)
-        {
-            switch (frame.getOpCode())
-            {
-                case OpCode.PING:
-                {
-                    // Prepend PINGs so they are processed first.
-                    queue.add(0, entry);
-                    break;
-                }
-                case OpCode.CLOSE:
-                {
-                    // There may be a chance that other frames are
-                    // added after this close frame, but we will
-                    // fail them later to keep it simple here.
-                    closed.set(true);
-                    queue.add(entry);
-                    break;
-                }
-                default:
-                {
-                    queue.add(entry);
-                    break;
-                }
-            }
-        }
-
-        if (LOG.isDebugEnabled())
-            LOG.debug("{} queued {}", this, entry);
-
-        flusher.iterate();
-    }
-
-    public void close()
-    {
-        if (closed.compareAndSet(false, true))
-        {
-            LOG.debug("{} closing {}", this);
-            EOFException eof = new EOFException("Connection has been closed locally");
-            flusher.failed(eof);
-
-            // Fail also queued entries.
-            List<FrameEntry> entries = new ArrayList<>();
-            synchronized (lock)
-            {
-                entries.addAll(queue);
-                queue.clear();
-            }
-            // Notify outside sync block.
-            for (FrameEntry entry : entries)
-                notifyCallbackFailure(entry.callback, eof);
-        }
-    }
-
-    protected void onFailure(Throwable x)
-    {
-        LOG.warn(x);
-    }
-
-    protected void notifyCallbackSuccess(WriteCallback callback)
-    {
-        try
-        {
-            if (callback != null)
-                callback.writeSuccess();
-        }
-        catch (Throwable x)
-        {
-            LOG.debug("Exception while notifying success of callback " + callback, x);
-        }
-    }
-
-    protected void notifyCallbackFailure(WriteCallback callback, Throwable failure)
-    {
-        try
-        {
-            if (callback != null)
-                callback.writeFailed(failure);
-        }
-        catch (Throwable x)
-        {
-            LOG.debug("Exception while notifying failure of callback " + callback, x);
-        }
-    }
-
-    @Override
-    public String toString()
-    {
-        ByteBuffer aggregate = flusher.aggregate;
-        return String.format("%s[queueSize=%d,aggregateSize=%d,failure=%s]",
-                getClass().getSimpleName(),
-                queue.size(),
-                aggregate == null ? 0 : aggregate.position(),
-                failure);
-    }
-
     private class Flusher extends IteratingCallback
     {
         private final List<FrameEntry> entries = new ArrayList<>(maxGather);
-        private final List<ByteBuffer> buffers = new ArrayList<>(maxGather * 2 + 1);
+        private final List<ByteBuffer> buffers = new ArrayList<>((maxGather * 2) + 1);
         private ByteBuffer aggregate;
         private BatchMode batchMode;
 
-        @Override
-        protected Action process() throws Exception
-        {
-            int space = aggregate == null ? bufferSize : BufferUtil.space(aggregate);
-            BatchMode currentBatchMode = BatchMode.AUTO;
-            synchronized (lock)
-            {
-                while (entries.size() <= maxGather && !queue.isEmpty())
-                {
-                    FrameEntry entry = queue.remove(0);
-                    currentBatchMode = BatchMode.max(currentBatchMode, entry.batchMode);
-
-                    // Force flush if we need to.
-                    if (entry.frame == FLUSH_FRAME)
-                        currentBatchMode = BatchMode.OFF;
-
-                    int payloadLength = BufferUtil.length(entry.frame.getPayload());
-                    int approxFrameLength = Generator.MAX_HEADER_LENGTH + payloadLength;
-
-                    // If it is a "big" frame, avoid copying into the aggregate buffer.
-                    if (approxFrameLength > (bufferSize >> 2))
-                        currentBatchMode = BatchMode.OFF;
-
-                    // If the aggregate buffer overflows, do not batch.
-                    space -= approxFrameLength;
-                    if (space <= 0)
-                        currentBatchMode = BatchMode.OFF;
-
-                    entries.add(entry);
-                }
-            }
-
-            if (LOG.isDebugEnabled())
-                LOG.debug("{} processing {} entries: {}", FrameFlusher.this, entries.size(), entries);
-
-            if (entries.isEmpty())
-            {
-                if (batchMode != BatchMode.AUTO)
-                {
-                    // Nothing more to do, release the aggregate buffer if we need to.
-                    // Releasing it here rather than in succeeded() allows for its reuse.
-                    releaseAggregate();
-                    return Action.IDLE;
-                }
-
-                LOG.debug("{} auto flushing", FrameFlusher.this);
-                return flush();
-            }
-
-            batchMode = currentBatchMode;
-
-            return currentBatchMode == BatchMode.OFF ? flush() : batch();
-        }
-
-        private Action flush()
-        {
-            if (!BufferUtil.isEmpty(aggregate))
-            {
-                buffers.add(aggregate);
-                if (LOG.isDebugEnabled())
-                    LOG.debug("{} flushing aggregate {}", FrameFlusher.this, aggregate);
-            }
-
-            // Do not allocate the iterator here.
-            for (int i = 0; i < entries.size(); ++i)
-            {
-                FrameEntry entry = entries.get(i);
-                // Skip the "synthetic" frame used for flushing.
-                if (entry.frame == FLUSH_FRAME)
-                    continue;
-                buffers.add(entry.generateHeaderBytes());
-                ByteBuffer payload = entry.frame.getPayload();
-                if (BufferUtil.hasContent(payload))
-                    buffers.add(payload);
-            }
-
-            if (LOG.isDebugEnabled())
-                LOG.debug("{} flushing {} frames: {}", FrameFlusher.this, entries.size(), entries);
-
-            if (buffers.isEmpty())
-            {
-                releaseAggregate();
-                // We may have the FLUSH_FRAME to notify.
-                succeedEntries();
-                return Action.IDLE;
-            }
-
-            endpoint.write(this, buffers.toArray(new ByteBuffer[buffers.size()]));
-            buffers.clear();
-            return Action.SCHEDULED;
-        }
-
         private Action batch()
         {
             if (aggregate == null)
             {
-                aggregate = bufferPool.acquire(bufferSize, true);
+                aggregate = bufferPool.acquire(bufferSize,true);
                 if (LOG.isDebugEnabled())
-                    LOG.debug("{} acquired aggregate buffer {}", FrameFlusher.this, aggregate);
+                {
+                    LOG.debug("{} acquired aggregate buffer {}",FrameFlusher.this,aggregate);
+                }
             }
 
             // Do not allocate the iterator here.
@@ -296,17 +71,149 @@
 
                 ByteBuffer payload = entry.frame.getPayload();
                 if (BufferUtil.hasContent(payload))
-                    BufferUtil.append(aggregate, payload);
+                {
+                    BufferUtil.append(aggregate,payload);
+                }
             }
             if (LOG.isDebugEnabled())
-                LOG.debug("{} aggregated {} frames: {}", FrameFlusher.this, entries.size(), entries);
+            {
+                LOG.debug("{} aggregated {} frames: {}",FrameFlusher.this,entries.size(),entries);
+            }
             succeeded();
             return Action.SCHEDULED;
         }
 
+        @Override
+        protected void completed()
+        {
+            // This IteratingCallback never completes.
+        }
+
+        @Override
+        public void failed(Throwable x)
+        {
+            for (FrameEntry entry : entries)
+            {
+                notifyCallbackFailure(entry.callback,x);
+                entry.release();
+            }
+            entries.clear();
+            super.failed(x);
+            failure = x;
+            onFailure(x);
+        }
+
+        private Action flush()
+        {
+            if (!BufferUtil.isEmpty(aggregate))
+            {
+                buffers.add(aggregate);
+                if (LOG.isDebugEnabled())
+                {
+                    LOG.debug("{} flushing aggregate {}",FrameFlusher.this,aggregate);
+                }
+            }
+
+            // Do not allocate the iterator here.
+            for (int i = 0; i < entries.size(); ++i)
+            {
+                FrameEntry entry = entries.get(i);
+                // Skip the "synthetic" frame used for flushing.
+                if (entry.frame == FLUSH_FRAME)
+                {
+                    continue;
+                }
+                buffers.add(entry.generateHeaderBytes());
+                ByteBuffer payload = entry.frame.getPayload();
+                if (BufferUtil.hasContent(payload))
+                {
+                    buffers.add(payload);
+                }
+            }
+
+            if (LOG.isDebugEnabled())
+            {
+                LOG.debug("{} flushing {} frames: {}",FrameFlusher.this,entries.size(),entries);
+            }
+
+            if (buffers.isEmpty())
+            {
+                releaseAggregate();
+                // We may have the FLUSH_FRAME to notify.
+                succeedEntries();
+                return Action.IDLE;
+            }
+
+            endpoint.write(this,buffers.toArray(new ByteBuffer[buffers.size()]));
+            buffers.clear();
+            return Action.SCHEDULED;
+        }
+
+        @Override
+        protected Action process() throws Exception
+        {
+            int space = aggregate == null?bufferSize:BufferUtil.space(aggregate);
+            BatchMode currentBatchMode = BatchMode.AUTO;
+            synchronized (lock)
+            {
+                while ((entries.size() <= maxGather) && !queue.isEmpty())
+                {
+                    FrameEntry entry = queue.remove(0);
+                    currentBatchMode = BatchMode.max(currentBatchMode,entry.batchMode);
+
+                    // Force flush if we need to.
+                    if (entry.frame == FLUSH_FRAME)
+                    {
+                        currentBatchMode = BatchMode.OFF;
+                    }
+
+                    int payloadLength = BufferUtil.length(entry.frame.getPayload());
+                    int approxFrameLength = Generator.MAX_HEADER_LENGTH + payloadLength;
+
+                    // If it is a "big" frame, avoid copying into the aggregate buffer.
+                    if (approxFrameLength > (bufferSize >> 2))
+                    {
+                        currentBatchMode = BatchMode.OFF;
+                    }
+
+                    // If the aggregate buffer overflows, do not batch.
+                    space -= approxFrameLength;
+                    if (space <= 0)
+                    {
+                        currentBatchMode = BatchMode.OFF;
+                    }
+
+                    entries.add(entry);
+                }
+            }
+
+            if (LOG.isDebugEnabled())
+            {
+                LOG.debug("{} processing {} entries: {}",FrameFlusher.this,entries.size(),entries);
+            }
+
+            if (entries.isEmpty())
+            {
+                if (batchMode != BatchMode.AUTO)
+                {
+                    // Nothing more to do, release the aggregate buffer if we need to.
+                    // Releasing it here rather than in succeeded() allows for its reuse.
+                    releaseAggregate();
+                    return Action.IDLE;
+                }
+
+                LOG.debug("{} auto flushing",FrameFlusher.this);
+                return flush();
+            }
+
+            batchMode = currentBatchMode;
+
+            return currentBatchMode == BatchMode.OFF?flush():batch();
+        }
+
         private void releaseAggregate()
         {
-            if (aggregate != null && BufferUtil.isEmpty(aggregate))
+            if ((aggregate != null) && BufferUtil.isEmpty(aggregate))
             {
                 bufferPool.release(aggregate);
                 aggregate = null;
@@ -331,26 +238,6 @@
             }
             entries.clear();
         }
-
-        @Override
-        protected void completed()
-        {
-            // This IteratingCallback never completes.
-        }
-
-        @Override
-        public void failed(Throwable x)
-        {
-            for (FrameEntry entry : entries)
-            {
-                notifyCallbackFailure(entry.callback, x);
-                entry.release();
-            }
-            entries.clear();
-            super.failed(x);
-            failure = x;
-            onFailure(x);
-        }
     }
 
     private class FrameEntry
@@ -374,7 +261,7 @@
 
         private void generateHeaderBytes(ByteBuffer buffer)
         {
-            generator.generateHeaderBytes(frame, buffer);
+            generator.generateHeaderBytes(frame,buffer);
         }
 
         private void release()
@@ -389,7 +276,145 @@
         @Override
         public String toString()
         {
-            return String.format("%s[%s,%s,%s,%s]", getClass().getSimpleName(), frame, callback, batchMode, failure);
+            return String.format("%s[%s,%s,%s,%s]",getClass().getSimpleName(),frame,callback,batchMode,failure);
         }
     }
+
+    public static final BinaryFrame FLUSH_FRAME = new BinaryFrame();
+    private static final Logger LOG = Log.getLogger(FrameFlusher.class);
+    private final ByteBufferPool bufferPool;
+    private final EndPoint endpoint;
+    private final int bufferSize;
+    private final Generator generator;
+    private final int maxGather;
+    private final Object lock = new Object();
+    private final ArrayQueue<FrameEntry> queue = new ArrayQueue<>(16,16,lock);
+    private final Flusher flusher = new Flusher();
+    private final AtomicBoolean closed = new AtomicBoolean();
+    private volatile Throwable failure;
+
+    public FrameFlusher(ByteBufferPool bufferPool, Generator generator, EndPoint endpoint, int bufferSize, int maxGather)
+    {
+        this.bufferPool = bufferPool;
+        this.endpoint = endpoint;
+        this.bufferSize = bufferSize;
+        this.generator = Objects.requireNonNull(generator);
+        this.maxGather = maxGather;
+    }
+
+    public void close()
+    {
+        if (closed.compareAndSet(false,true))
+        {
+            LOG.debug("{} closing {}",this);
+            EOFException eof = new EOFException("Connection has been closed locally");
+            flusher.failed(eof);
+
+            // Fail also queued entries.
+            List<FrameEntry> entries = new ArrayList<>();
+            synchronized (lock)
+            {
+                entries.addAll(queue);
+                queue.clear();
+            }
+            // Notify outside sync block.
+            for (FrameEntry entry : entries)
+            {
+                notifyCallbackFailure(entry.callback,eof);
+            }
+        }
+    }
+
+    public void enqueue(Frame frame, WriteCallback callback, BatchMode batchMode)
+    {
+        if (closed.get())
+        {
+            notifyCallbackFailure(callback,new EOFException("Connection has been closed locally"));
+            return;
+        }
+        if (flusher.isFailed())
+        {
+            notifyCallbackFailure(callback,failure);
+            return;
+        }
+
+        FrameEntry entry = new FrameEntry(frame,callback,batchMode);
+
+        synchronized (lock)
+        {
+            switch (frame.getOpCode())
+            {
+                case OpCode.PING:
+                {
+                    // Prepend PINGs so they are processed first.
+                    queue.add(0,entry);
+                    break;
+                }
+                case OpCode.CLOSE:
+                {
+                    // There may be a chance that other frames are
+                    // added after this close frame, but we will
+                    // fail them later to keep it simple here.
+                    closed.set(true);
+                    queue.add(entry);
+                    break;
+                }
+                default:
+                {
+                    queue.add(entry);
+                    break;
+                }
+            }
+        }
+
+        if (LOG.isDebugEnabled())
+        {
+            LOG.debug("{} queued {}",this,entry);
+        }
+
+        flusher.iterate();
+    }
+
+    protected void notifyCallbackFailure(WriteCallback callback, Throwable failure)
+    {
+        try
+        {
+            if (callback != null)
+            {
+                callback.writeFailed(failure);
+            }
+        }
+        catch (Throwable x)
+        {
+            LOG.debug("Exception while notifying failure of callback " + callback,x);
+        }
+    }
+
+    protected void notifyCallbackSuccess(WriteCallback callback)
+    {
+        try
+        {
+            if (callback != null)
+            {
+                callback.writeSuccess();
+            }
+        }
+        catch (Throwable x)
+        {
+            LOG.debug("Exception while notifying success of callback " + callback,x);
+        }
+    }
+
+    protected void onFailure(Throwable x)
+    {
+        LOG.warn(x);
+    }
+
+    @Override
+    public String toString()
+    {
+        ByteBuffer aggregate = flusher.aggregate;
+        return String.format("%s[queueSize=%d,aggregateSize=%d,failure=%s]",getClass().getSimpleName(),queue.size(),aggregate == null?0:aggregate.position(),
+                failure);
+    }
 }
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/IOState.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/IOState.java
index 6d4dbc4..815d2d3 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/IOState.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/IOState.java
@@ -139,6 +139,10 @@
     {
         for (ConnectionStateListener listener : listeners)
         {
+            if (LOG.isDebugEnabled())
+            {
+                LOG.debug("{}.onConnectionStateChange({})",listener.getClass().getSimpleName(),state.name());
+            }
             listener.onConnectionStateChange(state);
         }
     }
@@ -166,8 +170,7 @@
             }
 
             this.state = ConnectionState.CLOSED;
-            if (closeInfo == null)
-                this.closeInfo = close;
+            this.closeInfo = close;
             this.inputAvailable = false;
             this.outputAvailable = false;
             this.closeHandshakeSource = CloseHandshakeSource.ABNORMAL;
@@ -193,16 +196,16 @@
 
         if (initialState == ConnectionState.CONNECTED)
         {
-            // fast close. a local close request from end-user onConnected() method
+            // fast close. a local close request from end-user onConnect/onOpen method
             LOG.debug("FastClose in CONNECTED detected");
             // Force the state open (to allow read/write to endpoint)
             onOpened();
+            LOG.debug("FastClose continuing with Closure");
         }
 
         synchronized (this)
         {
-            if (closeInfo == null)
-                closeInfo = close;
+            closeInfo = close;
 
             boolean in = inputAvailable;
             boolean out = outputAvailable;
@@ -236,7 +239,6 @@
             LOG.debug("notifying state listeners: {}",event);
             notifyStateListeners(event);
 
-            /*
             // if abnormal, we don't expect an answer.
             if (close.isAbnormal())
             {
@@ -253,7 +255,6 @@
                 notifyStateListeners(event);
                 return;
             }
-            */
         }
     }
 
@@ -272,8 +273,7 @@
                 return;
             }
 
-            if (closeInfo == null)
-                closeInfo = close;
+            closeInfo = close;
 
             boolean in = inputAvailable;
             boolean out = outputAvailable;
@@ -360,7 +360,7 @@
             // already opened
             return;
         }
-        
+
         if (this.state != ConnectionState.CONNECTED)
         {
             LOG.debug("Unable to open, not in CONNECTED state: {}",this.state);
@@ -394,12 +394,11 @@
                 return;
             }
 
-            CloseInfo close = new CloseInfo(StatusCode.NO_CLOSE,"Read EOF");
+            CloseInfo close = new CloseInfo(StatusCode.ABNORMAL,"Read EOF");
 
             this.cleanClose = false;
             this.state = ConnectionState.CLOSED;
-            if (closeInfo == null)
-                this.closeInfo = close;
+            this.closeInfo = close;
             this.inputAvailable = false;
             this.outputAvailable = false;
             this.closeHandshakeSource = CloseHandshakeSource.ABNORMAL;
@@ -408,6 +407,58 @@
         notifyStateListeners(event);
     }
 
+    public void onDisconnected()
+    {
+        ConnectionState event = null;
+        synchronized (this)
+        {
+            if (this.state == ConnectionState.CLOSED)
+            {
+                // already closed
+                return;
+            }
+
+            CloseInfo close = new CloseInfo(StatusCode.ABNORMAL,"Disconnected");
+
+            this.cleanClose = false;
+            this.state = ConnectionState.CLOSED;
+            this.closeInfo = close;
+            this.inputAvailable = false;
+            this.outputAvailable = false;
+            this.closeHandshakeSource = CloseHandshakeSource.ABNORMAL;
+            event = this.state;
+        }
+        notifyStateListeners(event);
+    }
+
+    @Override
+    public String toString()
+    {
+        StringBuilder str = new StringBuilder();
+        str.append(this.getClass().getSimpleName());
+        str.append("@").append(Integer.toHexString(hashCode()));
+        str.append("[").append(state);
+        str.append(',');
+        if (!inputAvailable)
+        {
+            str.append('!');
+        }
+        str.append("in,");
+        if (!outputAvailable)
+        {
+            str.append('!');
+        }
+        str.append("out");
+        if ((state == ConnectionState.CLOSED) || (state == ConnectionState.CLOSING))
+        {
+            str.append(",close=").append(closeInfo);
+            str.append(",clean=").append(cleanClose);
+            str.append(",closeSource=").append(closeHandshakeSource);
+        }
+        str.append(']');
+        return str.toString();
+    }
+
     public boolean wasAbnormalClose()
     {
         return closeHandshakeSource == CloseHandshakeSource.ABNORMAL;
@@ -427,4 +478,5 @@
     {
         return closeHandshakeSource == CloseHandshakeSource.REMOTE;
     }
+
 }
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/util/ReflectUtils.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/util/ReflectUtils.java
index 32b9c1b..2194ec5 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/util/ReflectUtils.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/util/ReflectUtils.java
@@ -392,4 +392,4 @@
         }
         return name;
     }
-}
+}
\ No newline at end of file
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/CloseInfoTest.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/CloseInfoTest.java
new file mode 100644
index 0000000..a40e306
--- /dev/null
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/CloseInfoTest.java
@@ -0,0 +1,166 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2014 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.websocket.common;
+
+import static org.eclipse.jetty.websocket.api.StatusCode.*;
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.StringUtil;
+import org.eclipse.jetty.websocket.api.ProtocolException;
+import org.eclipse.jetty.websocket.common.frames.CloseFrame;
+import org.junit.Test;
+
+public class CloseInfoTest
+{
+    /**
+     * A test where no close is provided
+     */
+    @Test
+    public void testAnonymousClose()
+    {
+        CloseInfo close = new CloseInfo();
+        assertThat("close.code",close.getStatusCode(),is(NO_CODE));
+        assertThat("close.reason",close.getReason(),nullValue());
+
+        CloseFrame frame = close.asFrame();
+        assertThat("close frame op code",frame.getOpCode(),is(OpCode.CLOSE));
+        // should result in no payload
+        assertThat("close frame has payload",frame.hasPayload(),is(false));
+        assertThat("close frame payload length",frame.getPayloadLength(),is(0));
+    }
+    
+    /**
+     * A test where NO_CODE (1005) is provided
+     */
+    @Test
+    public void testNoCode()
+    {
+        CloseInfo close = new CloseInfo(NO_CODE);
+        assertThat("close.code",close.getStatusCode(),is(NO_CODE));
+        assertThat("close.reason",close.getReason(),nullValue());
+
+        CloseFrame frame = close.asFrame();
+        assertThat("close frame op code",frame.getOpCode(),is(OpCode.CLOSE));
+        // should result in no payload
+        assertThat("close frame has payload",frame.hasPayload(),is(false));
+        assertThat("close frame payload length",frame.getPayloadLength(),is(0));
+    }
+    
+    /**
+     * A test where NO_CLOSE (1006) is provided
+     */
+    @Test
+    public void testNoClose()
+    {
+        CloseInfo close = new CloseInfo(NO_CLOSE);
+        assertThat("close.code",close.getStatusCode(),is(NO_CLOSE));
+        assertThat("close.reason",close.getReason(),nullValue());
+
+        CloseFrame frame = close.asFrame();
+        assertThat("close frame op code",frame.getOpCode(),is(OpCode.CLOSE));
+        // should result in no payload
+        assertThat("close frame has payload",frame.hasPayload(),is(false));
+        assertThat("close frame payload length",frame.getPayloadLength(),is(0));
+    }
+
+    /**
+     * A test of FAILED_TLS_HANDSHAKE (1007)
+     */
+    @Test
+    public void testFailedTlsHandshake()
+    {
+        CloseInfo close = new CloseInfo(FAILED_TLS_HANDSHAKE);
+        assertThat("close.code",close.getStatusCode(),is(FAILED_TLS_HANDSHAKE));
+        assertThat("close.reason",close.getReason(),nullValue());
+
+        try
+        {
+            @SuppressWarnings("unused")
+            CloseFrame frame = close.asFrame();
+            fail("Expected " + ProtocolException.class.getName());
+        }
+        catch (ProtocolException e)
+        {
+            // expected path
+            assertThat("ProtocolException message",e.getMessage(),containsString("not allowed (per RFC6455)"));
+        }
+    }
+
+    /**
+     * A test of NORMAL (1000)
+     */
+    @Test
+    public void testNormal()
+    {
+        CloseInfo close = new CloseInfo(NORMAL);
+        assertThat("close.code",close.getStatusCode(),is(NORMAL));
+        assertThat("close.reason",close.getReason(),nullValue());
+
+        CloseFrame frame = close.asFrame();
+        assertThat("close frame op code",frame.getOpCode(),is(OpCode.CLOSE));
+        assertThat("close frame payload length",frame.getPayloadLength(),is(2));
+    }
+    
+    private ByteBuffer asByteBuffer(int statusCode, String reason)
+    {
+        int len = 2; // status code length
+        byte utf[] = null;
+        if (StringUtil.isNotBlank(reason))
+        {
+            utf = StringUtil.getUtf8Bytes(reason);
+            len += utf.length;
+        }
+
+        ByteBuffer buf = BufferUtil.allocate(len);
+        BufferUtil.flipToFill(buf);
+        buf.put((byte)((statusCode >>> 8) & 0xFF));
+        buf.put((byte)((statusCode >>> 0) & 0xFF));
+
+        if (utf != null)
+        {
+            buf.put(utf,0,utf.length);
+        }
+        BufferUtil.flipToFlush(buf,0);
+        
+        return buf;
+    }
+    
+    @Test
+    public void testFromFrame()
+    {
+        ByteBuffer payload = asByteBuffer(NORMAL,null);
+        assertThat("payload length", payload.remaining(), is(2));
+        CloseFrame frame = new CloseFrame();
+        frame.setPayload(payload);
+        
+        // create from frame
+        CloseInfo close = new CloseInfo(frame);
+        assertThat("close.code",close.getStatusCode(),is(NORMAL));
+        assertThat("close.reason",close.getReason(),nullValue());
+
+        // and back again
+        frame = close.asFrame();
+        assertThat("close frame op code",frame.getOpCode(),is(OpCode.CLOSE));
+        assertThat("close frame payload length",frame.getPayloadLength(),is(2));
+    }
+}
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/ab/TestABCase4.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/ab/TestABCase4.java
index fe6011d..6b0f018 100644
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/ab/TestABCase4.java
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/ab/TestABCase4.java
@@ -21,6 +21,7 @@
 import java.nio.ByteBuffer;
 
 import org.eclipse.jetty.util.log.StacklessLogging;
+import org.eclipse.jetty.websocket.api.ProtocolException;
 import org.eclipse.jetty.websocket.api.WebSocketBehavior;
 import org.eclipse.jetty.websocket.api.WebSocketException;
 import org.eclipse.jetty.websocket.api.WebSocketPolicy;
@@ -39,8 +40,7 @@
     {
         ByteBuffer expected = ByteBuffer.allocate(32);
 
-        expected.put(new byte[]
-                { (byte)0x8b, 0x00 });
+        expected.put(new byte[] { (byte)0x8b, 0x00 });
 
         expected.flip();
 
@@ -50,10 +50,17 @@
         {
             Parser parser = new UnitParser(policy);
             parser.setIncomingFramesHandler(capture);
-            parser.parse(expected);
+            try
+            {
+                parser.parse(expected);
+            }
+            catch (ProtocolException ignore)
+            {
+                // ignore
+            }
         }
 
-        Assert.assertEquals( "error on undefined opcode", 1, capture.getErrorCount(WebSocketException.class)) ;
+        Assert.assertEquals("error on undefined opcode",1,capture.getErrorCount(WebSocketException.class));
 
         Throwable known = capture.getErrors().poll();
 
@@ -65,8 +72,7 @@
     {
         ByteBuffer expected = ByteBuffer.allocate(32);
 
-        expected.put(new byte[]
-                { (byte)0x8c, 0x01, 0x00 });
+        expected.put(new byte[] { (byte)0x8c, 0x01, 0x00 });
 
         expected.flip();
 
@@ -76,24 +82,29 @@
         {
             Parser parser = new UnitParser(policy);
             parser.setIncomingFramesHandler(capture);
-            parser.parse(expected);
+            try
+            {
+                parser.parse(expected);
+            }
+            catch (ProtocolException ignore)
+            {
+                // ignore
+            }
         }
 
-        Assert.assertEquals( "error on undefined opcode", 1, capture.getErrorCount(WebSocketException.class)) ;
+        Assert.assertEquals("error on undefined opcode",1,capture.getErrorCount(WebSocketException.class));
 
         Throwable known = capture.getErrors().poll();
 
         Assert.assertTrue("undefined option should be in message",known.getMessage().contains("Unknown opcode: 12"));
     }
 
-
     @Test
     public void testParserNonControlOpCode3Case4_1_1() throws Exception
     {
         ByteBuffer expected = ByteBuffer.allocate(32);
 
-        expected.put(new byte[]
-                { (byte)0x83, 0x00 });
+        expected.put(new byte[] { (byte)0x83, 0x00 });
 
         expected.flip();
 
@@ -103,10 +114,17 @@
         {
             Parser parser = new UnitParser(policy);
             parser.setIncomingFramesHandler(capture);
-            parser.parse(expected);
+            try
+            {
+                parser.parse(expected);
+            }
+            catch (ProtocolException ignore)
+            {
+                // ignore
+            }
         }
 
-        Assert.assertEquals( "error on undefined opcode", 1, capture.getErrorCount(WebSocketException.class)) ;
+        Assert.assertEquals("error on undefined opcode",1,capture.getErrorCount(WebSocketException.class));
 
         Throwable known = capture.getErrors().poll();
 
@@ -118,8 +136,7 @@
     {
         ByteBuffer expected = ByteBuffer.allocate(32);
 
-        expected.put(new byte[]
-                { (byte)0x84, 0x01, 0x00 });
+        expected.put(new byte[] { (byte)0x84, 0x01, 0x00 });
 
         expected.flip();
 
@@ -129,10 +146,17 @@
         {
             Parser parser = new UnitParser(policy);
             parser.setIncomingFramesHandler(capture);
-            parser.parse(expected);
+            try
+            {
+                parser.parse(expected);
+            }
+            catch (ProtocolException ignore)
+            {
+                // ignore
+            }
         }
 
-        Assert.assertEquals( "error on undefined opcode", 1, capture.getErrorCount(WebSocketException.class)) ;
+        Assert.assertEquals("error on undefined opcode",1,capture.getErrorCount(WebSocketException.class));
 
         Throwable known = capture.getErrors().poll();
 
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/BlockheadClient.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/BlockheadClient.java
index bd02460..9f5f17e 100644
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/BlockheadClient.java
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/BlockheadClient.java
@@ -18,7 +18,8 @@
 
 package org.eclipse.jetty.websocket.common.test;
 
-import java.io.Closeable;
+import static org.hamcrest.Matchers.*;
+
 import java.io.EOFException;
 import java.io.IOException;
 import java.io.InputStream;
@@ -35,12 +36,13 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicInteger;
 
 import org.eclipse.jetty.io.ByteBufferPool;
 import org.eclipse.jetty.io.MappedByteBufferPool;
+import org.eclipse.jetty.toolchain.test.EventQueue;
 import org.eclipse.jetty.util.BufferUtil;
 import org.eclipse.jetty.util.IO;
 import org.eclipse.jetty.util.StringUtil;
@@ -68,10 +70,6 @@
 import org.eclipse.jetty.websocket.common.io.http.HttpResponseHeaderParser;
 import org.junit.Assert;
 
-import static org.hamcrest.Matchers.anyOf;
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.notNullValue;
-
 /**
  * A simple websocket client for performing unit tests with.
  * <p>
@@ -84,21 +82,110 @@
  * with regards to basic IO behavior, a write should work as expected, a read should work as expected, but <u>what</u> byte it sends or reads is not within its
  * scope.
  */
-public class BlockheadClient implements IncomingFrames, OutgoingFrames, ConnectionStateListener, Closeable
+public class BlockheadClient implements OutgoingFrames, ConnectionStateListener, AutoCloseable
 {
+    private class FrameReadingThread extends Thread implements Runnable, IncomingFrames
+    {
+        public long totalBytes = 0;
+        public long totalReadOps = 0;
+        public long totalParseOps = 0;
+
+        public EventQueue<WebSocketFrame> frames = new EventQueue<>();
+        public EventQueue<Throwable> errors = new EventQueue<>();
+
+        @Override
+        public void run()
+        {
+            LOG.debug("Reading frames from server");
+
+            byte buf[] = new byte[BUFFER_SIZE];
+            try
+            {
+                if ((remainingBuffer != null) && (remainingBuffer.remaining() > 0))
+                {
+                    LOG.debug("Reading bytes received during response header parse: {}",BufferUtil.toDetailString(remainingBuffer));
+                    totalBytes += remainingBuffer.remaining();
+                    totalReadOps++;
+                    parser.parse(remainingBuffer);
+                }
+
+                int len = 0;
+                int available = 0;
+                while (!eof)
+                {
+                    available = in.available();
+                    len = in.read(buf,0,Math.min(available,buf.length));
+                    totalReadOps++;
+                    if (len < 0)
+                    {
+                        eof = true;
+                        break;
+                    }
+                    else if (len > 0)
+                    {
+                        totalBytes += len;
+                        ByteBuffer bbuf = ByteBuffer.wrap(buf,0,len);
+                        if (LOG.isDebugEnabled())
+                        {
+                            LOG.debug("Read {} bytes: {}",len,BufferUtil.toDetailString(bbuf));
+                        }
+                        totalParseOps++;
+                        parser.parse(bbuf);
+                    }
+                }
+            }
+            catch (IOException e)
+            {
+                LOG.debug(e);
+            }
+        }
+
+        @Override
+        public String toString()
+        {
+            StringBuilder str = new StringBuilder();
+            str.append("FrameReadingThread[");
+            str.append(",frames=" + frames.size());
+            str.append(",errors=" + errors.size());
+            str.append(String.format(",totalBytes=%,d",totalBytes));
+            str.append(String.format(",totalReadOps=%,d",totalReadOps));
+            str.append(String.format(",totalParseOps=%,d",totalParseOps));
+            str.append("]");
+            return str.toString();
+        }
+
+        @Override
+        public synchronized void incomingError(Throwable t)
+        {
+            this.errors.add(t);
+        }
+
+        @Override
+        public synchronized void incomingFrame(Frame frame)
+        {
+            this.frames.add(WebSocketFrame.copy(frame));
+        }
+
+        public synchronized void clear()
+        {
+            this.frames.clear();
+            this.errors.clear();
+        }
+    }
+
     private static final String REQUEST_HASH_KEY = "dGhlIHNhbXBsZSBub25jZQ==";
-    private static final int BUFFER_SIZE = 8192;
+    private static final int BUFFER_SIZE = 64 * 1024;
     private static final Logger LOG = Log.getLogger(BlockheadClient.class);
-    /** Set to true to disable timeouts (for debugging reasons) */
-    private boolean debug = false;
     private final URI destHttpURI;
     private final URI destWebsocketURI;
     private final ByteBufferPool bufferPool;
     private final Generator generator;
     private final Parser parser;
-    private final IncomingFramesCapture incomingFrames;
-    private final WebSocketExtensionFactory extensionFactory;
 
+    private final WebSocketExtensionFactory extensionFactory;
+    private FrameReadingThread frameReader;
+
+    private ExecutorService executor;
     private Socket socket;
     private OutputStream out;
     private InputStream in;
@@ -106,16 +193,15 @@
     private String protocols;
     private List<String> extensions = new ArrayList<>();
     private List<String> headers = new ArrayList<>();
-    private byte[] clientmask = new byte[]
-    { (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF };
+    private byte[] clientmask = new byte[] { (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF };
     private int timeout = 1000;
-    private AtomicInteger parseCount;
     private OutgoingFrames outgoing = this;
     private boolean eof = false;
     private ExtensionStack extensionStack;
     private IOState ioState;
     private CountDownLatch disconnectedLatch = new CountDownLatch(1);
     private ByteBuffer remainingBuffer;
+
     private String connectionValue = "Upgrade";
 
     public BlockheadClient(URI destWebsocketURI) throws URISyntaxException
@@ -140,9 +226,6 @@
         this.bufferPool = new MappedByteBufferPool(8192);
         this.generator = new Generator(policy,bufferPool);
         this.parser = new Parser(policy,bufferPool);
-        this.parseCount = new AtomicInteger(0);
-
-        this.incomingFrames = new IncomingFramesCapture();
 
         this.extensionFactory = new WebSocketExtensionFactory(policy,bufferPool);
         this.ioState = new IOState();
@@ -166,7 +249,7 @@
 
     public void clearCaptured()
     {
-        this.incomingFrames.clear();
+        frameReader.clear();
     }
 
     public void clearExtensions()
@@ -174,6 +257,7 @@
         extensions.clear();
     }
 
+    @Override
     public void close()
     {
         LOG.debug("close()");
@@ -182,22 +266,16 @@
 
     public void close(int statusCode, String message)
     {
+        LOG.debug("close({},{})",statusCode,message);
         CloseInfo close = new CloseInfo(statusCode,message);
 
-        ioState.onCloseLocal(close);
-
         if (!ioState.isClosed())
         {
-            WebSocketFrame frame = close.asFrame();
-            LOG.debug("Issuing: {}",frame);
-            try
-            {
-                write(frame);
-            }
-            catch (IOException e)
-            {
-                LOG.debug(e);
-            }
+            ioState.onCloseLocal(close);
+        }
+        else
+        {
+            LOG.debug("Not issuing close. ioState = {}",ioState);
         }
     }
 
@@ -222,6 +300,10 @@
         IO.close(in);
         IO.close(out);
         disconnectedLatch.countDown();
+        if (frameReader != null)
+        {
+            frameReader.interrupt();
+        }
         if (socket != null)
         {
             try
@@ -293,8 +375,12 @@
         extensionStack = new ExtensionStack(this.extensionFactory);
         extensionStack.negotiate(configs);
 
+        // Setup Frame Reader
+        this.frameReader = new FrameReadingThread();
+        this.frameReader.start();
+
         // Start with default routing
-        extensionStack.setNextIncoming(this); // the websocket layer
+        extensionStack.setNextIncoming(frameReader); // the websocket layer
         extensionStack.setNextOutgoing(outgoing); // the network layer
 
         // Configure Parser / Generator
@@ -331,6 +417,15 @@
         return connectionValue;
     }
 
+    public ExecutorService getExecutor()
+    {
+        if (executor == null)
+        {
+            executor = Executors.newCachedThreadPool();
+        }
+        return executor;
+    }
+
     private List<ExtensionConfig> getExtensionConfigs(HttpResponse response)
     {
         List<ExtensionConfig> configs = new ArrayList<>();
@@ -408,38 +503,6 @@
         return destWebsocketURI;
     }
 
-    /**
-     * Errors received (after extensions)
-     */
-    @Override
-    public void incomingError(Throwable e)
-    {
-        incomingFrames.incomingError(e);
-    }
-
-    /**
-     * Frames received (after extensions)
-     */
-    @Override
-    public void incomingFrame(Frame frame)
-    {
-        LOG.debug("incoming({})",frame);
-        int count = parseCount.incrementAndGet();
-        if ((count % 10) == 0)
-        {
-            LOG.info("Client parsed {} frames",count);
-        }
-
-        if (frame.getOpCode() == OpCode.CLOSE)
-        {
-            CloseInfo close = new CloseInfo(frame);
-            ioState.onCloseRemote(close);
-        }
-
-        WebSocketFrame copy = WebSocketFrame.copy(frame);
-        incomingFrames.incomingFrame(copy);
-    }
-
     public boolean isConnected()
     {
         return (socket != null) && (socket.isConnected());
@@ -448,6 +511,7 @@
     @Override
     public void onConnectionStateChange(ConnectionState state)
     {
+        LOG.debug("CLIENT onConnectionStateChange() - {}",state);
         switch (state)
         {
             case CLOSED:
@@ -455,10 +519,17 @@
                 // this.disconnect();
                 break;
             case CLOSING:
-                if (ioState.wasRemoteCloseInitiated())
+                CloseInfo close = ioState.getCloseInfo();
+
+                WebSocketFrame frame = close.asFrame();
+                LOG.debug("Issuing: {}",frame);
+                try
                 {
-                    CloseInfo close = ioState.getCloseInfo();
-                    close(close.getStatusCode(),close.getReason());
+                    write(frame);
+                }
+                catch (IOException e)
+                {
+                    LOG.debug(e);
                 }
                 break;
             default:
@@ -503,107 +574,41 @@
         }
     }
 
-    public int read(ByteBuffer buf) throws IOException
+    public EventQueue<WebSocketFrame> readFrames(int expectedFrameCount, int timeoutDuration, TimeUnit timeoutUnit) throws Exception
     {
-        if (eof)
-        {
-            throw new EOFException("Hit EOF");
-        }
-
-        if ((remainingBuffer != null) && (remainingBuffer.remaining() > 0))
-        {
-            return BufferUtil.put(remainingBuffer,buf);
-        }
-
-        int len = -1;
-        int b;
-        while ((in.available() > 0) && (buf.remaining() > 0))
-        {
-            b = in.read();
-            if (b == (-1))
-            {
-                eof = true;
-                break;
-            }
-            buf.put((byte)b);
-            len++;
-        }
-
-        return len;
-    }
-
-    public IncomingFramesCapture readFrames(int expectedCount, TimeUnit timeoutUnit, int timeoutDuration) throws IOException, TimeoutException
-    {
-        LOG.debug("Read: waiting for {} frame(s) from server",expectedCount);
-
-        ByteBuffer buf = bufferPool.acquire(BUFFER_SIZE,false);
-        BufferUtil.clearToFill(buf);
-        try
-        {
-            long msDur = TimeUnit.MILLISECONDS.convert(timeoutDuration,timeoutUnit);
-            long now = System.currentTimeMillis();
-            long expireOn = now + msDur;
-            LOG.debug("Now: {} - expireOn: {} ({} ms)",now,expireOn,msDur);
-
-            long iter = 0;
-
-            int len = 0;
-            while (incomingFrames.size() < expectedCount)
-            {
-                BufferUtil.clearToFill(buf);
-                len = read(buf);
-                if (len > 0)
-                {
-                    BufferUtil.flipToFlush(buf,0);
-                    if (LOG.isDebugEnabled())
-                    {
-                        LOG.debug("Read {} bytes: {}",len,BufferUtil.toDetailString(buf));
-                    }
-                    parser.parse(buf);
-                }
-                else
-                {
-                    if (LOG.isDebugEnabled())
-                    {
-                        iter++;
-                        if ((iter % 10000000) == 0)
-                        {
-                            LOG.debug("10,000,000 reads of zero length");
-                            iter = 0;
-                        }
-                    }
-                }
-
-                if (!debug && (System.currentTimeMillis() > expireOn))
-                {
-                    incomingFrames.dump();
-                    throw new TimeoutException(String.format("Timeout reading all %d expected frames. (managed to only read %d frame(s))",expectedCount,
-                            incomingFrames.size()));
-                }
-            }
-        }
-        finally
-        {
-            bufferPool.release(buf);
-        }
-
-        return incomingFrames;
+        frameReader.frames.awaitEventCount(expectedFrameCount,timeoutDuration,timeoutUnit);
+        return frameReader.frames;
     }
 
     public HttpResponse readResponseHeader() throws IOException
     {
         HttpResponse response = new HttpResponse();
-        HttpResponseHeaderParser parser = new HttpResponseHeaderParser(response);
+        HttpResponseHeaderParser respParser = new HttpResponseHeaderParser(response);
 
-        ByteBuffer buf = BufferUtil.allocate(512);
+        byte buf[] = new byte[512];
 
-        do
+        while (!eof)
         {
-            BufferUtil.flipToFill(buf);
-            read(buf);
-            BufferUtil.flipToFlush(buf,0);
+            int available = in.available();
+            int len = in.read(buf,0,Math.min(available,buf.length));
+            if (len < 0)
+            {
+                eof = true;
+                break;
+            }
+            else if (len > 0)
+            {
+                ByteBuffer bbuf = ByteBuffer.wrap(buf,0,len);
+                if (LOG.isDebugEnabled())
+                {
+                    LOG.debug("Read {} bytes: {}",len,BufferUtil.toDetailString(bbuf));
+                }
+                if (respParser.parse(bbuf) != null)
+                {
+                    break;
+                }
+            }
         }
-        while (parser.parse(buf) == null);
 
         remainingBuffer = response.getRemainingBuffer();
 
@@ -643,9 +648,9 @@
         this.connectionValue = connectionValue;
     }
 
-    public void setDebug(boolean flag)
+    public void setExecutor(ExecutorService executor)
     {
-        this.debug = flag;
+        this.executor = executor;
     }
 
     public void setProtocols(String protocols)
@@ -653,7 +658,7 @@
         this.protocols = protocols;
     }
 
-    public void setTimeout(TimeUnit unit, int duration)
+    public void setTimeout(int duration, TimeUnit unit)
     {
         this.timeout = (int)TimeUnit.MILLISECONDS.convert(duration,unit);
     }
@@ -701,19 +706,19 @@
     {
         if (!ioState.isOpen())
         {
+            LOG.debug("IO Not Open / Not Writing: {}",frame);
             return;
         }
         LOG.debug("write(Frame->{}) to {}",frame,outgoing);
         if (LOG.isDebugEnabled())
         {
-            frame.setMask(new byte[]
-            { 0x00, 0x00, 0x00, 0x00 });
+            frame.setMask(new byte[] { 0x00, 0x00, 0x00, 0x00 });
         }
         else
         {
             frame.setMask(clientmask);
         }
-        extensionStack.outgoingFrame(frame,null, BatchMode.OFF);
+        extensionStack.outgoingFrame(frame,null,BatchMode.OFF);
     }
 
     public void writeRaw(ByteBuffer buf) throws IOException
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/BlockheadServer.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/BlockheadServer.java
index c3cd1ad..1b665bd 100644
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/BlockheadServer.java
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/BlockheadServer.java
@@ -54,6 +54,7 @@
 import org.eclipse.jetty.websocket.api.extensions.Frame;
 import org.eclipse.jetty.websocket.api.extensions.IncomingFrames;
 import org.eclipse.jetty.websocket.api.extensions.OutgoingFrames;
+import org.eclipse.jetty.websocket.api.extensions.Frame.Type;
 import org.eclipse.jetty.websocket.common.AcceptHash;
 import org.eclipse.jetty.websocket.common.CloseInfo;
 import org.eclipse.jetty.websocket.common.Generator;
@@ -124,7 +125,6 @@
         {
             write(new CloseFrame());
             flush();
-            disconnect();
         }
 
         public void close(int statusCode) throws IOException
@@ -132,7 +132,6 @@
             CloseInfo close = new CloseInfo(statusCode);
             write(close.asFrame());
             flush();
-            disconnect();
         }
 
         public void disconnect()
@@ -229,6 +228,19 @@
                 CloseInfo close = new CloseInfo(frame);
                 LOG.debug("Close frame: {}",close);
             }
+
+            Type type = frame.getType();
+            if (echoing.get() && (type.isData() || type.isContinuation()))
+            {
+                try
+                {
+                    write(WebSocketFrame.copy(frame));
+                }
+                catch (IOException e)
+                {
+                    LOG.warn(e);
+                }
+            }
         }
 
         @Override
@@ -317,9 +329,18 @@
             return len;
         }
 
+        /**
+         * @deprecated use {@link #readFrames(int, int, TimeUnit)} for correct parameter order
+         */
+        @Deprecated
         public IncomingFramesCapture readFrames(int expectedCount, TimeUnit timeoutUnit, int timeoutDuration) throws IOException, TimeoutException
         {
-            LOG.debug("Read: waiting for {} frame(s) from server",expectedCount);
+            return readFrames(expectedCount,timeoutDuration,timeoutUnit);
+        }
+
+        public IncomingFramesCapture readFrames(int expectedCount, int timeoutDuration, TimeUnit timeoutUnit) throws IOException, TimeoutException
+        {
+            LOG.debug("Read: waiting for {} frame(s) from client",expectedCount);
             int startCount = incomingFrames.size();
 
             ByteBuffer buf = bufferPool.acquire(BUFFER_SIZE,false);
@@ -562,13 +583,22 @@
         public void write(Frame frame) throws IOException
         {
             LOG.debug("write(Frame->{}) to {}",frame,outgoing);
-            outgoing.outgoingFrame(frame,null, BatchMode.OFF);
+            outgoing.outgoingFrame(frame,null,BatchMode.OFF);
         }
 
         public void write(int b) throws IOException
         {
             getOutputStream().write(b);
         }
+
+        public void write(ByteBuffer buf) throws IOException
+        {
+            byte arr[] = BufferUtil.toArray(buf);
+            if ((arr != null) && (arr.length > 0))
+            {
+                getOutputStream().write(arr);
+            }
+        }
     }
 
     private static final Logger LOG = Log.getLogger(BlockheadServer.class);
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/Fuzzer.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/Fuzzer.java
index 3d6c641..1a72c89 100644
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/Fuzzer.java
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/Fuzzer.java
@@ -18,6 +18,8 @@
 
 package org.eclipse.jetty.websocket.common.test;
 
+import static org.hamcrest.Matchers.*;
+
 import java.io.IOException;
 import java.net.SocketException;
 import java.nio.ByteBuffer;
@@ -25,8 +27,8 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
 
+import org.eclipse.jetty.toolchain.test.EventQueue;
 import org.eclipse.jetty.util.BufferUtil;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
@@ -39,13 +41,10 @@
 import org.eclipse.jetty.websocket.common.io.IOState;
 import org.junit.Assert;
 
-import static org.hamcrest.Matchers.containsString;
-import static org.hamcrest.Matchers.is;
-
 /**
  * Fuzzing utility for the AB tests.
  */
-public class Fuzzer
+public class Fuzzer implements AutoCloseable
 {
     public static enum CloseState
     {
@@ -95,6 +94,7 @@
         policy.setIdleTimeout(5000);
 
         this.client = new BlockheadClient(policy,testcase.getServerURI());
+        this.client.setTimeout(2,TimeUnit.SECONDS);
         this.generator = testcase.getLaxGenerator();
         this.testname = testcase.getTestMethodName();
     }
@@ -117,8 +117,14 @@
         buf.flip();
         return buf;
     }
+    
+    @Override
+    public void close() throws Exception
+    {
+        this.client.disconnect();
+    }
 
-    public void close()
+    public void disconnect()
     {
         this.client.disconnect();
     }
@@ -134,28 +140,24 @@
         }
     }
 
-    public void expect(List<WebSocketFrame> expect) throws IOException, TimeoutException
+    public void expect(List<WebSocketFrame> expect) throws Exception
     {
-        expect(expect,TimeUnit.SECONDS,10);
+        expect(expect,10,TimeUnit.SECONDS);
     }
 
-    public void expect(List<WebSocketFrame> expect, TimeUnit unit, int duration) throws IOException, TimeoutException
+    public void expect(List<WebSocketFrame> expect, int duration, TimeUnit unit) throws Exception
     {
         int expectedCount = expect.size();
         LOG.debug("expect() {} frame(s)",expect.size());
 
         // Read frames
-        IncomingFramesCapture capture = client.readFrames(expect.size(),unit,duration);
-        if (LOG.isDebugEnabled())
-        {
-            capture.dump();
-        }
-
+        EventQueue<WebSocketFrame> frames = client.readFrames(expect.size(),duration,unit);
+        
         String prefix = "";
         for (int i = 0; i < expectedCount; i++)
         {
             WebSocketFrame expected = expect.get(i);
-            WebSocketFrame actual = capture.getFrames().poll();
+            WebSocketFrame actual = frames.poll();
 
             prefix = "Frame[" + i + "]";
 
@@ -177,7 +179,7 @@
         }
     }
 
-    public void expect(WebSocketFrame expect) throws IOException, TimeoutException
+    public void expect(WebSocketFrame expect) throws Exception
     {
         expect(Collections.singletonList(expect));
     }
@@ -187,23 +189,6 @@
         // TODO Should test for no more frames. success if connection closed.
     }
 
-    public void expectServerDisconnect(DisconnectMode mode)
-    {
-        client.expectServerDisconnect();
-        IOState ios = client.getIOState();
-
-        switch (mode)
-        {
-            case CLEAN:
-                Assert.assertTrue(ios.wasRemoteCloseInitiated());
-                Assert.assertTrue(ios.wasCleanClose());
-                break;
-            case UNCLEAN:
-                Assert.assertTrue(ios.wasRemoteCloseInitiated());
-                break;
-        }
-    }
-
     public CloseState getCloseState()
     {
         IOState ios = client.getIOState();
diff --git a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketServerFactory.java b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketServerFactory.java
index bebe7c0..f6a2f2c 100644
--- a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketServerFactory.java
+++ b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketServerFactory.java
@@ -44,6 +44,7 @@
 import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
 import org.eclipse.jetty.util.thread.Scheduler;
 import org.eclipse.jetty.websocket.api.InvalidWebSocketException;
+import org.eclipse.jetty.websocket.api.StatusCode;
 import org.eclipse.jetty.websocket.api.WebSocketException;
 import org.eclipse.jetty.websocket.api.WebSocketPolicy;
 import org.eclipse.jetty.websocket.api.extensions.ExtensionFactory;
@@ -199,11 +200,23 @@
         }
     }
 
-    protected void closeAllConnections()
+    protected void shutdownAllConnections()
     {
         for (WebSocketSession session : openSessions)
         {
-            session.close();
+            if (session.getConnection() != null)
+            {
+                try
+                {
+                    session.getConnection().close(
+                            StatusCode.SHUTDOWN,
+                            "Shutdown");
+                }
+                catch (Throwable t)
+                {
+                    LOG.debug("During Shutdown All Connections",t);
+                }
+            }
         }
         openSessions.clear();
     }
@@ -269,7 +282,7 @@
     @Override
     protected void doStop() throws Exception
     {
-        closeAllConnections();
+        shutdownAllConnections();
         super.doStop();
     }
 
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/AnnotatedMaxMessageSizeTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/AnnotatedMaxMessageSizeTest.java
index b07597e..e6ca4dc 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/AnnotatedMaxMessageSizeTest.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/AnnotatedMaxMessageSizeTest.java
@@ -28,6 +28,7 @@
 
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.toolchain.test.EventQueue;
 import org.eclipse.jetty.util.log.StacklessLogging;
 import org.eclipse.jetty.websocket.api.StatusCode;
 import org.eclipse.jetty.websocket.common.CloseInfo;
@@ -36,7 +37,6 @@
 import org.eclipse.jetty.websocket.common.WebSocketFrame;
 import org.eclipse.jetty.websocket.common.frames.TextFrame;
 import org.eclipse.jetty.websocket.common.test.BlockheadClient;
-import org.eclipse.jetty.websocket.common.test.IncomingFramesCapture;
 import org.eclipse.jetty.websocket.server.examples.echo.BigEchoSocket;
 import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
 import org.junit.AfterClass;
@@ -100,8 +100,8 @@
             client.write(new TextFrame().setPayload(msg));
 
             // Read frame (hopefully text frame)
-            IncomingFramesCapture capture = client.readFrames(1,TimeUnit.MILLISECONDS,500);
-            WebSocketFrame tf = capture.getFrames().poll();
+            EventQueue<WebSocketFrame> frames = client.readFrames(1,500,TimeUnit.MILLISECONDS);
+            WebSocketFrame tf = frames.poll();
             Assert.assertThat("Text Frame.status code",tf.getPayloadAsUTF8(),is(msg));
         }
         finally
@@ -127,8 +127,8 @@
             client.write(new TextFrame().setPayload(ByteBuffer.wrap(buf)));
 
             // Read frame (hopefully close frame saying its too large)
-            IncomingFramesCapture capture = client.readFrames(1,TimeUnit.MILLISECONDS,500);
-            WebSocketFrame tf = capture.getFrames().poll();
+            EventQueue<WebSocketFrame> frames = client.readFrames(1,500,TimeUnit.MILLISECONDS);
+            WebSocketFrame tf = frames.poll();
             Assert.assertThat("Frame is close", tf.getOpCode(), is(OpCode.CLOSE));
             CloseInfo close = new CloseInfo(tf);
             Assert.assertThat("Close Code", close.getStatusCode(), is(StatusCode.MESSAGE_TOO_LARGE));
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ChromeTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ChromeTest.java
index 1abe09b..38ab890 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ChromeTest.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ChromeTest.java
@@ -22,19 +22,17 @@
 
 import java.util.concurrent.TimeUnit;
 
+import org.eclipse.jetty.toolchain.test.EventQueue;
 import org.eclipse.jetty.websocket.common.WebSocketFrame;
 import org.eclipse.jetty.websocket.common.frames.TextFrame;
 import org.eclipse.jetty.websocket.common.test.BlockheadClient;
 import org.eclipse.jetty.websocket.common.test.HttpResponse;
-import org.eclipse.jetty.websocket.common.test.IncomingFramesCapture;
 import org.eclipse.jetty.websocket.server.examples.MyEchoServlet;
 import org.junit.AfterClass;
 import org.junit.Assert;
 import org.junit.BeforeClass;
-import org.junit.Ignore;
 import org.junit.Test;
 
-@Ignore("Bug 395444")
 public class ChromeTest
 {
     private static SimpleServletServer server;
@@ -70,8 +68,8 @@
             client.write(new TextFrame().setPayload(msg));
 
             // Read frame (hopefully text frame)
-            IncomingFramesCapture capture = client.readFrames(1,TimeUnit.MILLISECONDS,500);
-            WebSocketFrame tf = capture.getFrames().poll();
+            EventQueue<WebSocketFrame> frames = client.readFrames(1,500,TimeUnit.MILLISECONDS);
+            WebSocketFrame tf = frames.poll();
             Assert.assertThat("Text Frame.status code",tf.getPayloadAsUTF8(),is(msg));
         }
         finally
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/FirefoxTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/FirefoxTest.java
index 46dbdd9..771cdd4 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/FirefoxTest.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/FirefoxTest.java
@@ -18,20 +18,20 @@
 
 package org.eclipse.jetty.websocket.server;
 
+import static org.hamcrest.Matchers.*;
+
 import java.util.concurrent.TimeUnit;
 
+import org.eclipse.jetty.toolchain.test.EventQueue;
 import org.eclipse.jetty.websocket.common.WebSocketFrame;
 import org.eclipse.jetty.websocket.common.frames.TextFrame;
 import org.eclipse.jetty.websocket.common.test.BlockheadClient;
-import org.eclipse.jetty.websocket.common.test.IncomingFramesCapture;
 import org.eclipse.jetty.websocket.server.examples.MyEchoServlet;
 import org.junit.AfterClass;
 import org.junit.Assert;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
-import static org.hamcrest.Matchers.is;
-
 public class FirefoxTest
 {
     private static SimpleServletServer server;
@@ -65,8 +65,8 @@
             client.write(new TextFrame().setPayload(msg));
 
             // Read frame (hopefully text frame)
-            IncomingFramesCapture capture = client.readFrames(1, TimeUnit.MILLISECONDS, 500);
-            WebSocketFrame tf = capture.getFrames().poll();
+            EventQueue<WebSocketFrame> frames = client.readFrames(1, 500, TimeUnit.MILLISECONDS);
+            WebSocketFrame tf = frames.poll();
             Assert.assertThat("Text Frame.status code", tf.getPayloadAsUTF8(), is(msg));
         }
     }
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/FragmentExtensionTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/FragmentExtensionTest.java
index 045b96c..be15bac 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/FragmentExtensionTest.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/FragmentExtensionTest.java
@@ -22,11 +22,11 @@
 
 import java.util.concurrent.TimeUnit;
 
+import org.eclipse.jetty.toolchain.test.EventQueue;
 import org.eclipse.jetty.websocket.common.WebSocketFrame;
 import org.eclipse.jetty.websocket.common.frames.TextFrame;
 import org.eclipse.jetty.websocket.common.test.BlockheadClient;
 import org.eclipse.jetty.websocket.common.test.HttpResponse;
-import org.eclipse.jetty.websocket.common.test.IncomingFramesCapture;
 import org.eclipse.jetty.websocket.server.helper.EchoServlet;
 import org.junit.AfterClass;
 import org.junit.Assert;
@@ -79,7 +79,7 @@
         try
         {
             // Make sure the read times out if there are problems with the implementation
-            client.setTimeout(TimeUnit.SECONDS,1);
+            client.setTimeout(1,TimeUnit.SECONDS);
             client.connect();
             client.sendStandardRequest();
             HttpResponse resp = client.expectUpgradeResponse();
@@ -90,10 +90,10 @@
             client.write(new TextFrame().setPayload(msg));
 
             String parts[] = split(msg,fragSize);
-            IncomingFramesCapture capture = client.readFrames(parts.length,TimeUnit.MILLISECONDS,1000);
+            EventQueue<WebSocketFrame> frames = client.readFrames(parts.length,1000,TimeUnit.MILLISECONDS);
             for (int i = 0; i < parts.length; i++)
             {
-                WebSocketFrame frame = capture.getFrames().poll();
+                WebSocketFrame frame = frames.poll();
                 Assert.assertThat("text[" + i + "].payload",frame.getPayloadAsUTF8(),is(parts[i]));
             }
         }
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/FrameCompressionExtensionTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/FrameCompressionExtensionTest.java
index 607ba24..8811cc6 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/FrameCompressionExtensionTest.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/FrameCompressionExtensionTest.java
@@ -22,11 +22,11 @@
 
 import java.util.concurrent.TimeUnit;
 
+import org.eclipse.jetty.toolchain.test.EventQueue;
 import org.eclipse.jetty.websocket.common.WebSocketFrame;
 import org.eclipse.jetty.websocket.common.frames.TextFrame;
 import org.eclipse.jetty.websocket.common.test.BlockheadClient;
 import org.eclipse.jetty.websocket.common.test.HttpResponse;
-import org.eclipse.jetty.websocket.common.test.IncomingFramesCapture;
 import org.eclipse.jetty.websocket.server.helper.EchoServlet;
 import org.junit.AfterClass;
 import org.junit.Assert;
@@ -61,7 +61,7 @@
         try
         {
             // Make sure the read times out if there are problems with the implementation
-            client.setTimeout(TimeUnit.SECONDS,1);
+            client.setTimeout(1,TimeUnit.SECONDS);
             client.connect();
             client.sendStandardRequest();
             HttpResponse resp = client.expectUpgradeResponse();
@@ -73,8 +73,8 @@
             // Client sends first message
             client.write(new TextFrame().setPayload(msg));
 
-            IncomingFramesCapture capture = client.readFrames(1,TimeUnit.MILLISECONDS,1000);
-            WebSocketFrame frame = capture.getFrames().poll();
+            EventQueue<WebSocketFrame> frames = client.readFrames(1,1000,TimeUnit.MILLISECONDS);
+            WebSocketFrame frame = frames.poll();
             Assert.assertThat("TEXT.payload",frame.getPayloadAsUTF8(),is(msg.toString()));
 
             // Client sends second message
@@ -82,8 +82,8 @@
             msg = "There";
             client.write(new TextFrame().setPayload(msg));
 
-            capture = client.readFrames(1,TimeUnit.SECONDS,1);
-            frame = capture.getFrames().poll();
+            frames = client.readFrames(1,1,TimeUnit.SECONDS);
+            frame = frames.poll();
             Assert.assertThat("TEXT.payload",frame.getPayloadAsUTF8(),is(msg.toString()));
         }
         finally
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/IdentityExtensionTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/IdentityExtensionTest.java
index c0ca20f..5c12713 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/IdentityExtensionTest.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/IdentityExtensionTest.java
@@ -22,11 +22,11 @@
 
 import java.util.concurrent.TimeUnit;
 
+import org.eclipse.jetty.toolchain.test.EventQueue;
 import org.eclipse.jetty.websocket.common.WebSocketFrame;
 import org.eclipse.jetty.websocket.common.frames.TextFrame;
 import org.eclipse.jetty.websocket.common.test.BlockheadClient;
 import org.eclipse.jetty.websocket.common.test.HttpResponse;
-import org.eclipse.jetty.websocket.common.test.IncomingFramesCapture;
 import org.eclipse.jetty.websocket.server.helper.EchoServlet;
 import org.junit.AfterClass;
 import org.junit.Assert;
@@ -64,7 +64,7 @@
         try
         {
             // Make sure the read times out if there are problems with the implementation
-            client.setTimeout(TimeUnit.SECONDS,1);
+            client.setTimeout(1,TimeUnit.SECONDS);
             client.connect();
             client.sendStandardRequest();
             HttpResponse resp = client.expectUpgradeResponse();
@@ -73,8 +73,8 @@
 
             client.write(new TextFrame().setPayload("Hello"));
 
-            IncomingFramesCapture capture = client.readFrames(1,TimeUnit.MILLISECONDS,1000);
-            WebSocketFrame frame = capture.getFrames().poll();
+            EventQueue<WebSocketFrame> frames = client.readFrames(1,1000,TimeUnit.MILLISECONDS);
+            WebSocketFrame frame = frames.poll();
             Assert.assertThat("TEXT.payload",frame.getPayloadAsUTF8(),is("Hello"));
         }
         finally
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/IdleTimeoutTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/IdleTimeoutTest.java
index 7a6998b..4b36004 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/IdleTimeoutTest.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/IdleTimeoutTest.java
@@ -18,14 +18,22 @@
 
 package org.eclipse.jetty.websocket.server;
 
+import static org.hamcrest.Matchers.*;
+
 import java.util.concurrent.TimeUnit;
 
+import org.eclipse.jetty.toolchain.test.EventQueue;
+import org.eclipse.jetty.websocket.api.StatusCode;
+import org.eclipse.jetty.websocket.common.CloseInfo;
+import org.eclipse.jetty.websocket.common.OpCode;
+import org.eclipse.jetty.websocket.common.WebSocketFrame;
 import org.eclipse.jetty.websocket.common.frames.TextFrame;
 import org.eclipse.jetty.websocket.common.test.BlockheadClient;
 import org.eclipse.jetty.websocket.server.helper.RFCSocket;
 import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
 import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
 import org.junit.AfterClass;
+import org.junit.Assert;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
@@ -65,7 +73,7 @@
     {
         BlockheadClient client = new BlockheadClient(server.getServerUri());
         client.setProtocols("onConnect");
-        client.setTimeout(TimeUnit.MILLISECONDS,2500);
+        client.setTimeout(2500,TimeUnit.MILLISECONDS);
         try
         {
             client.connect();
@@ -83,8 +91,13 @@
             // The server could not read this frame, if it is in this half closed state
             client.write(new TextFrame().setPayload("Hello"));
 
-            // Expect server to be disconnected at this point
-            client.expectServerDisconnect();
+            // Expect server to have closed due to its own timeout
+            EventQueue<WebSocketFrame> frames = client.readFrames(1,500,TimeUnit.MILLISECONDS);
+            WebSocketFrame frame = frames.poll();
+            Assert.assertThat("frame opcode",frame.getOpCode(),is(OpCode.CLOSE));
+            CloseInfo close = new CloseInfo(frame);
+            Assert.assertThat("close code",close.getStatusCode(),is(StatusCode.SHUTDOWN));
+            Assert.assertThat("close reason",close.getReason(),containsString("Timeout"));
         }
         finally
         {
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/PerMessageDeflateExtensionTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/PerMessageDeflateExtensionTest.java
index 4747e8a..d06dd75 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/PerMessageDeflateExtensionTest.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/PerMessageDeflateExtensionTest.java
@@ -18,22 +18,21 @@
 
 package org.eclipse.jetty.websocket.server;
 
+import static org.hamcrest.Matchers.*;
+
 import java.util.concurrent.TimeUnit;
 
+import org.eclipse.jetty.toolchain.test.EventQueue;
 import org.eclipse.jetty.websocket.common.WebSocketFrame;
 import org.eclipse.jetty.websocket.common.frames.TextFrame;
 import org.eclipse.jetty.websocket.common.test.BlockheadClient;
 import org.eclipse.jetty.websocket.common.test.HttpResponse;
-import org.eclipse.jetty.websocket.common.test.IncomingFramesCapture;
 import org.eclipse.jetty.websocket.server.helper.EchoServlet;
 import org.junit.AfterClass;
 import org.junit.Assert;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
-import static org.hamcrest.Matchers.containsString;
-import static org.hamcrest.Matchers.is;
-
 public class PerMessageDeflateExtensionTest
 {
     private static SimpleServletServer server;
@@ -65,7 +64,7 @@
         try
         {
             // Make sure the read times out if there are problems with the implementation
-            client.setTimeout(TimeUnit.SECONDS,1);
+            client.setTimeout(1,TimeUnit.SECONDS);
             client.connect();
             client.sendStandardRequest();
             HttpResponse resp = client.expectUpgradeResponse();
@@ -77,8 +76,8 @@
             // Client sends first message
             client.write(new TextFrame().setPayload(msg));
 
-            IncomingFramesCapture capture = client.readFrames(1,TimeUnit.MILLISECONDS,1000);
-            WebSocketFrame frame = capture.getFrames().poll();
+            EventQueue<WebSocketFrame> frames = client.readFrames(1,1000,TimeUnit.MILLISECONDS);
+            WebSocketFrame frame = frames.poll();
             Assert.assertThat("TEXT.payload",frame.getPayloadAsUTF8(),is(msg.toString()));
 
             // Client sends second message
@@ -86,8 +85,8 @@
             msg = "There";
             client.write(new TextFrame().setPayload(msg));
 
-            capture = client.readFrames(1,TimeUnit.SECONDS,1);
-            frame = capture.getFrames().poll();
+            frames = client.readFrames(1,1,TimeUnit.SECONDS);
+            frame = frames.poll();
             Assert.assertThat("TEXT.payload",frame.getPayloadAsUTF8(),is(msg.toString()));
         }
         finally
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/RequestHeadersTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/RequestHeadersTest.java
index 6cc9950..4726858 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/RequestHeadersTest.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/RequestHeadersTest.java
@@ -107,7 +107,7 @@
     public void testAccessRequestCookies() throws Exception
     {
         BlockheadClient client = new BlockheadClient(server.getServerUri());
-        client.setTimeout(TimeUnit.SECONDS,1);
+        client.setTimeout(1,TimeUnit.SECONDS);
 
         try
         {
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketCloseTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketCloseTest.java
index 0c2ddb3..0ec98a3 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketCloseTest.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketCloseTest.java
@@ -25,6 +25,7 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
+import org.eclipse.jetty.toolchain.test.EventQueue;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 import org.eclipse.jetty.util.log.StacklessLogging;
@@ -36,7 +37,6 @@
 import org.eclipse.jetty.websocket.common.WebSocketFrame;
 import org.eclipse.jetty.websocket.common.events.AbstractEventDriver;
 import org.eclipse.jetty.websocket.common.test.BlockheadClient;
-import org.eclipse.jetty.websocket.common.test.IncomingFramesCapture;
 import org.eclipse.jetty.websocket.server.helper.RFCSocket;
 import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest;
 import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse;
@@ -114,7 +114,7 @@
         public void onWebSocketConnect(Session sess)
         {
             LOG.debug("onWebSocketConnect({})",sess);
-            sess.close();
+            sess.close(StatusCode.NORMAL,"FastCloseServer");
         }
     }
 
@@ -129,14 +129,10 @@
         public void onWebSocketConnect(Session sess)
         {
             LOG.debug("onWebSocketConnect({})",sess);
+            // Test failure due to unhandled exception
+            // this should trigger a fast-fail closure during open/connect
             throw new RuntimeException("Intentional FastFail");
         }
-
-        @Override
-        public void onWebSocketError(Throwable cause)
-        {
-            errors.add(cause);
-        }
     }
 
     private static final Logger LOG = Log.getLogger(WebSocketCloseTest.class);
@@ -163,30 +159,28 @@
     @Test
     public void testFastClose() throws Exception
     {
-        BlockheadClient client = new BlockheadClient(server.getServerUri());
-        client.setProtocols("fastclose");
-        client.setTimeout(TimeUnit.SECONDS,1);
-        try
+        try (BlockheadClient client = new BlockheadClient(server.getServerUri()))
         {
+            client.setProtocols("fastclose");
+            client.setTimeout(1,TimeUnit.SECONDS);
             client.connect();
             client.sendStandardRequest();
             client.expectUpgradeResponse();
 
-            IncomingFramesCapture capture = client.readFrames(1,TimeUnit.SECONDS,1);
-            WebSocketFrame frame = capture.getFrames().poll();
+            // Verify that client got close frame
+            EventQueue<WebSocketFrame> frames = client.readFrames(1,1,TimeUnit.SECONDS);
+            WebSocketFrame frame = frames.poll();
             Assert.assertThat("frames[0].opcode",frame.getOpCode(),is(OpCode.CLOSE));
             CloseInfo close = new CloseInfo(frame);
             Assert.assertThat("Close Status Code",close.getStatusCode(),is(StatusCode.NORMAL));
-
+            
+            // Notify server of close handshake
             client.write(close.asFrame()); // respond with close
-
+            
+            // ensure server socket got close event
             Assert.assertThat("Fast Close Latch",closeSocket.closeLatch.await(1,TimeUnit.SECONDS),is(true));
             Assert.assertThat("Fast Close.statusCode",closeSocket.closeStatusCode,is(StatusCode.NORMAL));
         }
-        finally
-        {
-            client.close();
-        }
     }
 
     /**
@@ -195,33 +189,29 @@
     @Test
     public void testFastFail() throws Exception
     {
-        BlockheadClient client = new BlockheadClient(server.getServerUri());
-        client.setProtocols("fastfail");
-        client.setTimeout(TimeUnit.SECONDS,1);
-        try
+        try (BlockheadClient client = new BlockheadClient(server.getServerUri()))
         {
+            client.setProtocols("fastfail");
+            client.setTimeout(1,TimeUnit.SECONDS);
             try (StacklessLogging scope = new StacklessLogging(AbstractEventDriver.class))
             {
                 client.connect();
                 client.sendStandardRequest();
                 client.expectUpgradeResponse();
 
-                IncomingFramesCapture capture = client.readFrames(1,TimeUnit.SECONDS,1);
-                WebSocketFrame frame = capture.getFrames().poll();
+                EventQueue<WebSocketFrame> frames = client.readFrames(1,1,TimeUnit.SECONDS);
+                WebSocketFrame frame = frames.poll();
                 Assert.assertThat("frames[0].opcode",frame.getOpCode(),is(OpCode.CLOSE));
                 CloseInfo close = new CloseInfo(frame);
                 Assert.assertThat("Close Status Code",close.getStatusCode(),is(StatusCode.SERVER_ERROR));
 
                 client.write(close.asFrame()); // respond with close
 
+                // ensure server socket got close event
                 Assert.assertThat("Fast Fail Latch",closeSocket.closeLatch.await(1,TimeUnit.SECONDS),is(true));
                 Assert.assertThat("Fast Fail.statusCode",closeSocket.closeStatusCode,is(StatusCode.SERVER_ERROR));
                 Assert.assertThat("Fast Fail.errors",closeSocket.errors.size(),is(1));
             }
         }
-        finally
-        {
-            client.close();
-        }
     }
 }
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketServerSessionTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketServerSessionTest.java
index 8b3732b..4d3568b 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketServerSessionTest.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketServerSessionTest.java
@@ -18,15 +18,16 @@
 
 package org.eclipse.jetty.websocket.server;
 
+import static org.hamcrest.Matchers.*;
+
 import java.net.URI;
-import java.util.Queue;
 import java.util.concurrent.TimeUnit;
 
 import org.eclipse.jetty.toolchain.test.AdvancedRunner;
+import org.eclipse.jetty.toolchain.test.EventQueue;
 import org.eclipse.jetty.websocket.common.WebSocketFrame;
 import org.eclipse.jetty.websocket.common.frames.TextFrame;
 import org.eclipse.jetty.websocket.common.test.BlockheadClient;
-import org.eclipse.jetty.websocket.common.test.IncomingFramesCapture;
 import org.eclipse.jetty.websocket.server.helper.SessionServlet;
 import org.junit.AfterClass;
 import org.junit.Assert;
@@ -34,8 +35,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import static org.hamcrest.Matchers.is;
-
 /**
  * Testing various aspects of the server side support for WebSocket {@link Session}
  */
@@ -90,8 +89,7 @@
             client.write(new TextFrame().setPayload("getParameterMap|cost")); // intentionally invalid
 
             // Read frame (hopefully text frame)
-            IncomingFramesCapture capture = client.readFrames(4, TimeUnit.SECONDS, 5);
-            Queue<WebSocketFrame> frames = capture.getFrames();
+            EventQueue<WebSocketFrame> frames = client.readFrames(4,5,TimeUnit.SECONDS);
             WebSocketFrame tf = frames.poll();
             Assert.assertThat("Parameter Map[snack]", tf.getPayloadAsUTF8(), is("[cashews]"));
             tf = frames.poll();
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketServletRFCTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketServletRFCTest.java
index fe27a9d..7071838 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketServletRFCTest.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketServletRFCTest.java
@@ -25,6 +25,7 @@
 import java.util.concurrent.TimeUnit;
 
 import org.eclipse.jetty.toolchain.test.AdvancedRunner;
+import org.eclipse.jetty.toolchain.test.EventQueue;
 import org.eclipse.jetty.util.Utf8Appendable.NotUtf8Exception;
 import org.eclipse.jetty.util.Utf8StringBuilder;
 import org.eclipse.jetty.util.log.StacklessLogging;
@@ -41,7 +42,6 @@
 import org.eclipse.jetty.websocket.common.frames.ContinuationFrame;
 import org.eclipse.jetty.websocket.common.frames.TextFrame;
 import org.eclipse.jetty.websocket.common.test.BlockheadClient;
-import org.eclipse.jetty.websocket.common.test.IncomingFramesCapture;
 import org.eclipse.jetty.websocket.common.test.UnitGenerator;
 import org.eclipse.jetty.websocket.common.util.Hex;
 import org.eclipse.jetty.websocket.server.helper.RFCServlet;
@@ -116,8 +116,8 @@
             client.write(bin); // write buf3 (fin=true)
 
             // Read frame echo'd back (hopefully a single binary frame)
-            IncomingFramesCapture capture = client.readFrames(1,TimeUnit.MILLISECONDS,1000);
-            Frame binmsg = capture.getFrames().poll();
+            EventQueue<WebSocketFrame> frames = client.readFrames(1,1000,TimeUnit.MILLISECONDS);
+            Frame binmsg = frames.poll();
             int expectedSize = buf1.length + buf2.length + buf3.length;
             Assert.assertThat("BinaryFrame.payloadLength",binmsg.getPayloadLength(),is(expectedSize));
 
@@ -182,8 +182,8 @@
             client.write(new TextFrame().setPayload(msg));
 
             // Read frame (hopefully text frame)
-            IncomingFramesCapture capture = client.readFrames(1,TimeUnit.MILLISECONDS,500);
-            WebSocketFrame tf = capture.getFrames().poll();
+            EventQueue<WebSocketFrame> frames = client.readFrames(1,500,TimeUnit.MILLISECONDS);
+            WebSocketFrame tf = frames.poll();
             Assert.assertThat("Text Frame.status code",tf.getPayloadAsUTF8(),is(msg));
         }
         finally
@@ -215,8 +215,8 @@
                 client.write(new TextFrame().setPayload("CRASH"));
 
                 // Read frame (hopefully close frame)
-                IncomingFramesCapture capture = client.readFrames(1,TimeUnit.MILLISECONDS,500);
-                Frame cf = capture.getFrames().poll();
+                EventQueue<WebSocketFrame> frames = client.readFrames(1,500,TimeUnit.MILLISECONDS);
+                Frame cf = frames.poll();
                 CloseInfo close = new CloseInfo(cf);
                 Assert.assertThat("Close Frame.status code",close.getStatusCode(),is(StatusCode.SERVER_ERROR));
             }
@@ -261,8 +261,8 @@
             client.write(new TextFrame().setPayload(msg));
 
             // Read frame (hopefully text frame)
-            IncomingFramesCapture capture = client.readFrames(1,TimeUnit.MILLISECONDS,500);
-            WebSocketFrame tf = capture.getFrames().poll();
+            EventQueue<WebSocketFrame> frames = client.readFrames(1,500,TimeUnit.MILLISECONDS);
+            WebSocketFrame tf = frames.poll();
             Assert.assertThat("Text Frame.status code",tf.getPayloadAsUTF8(),is(msg));
         }
         finally
@@ -294,8 +294,8 @@
             client.writeRaw(bbHeader);
             client.writeRaw(txt.getPayload());
 
-            IncomingFramesCapture capture = client.readFrames(1,TimeUnit.SECONDS,1);
-            WebSocketFrame frame = capture.getFrames().poll();
+            EventQueue<WebSocketFrame> frames = client.readFrames(1,1,TimeUnit.SECONDS);
+            WebSocketFrame frame = frames.poll();
             Assert.assertThat("frames[0].opcode",frame.getOpCode(),is(OpCode.CLOSE));
             CloseInfo close = new CloseInfo(frame);
             Assert.assertThat("Close Status Code",close.getStatusCode(),is(StatusCode.BAD_PAYLOAD));
@@ -340,8 +340,8 @@
             client.write(new TextFrame().setPayload(msg));
 
             // Read frame (hopefully text frame)
-            IncomingFramesCapture capture = client.readFrames(1,TimeUnit.MILLISECONDS,500);
-            WebSocketFrame tf = capture.getFrames().poll();
+            EventQueue<WebSocketFrame> frames = client.readFrames(1,500,TimeUnit.MILLISECONDS);
+            WebSocketFrame tf = frames.poll();
             Assert.assertThat("Text Frame.status code",tf.getPayloadAsUTF8(),is(msg));
         }
         finally
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase1.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase1.java
index 20ff642..502fb80 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase1.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase1.java
@@ -48,18 +48,12 @@
         expect.add(new TextFrame());
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
-            fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
-        }
-        finally
-        {
-            fuzzer.close();
         }
     }
 
@@ -81,18 +75,12 @@
         expect.add(new TextFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
-            fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
-        }
-        finally
-        {
-            fuzzer.close();
         }
     }
 
@@ -114,18 +102,12 @@
         expect.add(new TextFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
-            fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
-        }
-        finally
-        {
-            fuzzer.close();
         }
     }
 
@@ -147,18 +129,12 @@
         expect.add(new TextFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
-            fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
-        }
-        finally
-        {
-            fuzzer.close();
         }
     }
 
@@ -180,18 +156,12 @@
         expect.add(new TextFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
-            fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
-        }
-        finally
-        {
-            fuzzer.close();
         }
     }
 
@@ -213,18 +183,12 @@
         expect.add(new TextFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
-            fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
-        }
-        finally
-        {
-            fuzzer.close();
         }
     }
 
@@ -246,18 +210,12 @@
         expect.add(new TextFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
-            fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
-        }
-        finally
-        {
-            fuzzer.close();
         }
     }
 
@@ -284,19 +242,13 @@
         expect.add(new TextFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(SendMode.SLOW);
             fuzzer.setSlowSendSegmentSize(segmentSize);
             fuzzer.send(send);
             fuzzer.expect(expect);
-            fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
-        }
-        finally
-        {
-            fuzzer.close();
         }
     }
 
@@ -314,18 +266,12 @@
         expect.add(new BinaryFrame());
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
-            fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
-        }
-        finally
-        {
-            fuzzer.close();
         }
     }
 
@@ -347,18 +293,12 @@
         expect.add(new BinaryFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
-            fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
-        }
-        finally
-        {
-            fuzzer.close();
         }
     }
 
@@ -380,18 +320,12 @@
         expect.add(new BinaryFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
-            fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
-        }
-        finally
-        {
-            fuzzer.close();
         }
     }
 
@@ -413,18 +347,12 @@
         expect.add(new BinaryFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
-            fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
-        }
-        finally
-        {
-            fuzzer.close();
         }
     }
 
@@ -446,18 +374,12 @@
         expect.add(new BinaryFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
-            fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
-        }
-        finally
-        {
-            fuzzer.close();
         }
     }
 
@@ -479,18 +401,12 @@
         expect.add(new BinaryFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
-            fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
-        }
-        finally
-        {
-            fuzzer.close();
         }
     }
 
@@ -512,18 +428,12 @@
         expect.add(new BinaryFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
-            fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
-        }
-        finally
-        {
-            fuzzer.close();
         }
     }
 
@@ -550,19 +460,13 @@
         expect.add(new BinaryFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(SendMode.SLOW);
             fuzzer.setSlowSendSegmentSize(segmentSize);
             fuzzer.send(send);
             fuzzer.expect(expect);
-            fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
-        }
-        finally
-        {
-            fuzzer.close();
         }
     }
 }
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase2.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase2.java
index 0e43806..4a1a1c1 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase2.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase2.java
@@ -50,18 +50,13 @@
 
         WebSocketFrame expect = new PongFrame();
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -89,18 +84,13 @@
         send.add(new CloseInfo(StatusCode.NORMAL).asFrame());
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -128,8 +118,7 @@
         send.add(new CloseInfo(StatusCode.NORMAL).asFrame());
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.SLOW);
@@ -137,10 +126,6 @@
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -159,18 +144,13 @@
         expect.add(new PongFrame().setPayload(copyOf(payload)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -179,8 +159,7 @@
     @Test
     public void testCase2_3() throws Exception
     {
-        byte payload[] = new byte[]
-        { 0x00, (byte)0xFF, (byte)0xFE, (byte)0xFD, (byte)0xFC, (byte)0xFB, 0x00, (byte)0xFF };
+        byte payload[] = new byte[] { 0x00, (byte)0xFF, (byte)0xFE, (byte)0xFD, (byte)0xFC, (byte)0xFB, 0x00, (byte)0xFF };
 
         List<WebSocketFrame> send = new ArrayList<>();
         send.add(new PingFrame().setPayload(payload));
@@ -190,18 +169,13 @@
         expect.add(new PongFrame().setPayload(copyOf(payload)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -221,18 +195,13 @@
         expect.add(new PongFrame().setPayload(copyOf(payload)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -241,32 +210,26 @@
     @Test
     public void testCase2_5() throws Exception
     {
-        try(StacklessLogging scope = new StacklessLogging(Parser.class))
+        try (StacklessLogging scope = new StacklessLogging(Parser.class))
         {
             byte payload[] = new byte[126]; // intentionally too big
             Arrays.fill(payload,(byte)'5');
             ByteBuffer buf = ByteBuffer.wrap(payload);
-    
+
             List<WebSocketFrame> send = new ArrayList<>();
             // trick websocket frame into making extra large payload for ping
             send.add(new BadFrame(OpCode.PING).setPayload(buf));
             send.add(new CloseInfo(StatusCode.NORMAL,"Test 2.5").asFrame());
-    
+
             List<WebSocketFrame> expect = new ArrayList<>();
             expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
-    
-            Fuzzer fuzzer = new Fuzzer(this);
-            try
+
+            try (Fuzzer fuzzer = new Fuzzer(this))
             {
                 fuzzer.connect();
                 fuzzer.setSendMode(Fuzzer.SendMode.BULK);
                 fuzzer.send(send);
                 fuzzer.expect(expect);
-                fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
-            }
-            finally
-            {
-                fuzzer.close();
             }
         }
     }
@@ -288,8 +251,7 @@
         expect.add(new PongFrame().setPayload(copyOf(payload)));
         expect.add(new CloseInfo(StatusCode.NORMAL,"Test 2.6").asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.SLOW);
@@ -297,10 +259,6 @@
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -316,18 +274,13 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -343,18 +296,13 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -372,17 +320,12 @@
         expect.add(new PongFrame().setPayload("our ping")); // our pong
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 }
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase3.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase3.java
index 4b5373b..de9ec69 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase3.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase3.java
@@ -51,18 +51,13 @@
 
         WebSocketFrame expect = new CloseInfo(StatusCode.PROTOCOL).asFrame();
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging logging = new StacklessLogging(Parser.class))
+        try (Fuzzer fuzzer = new Fuzzer(this); StacklessLogging logging = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -80,18 +75,13 @@
         expect.add(new TextFrame().setPayload("small")); // echo on good frame
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging logging = new StacklessLogging(Parser.class))
+        try (Fuzzer fuzzer = new Fuzzer(this); StacklessLogging logging = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -109,18 +99,13 @@
         expect.add(new TextFrame().setPayload("small")); // echo on good frame
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging logging = new StacklessLogging(Parser.class))
+        try (Fuzzer fuzzer = new Fuzzer(this); StacklessLogging logging = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.PER_FRAME);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -138,8 +123,7 @@
         expect.add(new TextFrame().setPayload("small")); // echo on good frame
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging logging = new StacklessLogging(Parser.class))
+        try (Fuzzer fuzzer = new Fuzzer(this); StacklessLogging logging = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.SLOW);
@@ -147,10 +131,6 @@
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -168,18 +148,13 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging logging = new StacklessLogging(Parser.class))
+        try (Fuzzer fuzzer = new Fuzzer(this); StacklessLogging logging = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -197,18 +172,13 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging logging = new StacklessLogging(Parser.class))
+        try (Fuzzer fuzzer = new Fuzzer(this); StacklessLogging logging = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -230,17 +200,12 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging logging = new StacklessLogging(Parser.class))
+        try (Fuzzer fuzzer = new Fuzzer(this); StacklessLogging logging = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 }
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase4.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase4.java
index ffb6961..61f59ad 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase4.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase4.java
@@ -53,18 +53,13 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging logging = new StacklessLogging(Parser.class))
+        try (Fuzzer fuzzer = new Fuzzer(this); StacklessLogging logging = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -82,18 +77,13 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging logging = new StacklessLogging(Parser.class))
+        try (Fuzzer fuzzer = new Fuzzer(this); StacklessLogging logging = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -111,18 +101,13 @@
         expect.add(new TextFrame().setPayload("hello")); // echo
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging logging = new StacklessLogging(Parser.class))
+        try (Fuzzer fuzzer = new Fuzzer(this); StacklessLogging logging = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -132,7 +117,7 @@
     public void testCase4_1_4() throws Exception
     {
         ByteBuffer buf = ByteBuffer.wrap(StringUtil.getUtf8Bytes("bad"));
-        
+
         List<WebSocketFrame> send = new ArrayList<>();
         send.add(new TextFrame().setPayload("hello"));
         send.add(new BadFrame((byte)6).setPayload(buf)); // intentionally bad
@@ -142,18 +127,13 @@
         expect.add(new TextFrame().setPayload("hello")); // echo
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging logging = new StacklessLogging(Parser.class))
+        try (Fuzzer fuzzer = new Fuzzer(this); StacklessLogging logging = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -163,7 +143,7 @@
     public void testCase4_1_5() throws Exception
     {
         ByteBuffer buf = ByteBuffer.wrap(StringUtil.getUtf8Bytes("bad"));
-        
+
         List<WebSocketFrame> send = new ArrayList<>();
         send.add(new TextFrame().setPayload("hello"));
         send.add(new BadFrame((byte)7).setPayload(buf)); // intentionally bad
@@ -173,18 +153,13 @@
         expect.add(new TextFrame().setPayload("hello")); // echo
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging logging = new StacklessLogging(Parser.class))
+        try (Fuzzer fuzzer = new Fuzzer(this); StacklessLogging logging = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -199,18 +174,13 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging logging = new StacklessLogging(Parser.class))
+        try (Fuzzer fuzzer = new Fuzzer(this); StacklessLogging logging = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -220,25 +190,20 @@
     public void testCase4_2_2() throws Exception
     {
         ByteBuffer buf = ByteBuffer.wrap(StringUtil.getUtf8Bytes("bad"));
-        
+
         List<WebSocketFrame> send = new ArrayList<>();
         send.add(new BadFrame((byte)12).setPayload(buf)); // intentionally bad
 
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging logging = new StacklessLogging(Parser.class))
+        try (Fuzzer fuzzer = new Fuzzer(this); StacklessLogging logging = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -256,18 +221,13 @@
         expect.add(new TextFrame().setPayload("hello")); // echo
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging logging = new StacklessLogging(Parser.class))
+        try (Fuzzer fuzzer = new Fuzzer(this); StacklessLogging logging = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -277,7 +237,7 @@
     public void testCase4_2_4() throws Exception
     {
         ByteBuffer buf = ByteBuffer.wrap(StringUtil.getUtf8Bytes("bad"));
-        
+
         List<WebSocketFrame> send = new ArrayList<>();
         send.add(new TextFrame().setPayload("hello"));
         send.add(new BadFrame((byte)14).setPayload(buf)); // intentionally bad
@@ -287,18 +247,13 @@
         expect.add(new TextFrame().setPayload("hello")); // echo
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging logging = new StacklessLogging(Parser.class))
+        try (Fuzzer fuzzer = new Fuzzer(this); StacklessLogging logging = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -308,7 +263,7 @@
     public void testCase4_2_5() throws Exception
     {
         ByteBuffer buf = ByteBuffer.wrap(StringUtil.getUtf8Bytes("bad"));
-        
+
         List<WebSocketFrame> send = new ArrayList<>();
         send.add(new TextFrame().setPayload("hello"));
         send.add(new BadFrame((byte)15).setPayload(buf)); // intentionally bad
@@ -318,17 +273,12 @@
         expect.add(new TextFrame().setPayload("hello")); // echo
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging logging = new StacklessLogging(Parser.class))
+        try (Fuzzer fuzzer = new Fuzzer(this); StacklessLogging logging = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 }
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase5.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase5.java
index c957fc9..c6349a3 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase5.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase5.java
@@ -57,19 +57,14 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging supress = new StacklessLogging(Parser.class))
+        try(Fuzzer fuzzer = new Fuzzer(this);StacklessLogging supress = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
-    }
+            }
 
     /**
      * Send continuation+fin, then text+fin (framewise)
@@ -85,18 +80,13 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging supress = new StacklessLogging(Parser.class))
+        try(Fuzzer fuzzer = new Fuzzer(this);StacklessLogging supress = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.PER_FRAME);
             fuzzer.sendAndIgnoreBrokenPipe(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -113,8 +103,7 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging supress = new StacklessLogging(Parser.class))
+        try(Fuzzer fuzzer = new Fuzzer(this);StacklessLogging supress = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.SLOW);
@@ -122,10 +111,6 @@
             fuzzer.sendAndIgnoreBrokenPipe(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -142,18 +127,13 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging supress = new StacklessLogging(Parser.class))
+        try(Fuzzer fuzzer = new Fuzzer(this);StacklessLogging supress = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.sendAndIgnoreBrokenPipe(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -170,18 +150,13 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging supress = new StacklessLogging(Parser.class))
+        try(Fuzzer fuzzer = new Fuzzer(this);StacklessLogging supress = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.PER_FRAME);
             fuzzer.sendAndIgnoreBrokenPipe(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -198,8 +173,7 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging supress = new StacklessLogging(Parser.class))
+        try(Fuzzer fuzzer = new Fuzzer(this);StacklessLogging supress = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.SLOW);
@@ -207,10 +181,6 @@
             fuzzer.sendAndIgnoreBrokenPipe(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -230,18 +200,13 @@
         expect.add(new TextFrame().setPayload("fragment1fragment2"));
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging supress = new StacklessLogging(Parser.class))
+        try(Fuzzer fuzzer = new Fuzzer(this);StacklessLogging supress = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -262,18 +227,13 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging supress = new StacklessLogging(Parser.class))
+        try(Fuzzer fuzzer = new Fuzzer(this);StacklessLogging supress = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.sendAndIgnoreBrokenPipe(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -294,18 +254,13 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging supress = new StacklessLogging(Parser.class))
+        try(Fuzzer fuzzer = new Fuzzer(this);StacklessLogging supress = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -322,18 +277,13 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging supress = new StacklessLogging(Parser.class))
+        try(Fuzzer fuzzer = new Fuzzer(this);StacklessLogging supress = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -365,8 +315,7 @@
         expect2.add(new TextFrame().setPayload("f1,f2,f3,f4,f5"));
         expect2.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging supress = new StacklessLogging(Parser.class))
+        try(Fuzzer fuzzer = new Fuzzer(this);StacklessLogging supress = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
@@ -382,10 +331,6 @@
             fuzzer.send(send2);
             fuzzer.expect(expect2);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -402,18 +347,13 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging supress = new StacklessLogging(Parser.class))
+        try(Fuzzer fuzzer = new Fuzzer(this);StacklessLogging supress = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -442,8 +382,7 @@
         expect2.add(new TextFrame().setPayload("f1,f2,f3,f4,f5"));
         expect2.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging supress = new StacklessLogging(Parser.class))
+        try(Fuzzer fuzzer = new Fuzzer(this);StacklessLogging supress = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.PER_FRAME);
@@ -456,10 +395,6 @@
             fuzzer.send(send2);
             fuzzer.expect(expect2);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -488,8 +423,7 @@
         expect2.add(new TextFrame().setPayload("f1,f2,f3,f4,f5"));
         expect2.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging supress = new StacklessLogging(Parser.class))
+        try(Fuzzer fuzzer = new Fuzzer(this);StacklessLogging supress = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.SLOW);
@@ -503,10 +437,6 @@
             fuzzer.send(send2);
             fuzzer.expect(expect2);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -524,18 +454,13 @@
         expect.add(new TextFrame().setPayload("hello, world"));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging supress = new StacklessLogging(Parser.class))
+        try(Fuzzer fuzzer = new Fuzzer(this);StacklessLogging supress = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -553,18 +478,13 @@
         expect.add(new TextFrame().setPayload("hello, world"));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging supress = new StacklessLogging(Parser.class))
+        try(Fuzzer fuzzer = new Fuzzer(this);StacklessLogging supress = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.PER_FRAME);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -582,8 +502,7 @@
         expect.add(new TextFrame().setPayload("hello, world"));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging supress = new StacklessLogging(Parser.class))
+        try(Fuzzer fuzzer = new Fuzzer(this);StacklessLogging supress = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.SLOW);
@@ -591,10 +510,6 @@
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -614,18 +529,13 @@
         expect.add(new TextFrame().setPayload("hello, world"));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging supress = new StacklessLogging(Parser.class))
+        try(Fuzzer fuzzer = new Fuzzer(this);StacklessLogging supress = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -645,18 +555,13 @@
         expect.add(new TextFrame().setPayload("hello, world"));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging supress = new StacklessLogging(Parser.class))
+        try(Fuzzer fuzzer = new Fuzzer(this);StacklessLogging supress = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.PER_FRAME);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -676,8 +581,7 @@
         expect.add(new TextFrame().setPayload("hello, world"));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging supress = new StacklessLogging(Parser.class))
+        try(Fuzzer fuzzer = new Fuzzer(this);StacklessLogging supress = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.SLOW);
@@ -685,10 +589,6 @@
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -706,17 +606,12 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging supress = new StacklessLogging(Parser.class))
+        try(Fuzzer fuzzer = new Fuzzer(this);StacklessLogging supress = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 }
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase6.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase6.java
index 6ead418..c11aebf 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase6.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase6.java
@@ -90,18 +90,13 @@
         expect.add(new TextFrame());
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -120,18 +115,13 @@
         expect.add(new TextFrame());
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -150,18 +140,13 @@
         expect.add(new TextFrame().setPayload("middle"));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -172,7 +157,7 @@
     {
         String utf1 = "Hello-\uC2B5@\uC39F\uC3A4";
         String utf2 = "\uC3BC\uC3A0\uC3A1-UTF-8!!";
-        
+
         ByteBuffer b1 = ByteBuffer.wrap(StringUtil.getUtf8Bytes(utf1));
         ByteBuffer b2 = ByteBuffer.wrap(StringUtil.getUtf8Bytes(utf2));
 
@@ -189,18 +174,13 @@
         expect.add(new TextFrame().setPayload(e1));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -220,18 +200,13 @@
         expect.add(new TextFrame().setPayload(ByteBuffer.wrap(msg)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -250,18 +225,13 @@
         expect.add(new TextFrame().setPayload(ByteBuffer.wrap(msg)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -279,18 +249,13 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.BAD_PAYLOAD).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -311,8 +276,7 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.BAD_PAYLOAD).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
@@ -325,10 +289,6 @@
 
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -351,8 +311,7 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.BAD_PAYLOAD).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
@@ -363,10 +322,6 @@
             fuzzer.send(new ContinuationFrame().setPayload(ByteBuffer.wrap(part3)).setFin(true));
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -393,15 +348,13 @@
             List<WebSocketFrame> expect = new ArrayList<>();
             expect.add(new CloseInfo(StatusCode.BAD_PAYLOAD).asFrame());
 
-            Fuzzer fuzzer = new Fuzzer(this);
-            try
+            try (Fuzzer fuzzer = new Fuzzer(this))
             {
                 fuzzer.connect();
 
                 ByteBuffer net = fuzzer.asNetworkBuffer(send);
 
-                int splits[] =
-                { 17, 21, net.limit() };
+                int splits[] = { 17, 21, net.limit() };
 
                 ByteBuffer part1 = net.slice(); // Header + good UTF
                 part1.limit(splits[0]);
@@ -419,12 +372,6 @@
                 fuzzer.send(part3); // the rest (shouldn't work)
 
                 fuzzer.expect(expect);
-
-                fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.UNCLEAN);
-            }
-            finally
-            {
-                fuzzer.close();
             }
         }
     }
@@ -445,8 +392,7 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.BAD_PAYLOAD).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging scope = new StacklessLogging(Parser.class))
+        try (Fuzzer fuzzer = new Fuzzer(this); StacklessLogging scope = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
 
@@ -460,9 +406,5 @@
 
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 }
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase6_BadUTF.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase6_BadUTF.java
index 7c61776..3ff1643 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase6_BadUTF.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase6_BadUTF.java
@@ -163,18 +163,15 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.BAD_PAYLOAD).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging supress = new StacklessLogging(Parser.class))
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
-            fuzzer.connect();
-            fuzzer.setSendMode(Fuzzer.SendMode.BULK);
-            fuzzer.send(send);
-            fuzzer.expect(expect);
-            fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.UNCLEAN);
-        }
-        finally
-        {
-            fuzzer.close();
+            try (StacklessLogging supress = new StacklessLogging(Parser.class))
+            {
+                fuzzer.connect();
+                fuzzer.setSendMode(Fuzzer.SendMode.BULK);
+                fuzzer.send(send);
+                fuzzer.expect(expect);
+            }
         }
     }
 }
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase6_GoodUTF.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase6_GoodUTF.java
index 8eebe6e..f5e9cbe 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase6_GoodUTF.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase6_GoodUTF.java
@@ -139,17 +139,12 @@
         expect.add(new TextFrame().setPayload(clone(msg)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 }
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase7.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase7.java
index 8c60551..b8ad71b 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase7.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase7.java
@@ -64,18 +64,13 @@
         expect.add(new TextFrame().setPayload("Hello World"));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -91,8 +86,7 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
@@ -100,10 +94,6 @@
             fuzzer.expect(expect);
             fuzzer.expectNoMoreFrames();
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -119,8 +109,7 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
@@ -128,10 +117,6 @@
             fuzzer.expect(expect);
             fuzzer.expectNoMoreFrames();
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -147,8 +132,7 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
@@ -156,10 +140,6 @@
             fuzzer.expect(expect);
             fuzzer.expectNoMoreFrames();
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -176,8 +156,7 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
@@ -185,10 +164,6 @@
             fuzzer.expect(expect);
             fuzzer.expectNoMoreFrames();
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -210,8 +185,7 @@
         expect.add(new TextFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
@@ -219,10 +193,6 @@
             fuzzer.expect(expect);
             fuzzer.expectNoMoreFrames();
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -237,8 +207,7 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
@@ -246,10 +215,6 @@
             fuzzer.expect(expect);
             fuzzer.expectNoMoreFrames();
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -258,8 +223,7 @@
     @Test
     public void testCase7_3_2() throws Exception
     {
-        byte payload[] = new byte[]
-        { 0x00 };
+        byte payload[] = new byte[] { 0x00 };
         ByteBuffer buf = ByteBuffer.wrap(payload);
 
         List<WebSocketFrame> send = new ArrayList<>();
@@ -268,8 +232,7 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging scope = new StacklessLogging(Parser.class))
+        try (Fuzzer fuzzer = new Fuzzer(this); StacklessLogging scope = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
@@ -277,10 +240,6 @@
             fuzzer.expect(expect);
             fuzzer.expectNoMoreFrames();
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -295,8 +254,7 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
@@ -304,10 +262,6 @@
             fuzzer.expect(expect);
             fuzzer.expectNoMoreFrames();
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -322,8 +276,7 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.NORMAL,"Hic").asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
@@ -331,10 +284,6 @@
             fuzzer.expect(expect);
             fuzzer.expectNoMoreFrames();
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -353,8 +302,7 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.NORMAL,reason).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging logging = new StacklessLogging(AbstractWebSocketConnection.class))
+        try (Fuzzer fuzzer = new Fuzzer(this); StacklessLogging logging = new StacklessLogging(AbstractWebSocketConnection.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
@@ -362,10 +310,6 @@
             fuzzer.expect(expect);
             fuzzer.expectNoMoreFrames();
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -390,8 +334,7 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.BAD_PAYLOAD).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging scope = new StacklessLogging(Parser.class,CloseInfo.class))
+        try (Fuzzer fuzzer = new Fuzzer(this); StacklessLogging scope = new StacklessLogging(Parser.class,CloseInfo.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
@@ -399,9 +342,5 @@
             fuzzer.expect(expect);
             fuzzer.expectNoMoreFrames();
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 }
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase7_BadStatusCodes.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase7_BadStatusCodes.java
index b3626f1..c76e174 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase7_BadStatusCodes.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase7_BadStatusCodes.java
@@ -98,8 +98,7 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
@@ -107,10 +106,6 @@
             fuzzer.expect(expect);
             fuzzer.expectNoMoreFrames();
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -131,8 +126,7 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
@@ -140,9 +134,5 @@
             fuzzer.expect(expect);
             fuzzer.expectNoMoreFrames();
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 }
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase7_GoodStatusCodes.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase7_GoodStatusCodes.java
index 425f657..5e8da15 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase7_GoodStatusCodes.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase7_GoodStatusCodes.java
@@ -93,8 +93,7 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseFrame().setPayload(clone(payload)));
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
@@ -102,10 +101,6 @@
             fuzzer.expect(expect);
             fuzzer.expectNoMoreFrames();
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -125,8 +120,7 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseFrame().setPayload(clone(payload)));
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
@@ -134,9 +128,5 @@
             fuzzer.expect(expect);
             fuzzer.expectNoMoreFrames();
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 }
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase9.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase9.java
index 3950394..274e91d 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase9.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase9.java
@@ -96,17 +96,12 @@
         expect.add(toDataFrame(opcode).setPayload(copyOf(msg)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
-            fuzzer.expect(expect,TimeUnit.SECONDS,8);
-        }
-        finally
-        {
-            fuzzer.close();
+            fuzzer.expect(expect,8,TimeUnit.SECONDS);
         }
     }
 
@@ -124,18 +119,13 @@
         expect.add(toDataFrame(opcode).setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.SLOW);
             fuzzer.setSlowSendSegmentSize(segmentSize);
             fuzzer.send(send);
-            fuzzer.expect(expect,TimeUnit.SECONDS,8);
-        }
-        finally
-        {
-            fuzzer.close();
+            fuzzer.expect(expect,8,TimeUnit.SECONDS);
         }
     }
 
@@ -157,18 +147,13 @@
         expect.add(new TextFrame().setPayload(msg));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -189,25 +174,19 @@
         expect.add(new TextFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
      * Echo 1MB text message (1 frame)
      */
     @Test
-    @Stress("High I/O use")
     public void testCase9_1_3() throws Exception
     {
         byte utf[] = new byte[1 * MBYTE];
@@ -222,17 +201,12 @@
         expect.add(new TextFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
-            fuzzer.expect(expect,TimeUnit.SECONDS,4);
-        }
-        finally
-        {
-            fuzzer.close();
+            fuzzer.expect(expect,4,TimeUnit.SECONDS);
         }
     }
 
@@ -240,7 +214,6 @@
      * Echo 4MB text message (1 frame)
      */
     @Test
-    @Stress("High I/O use")
     public void testCase9_1_4() throws Exception
     {
         byte utf[] = new byte[4 * MBYTE];
@@ -255,17 +228,12 @@
         expect.add(new TextFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
-            fuzzer.expect(expect,TimeUnit.SECONDS,8);
-        }
-        finally
-        {
-            fuzzer.close();
+            fuzzer.expect(expect,8,TimeUnit.SECONDS);
         }
     }
 
@@ -288,17 +256,12 @@
         expect.add(new TextFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
-            fuzzer.expect(expect,TimeUnit.SECONDS,16);
-        }
-        finally
-        {
-            fuzzer.close();
+            fuzzer.expect(expect,16,TimeUnit.SECONDS);
         }
     }
 
@@ -321,17 +284,12 @@
         expect.add(new TextFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
-            fuzzer.expect(expect,TimeUnit.SECONDS,32);
-        }
-        finally
-        {
-            fuzzer.close();
+            fuzzer.expect(expect,32,TimeUnit.SECONDS);
         }
     }
 
@@ -352,18 +310,13 @@
         expect.add(new BinaryFrame().setPayload(copyOf(data)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -384,18 +337,13 @@
         expect.add(new BinaryFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -417,17 +365,12 @@
         expect.add(new BinaryFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
-            fuzzer.expect(expect,TimeUnit.SECONDS,4);
-        }
-        finally
-        {
-            fuzzer.close();
+            fuzzer.expect(expect,4,TimeUnit.SECONDS);
         }
     }
 
@@ -450,17 +393,12 @@
         expect.add(new BinaryFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
-            fuzzer.expect(expect,TimeUnit.SECONDS,8);
-        }
-        finally
-        {
-            fuzzer.close();
+            fuzzer.expect(expect,8,TimeUnit.SECONDS);
         }
     }
 
@@ -483,17 +421,12 @@
         expect.add(new BinaryFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
-            fuzzer.expect(expect,TimeUnit.SECONDS,16);
-        }
-        finally
-        {
-            fuzzer.close();
+            fuzzer.expect(expect,16,TimeUnit.SECONDS);
         }
     }
 
@@ -516,17 +449,12 @@
         expect.add(new BinaryFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
-            fuzzer.expect(expect,TimeUnit.SECONDS,32);
-        }
-        finally
-        {
-            fuzzer.close();
+            fuzzer.expect(expect,32,TimeUnit.SECONDS);
         }
     }
 
diff --git a/jetty-websocket/websocket-server/src/test/resources/jetty-logging.properties b/jetty-websocket/websocket-server/src/test/resources/jetty-logging.properties
index 994503c..fda5d88 100644
--- a/jetty-websocket/websocket-server/src/test/resources/jetty-logging.properties
+++ b/jetty-websocket/websocket-server/src/test/resources/jetty-logging.properties
@@ -4,7 +4,7 @@
 # org.eclipse.jetty.io.WriteFlusher.LEVEL=DEBUG
 # org.eclipse.jetty.websocket.LEVEL=DEBUG
 # org.eclipse.jetty.websocket.LEVEL=INFO
-# org.eclipse.jetty.websocket.common.io.LEVEL=DEBUG
+org.eclipse.jetty.websocket.common.io.LEVEL=DEBUG
 # org.eclipse.jetty.websocket.server.ab.LEVEL=DEBUG
 # org.eclipse.jetty.websocket.common.Parser.LEVEL=DEBUG
 # org.eclipse.jetty.websocket.common.Generator.LEVEL=DEBUG
diff --git a/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/SessionRenewTest.java b/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/SessionRenewTest.java
index e629638..9a8d616 100644
--- a/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/SessionRenewTest.java
+++ b/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/SessionRenewTest.java
@@ -21,11 +21,30 @@
 import java.io.File;
 
 import org.eclipse.jetty.server.SessionManager;
+import org.eclipse.jetty.util.IO;
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Test;
 
 public class SessionRenewTest extends AbstractSessionRenewTest
 {
-
+    File tmpDir;
+    
+    @Before
+    public void before() throws Exception
+    {
+        tmpDir = File.createTempFile("hash-session-renew-test", null);
+        tmpDir.delete();
+        tmpDir.mkdirs();
+        tmpDir.deleteOnExit();
+    }
+    
+    @After 
+    public void after()
+    {
+        IO.delete(tmpDir);
+    }
+    
     @Override
     public AbstractTestServer createServer(int port, int max, int scavenge)
     {
@@ -37,9 +56,7 @@
             {
                 HashSessionManager sessionManager = (HashSessionManager)super.newSessionManager();
                 sessionManager.setSavePeriod(2);
-                File tmpDir = new File(System.getProperty("java.io.tmpdir"), "hash-session-renew-test");
-                tmpDir.deleteOnExit();
-                tmpDir.mkdirs();
+                
                 try
                 {
                     sessionManager.setStoreDirectory(tmpDir);